@mindstudio-ai/local-model-tunnel 0.5.7 → 0.5.8
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 +183 -11
- package/dist/chunk-C3JPRLSS.js +1485 -0
- package/dist/chunk-C3JPRLSS.js.map +1 -0
- package/dist/{chunk-KLOTDVWL.js → chunk-QALGC7T7.js} +56 -310
- package/dist/chunk-QALGC7T7.js.map +1 -0
- package/dist/chunk-WFQXIMTS.js +378 -0
- package/dist/chunk-WFQXIMTS.js.map +1 -0
- package/dist/cli.js +18 -2
- package/dist/cli.js.map +1 -1
- package/dist/headless.d.ts +113 -0
- package/dist/headless.js +8 -0
- package/dist/headless.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -2
- package/dist/{tui-UNFZSO7R.js → tui-4PJCFILV.js} +1467 -318
- package/dist/tui-4PJCFILV.js.map +1 -0
- package/package.json +3 -1
- package/dist/chunk-KLOTDVWL.js.map +0 -1
- package/dist/tui-UNFZSO7R.js.map +0 -1
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TunnelRunner,
|
|
3
|
-
deleteLocalInterfacePath,
|
|
4
3
|
detectAllProviderStatuses,
|
|
5
4
|
discoverAllModelsWithParameters,
|
|
5
|
+
requestEvents
|
|
6
|
+
} from "./chunk-QALGC7T7.js";
|
|
7
|
+
import {
|
|
8
|
+
DevProxy,
|
|
9
|
+
DevRunner,
|
|
10
|
+
deleteLocalInterfacePath,
|
|
11
|
+
detectAppConfig,
|
|
12
|
+
detectGitBranch,
|
|
13
|
+
devRequestEvents,
|
|
14
|
+
findDirsNeedingInstall,
|
|
6
15
|
getApiBaseUrl,
|
|
7
16
|
getApiKey,
|
|
8
17
|
getConfigPath,
|
|
@@ -12,15 +21,21 @@ import {
|
|
|
12
21
|
getLocalInterfacesDir,
|
|
13
22
|
getSyncedModels,
|
|
14
23
|
getUserId,
|
|
24
|
+
getWebInterfaceConfig,
|
|
25
|
+
getWebProjectDir,
|
|
26
|
+
initLoggerInteractive,
|
|
15
27
|
pollDeviceAuth,
|
|
28
|
+
readTableSources,
|
|
16
29
|
requestDeviceAuth,
|
|
17
|
-
requestEvents,
|
|
18
30
|
setApiKey,
|
|
19
31
|
setLocalInterfacePath,
|
|
20
32
|
setUserId,
|
|
33
|
+
stablePort,
|
|
21
34
|
syncModels,
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
syncSchema,
|
|
36
|
+
verifyApiKey,
|
|
37
|
+
watchTableFiles
|
|
38
|
+
} from "./chunk-C3JPRLSS.js";
|
|
24
39
|
|
|
25
40
|
// src/tui/index.tsx
|
|
26
41
|
import { render } from "ink";
|
|
@@ -30,8 +45,8 @@ import { get as httpsGet } from "https";
|
|
|
30
45
|
import { get as httpGet } from "http";
|
|
31
46
|
|
|
32
47
|
// src/tui/App.tsx
|
|
33
|
-
import { useEffect as
|
|
34
|
-
import { Box as
|
|
48
|
+
import { useEffect as useEffect20, useCallback as useCallback12, useState as useState21, useRef as useRef12 } from "react";
|
|
49
|
+
import { Box as Box14, useApp, useStdout as useStdout8 } from "ink";
|
|
35
50
|
|
|
36
51
|
// src/tui/components/Header.tsx
|
|
37
52
|
import { useState, useEffect, useMemo } from "react";
|
|
@@ -135,7 +150,7 @@ function Header({
|
|
|
135
150
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "white", children: "MindStudio Local Tunnel" }),
|
|
136
151
|
compact && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
137
152
|
" v",
|
|
138
|
-
"0.5.
|
|
153
|
+
"0.5.8"
|
|
139
154
|
] }),
|
|
140
155
|
environment !== "prod" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
141
156
|
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
@@ -153,7 +168,7 @@ function Header({
|
|
|
153
168
|
] }),
|
|
154
169
|
!compact && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
155
170
|
"v",
|
|
156
|
-
"0.5.
|
|
171
|
+
"0.5.8"
|
|
157
172
|
] })
|
|
158
173
|
] })
|
|
159
174
|
]
|
|
@@ -161,150 +176,11 @@ function Header({
|
|
|
161
176
|
);
|
|
162
177
|
}
|
|
163
178
|
|
|
164
|
-
// src/tui/components/NavigationMenu.tsx
|
|
165
|
-
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
166
|
-
import { Box as Box2, Text as Text2, useInput, useStdout } from "ink";
|
|
167
|
-
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
168
|
-
function NavigationMenu({
|
|
169
|
-
items,
|
|
170
|
-
onSelect,
|
|
171
|
-
title
|
|
172
|
-
}) {
|
|
173
|
-
const { stdout } = useStdout();
|
|
174
|
-
const compact = (stdout?.rows ?? 24) < 40;
|
|
175
|
-
const getDefaultIndex = () => {
|
|
176
|
-
const firstIdx = items.findIndex((i) => !i.disabled && !i.isSeparator);
|
|
177
|
-
return firstIdx >= 0 ? firstIdx : 0;
|
|
178
|
-
};
|
|
179
|
-
const [selectedIndex, setSelectedIndex] = useState2(getDefaultIndex);
|
|
180
|
-
useEffect2(() => {
|
|
181
|
-
setSelectedIndex(getDefaultIndex());
|
|
182
|
-
}, [items]);
|
|
183
|
-
const selectableItems = items.filter((i) => !i.isSeparator);
|
|
184
|
-
const findNextEnabled = (from, direction) => {
|
|
185
|
-
let idx = from;
|
|
186
|
-
for (let i = 0; i < items.length; i++) {
|
|
187
|
-
idx = (idx + direction + items.length) % items.length;
|
|
188
|
-
if (!items[idx].disabled && !items[idx].isSeparator) return idx;
|
|
189
|
-
}
|
|
190
|
-
return from;
|
|
191
|
-
};
|
|
192
|
-
useInput((input, key) => {
|
|
193
|
-
if (input === "q" || key.escape) {
|
|
194
|
-
const backItem = items.find((i) => i.id === "back");
|
|
195
|
-
if (backItem) {
|
|
196
|
-
onSelect("back");
|
|
197
|
-
} else if (input === "q") {
|
|
198
|
-
onSelect("quit");
|
|
199
|
-
}
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
if (key.upArrow || compact && key.leftArrow) {
|
|
203
|
-
setSelectedIndex((prev) => findNextEnabled(prev, -1));
|
|
204
|
-
} else if (key.downArrow || compact && key.rightArrow) {
|
|
205
|
-
setSelectedIndex((prev) => findNextEnabled(prev, 1));
|
|
206
|
-
} else if (key.return) {
|
|
207
|
-
const item = items[selectedIndex];
|
|
208
|
-
if (item && !item.disabled) {
|
|
209
|
-
onSelect(item.id);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
const hasBack = items.some((i) => i.id === "back");
|
|
214
|
-
if (compact) {
|
|
215
|
-
const selectedItem = items[selectedIndex];
|
|
216
|
-
return /* @__PURE__ */ jsx2(
|
|
217
|
-
Box2,
|
|
218
|
-
{
|
|
219
|
-
flexDirection: "column",
|
|
220
|
-
paddingX: 1,
|
|
221
|
-
borderStyle: "single",
|
|
222
|
-
borderTop: true,
|
|
223
|
-
borderBottom: false,
|
|
224
|
-
borderLeft: false,
|
|
225
|
-
borderRight: false,
|
|
226
|
-
borderColor: "gray",
|
|
227
|
-
children: /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", gap: 1, children: items.map((item, index) => {
|
|
228
|
-
if (item.isSeparator) return null;
|
|
229
|
-
const isSelected = index === selectedIndex;
|
|
230
|
-
return /* @__PURE__ */ jsx2(
|
|
231
|
-
Text2,
|
|
232
|
-
{
|
|
233
|
-
color: item.disabled ? "gray" : isSelected ? "cyan" : "white",
|
|
234
|
-
bold: isSelected,
|
|
235
|
-
wrap: "truncate-end",
|
|
236
|
-
children: isSelected ? `\u276F ${item.label}` : ` ${item.label}`
|
|
237
|
-
},
|
|
238
|
-
item.id
|
|
239
|
-
);
|
|
240
|
-
}) })
|
|
241
|
-
}
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
const separatorExtraLines = items.filter(
|
|
245
|
-
(item, idx) => item.isSeparator && idx > 0
|
|
246
|
-
).length;
|
|
247
|
-
const menuHeight = items.length + 4 + separatorExtraLines;
|
|
248
|
-
return /* @__PURE__ */ jsxs2(
|
|
249
|
-
Box2,
|
|
250
|
-
{
|
|
251
|
-
flexDirection: "column",
|
|
252
|
-
paddingX: 1,
|
|
253
|
-
marginBottom: 1,
|
|
254
|
-
borderStyle: "single",
|
|
255
|
-
borderTop: true,
|
|
256
|
-
borderBottom: false,
|
|
257
|
-
borderLeft: false,
|
|
258
|
-
borderRight: false,
|
|
259
|
-
borderColor: "gray",
|
|
260
|
-
children: [
|
|
261
|
-
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: title ?? "Actions" }) }),
|
|
262
|
-
/* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: items.map((item, index) => {
|
|
263
|
-
if (item.isSeparator) {
|
|
264
|
-
return /* @__PURE__ */ jsx2(Box2, { marginTop: index > 0 ? 1 : 0, children: item.label ? /* @__PURE__ */ jsx2(Text2, { bold: true, color: item.color ?? "gray", wrap: "truncate-end", children: item.label }) : null }, item.id);
|
|
265
|
-
}
|
|
266
|
-
const isSelected = index === selectedIndex;
|
|
267
|
-
const prefix = isSelected ? "\u276F" : " ";
|
|
268
|
-
if (item.disabled) {
|
|
269
|
-
return /* @__PURE__ */ jsx2(Box2, { height: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
|
|
270
|
-
prefix,
|
|
271
|
-
" ",
|
|
272
|
-
item.label,
|
|
273
|
-
item.disabledReason ? ` (${item.disabledReason})` : ""
|
|
274
|
-
] }) }, item.id);
|
|
275
|
-
}
|
|
276
|
-
return /* @__PURE__ */ jsxs2(Box2, { height: 1, overflow: "hidden", children: [
|
|
277
|
-
/* @__PURE__ */ jsxs2(
|
|
278
|
-
Text2,
|
|
279
|
-
{
|
|
280
|
-
color: isSelected ? "cyan" : "white",
|
|
281
|
-
bold: isSelected,
|
|
282
|
-
wrap: "truncate-end",
|
|
283
|
-
children: [
|
|
284
|
-
prefix,
|
|
285
|
-
" ",
|
|
286
|
-
item.label
|
|
287
|
-
]
|
|
288
|
-
}
|
|
289
|
-
),
|
|
290
|
-
isSelected && /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
|
|
291
|
-
" ",
|
|
292
|
-
"- ",
|
|
293
|
-
item.description
|
|
294
|
-
] })
|
|
295
|
-
] }, item.id);
|
|
296
|
-
}) }),
|
|
297
|
-
/* @__PURE__ */ jsx2(Box2, { marginTop: 1, height: 1, children: /* @__PURE__ */ jsx2(Text2, { color: "gray", wrap: "truncate-end", children: hasBack ? "Up/Down Navigate \u2022 Enter Select \u2022 q/Esc Back" : "Up/Down Navigate \u2022 Enter Select \u2022 q Quit" }) })
|
|
298
|
-
]
|
|
299
|
-
}
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
179
|
// src/tui/hooks/useConnection.ts
|
|
304
|
-
import { useState as
|
|
180
|
+
import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
|
|
305
181
|
function useConnection() {
|
|
306
|
-
const [status, setStatus] =
|
|
307
|
-
const [error, setError] =
|
|
182
|
+
const [status, setStatus] = useState2("connecting");
|
|
183
|
+
const [error, setError] = useState2(null);
|
|
308
184
|
const environment = getEnvironment();
|
|
309
185
|
const connect = useCallback(async () => {
|
|
310
186
|
setStatus("connecting");
|
|
@@ -327,7 +203,7 @@ function useConnection() {
|
|
|
327
203
|
setError(err instanceof Error ? err.message : "Connection failed");
|
|
328
204
|
}
|
|
329
205
|
}, []);
|
|
330
|
-
|
|
206
|
+
useEffect2(() => {
|
|
331
207
|
connect();
|
|
332
208
|
}, [connect]);
|
|
333
209
|
return {
|
|
@@ -339,12 +215,12 @@ function useConnection() {
|
|
|
339
215
|
}
|
|
340
216
|
|
|
341
217
|
// src/tui/interfaces/hooks/useEditorSessions.ts
|
|
342
|
-
import { useState as
|
|
218
|
+
import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2, useRef } from "react";
|
|
343
219
|
function useEditorSessions() {
|
|
344
|
-
const [sessions, setSessions] =
|
|
345
|
-
const [loading, setLoading] =
|
|
346
|
-
const [error, setError] =
|
|
347
|
-
const [refreshStatus, setRefreshStatus] =
|
|
220
|
+
const [sessions, setSessions] = useState3([]);
|
|
221
|
+
const [loading, setLoading] = useState3(true);
|
|
222
|
+
const [error, setError] = useState3(null);
|
|
223
|
+
const [refreshStatus, setRefreshStatus] = useState3("idle");
|
|
348
224
|
const initialLoadDone = useRef(false);
|
|
349
225
|
const timerRef = useRef();
|
|
350
226
|
const pollRef = useRef();
|
|
@@ -369,10 +245,10 @@ function useEditorSessions() {
|
|
|
369
245
|
setLoading(false);
|
|
370
246
|
}
|
|
371
247
|
}, []);
|
|
372
|
-
|
|
248
|
+
useEffect3(() => {
|
|
373
249
|
refresh();
|
|
374
250
|
}, [refresh]);
|
|
375
|
-
|
|
251
|
+
useEffect3(() => {
|
|
376
252
|
pollRef.current = setInterval(async () => {
|
|
377
253
|
if (!initialLoadDone.current) return;
|
|
378
254
|
try {
|
|
@@ -383,18 +259,18 @@ function useEditorSessions() {
|
|
|
383
259
|
}, 5e3);
|
|
384
260
|
return () => clearInterval(pollRef.current);
|
|
385
261
|
}, []);
|
|
386
|
-
|
|
262
|
+
useEffect3(() => {
|
|
387
263
|
return () => clearTimeout(timerRef.current);
|
|
388
264
|
}, []);
|
|
389
265
|
return { sessions, loading, error, refreshStatus, refresh };
|
|
390
266
|
}
|
|
391
267
|
|
|
392
268
|
// src/tui/models/hooks/useSetupProviders.ts
|
|
393
|
-
import { useState as
|
|
269
|
+
import { useState as useState4, useEffect as useEffect4, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
394
270
|
function useSetupProviders() {
|
|
395
|
-
const [providers, setProviders] =
|
|
396
|
-
const [loading, setLoading] =
|
|
397
|
-
const [refreshing, setRefreshing] =
|
|
271
|
+
const [providers, setProviders] = useState4([]);
|
|
272
|
+
const [loading, setLoading] = useState4(true);
|
|
273
|
+
const [refreshing, setRefreshing] = useState4(false);
|
|
398
274
|
const initialLoadDone = useRef2(false);
|
|
399
275
|
const refresh = useCallback3(async () => {
|
|
400
276
|
if (!initialLoadDone.current) {
|
|
@@ -408,19 +284,19 @@ function useSetupProviders() {
|
|
|
408
284
|
setLoading(false);
|
|
409
285
|
setRefreshing(false);
|
|
410
286
|
}, []);
|
|
411
|
-
|
|
287
|
+
useEffect4(() => {
|
|
412
288
|
refresh();
|
|
413
289
|
}, [refresh]);
|
|
414
290
|
return { providers, loading, refreshing, refresh };
|
|
415
291
|
}
|
|
416
292
|
|
|
417
293
|
// src/tui/models/hooks/useModels.ts
|
|
418
|
-
import { useState as
|
|
294
|
+
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback4, useRef as useRef3 } from "react";
|
|
419
295
|
function useModels() {
|
|
420
|
-
const [models, setModels] =
|
|
421
|
-
const [warnings, setWarnings] =
|
|
422
|
-
const [loading, setLoading] =
|
|
423
|
-
const [refreshing, setRefreshing] =
|
|
296
|
+
const [models, setModels] = useState5([]);
|
|
297
|
+
const [warnings, setWarnings] = useState5([]);
|
|
298
|
+
const [loading, setLoading] = useState5(true);
|
|
299
|
+
const [refreshing, setRefreshing] = useState5(false);
|
|
424
300
|
const initialLoadDone = useRef3(false);
|
|
425
301
|
const refresh = useCallback4(async () => {
|
|
426
302
|
if (!initialLoadDone.current) {
|
|
@@ -441,7 +317,7 @@ function useModels() {
|
|
|
441
317
|
setRefreshing(false);
|
|
442
318
|
}
|
|
443
319
|
}, []);
|
|
444
|
-
|
|
320
|
+
useEffect5(() => {
|
|
445
321
|
refresh();
|
|
446
322
|
}, [refresh]);
|
|
447
323
|
return {
|
|
@@ -454,11 +330,11 @@ function useModels() {
|
|
|
454
330
|
}
|
|
455
331
|
|
|
456
332
|
// src/tui/models/hooks/useRequests.ts
|
|
457
|
-
import { useState as
|
|
333
|
+
import { useState as useState6, useEffect as useEffect6, useCallback as useCallback5, useRef as useRef4 } from "react";
|
|
458
334
|
function useRequests(maxHistory = 50) {
|
|
459
|
-
const [requests, setRequests] =
|
|
335
|
+
const [requests, setRequests] = useState6([]);
|
|
460
336
|
const requestsRef = useRef4(/* @__PURE__ */ new Map());
|
|
461
|
-
|
|
337
|
+
useEffect6(() => {
|
|
462
338
|
const interval = setInterval(() => {
|
|
463
339
|
setRequests((prev) => {
|
|
464
340
|
const hasActive = prev.some((r) => r.status === "processing");
|
|
@@ -467,7 +343,7 @@ function useRequests(maxHistory = 50) {
|
|
|
467
343
|
}, 1e3);
|
|
468
344
|
return () => clearInterval(interval);
|
|
469
345
|
}, []);
|
|
470
|
-
|
|
346
|
+
useEffect6(() => {
|
|
471
347
|
const unsubStart = requestEvents.onStart((event) => {
|
|
472
348
|
const entry = {
|
|
473
349
|
id: event.id,
|
|
@@ -532,10 +408,10 @@ function useRequests(maxHistory = 50) {
|
|
|
532
408
|
}
|
|
533
409
|
|
|
534
410
|
// src/tui/models/hooks/useRegisteredModels.ts
|
|
535
|
-
import { useState as
|
|
411
|
+
import { useState as useState7, useEffect as useEffect7, useCallback as useCallback6 } from "react";
|
|
536
412
|
function useSyncedModels(connectionStatus) {
|
|
537
|
-
const [syncedNames, setSyncedNames] =
|
|
538
|
-
const [syncedModels, setSyncedModels] =
|
|
413
|
+
const [syncedNames, setSyncedNames] = useState7(/* @__PURE__ */ new Set());
|
|
414
|
+
const [syncedModels, setSyncedModels] = useState7([]);
|
|
539
415
|
const refresh = useCallback6(async () => {
|
|
540
416
|
if (connectionStatus !== "connected") {
|
|
541
417
|
setSyncedNames(/* @__PURE__ */ new Set());
|
|
@@ -549,7 +425,7 @@ function useSyncedModels(connectionStatus) {
|
|
|
549
425
|
} catch {
|
|
550
426
|
}
|
|
551
427
|
}, [connectionStatus]);
|
|
552
|
-
|
|
428
|
+
useEffect7(() => {
|
|
553
429
|
refresh();
|
|
554
430
|
}, [refresh]);
|
|
555
431
|
return {
|
|
@@ -565,9 +441,9 @@ import { Box as Box4, Text as Text4, useStdout as useStdout3 } from "ink";
|
|
|
565
441
|
import Spinner2 from "ink-spinner";
|
|
566
442
|
|
|
567
443
|
// src/tui/models/components/RequestLog.tsx
|
|
568
|
-
import { Box as
|
|
444
|
+
import { Box as Box2, Text as Text2, useStdout } from "ink";
|
|
569
445
|
import Spinner from "ink-spinner";
|
|
570
|
-
import { jsx as
|
|
446
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
571
447
|
function formatTime(timestamp) {
|
|
572
448
|
const date = new Date(timestamp);
|
|
573
449
|
return date.toLocaleTimeString("en-US", {
|
|
@@ -610,28 +486,28 @@ function RequestItem({
|
|
|
610
486
|
const elapsed = Date.now() - request.startTime;
|
|
611
487
|
const snippet = request.content && request.requestType === "llm_chat" ? snippetLine(request.content, snippetWidth) : null;
|
|
612
488
|
const stepProgress = request.step !== void 0 && request.totalSteps ? `Step ${request.step}/${request.totalSteps}` : null;
|
|
613
|
-
return /* @__PURE__ */
|
|
614
|
-
/* @__PURE__ */
|
|
615
|
-
/* @__PURE__ */
|
|
616
|
-
/* @__PURE__ */
|
|
489
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
490
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
491
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
|
|
492
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
617
493
|
" ",
|
|
618
494
|
time,
|
|
619
495
|
" "
|
|
620
496
|
] }),
|
|
621
|
-
/* @__PURE__ */
|
|
622
|
-
/* @__PURE__ */
|
|
623
|
-
/* @__PURE__ */
|
|
624
|
-
/* @__PURE__ */
|
|
497
|
+
/* @__PURE__ */ jsx2(Text2, { color: "white", children: request.modelId }),
|
|
498
|
+
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: " " }),
|
|
499
|
+
/* @__PURE__ */ jsx2(Text2, { color: typeLabel.color, children: typeLabel.label }),
|
|
500
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
625
501
|
" ",
|
|
626
502
|
formatDuration(elapsed),
|
|
627
503
|
"..."
|
|
628
504
|
] })
|
|
629
505
|
] }),
|
|
630
|
-
snippet && /* @__PURE__ */
|
|
506
|
+
snippet && /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
|
|
631
507
|
snippetIndent,
|
|
632
508
|
snippet
|
|
633
509
|
] }),
|
|
634
|
-
stepProgress && /* @__PURE__ */
|
|
510
|
+
stepProgress && /* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
635
511
|
snippetIndent,
|
|
636
512
|
stepProgress
|
|
637
513
|
] })
|
|
@@ -648,40 +524,40 @@ function RequestItem({
|
|
|
648
524
|
resultInfo = ` \xB7 ${Math.round(request.result.videoSize / 1024 / 1024)}MB`;
|
|
649
525
|
}
|
|
650
526
|
const snippet = request.content && request.requestType === "llm_chat" ? snippetLine(request.content, snippetWidth) : null;
|
|
651
|
-
return /* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
654
|
-
/* @__PURE__ */
|
|
527
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
|
|
528
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
529
|
+
/* @__PURE__ */ jsx2(Text2, { color: "green", children: "\u2713" }),
|
|
530
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
655
531
|
" ",
|
|
656
532
|
time,
|
|
657
533
|
" "
|
|
658
534
|
] }),
|
|
659
|
-
/* @__PURE__ */
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
662
|
-
/* @__PURE__ */
|
|
535
|
+
/* @__PURE__ */ jsx2(Text2, { color: "white", children: request.modelId }),
|
|
536
|
+
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: " " }),
|
|
537
|
+
/* @__PURE__ */ jsx2(Text2, { color: typeLabel.color, children: typeLabel.label }),
|
|
538
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
663
539
|
" ",
|
|
664
540
|
duration,
|
|
665
541
|
resultInfo
|
|
666
542
|
] })
|
|
667
543
|
] }),
|
|
668
|
-
snippet && /* @__PURE__ */
|
|
544
|
+
snippet && /* @__PURE__ */ jsxs2(Text2, { color: "gray", wrap: "truncate-end", children: [
|
|
669
545
|
snippetIndent,
|
|
670
546
|
snippet
|
|
671
547
|
] })
|
|
672
548
|
] });
|
|
673
549
|
}
|
|
674
|
-
return /* @__PURE__ */
|
|
675
|
-
/* @__PURE__ */
|
|
676
|
-
/* @__PURE__ */
|
|
550
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: /* @__PURE__ */ jsxs2(Box2, { children: [
|
|
551
|
+
/* @__PURE__ */ jsx2(Text2, { color: "red", children: "\u25CF" }),
|
|
552
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "gray", children: [
|
|
677
553
|
" ",
|
|
678
554
|
time,
|
|
679
555
|
" "
|
|
680
556
|
] }),
|
|
681
|
-
/* @__PURE__ */
|
|
682
|
-
/* @__PURE__ */
|
|
683
|
-
/* @__PURE__ */
|
|
684
|
-
/* @__PURE__ */
|
|
557
|
+
/* @__PURE__ */ jsx2(Text2, { color: "white", children: request.modelId }),
|
|
558
|
+
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: " " }),
|
|
559
|
+
/* @__PURE__ */ jsx2(Text2, { color: typeLabel.color, children: typeLabel.label }),
|
|
560
|
+
/* @__PURE__ */ jsxs2(Text2, { color: "red", children: [
|
|
685
561
|
" ",
|
|
686
562
|
request.error || "Failed"
|
|
687
563
|
] })
|
|
@@ -692,7 +568,7 @@ function RequestLog({
|
|
|
692
568
|
maxVisible = 8,
|
|
693
569
|
hasModels = true
|
|
694
570
|
}) {
|
|
695
|
-
const { stdout } =
|
|
571
|
+
const { stdout } = useStdout();
|
|
696
572
|
const width = stdout?.columns ?? 80;
|
|
697
573
|
const activeRequests = requests.filter((r) => r.status === "processing");
|
|
698
574
|
const completedRequests = requests.filter((r) => r.status !== "processing");
|
|
@@ -714,8 +590,8 @@ function RequestLog({
|
|
|
714
590
|
}
|
|
715
591
|
}
|
|
716
592
|
const visibleRequests = [...completedToShow, ...activeRequests];
|
|
717
|
-
return /* @__PURE__ */
|
|
718
|
-
|
|
593
|
+
return /* @__PURE__ */ jsxs2(
|
|
594
|
+
Box2,
|
|
719
595
|
{
|
|
720
596
|
flexDirection: "column",
|
|
721
597
|
flexGrow: 1,
|
|
@@ -723,71 +599,210 @@ function RequestLog({
|
|
|
723
599
|
paddingX: 1,
|
|
724
600
|
marginTop: 1,
|
|
725
601
|
children: [
|
|
726
|
-
/* @__PURE__ */
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
activeRequests.length > 0 && /* @__PURE__ */
|
|
602
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
603
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, underline: true, color: "white", children: "Generation Requests" }),
|
|
604
|
+
activeRequests.length > 0 && /* @__PURE__ */ jsxs2(Text2, { color: "cyan", children: [
|
|
729
605
|
" (",
|
|
730
606
|
activeRequests.length,
|
|
731
607
|
" active)"
|
|
732
608
|
] })
|
|
733
609
|
] }),
|
|
734
|
-
requests.length === 0 ? /* @__PURE__ */
|
|
610
|
+
requests.length === 0 ? /* @__PURE__ */ jsx2(Box2, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: hasModels ? "Tunnel is live \u2014 requests will appear here when models are used in MindStudio" : "Start a model to begin receiving generation requests." }) }) : /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginTop: 1, children: visibleRequests.map((request) => /* @__PURE__ */ jsx2(RequestItem, { request, width }, request.id)) })
|
|
735
611
|
]
|
|
736
612
|
}
|
|
737
613
|
);
|
|
738
614
|
}
|
|
739
615
|
|
|
740
|
-
// src/tui/
|
|
741
|
-
import {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
switch (capability) {
|
|
749
|
-
case "text":
|
|
750
|
-
return { label: "Text Generation", color: "gray" };
|
|
751
|
-
case "image":
|
|
752
|
-
return { label: "Image Generation", color: "gray" };
|
|
753
|
-
case "video":
|
|
754
|
-
return { label: "Video Generation", color: "gray" };
|
|
755
|
-
default:
|
|
756
|
-
return { label: capability, color: "gray" };
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
function DashboardPage({
|
|
760
|
-
requests,
|
|
761
|
-
models,
|
|
762
|
-
modelWarnings = [],
|
|
763
|
-
providers,
|
|
764
|
-
providersLoading,
|
|
765
|
-
syncedNames,
|
|
766
|
-
modelsLoading,
|
|
767
|
-
syncStatus = "idle",
|
|
768
|
-
editorSessions,
|
|
769
|
-
editorsLoading,
|
|
770
|
-
onNavigate
|
|
616
|
+
// src/tui/components/NavigationMenu.tsx
|
|
617
|
+
import { useState as useState8, useEffect as useEffect8 } from "react";
|
|
618
|
+
import { Box as Box3, Text as Text3, useInput, useStdout as useStdout2 } from "ink";
|
|
619
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
620
|
+
function NavigationMenu({
|
|
621
|
+
items,
|
|
622
|
+
onSelect,
|
|
623
|
+
title
|
|
771
624
|
}) {
|
|
772
|
-
const { stdout } =
|
|
773
|
-
const
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
const
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
);
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
625
|
+
const { stdout } = useStdout2();
|
|
626
|
+
const compact = (stdout?.rows ?? 24) < 40;
|
|
627
|
+
const getDefaultIndex = () => {
|
|
628
|
+
const firstIdx = items.findIndex((i) => !i.disabled && !i.isSeparator);
|
|
629
|
+
return firstIdx >= 0 ? firstIdx : 0;
|
|
630
|
+
};
|
|
631
|
+
const [selectedIndex, setSelectedIndex] = useState8(getDefaultIndex);
|
|
632
|
+
useEffect8(() => {
|
|
633
|
+
setSelectedIndex(getDefaultIndex());
|
|
634
|
+
}, [items]);
|
|
635
|
+
const selectableItems = items.filter((i) => !i.isSeparator);
|
|
636
|
+
const findNextEnabled = (from, direction) => {
|
|
637
|
+
let idx = from;
|
|
638
|
+
for (let i = 0; i < items.length; i++) {
|
|
639
|
+
idx = (idx + direction + items.length) % items.length;
|
|
640
|
+
if (!items[idx].disabled && !items[idx].isSeparator) return idx;
|
|
641
|
+
}
|
|
642
|
+
return from;
|
|
643
|
+
};
|
|
644
|
+
useInput((input, key) => {
|
|
645
|
+
if (input === "q" || key.escape) {
|
|
646
|
+
const backItem = items.find((i) => i.id === "back");
|
|
647
|
+
if (backItem) {
|
|
648
|
+
onSelect("back");
|
|
649
|
+
} else if (input === "q") {
|
|
650
|
+
onSelect("quit");
|
|
651
|
+
}
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (key.upArrow || compact && key.leftArrow) {
|
|
655
|
+
setSelectedIndex((prev) => findNextEnabled(prev, -1));
|
|
656
|
+
} else if (key.downArrow || compact && key.rightArrow) {
|
|
657
|
+
setSelectedIndex((prev) => findNextEnabled(prev, 1));
|
|
658
|
+
} else if (key.return) {
|
|
659
|
+
const item = items[selectedIndex];
|
|
660
|
+
if (item && !item.disabled) {
|
|
661
|
+
onSelect(item.id);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
const hasBack = items.some((i) => i.id === "back");
|
|
666
|
+
if (compact) {
|
|
667
|
+
const selectedItem = items[selectedIndex];
|
|
668
|
+
return /* @__PURE__ */ jsx3(
|
|
669
|
+
Box3,
|
|
786
670
|
{
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
671
|
+
flexDirection: "column",
|
|
672
|
+
paddingX: 1,
|
|
673
|
+
borderStyle: "single",
|
|
674
|
+
borderTop: true,
|
|
675
|
+
borderBottom: false,
|
|
676
|
+
borderLeft: false,
|
|
677
|
+
borderRight: false,
|
|
678
|
+
borderColor: "gray",
|
|
679
|
+
children: /* @__PURE__ */ jsx3(Box3, { height: 1, overflow: "hidden", gap: 1, children: items.map((item, index) => {
|
|
680
|
+
if (item.isSeparator) return null;
|
|
681
|
+
const isSelected = index === selectedIndex;
|
|
682
|
+
return /* @__PURE__ */ jsx3(
|
|
683
|
+
Text3,
|
|
684
|
+
{
|
|
685
|
+
color: item.disabled ? "gray" : isSelected ? "cyan" : "white",
|
|
686
|
+
bold: isSelected,
|
|
687
|
+
wrap: "truncate-end",
|
|
688
|
+
children: isSelected ? `\u276F ${item.label}` : ` ${item.label}`
|
|
689
|
+
},
|
|
690
|
+
item.id
|
|
691
|
+
);
|
|
692
|
+
}) })
|
|
693
|
+
}
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
const separatorExtraLines = items.filter(
|
|
697
|
+
(item, idx) => item.isSeparator && idx > 0
|
|
698
|
+
).length;
|
|
699
|
+
const menuHeight = items.length + 4 + separatorExtraLines;
|
|
700
|
+
return /* @__PURE__ */ jsxs3(
|
|
701
|
+
Box3,
|
|
702
|
+
{
|
|
703
|
+
flexDirection: "column",
|
|
704
|
+
paddingX: 1,
|
|
705
|
+
marginBottom: 1,
|
|
706
|
+
borderStyle: "single",
|
|
707
|
+
borderTop: true,
|
|
708
|
+
borderBottom: false,
|
|
709
|
+
borderLeft: false,
|
|
710
|
+
borderRight: false,
|
|
711
|
+
borderColor: "gray",
|
|
712
|
+
children: [
|
|
713
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "gray", children: title ?? "Actions" }) }),
|
|
714
|
+
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: items.map((item, index) => {
|
|
715
|
+
if (item.isSeparator) {
|
|
716
|
+
return /* @__PURE__ */ jsx3(Box3, { marginTop: index > 0 ? 1 : 0, children: item.label ? /* @__PURE__ */ jsx3(Text3, { bold: true, color: item.color ?? "gray", wrap: "truncate-end", children: item.label }) : null }, item.id);
|
|
717
|
+
}
|
|
718
|
+
const isSelected = index === selectedIndex;
|
|
719
|
+
const prefix = isSelected ? "\u276F" : " ";
|
|
720
|
+
if (item.disabled) {
|
|
721
|
+
return /* @__PURE__ */ jsx3(Box3, { height: 1, overflow: "hidden", children: /* @__PURE__ */ jsxs3(Text3, { color: "gray", wrap: "truncate-end", children: [
|
|
722
|
+
prefix,
|
|
723
|
+
" ",
|
|
724
|
+
item.label,
|
|
725
|
+
item.disabledReason ? ` (${item.disabledReason})` : ""
|
|
726
|
+
] }) }, item.id);
|
|
727
|
+
}
|
|
728
|
+
return /* @__PURE__ */ jsxs3(Box3, { height: 1, overflow: "hidden", children: [
|
|
729
|
+
/* @__PURE__ */ jsxs3(
|
|
730
|
+
Text3,
|
|
731
|
+
{
|
|
732
|
+
color: isSelected ? "cyan" : "white",
|
|
733
|
+
bold: isSelected,
|
|
734
|
+
wrap: "truncate-end",
|
|
735
|
+
children: [
|
|
736
|
+
prefix,
|
|
737
|
+
" ",
|
|
738
|
+
item.label
|
|
739
|
+
]
|
|
740
|
+
}
|
|
741
|
+
),
|
|
742
|
+
isSelected && /* @__PURE__ */ jsxs3(Text3, { color: "gray", wrap: "truncate-end", children: [
|
|
743
|
+
" ",
|
|
744
|
+
"- ",
|
|
745
|
+
item.description
|
|
746
|
+
] })
|
|
747
|
+
] }, item.id);
|
|
748
|
+
}) }),
|
|
749
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, height: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "gray", wrap: "truncate-end", children: hasBack ? "Up/Down Navigate \u2022 Enter Select \u2022 q/Esc Back" : "Up/Down Navigate \u2022 Enter Select \u2022 q Quit" }) })
|
|
750
|
+
]
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// src/tui/models/pages/DashboardPage.tsx
|
|
756
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
757
|
+
function getWorkflowCount(model) {
|
|
758
|
+
const param = model.parameters?.find((p) => p.type === "comfyWorkflow");
|
|
759
|
+
if (!param) return null;
|
|
760
|
+
return param.comfyWorkflowOptions.availableWorkflows.length;
|
|
761
|
+
}
|
|
762
|
+
function getCapabilityLabel(capability) {
|
|
763
|
+
switch (capability) {
|
|
764
|
+
case "text":
|
|
765
|
+
return { label: "Text Generation", color: "gray" };
|
|
766
|
+
case "image":
|
|
767
|
+
return { label: "Image Generation", color: "gray" };
|
|
768
|
+
case "video":
|
|
769
|
+
return { label: "Video Generation", color: "gray" };
|
|
770
|
+
default:
|
|
771
|
+
return { label: capability, color: "gray" };
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function DashboardPage({
|
|
775
|
+
requests,
|
|
776
|
+
models,
|
|
777
|
+
modelWarnings = [],
|
|
778
|
+
providers,
|
|
779
|
+
providersLoading,
|
|
780
|
+
syncedNames,
|
|
781
|
+
modelsLoading,
|
|
782
|
+
syncStatus = "idle",
|
|
783
|
+
editorSessions,
|
|
784
|
+
editorsLoading,
|
|
785
|
+
onNavigate
|
|
786
|
+
}) {
|
|
787
|
+
const { stdout } = useStdout3();
|
|
788
|
+
const installedProviders = providers.filter(({ status }) => status.installed);
|
|
789
|
+
const provNameWidth = Math.max(
|
|
790
|
+
...installedProviders.map((p) => p.provider.displayName.length),
|
|
791
|
+
8
|
|
792
|
+
);
|
|
793
|
+
const provStatusWidth = "Local Server Running".length;
|
|
794
|
+
const allModelNames = new Set(models.map((m) => m.name));
|
|
795
|
+
const unavailableSynced = [...syncedNames].filter(
|
|
796
|
+
(name) => !allModelNames.has(name)
|
|
797
|
+
);
|
|
798
|
+
const syncDescription = syncStatus === "syncing" ? "Syncing..." : syncStatus === "synced" ? "\u2713 Synced" : "Re-detect providers and sync models to MindStudio";
|
|
799
|
+
const menuItems = useMemo2(() => {
|
|
800
|
+
return [
|
|
801
|
+
{
|
|
802
|
+
id: "interfaces",
|
|
803
|
+
label: "Connect to Agent",
|
|
804
|
+
description: "Connect your local editor to a MindStudio interface or script"
|
|
805
|
+
},
|
|
791
806
|
{
|
|
792
807
|
id: "refresh",
|
|
793
808
|
label: "Sync Models",
|
|
@@ -2442,16 +2457,1142 @@ function OnboardingPage({ onComplete }) {
|
|
|
2442
2457
|
] });
|
|
2443
2458
|
}
|
|
2444
2459
|
|
|
2460
|
+
// src/tui/dev/pages/DevPage.tsx
|
|
2461
|
+
import { useState as useState20, useEffect as useEffect19, useMemo as useMemo8 } from "react";
|
|
2462
|
+
import { Box as Box13, Text as Text14, useInput as useInput7 } from "ink";
|
|
2463
|
+
import Spinner7 from "ink-spinner";
|
|
2464
|
+
|
|
2465
|
+
// src/tui/dev/components/DevRequestLog.tsx
|
|
2466
|
+
import { Box as Box10, Text as Text11 } from "ink";
|
|
2467
|
+
import Spinner6 from "ink-spinner";
|
|
2468
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2469
|
+
function DevRequestLog({
|
|
2470
|
+
requests,
|
|
2471
|
+
maxVisible = 10
|
|
2472
|
+
}) {
|
|
2473
|
+
if (requests.length === 0) {
|
|
2474
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
2475
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: "white", underline: true, children: "Request Log" }),
|
|
2476
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: " Waiting for requests..." })
|
|
2477
|
+
] });
|
|
2478
|
+
}
|
|
2479
|
+
const active = requests.filter((r) => r.status === "processing");
|
|
2480
|
+
const completed = requests.filter((r) => r.status !== "processing");
|
|
2481
|
+
const recent = completed.slice(-Math.max(0, maxVisible - active.length));
|
|
2482
|
+
const visible = [...recent, ...active].slice(-maxVisible);
|
|
2483
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
2484
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: "white", underline: true, children: "Request Log" }),
|
|
2485
|
+
visible.map((entry) => /* @__PURE__ */ jsx11(RequestEntry, { entry }, entry.id))
|
|
2486
|
+
] });
|
|
2487
|
+
}
|
|
2488
|
+
function RequestEntry({ entry }) {
|
|
2489
|
+
const methodLabel = entry.method ?? "unknown";
|
|
2490
|
+
if (entry.status === "processing") {
|
|
2491
|
+
const elapsed = Math.round((Date.now() - entry.startTime) / 1e3);
|
|
2492
|
+
return /* @__PURE__ */ jsxs10(Box10, { gap: 1, children: [
|
|
2493
|
+
/* @__PURE__ */ jsx11(Text11, { color: "cyan", children: /* @__PURE__ */ jsx11(Spinner6, { type: "dots" }) }),
|
|
2494
|
+
/* @__PURE__ */ jsx11(Text11, { children: methodLabel }),
|
|
2495
|
+
/* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2496
|
+
elapsed,
|
|
2497
|
+
"s"
|
|
2498
|
+
] })
|
|
2499
|
+
] });
|
|
2500
|
+
}
|
|
2501
|
+
if (entry.status === "failed") {
|
|
2502
|
+
const errorLines = (entry.error ?? "Unknown error").split("\n").slice(0, 4);
|
|
2503
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
2504
|
+
/* @__PURE__ */ jsxs10(Box10, { gap: 1, children: [
|
|
2505
|
+
/* @__PURE__ */ jsx11(Text11, { color: "red", children: "\u2716" }),
|
|
2506
|
+
/* @__PURE__ */ jsx11(Text11, { children: methodLabel }),
|
|
2507
|
+
entry.duration != null && /* @__PURE__ */ jsxs10(Text11, { color: "gray", children: [
|
|
2508
|
+
entry.duration,
|
|
2509
|
+
"ms"
|
|
2510
|
+
] })
|
|
2511
|
+
] }),
|
|
2512
|
+
errorLines.map((line, i) => /* @__PURE__ */ jsxs10(Text11, { color: "red", wrap: "truncate", children: [
|
|
2513
|
+
" ",
|
|
2514
|
+
line
|
|
2515
|
+
] }, i))
|
|
2516
|
+
] });
|
|
2517
|
+
}
|
|
2518
|
+
const duration = entry.duration != null ? `${entry.duration}ms` : "";
|
|
2519
|
+
return /* @__PURE__ */ jsxs10(Box10, { gap: 1, children: [
|
|
2520
|
+
/* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713" }),
|
|
2521
|
+
/* @__PURE__ */ jsx11(Text11, { children: methodLabel }),
|
|
2522
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: duration })
|
|
2523
|
+
] });
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2526
|
+
// src/tui/dev/components/DevPortPrompt.tsx
|
|
2527
|
+
import { useState as useState16 } from "react";
|
|
2528
|
+
import { Box as Box11, Text as Text12 } from "ink";
|
|
2529
|
+
import TextInput from "ink-text-input";
|
|
2530
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2531
|
+
function DevPortPrompt({ onSubmit, onSkip }) {
|
|
2532
|
+
const [value, setValue] = useState16("");
|
|
2533
|
+
const [error, setError] = useState16(null);
|
|
2534
|
+
const handleSubmit = (input) => {
|
|
2535
|
+
const trimmed = input.trim();
|
|
2536
|
+
if (trimmed === "" || trimmed === "skip") {
|
|
2537
|
+
onSkip();
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
const port = parseInt(trimmed, 10);
|
|
2541
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
2542
|
+
setError('Enter a valid port number (1-65535) or "skip"');
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2545
|
+
onSubmit(port);
|
|
2546
|
+
};
|
|
2547
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
2548
|
+
/* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "No devPort found in your web interface config." }),
|
|
2549
|
+
/* @__PURE__ */ jsx12(Text12, { color: "gray", children: "What port is your local dev server running on?" }),
|
|
2550
|
+
/* @__PURE__ */ jsx12(Text12, { color: "gray", dimColor: true, children: 'Type "skip" for backend-only mode (no frontend proxying).' }),
|
|
2551
|
+
/* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
|
|
2552
|
+
/* @__PURE__ */ jsx12(Text12, { color: "cyan", children: "Port: " }),
|
|
2553
|
+
/* @__PURE__ */ jsx12(
|
|
2554
|
+
TextInput,
|
|
2555
|
+
{
|
|
2556
|
+
value,
|
|
2557
|
+
onChange: (val) => {
|
|
2558
|
+
setValue(val);
|
|
2559
|
+
setError(null);
|
|
2560
|
+
},
|
|
2561
|
+
onSubmit: handleSubmit,
|
|
2562
|
+
placeholder: "5173"
|
|
2563
|
+
}
|
|
2564
|
+
)
|
|
2565
|
+
] }),
|
|
2566
|
+
error && /* @__PURE__ */ jsx12(Text12, { color: "red", children: error })
|
|
2567
|
+
] });
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// src/tui/dev/components/TabBar.tsx
|
|
2571
|
+
import { Box as Box12, Text as Text13, useStdout as useStdout7 } from "ink";
|
|
2572
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2573
|
+
function TabBar({ tabs, activeTab }) {
|
|
2574
|
+
const { stdout } = useStdout7();
|
|
2575
|
+
const width = stdout?.columns ?? 80;
|
|
2576
|
+
const tabSegments = tabs.map((tab) => {
|
|
2577
|
+
const isActive = tab.id === activeTab;
|
|
2578
|
+
const padded = ` ${tab.label} `;
|
|
2579
|
+
return { tab, isActive, padded };
|
|
2580
|
+
});
|
|
2581
|
+
const labelText = "Screen ";
|
|
2582
|
+
const hint = " \u2190/\u2192 ";
|
|
2583
|
+
const tabChars = tabSegments.reduce((sum, s) => sum + s.padded.length, 0);
|
|
2584
|
+
const remaining = Math.max(0, width - tabChars - hint.length - labelText.length);
|
|
2585
|
+
return /* @__PURE__ */ jsxs12(Box12, { children: [
|
|
2586
|
+
/* @__PURE__ */ jsx13(Text13, { color: "gray", children: labelText }),
|
|
2587
|
+
tabSegments.map(({ tab, isActive, padded }) => /* @__PURE__ */ jsx13(
|
|
2588
|
+
Text13,
|
|
2589
|
+
{
|
|
2590
|
+
bold: isActive,
|
|
2591
|
+
color: isActive ? "white" : "#aaaaaa",
|
|
2592
|
+
backgroundColor: isActive ? "blueBright" : "#333333",
|
|
2593
|
+
children: padded
|
|
2594
|
+
},
|
|
2595
|
+
tab.id
|
|
2596
|
+
)),
|
|
2597
|
+
/* @__PURE__ */ jsxs12(Text13, { color: "#666666", backgroundColor: "#333333", children: [
|
|
2598
|
+
" ".repeat(remaining),
|
|
2599
|
+
hint
|
|
2600
|
+
] })
|
|
2601
|
+
] });
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
// src/tui/dev/hooks/useDevSession.ts
|
|
2605
|
+
import { useState as useState18, useEffect as useEffect17, useRef as useRef10, useCallback as useCallback11 } from "react";
|
|
2606
|
+
import { spawn as spawn3 } from "child_process";
|
|
2607
|
+
import { watch } from "fs";
|
|
2608
|
+
import { join } from "path";
|
|
2609
|
+
|
|
2610
|
+
// src/tui/dev/hooks/useDevServer.ts
|
|
2611
|
+
import { useState as useState17, useCallback as useCallback10, useRef as useRef9, useEffect as useEffect16 } from "react";
|
|
2612
|
+
import { spawn as spawn2 } from "child_process";
|
|
2613
|
+
var MAX_OUTPUT_LINES2 = 200;
|
|
2614
|
+
var PORT_READY_TIMEOUT_MS = 3e4;
|
|
2615
|
+
var PORT_CHECK_INTERVAL_MS = 500;
|
|
2616
|
+
function useDevServer() {
|
|
2617
|
+
const [phase, setPhase] = useState17("idle");
|
|
2618
|
+
const [outputLines, setOutputLines] = useState17([]);
|
|
2619
|
+
const [error, setError] = useState17(null);
|
|
2620
|
+
const processRef = useRef9(null);
|
|
2621
|
+
const mountedRef = useRef9(true);
|
|
2622
|
+
useEffect16(() => {
|
|
2623
|
+
mountedRef.current = true;
|
|
2624
|
+
return () => {
|
|
2625
|
+
mountedRef.current = false;
|
|
2626
|
+
killProcess();
|
|
2627
|
+
};
|
|
2628
|
+
}, []);
|
|
2629
|
+
const appendOutput = useCallback10((line) => {
|
|
2630
|
+
if (!mountedRef.current) return;
|
|
2631
|
+
setOutputLines((prev) => {
|
|
2632
|
+
const next = [...prev, line];
|
|
2633
|
+
return next.length > MAX_OUTPUT_LINES2 ? next.slice(next.length - MAX_OUTPUT_LINES2) : next;
|
|
2634
|
+
});
|
|
2635
|
+
}, []);
|
|
2636
|
+
const killProcess = useCallback10(() => {
|
|
2637
|
+
if (processRef.current) {
|
|
2638
|
+
processRef.current.kill("SIGTERM");
|
|
2639
|
+
const proc = processRef.current;
|
|
2640
|
+
setTimeout(() => {
|
|
2641
|
+
if (proc && !proc.killed) {
|
|
2642
|
+
proc.kill("SIGKILL");
|
|
2643
|
+
}
|
|
2644
|
+
}, 2e3);
|
|
2645
|
+
processRef.current = null;
|
|
2646
|
+
}
|
|
2647
|
+
}, []);
|
|
2648
|
+
const start = useCallback10(
|
|
2649
|
+
async (opts) => {
|
|
2650
|
+
setPhase("starting");
|
|
2651
|
+
setError(null);
|
|
2652
|
+
setOutputLines([]);
|
|
2653
|
+
appendOutput(`$ ${opts.command}`);
|
|
2654
|
+
const proc = spawn2(opts.command, [], {
|
|
2655
|
+
cwd: opts.cwd,
|
|
2656
|
+
shell: true,
|
|
2657
|
+
env: { ...process.env, FORCE_COLOR: "1" },
|
|
2658
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2659
|
+
});
|
|
2660
|
+
processRef.current = proc;
|
|
2661
|
+
const handleData = (data) => {
|
|
2662
|
+
const lines = data.toString().split("\n");
|
|
2663
|
+
for (const line of lines) {
|
|
2664
|
+
if (line.length > 0) {
|
|
2665
|
+
appendOutput(line);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
};
|
|
2669
|
+
proc.stdout?.on("data", handleData);
|
|
2670
|
+
proc.stderr?.on("data", handleData);
|
|
2671
|
+
proc.on("close", (code) => {
|
|
2672
|
+
processRef.current = null;
|
|
2673
|
+
if (mountedRef.current && phase !== "idle") {
|
|
2674
|
+
if (code !== 0 && code !== null) {
|
|
2675
|
+
setError(`Dev server exited with code ${code}`);
|
|
2676
|
+
setPhase("error");
|
|
2677
|
+
} else {
|
|
2678
|
+
setPhase("idle");
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
});
|
|
2682
|
+
proc.on("error", (err) => {
|
|
2683
|
+
processRef.current = null;
|
|
2684
|
+
if (mountedRef.current) {
|
|
2685
|
+
setError(err.message);
|
|
2686
|
+
setPhase("error");
|
|
2687
|
+
}
|
|
2688
|
+
});
|
|
2689
|
+
await waitForPort(opts.port, PORT_READY_TIMEOUT_MS);
|
|
2690
|
+
if (mountedRef.current && processRef.current) {
|
|
2691
|
+
setPhase("running");
|
|
2692
|
+
}
|
|
2693
|
+
},
|
|
2694
|
+
[appendOutput]
|
|
2695
|
+
);
|
|
2696
|
+
const stop = useCallback10(() => {
|
|
2697
|
+
killProcess();
|
|
2698
|
+
if (mountedRef.current) {
|
|
2699
|
+
setPhase("idle");
|
|
2700
|
+
}
|
|
2701
|
+
}, [killProcess]);
|
|
2702
|
+
return {
|
|
2703
|
+
phase,
|
|
2704
|
+
outputLines,
|
|
2705
|
+
error,
|
|
2706
|
+
start,
|
|
2707
|
+
stop
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
async function waitForPort(port, timeoutMs) {
|
|
2711
|
+
const start = Date.now();
|
|
2712
|
+
while (Date.now() - start < timeoutMs) {
|
|
2713
|
+
try {
|
|
2714
|
+
const response = await fetch(`http://localhost:${port}/`, {
|
|
2715
|
+
signal: AbortSignal.timeout(1e3)
|
|
2716
|
+
});
|
|
2717
|
+
return;
|
|
2718
|
+
} catch {
|
|
2719
|
+
}
|
|
2720
|
+
await new Promise((resolve) => setTimeout(resolve, PORT_CHECK_INTERVAL_MS));
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
// src/tui/dev/hooks/useDevSession.ts
|
|
2725
|
+
function runNpmInstall(cwd) {
|
|
2726
|
+
return new Promise((resolve, reject) => {
|
|
2727
|
+
const proc = spawn3("npm", ["install"], {
|
|
2728
|
+
cwd,
|
|
2729
|
+
shell: true,
|
|
2730
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
2731
|
+
});
|
|
2732
|
+
let stderr = "";
|
|
2733
|
+
proc.stderr?.on("data", (chunk) => {
|
|
2734
|
+
stderr += chunk.toString();
|
|
2735
|
+
});
|
|
2736
|
+
proc.on("close", (code) => {
|
|
2737
|
+
if (code === 0) resolve();
|
|
2738
|
+
else reject(new Error(`npm install failed in ${cwd}: ${stderr.slice(-200)}`));
|
|
2739
|
+
});
|
|
2740
|
+
proc.on("error", reject);
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
function useDevSession(appConfig) {
|
|
2744
|
+
const logInitRef = useRef10(false);
|
|
2745
|
+
if (!logInitRef.current) {
|
|
2746
|
+
initLoggerInteractive("error");
|
|
2747
|
+
logInitRef.current = true;
|
|
2748
|
+
}
|
|
2749
|
+
const [phase, setPhase] = useState18("detecting");
|
|
2750
|
+
const [session, setSession] = useState18(null);
|
|
2751
|
+
const [error, setError] = useState18(null);
|
|
2752
|
+
const [devPort, setDevPort] = useState18(null);
|
|
2753
|
+
const [proxyPort, setProxyPort] = useState18(null);
|
|
2754
|
+
const [webConfig, setWebConfig] = useState18(null);
|
|
2755
|
+
const [syncResult, setSyncResult] = useState18(null);
|
|
2756
|
+
const [scenarioResult, setScenarioResult] = useState18(null);
|
|
2757
|
+
const [roleOverride, setRoleOverride] = useState18(null);
|
|
2758
|
+
const [installStatus, setInstallStatus] = useState18(null);
|
|
2759
|
+
const [authRefreshUrl, setAuthRefreshUrl] = useState18(null);
|
|
2760
|
+
const runnerRef = useRef10(null);
|
|
2761
|
+
const proxyRef = useRef10(null);
|
|
2762
|
+
const tableWatcherCleanupRef = useRef10(() => {
|
|
2763
|
+
});
|
|
2764
|
+
const mountedRef = useRef10(true);
|
|
2765
|
+
const devServer = useDevServer();
|
|
2766
|
+
const cleanupTableWatchers = useCallback11(() => {
|
|
2767
|
+
tableWatcherCleanupRef.current();
|
|
2768
|
+
tableWatcherCleanupRef.current = () => {
|
|
2769
|
+
};
|
|
2770
|
+
}, []);
|
|
2771
|
+
const setupTableWatchers = useCallback11((config) => {
|
|
2772
|
+
cleanupTableWatchers();
|
|
2773
|
+
tableWatcherCleanupRef.current = watchTableFiles(
|
|
2774
|
+
config.tables,
|
|
2775
|
+
process.cwd(),
|
|
2776
|
+
() => {
|
|
2777
|
+
resyncRef.current?.();
|
|
2778
|
+
}
|
|
2779
|
+
);
|
|
2780
|
+
}, [cleanupTableWatchers]);
|
|
2781
|
+
useEffect17(() => {
|
|
2782
|
+
const config = getWebInterfaceConfig(appConfig);
|
|
2783
|
+
setWebConfig(config);
|
|
2784
|
+
if (config?.devPort) {
|
|
2785
|
+
setDevPort(config.devPort);
|
|
2786
|
+
setPhase("ready");
|
|
2787
|
+
} else if (!appConfig.interfaces.some(
|
|
2788
|
+
(i) => i.type === "web" && i.enabled !== false
|
|
2789
|
+
)) {
|
|
2790
|
+
setDevPort(null);
|
|
2791
|
+
setPhase("ready");
|
|
2792
|
+
} else {
|
|
2793
|
+
setPhase("needs_port");
|
|
2794
|
+
}
|
|
2795
|
+
}, [appConfig]);
|
|
2796
|
+
useEffect17(() => {
|
|
2797
|
+
const unsubExpired = devRequestEvents.onSessionExpired(() => {
|
|
2798
|
+
if (mountedRef.current) {
|
|
2799
|
+
setPhase("expired");
|
|
2800
|
+
setAuthRefreshUrl(null);
|
|
2801
|
+
runnerRef.current = null;
|
|
2802
|
+
}
|
|
2803
|
+
});
|
|
2804
|
+
const unsubImpersonate = devRequestEvents.onImpersonate((event) => {
|
|
2805
|
+
if (mountedRef.current) {
|
|
2806
|
+
setRoleOverride(event.roles);
|
|
2807
|
+
}
|
|
2808
|
+
});
|
|
2809
|
+
const unsubAuthStart = devRequestEvents.onAuthRefreshStart((url) => {
|
|
2810
|
+
if (mountedRef.current) {
|
|
2811
|
+
setAuthRefreshUrl(url);
|
|
2812
|
+
}
|
|
2813
|
+
});
|
|
2814
|
+
const unsubAuthSuccess = devRequestEvents.onAuthRefreshSuccess(() => {
|
|
2815
|
+
if (mountedRef.current) {
|
|
2816
|
+
setAuthRefreshUrl(null);
|
|
2817
|
+
}
|
|
2818
|
+
});
|
|
2819
|
+
const unsubAuthFailed = devRequestEvents.onAuthRefreshFailed(() => {
|
|
2820
|
+
if (mountedRef.current) {
|
|
2821
|
+
setAuthRefreshUrl(null);
|
|
2822
|
+
}
|
|
2823
|
+
});
|
|
2824
|
+
return () => {
|
|
2825
|
+
unsubExpired();
|
|
2826
|
+
unsubImpersonate();
|
|
2827
|
+
unsubAuthStart();
|
|
2828
|
+
unsubAuthSuccess();
|
|
2829
|
+
unsubAuthFailed();
|
|
2830
|
+
};
|
|
2831
|
+
}, []);
|
|
2832
|
+
const restartTimerRef = useRef10(void 0);
|
|
2833
|
+
useEffect17(() => {
|
|
2834
|
+
let watcher;
|
|
2835
|
+
try {
|
|
2836
|
+
const configPath = join(process.cwd(), "mindstudio.json");
|
|
2837
|
+
watcher = watch(configPath, () => {
|
|
2838
|
+
clearTimeout(restartTimerRef.current);
|
|
2839
|
+
restartTimerRef.current = setTimeout(async () => {
|
|
2840
|
+
if (!mountedRef.current || phase !== "running") return;
|
|
2841
|
+
cleanupTableWatchers();
|
|
2842
|
+
proxyRef.current?.stop();
|
|
2843
|
+
proxyRef.current = null;
|
|
2844
|
+
devServer.stop();
|
|
2845
|
+
if (runnerRef.current) {
|
|
2846
|
+
await runnerRef.current.stop().catch(() => {
|
|
2847
|
+
});
|
|
2848
|
+
runnerRef.current = null;
|
|
2849
|
+
}
|
|
2850
|
+
if (mountedRef.current) {
|
|
2851
|
+
setSession(null);
|
|
2852
|
+
setProxyPort(null);
|
|
2853
|
+
setSyncResult(null);
|
|
2854
|
+
setPhase("ready");
|
|
2855
|
+
}
|
|
2856
|
+
}, 500);
|
|
2857
|
+
});
|
|
2858
|
+
} catch {
|
|
2859
|
+
}
|
|
2860
|
+
return () => {
|
|
2861
|
+
clearTimeout(restartTimerRef.current);
|
|
2862
|
+
watcher?.close();
|
|
2863
|
+
};
|
|
2864
|
+
}, [phase, devServer]);
|
|
2865
|
+
useEffect17(() => {
|
|
2866
|
+
mountedRef.current = true;
|
|
2867
|
+
return () => {
|
|
2868
|
+
mountedRef.current = false;
|
|
2869
|
+
cleanupTableWatchers();
|
|
2870
|
+
runnerRef.current?.stop().catch(() => {
|
|
2871
|
+
});
|
|
2872
|
+
proxyRef.current?.stop();
|
|
2873
|
+
devServer.stop();
|
|
2874
|
+
};
|
|
2875
|
+
}, []);
|
|
2876
|
+
const start = useCallback11(
|
|
2877
|
+
async (port) => {
|
|
2878
|
+
const currentConfig = detectAppConfig() ?? appConfig;
|
|
2879
|
+
const actualPort = port ?? devPort;
|
|
2880
|
+
if (!currentConfig.appId) {
|
|
2881
|
+
setPhase("error");
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
if (actualPort !== void 0 && actualPort !== null) {
|
|
2885
|
+
setDevPort(actualPort);
|
|
2886
|
+
}
|
|
2887
|
+
setPhase("starting");
|
|
2888
|
+
setError(null);
|
|
2889
|
+
try {
|
|
2890
|
+
const dirsToInstall = findDirsNeedingInstall(currentConfig);
|
|
2891
|
+
for (const dir of dirsToInstall) {
|
|
2892
|
+
const dirName = dir.split("/").slice(-2).join("/");
|
|
2893
|
+
if (mountedRef.current) {
|
|
2894
|
+
setInstallStatus(`Installing dependencies in ${dirName}...`);
|
|
2895
|
+
}
|
|
2896
|
+
await runNpmInstall(dir);
|
|
2897
|
+
}
|
|
2898
|
+
if (mountedRef.current) {
|
|
2899
|
+
setInstallStatus(null);
|
|
2900
|
+
}
|
|
2901
|
+
if (actualPort != null) {
|
|
2902
|
+
const webProjectDir = getWebProjectDir(currentConfig);
|
|
2903
|
+
if (webProjectDir) {
|
|
2904
|
+
const devCommand = webConfig?.devCommand ?? "npm run dev";
|
|
2905
|
+
await devServer.start({
|
|
2906
|
+
command: devCommand,
|
|
2907
|
+
cwd: webProjectDir,
|
|
2908
|
+
port: actualPort
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
const branch = detectGitBranch();
|
|
2913
|
+
const proxyUrl = actualPort != null ? `http://localhost:${stablePort(currentConfig.appId)}` : void 0;
|
|
2914
|
+
const runner = new DevRunner(
|
|
2915
|
+
currentConfig.appId,
|
|
2916
|
+
process.cwd(),
|
|
2917
|
+
{
|
|
2918
|
+
branch,
|
|
2919
|
+
proxyUrl,
|
|
2920
|
+
methods: currentConfig.methods.map((m) => ({ id: m.id, export: m.export, path: m.path }))
|
|
2921
|
+
}
|
|
2922
|
+
);
|
|
2923
|
+
runnerRef.current = runner;
|
|
2924
|
+
const devSession = await runner.start();
|
|
2925
|
+
if (currentConfig.tables.length > 0) {
|
|
2926
|
+
try {
|
|
2927
|
+
const tableSources = readTableSources(currentConfig);
|
|
2928
|
+
if (tableSources.length > 0) {
|
|
2929
|
+
const result = await syncSchema(
|
|
2930
|
+
currentConfig.appId,
|
|
2931
|
+
devSession.sessionId,
|
|
2932
|
+
tableSources
|
|
2933
|
+
);
|
|
2934
|
+
devSession.databases = result.databases;
|
|
2935
|
+
if (mountedRef.current) {
|
|
2936
|
+
setSyncResult(result);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
} catch {
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
if (actualPort != null && devSession.clientContext) {
|
|
2943
|
+
const proxy = new DevProxy(actualPort, devSession.clientContext);
|
|
2944
|
+
const preferredProxyPort = stablePort(currentConfig.appId);
|
|
2945
|
+
const pPort = await proxy.start(preferredProxyPort);
|
|
2946
|
+
proxyRef.current = proxy;
|
|
2947
|
+
runner.setProxyUrl(`http://localhost:${pPort}`);
|
|
2948
|
+
runner.setProxy(proxy);
|
|
2949
|
+
if (mountedRef.current) {
|
|
2950
|
+
setProxyPort(pPort);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
setupTableWatchers(currentConfig);
|
|
2954
|
+
if (mountedRef.current) {
|
|
2955
|
+
setSession(devSession);
|
|
2956
|
+
setPhase("running");
|
|
2957
|
+
}
|
|
2958
|
+
} catch (err) {
|
|
2959
|
+
if (mountedRef.current) {
|
|
2960
|
+
setError(
|
|
2961
|
+
err instanceof Error ? err.message : "Failed to start dev session"
|
|
2962
|
+
);
|
|
2963
|
+
setPhase("error");
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
},
|
|
2967
|
+
[appConfig, devPort, webConfig, devServer, setupTableWatchers]
|
|
2968
|
+
);
|
|
2969
|
+
const stop = useCallback11(async () => {
|
|
2970
|
+
cleanupTableWatchers();
|
|
2971
|
+
proxyRef.current?.stop();
|
|
2972
|
+
proxyRef.current = null;
|
|
2973
|
+
devServer.stop();
|
|
2974
|
+
if (runnerRef.current) {
|
|
2975
|
+
await runnerRef.current.stop().catch(() => {
|
|
2976
|
+
});
|
|
2977
|
+
runnerRef.current = null;
|
|
2978
|
+
}
|
|
2979
|
+
if (mountedRef.current) {
|
|
2980
|
+
setSession(null);
|
|
2981
|
+
setProxyPort(null);
|
|
2982
|
+
setPhase("stopped");
|
|
2983
|
+
}
|
|
2984
|
+
}, [devServer, cleanupTableWatchers]);
|
|
2985
|
+
const resyncRef = useRef10();
|
|
2986
|
+
const resync = useCallback11(async () => {
|
|
2987
|
+
if (!session) return;
|
|
2988
|
+
const freshConfig = detectAppConfig() ?? appConfig;
|
|
2989
|
+
if (!freshConfig.appId) return;
|
|
2990
|
+
try {
|
|
2991
|
+
const tableSources = readTableSources(freshConfig);
|
|
2992
|
+
if (tableSources.length === 0) return;
|
|
2993
|
+
const result = await syncSchema(
|
|
2994
|
+
freshConfig.appId,
|
|
2995
|
+
session.sessionId,
|
|
2996
|
+
tableSources
|
|
2997
|
+
);
|
|
2998
|
+
if (mountedRef.current) {
|
|
2999
|
+
setSyncResult(result);
|
|
3000
|
+
setSession(
|
|
3001
|
+
(prev) => prev ? { ...prev, databases: result.databases } : prev
|
|
3002
|
+
);
|
|
3003
|
+
}
|
|
3004
|
+
} catch (err) {
|
|
3005
|
+
if (mountedRef.current) {
|
|
3006
|
+
setSyncResult({
|
|
3007
|
+
created: [],
|
|
3008
|
+
altered: [],
|
|
3009
|
+
errors: [err instanceof Error ? err.message : "Sync failed"],
|
|
3010
|
+
databases: session.databases
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
}, [appConfig, session]);
|
|
3015
|
+
resyncRef.current = resync;
|
|
3016
|
+
const setImpersonation = useCallback11(async (roles) => {
|
|
3017
|
+
if (!runnerRef.current) return;
|
|
3018
|
+
await runnerRef.current.setImpersonation(roles);
|
|
3019
|
+
}, []);
|
|
3020
|
+
const clearImpersonation = useCallback11(async () => {
|
|
3021
|
+
if (!runnerRef.current) return;
|
|
3022
|
+
await runnerRef.current.clearImpersonation();
|
|
3023
|
+
}, []);
|
|
3024
|
+
const runScenario = useCallback11(async (scenarioId) => {
|
|
3025
|
+
if (!runnerRef.current) return;
|
|
3026
|
+
const freshConfig = detectAppConfig() ?? appConfig;
|
|
3027
|
+
const scenario = freshConfig.scenarios.find((s) => s.id === scenarioId);
|
|
3028
|
+
if (!scenario) return;
|
|
3029
|
+
const result = await runnerRef.current.runScenario(scenario);
|
|
3030
|
+
if (mountedRef.current) {
|
|
3031
|
+
setSession(
|
|
3032
|
+
(prev) => prev ? { ...prev, databases: result.databases } : prev
|
|
3033
|
+
);
|
|
3034
|
+
setScenarioResult({
|
|
3035
|
+
id: scenario.id,
|
|
3036
|
+
name: scenario.name,
|
|
3037
|
+
success: result.success,
|
|
3038
|
+
duration: 0,
|
|
3039
|
+
// filled by event listener if needed
|
|
3040
|
+
roles: scenario.roles,
|
|
3041
|
+
error: result.error
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
}, [appConfig]);
|
|
3045
|
+
const submitPort = useCallback11(
|
|
3046
|
+
(port) => {
|
|
3047
|
+
setDevPort(port);
|
|
3048
|
+
setPhase("ready");
|
|
3049
|
+
},
|
|
3050
|
+
[]
|
|
3051
|
+
);
|
|
3052
|
+
const skipFrontend = useCallback11(() => {
|
|
3053
|
+
setDevPort(null);
|
|
3054
|
+
setPhase("ready");
|
|
3055
|
+
}, []);
|
|
3056
|
+
return {
|
|
3057
|
+
phase,
|
|
3058
|
+
session,
|
|
3059
|
+
error,
|
|
3060
|
+
devPort,
|
|
3061
|
+
proxyPort,
|
|
3062
|
+
webConfig,
|
|
3063
|
+
devServer,
|
|
3064
|
+
syncResult,
|
|
3065
|
+
scenarioResult,
|
|
3066
|
+
roleOverride,
|
|
3067
|
+
installStatus,
|
|
3068
|
+
authRefreshUrl,
|
|
3069
|
+
start,
|
|
3070
|
+
stop,
|
|
3071
|
+
resync,
|
|
3072
|
+
runScenario,
|
|
3073
|
+
setImpersonation,
|
|
3074
|
+
clearImpersonation,
|
|
3075
|
+
submitPort,
|
|
3076
|
+
skipFrontend
|
|
3077
|
+
};
|
|
3078
|
+
}
|
|
3079
|
+
|
|
3080
|
+
// src/tui/dev/hooks/useDevRequests.ts
|
|
3081
|
+
import { useState as useState19, useEffect as useEffect18, useRef as useRef11 } from "react";
|
|
3082
|
+
var MAX_HISTORY = 50;
|
|
3083
|
+
function useDevRequests() {
|
|
3084
|
+
const requestsRef = useRef11(/* @__PURE__ */ new Map());
|
|
3085
|
+
const [requests, setRequests] = useState19([]);
|
|
3086
|
+
useEffect18(() => {
|
|
3087
|
+
const unsubStart = devRequestEvents.onStart((event) => {
|
|
3088
|
+
const entry = {
|
|
3089
|
+
id: event.id,
|
|
3090
|
+
type: event.type,
|
|
3091
|
+
method: event.method,
|
|
3092
|
+
status: "processing",
|
|
3093
|
+
startTime: event.timestamp
|
|
3094
|
+
};
|
|
3095
|
+
requestsRef.current.set(event.id, entry);
|
|
3096
|
+
setRequests(
|
|
3097
|
+
Array.from(requestsRef.current.values()).slice(-MAX_HISTORY)
|
|
3098
|
+
);
|
|
3099
|
+
});
|
|
3100
|
+
const unsubComplete = devRequestEvents.onComplete((event) => {
|
|
3101
|
+
const existing = requestsRef.current.get(event.id);
|
|
3102
|
+
if (existing) {
|
|
3103
|
+
existing.status = event.success ? "completed" : "failed";
|
|
3104
|
+
existing.endTime = existing.startTime + event.duration;
|
|
3105
|
+
existing.duration = event.duration;
|
|
3106
|
+
existing.error = event.error;
|
|
3107
|
+
setRequests(
|
|
3108
|
+
Array.from(requestsRef.current.values()).slice(-MAX_HISTORY)
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3111
|
+
});
|
|
3112
|
+
return () => {
|
|
3113
|
+
unsubStart();
|
|
3114
|
+
unsubComplete();
|
|
3115
|
+
};
|
|
3116
|
+
}, []);
|
|
3117
|
+
useEffect18(() => {
|
|
3118
|
+
const hasActive = requests.some((r) => r.status === "processing");
|
|
3119
|
+
if (!hasActive) return;
|
|
3120
|
+
const interval = setInterval(() => {
|
|
3121
|
+
setRequests(
|
|
3122
|
+
Array.from(requestsRef.current.values()).slice(-MAX_HISTORY)
|
|
3123
|
+
);
|
|
3124
|
+
}, 1e3);
|
|
3125
|
+
return () => clearInterval(interval);
|
|
3126
|
+
}, [requests]);
|
|
3127
|
+
const activeCount = requests.filter(
|
|
3128
|
+
(r) => r.status === "processing"
|
|
3129
|
+
).length;
|
|
3130
|
+
return { requests, activeCount };
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
// src/tui/dev/pages/DevPage.tsx
|
|
3134
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3135
|
+
var TABS = [
|
|
3136
|
+
{ id: "info", label: "Info" },
|
|
3137
|
+
{ id: "requests", label: "Requests" },
|
|
3138
|
+
{ id: "methods", label: "Methods" },
|
|
3139
|
+
{ id: "server", label: "Dev Server" }
|
|
3140
|
+
];
|
|
3141
|
+
function DevPage({ appConfig, onNavigate, termHeight }) {
|
|
3142
|
+
const {
|
|
3143
|
+
phase,
|
|
3144
|
+
session,
|
|
3145
|
+
error,
|
|
3146
|
+
devPort,
|
|
3147
|
+
proxyPort,
|
|
3148
|
+
devServer,
|
|
3149
|
+
syncResult,
|
|
3150
|
+
scenarioResult,
|
|
3151
|
+
roleOverride,
|
|
3152
|
+
installStatus,
|
|
3153
|
+
start,
|
|
3154
|
+
stop,
|
|
3155
|
+
resync,
|
|
3156
|
+
runScenario,
|
|
3157
|
+
setImpersonation,
|
|
3158
|
+
clearImpersonation,
|
|
3159
|
+
submitPort,
|
|
3160
|
+
skipFrontend
|
|
3161
|
+
} = useDevSession(appConfig);
|
|
3162
|
+
const { requests, activeCount } = useDevRequests();
|
|
3163
|
+
const [activeTab, setActiveTab] = useState20("info");
|
|
3164
|
+
const [showScenarioPicker, setShowScenarioPicker] = useState20(false);
|
|
3165
|
+
const [showRolePicker, setShowRolePicker] = useState20(false);
|
|
3166
|
+
const hasScenarios = appConfig.scenarios.length > 0;
|
|
3167
|
+
const hasRoles = appConfig.roles.length > 0;
|
|
3168
|
+
const runningMenuItems = useMemo8(
|
|
3169
|
+
() => [
|
|
3170
|
+
...hasScenarios ? [{ id: "scenario", label: "Run Scenario", description: "Seed database with test data" }] : [],
|
|
3171
|
+
...hasRoles ? [{ id: "impersonate", label: "Impersonate", description: roleOverride ? `Active: ${roleOverride.join(", ")}` : "Test as a different role" }] : [],
|
|
3172
|
+
{ id: "sync", label: "Sync Schema", description: "Re-sync table definitions from disk" },
|
|
3173
|
+
{ id: "stop", label: "Stop Session", description: "Stop the dev session and clean up" },
|
|
3174
|
+
{ id: "dashboard", label: "Local Models", description: "Switch to local models view" },
|
|
3175
|
+
{ id: "quit", label: "Quit", description: "Exit the application" }
|
|
3176
|
+
],
|
|
3177
|
+
[hasScenarios, hasRoles, roleOverride]
|
|
3178
|
+
);
|
|
3179
|
+
useInput7((_input, key) => {
|
|
3180
|
+
if (phase !== "running") return;
|
|
3181
|
+
if (key.leftArrow) {
|
|
3182
|
+
const idx = TABS.findIndex((t) => t.id === activeTab);
|
|
3183
|
+
setActiveTab(TABS[(idx - 1 + TABS.length) % TABS.length].id);
|
|
3184
|
+
} else if (key.rightArrow) {
|
|
3185
|
+
const idx = TABS.findIndex((t) => t.id === activeTab);
|
|
3186
|
+
setActiveTab(TABS[(idx + 1) % TABS.length].id);
|
|
3187
|
+
}
|
|
3188
|
+
});
|
|
3189
|
+
useEffect19(() => {
|
|
3190
|
+
if (phase === "ready") {
|
|
3191
|
+
start();
|
|
3192
|
+
}
|
|
3193
|
+
}, [phase, start]);
|
|
3194
|
+
if (phase === "detecting") {
|
|
3195
|
+
return /* @__PURE__ */ jsx14(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3196
|
+
/* @__PURE__ */ jsx14(Spinner7, { type: "dots" }),
|
|
3197
|
+
" Checking app configuration..."
|
|
3198
|
+
] }) });
|
|
3199
|
+
}
|
|
3200
|
+
if (phase === "needs_port") {
|
|
3201
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
3202
|
+
/* @__PURE__ */ jsx14(AppInfoHeader, { appConfig }),
|
|
3203
|
+
/* @__PURE__ */ jsx14(DevPortPrompt, { onSubmit: submitPort, onSkip: skipFrontend })
|
|
3204
|
+
] });
|
|
3205
|
+
}
|
|
3206
|
+
if (phase === "starting") {
|
|
3207
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
3208
|
+
/* @__PURE__ */ jsx14(AppInfoHeader, { appConfig }),
|
|
3209
|
+
/* @__PURE__ */ jsxs13(Box13, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [
|
|
3210
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3211
|
+
/* @__PURE__ */ jsx14(Spinner7, { type: "dots" }),
|
|
3212
|
+
" Starting dev session..."
|
|
3213
|
+
] }),
|
|
3214
|
+
installStatus && /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3215
|
+
/* @__PURE__ */ jsx14(Spinner7, { type: "dots" }),
|
|
3216
|
+
" ",
|
|
3217
|
+
installStatus
|
|
3218
|
+
] }),
|
|
3219
|
+
devServer.phase === "starting" && /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3220
|
+
/* @__PURE__ */ jsx14(Spinner7, { type: "dots" }),
|
|
3221
|
+
" Waiting for dev server on port ",
|
|
3222
|
+
devPort,
|
|
3223
|
+
"..."
|
|
3224
|
+
] }),
|
|
3225
|
+
devServer.outputLines.slice(-6).map((line, i) => /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, wrap: "truncate", children: line }, i))
|
|
3226
|
+
] })
|
|
3227
|
+
] });
|
|
3228
|
+
}
|
|
3229
|
+
if (phase === "error") {
|
|
3230
|
+
const errorMenuItems = [
|
|
3231
|
+
{ id: "retry", label: "Retry", description: "Try starting the session again" },
|
|
3232
|
+
{ id: "dashboard", label: "Local Models", description: "Switch to local models view" },
|
|
3233
|
+
{ id: "quit", label: "Quit", description: "Exit the application" }
|
|
3234
|
+
];
|
|
3235
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
3236
|
+
/* @__PURE__ */ jsx14(AppInfoHeader, { appConfig }),
|
|
3237
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
3238
|
+
error && /* @__PURE__ */ jsxs13(Text14, { color: "red", children: [
|
|
3239
|
+
"\u2716 ",
|
|
3240
|
+
error
|
|
3241
|
+
] }),
|
|
3242
|
+
devServer.error && /* @__PURE__ */ jsxs13(Text14, { color: "red", children: [
|
|
3243
|
+
"Dev server: ",
|
|
3244
|
+
devServer.error
|
|
3245
|
+
] }),
|
|
3246
|
+
devServer.outputLines.slice(-5).map((line, i) => /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, wrap: "truncate", children: line }, i))
|
|
3247
|
+
] }),
|
|
3248
|
+
/* @__PURE__ */ jsx14(Box13, { flexGrow: 1 }),
|
|
3249
|
+
/* @__PURE__ */ jsx14(
|
|
3250
|
+
NavigationMenu,
|
|
3251
|
+
{
|
|
3252
|
+
items: errorMenuItems,
|
|
3253
|
+
onSelect: (id) => {
|
|
3254
|
+
if (id === "retry") start();
|
|
3255
|
+
else onNavigate(id);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
)
|
|
3259
|
+
] });
|
|
3260
|
+
}
|
|
3261
|
+
if (phase === "stopped") {
|
|
3262
|
+
const stoppedMenuItems = [
|
|
3263
|
+
{ id: "restart", label: "Restart", description: "Start a new dev session" },
|
|
3264
|
+
{ id: "dashboard", label: "Local Models", description: "Switch to local models view" },
|
|
3265
|
+
{ id: "quit", label: "Quit", description: "Exit the application" }
|
|
3266
|
+
];
|
|
3267
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
3268
|
+
/* @__PURE__ */ jsx14(AppInfoHeader, { appConfig }),
|
|
3269
|
+
/* @__PURE__ */ jsx14(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "Session stopped." }) }),
|
|
3270
|
+
/* @__PURE__ */ jsx14(Box13, { flexGrow: 1 }),
|
|
3271
|
+
/* @__PURE__ */ jsx14(
|
|
3272
|
+
NavigationMenu,
|
|
3273
|
+
{
|
|
3274
|
+
items: stoppedMenuItems,
|
|
3275
|
+
onSelect: (id) => {
|
|
3276
|
+
if (id === "restart") start();
|
|
3277
|
+
else onNavigate(id);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
)
|
|
3281
|
+
] });
|
|
3282
|
+
}
|
|
3283
|
+
if (phase === "expired") {
|
|
3284
|
+
const expiredMenuItems = [
|
|
3285
|
+
{ id: "restart", label: "Restart", description: "Start a new dev session" },
|
|
3286
|
+
{ id: "dashboard", label: "Local Models", description: "Switch to local models view" },
|
|
3287
|
+
{ id: "quit", label: "Quit", description: "Exit the application" }
|
|
3288
|
+
];
|
|
3289
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
3290
|
+
/* @__PURE__ */ jsx14(AppInfoHeader, { appConfig }),
|
|
3291
|
+
/* @__PURE__ */ jsx14(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: "\u26A0 Dev session expired. The platform may have timed it out." }) }),
|
|
3292
|
+
/* @__PURE__ */ jsx14(Box13, { flexGrow: 1 }),
|
|
3293
|
+
/* @__PURE__ */ jsx14(
|
|
3294
|
+
NavigationMenu,
|
|
3295
|
+
{
|
|
3296
|
+
items: expiredMenuItems,
|
|
3297
|
+
onSelect: (id) => {
|
|
3298
|
+
if (id === "restart") start();
|
|
3299
|
+
else onNavigate(id);
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
)
|
|
3303
|
+
] });
|
|
3304
|
+
}
|
|
3305
|
+
const statusLines = 4;
|
|
3306
|
+
const menuLines = 8;
|
|
3307
|
+
const contentHeight = Math.max(5, (termHeight ?? 30) - statusLines - menuLines);
|
|
3308
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, children: [
|
|
3309
|
+
/* @__PURE__ */ jsx14(Box13, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: /* @__PURE__ */ jsxs13(Box13, { gap: 2, children: [
|
|
3310
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", children: appConfig.name }),
|
|
3311
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "green", children: [
|
|
3312
|
+
"\u25CF ",
|
|
3313
|
+
session?.branch ?? "main"
|
|
3314
|
+
] }),
|
|
3315
|
+
/* @__PURE__ */ jsx14(Text14, { color: "cyan", children: session?.previewUrl ?? session?.webInterfaceUrl ?? "" })
|
|
3316
|
+
] }) }),
|
|
3317
|
+
/* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: [
|
|
3318
|
+
activeTab === "info" && /* @__PURE__ */ jsx14(
|
|
3319
|
+
InfoTab,
|
|
3320
|
+
{
|
|
3321
|
+
appConfig,
|
|
3322
|
+
session,
|
|
3323
|
+
devPort,
|
|
3324
|
+
proxyPort,
|
|
3325
|
+
devServerPhase: devServer.phase,
|
|
3326
|
+
requests,
|
|
3327
|
+
syncResult,
|
|
3328
|
+
scenarioResult,
|
|
3329
|
+
roleOverride,
|
|
3330
|
+
contentHeight
|
|
3331
|
+
}
|
|
3332
|
+
),
|
|
3333
|
+
activeTab === "requests" && /* @__PURE__ */ jsx14(DevRequestLog, { requests, maxVisible: contentHeight }),
|
|
3334
|
+
activeTab === "methods" && /* @__PURE__ */ jsx14(MethodsTab, { appConfig, contentHeight }),
|
|
3335
|
+
activeTab === "server" && /* @__PURE__ */ jsx14(
|
|
3336
|
+
DevServerTab,
|
|
3337
|
+
{
|
|
3338
|
+
devPort,
|
|
3339
|
+
phase: devServer.phase,
|
|
3340
|
+
outputLines: devServer.outputLines,
|
|
3341
|
+
error: devServer.error,
|
|
3342
|
+
contentHeight
|
|
3343
|
+
}
|
|
3344
|
+
)
|
|
3345
|
+
] }),
|
|
3346
|
+
showScenarioPicker ? /* @__PURE__ */ jsx14(
|
|
3347
|
+
NavigationMenu,
|
|
3348
|
+
{
|
|
3349
|
+
title: "Select Scenario",
|
|
3350
|
+
items: [
|
|
3351
|
+
...appConfig.scenarios.map((s) => ({
|
|
3352
|
+
id: `scenario:${s.id}`,
|
|
3353
|
+
label: s.name ?? s.id,
|
|
3354
|
+
description: s.description ?? `Roles: ${s.roles.length > 0 ? s.roles.join(", ") : "none"}`
|
|
3355
|
+
})),
|
|
3356
|
+
{ id: "back", label: "Back", description: "Return to actions" }
|
|
3357
|
+
],
|
|
3358
|
+
onSelect: (id) => {
|
|
3359
|
+
setShowScenarioPicker(false);
|
|
3360
|
+
if (id.startsWith("scenario:")) {
|
|
3361
|
+
runScenario(id.slice("scenario:".length));
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
) : showRolePicker ? /* @__PURE__ */ jsx14(
|
|
3366
|
+
NavigationMenu,
|
|
3367
|
+
{
|
|
3368
|
+
title: "Impersonate Role",
|
|
3369
|
+
items: [
|
|
3370
|
+
...appConfig.roles.map((r) => ({
|
|
3371
|
+
id: `role:${r.id}`,
|
|
3372
|
+
label: r.name ?? r.id,
|
|
3373
|
+
description: r.description ?? r.id
|
|
3374
|
+
})),
|
|
3375
|
+
...roleOverride ? [{ id: "clear", label: "Clear Override", description: "Revert to default session roles" }] : [],
|
|
3376
|
+
{ id: "back", label: "Back", description: "Return to actions" }
|
|
3377
|
+
],
|
|
3378
|
+
onSelect: (id) => {
|
|
3379
|
+
setShowRolePicker(false);
|
|
3380
|
+
if (id === "clear") {
|
|
3381
|
+
clearImpersonation();
|
|
3382
|
+
} else if (id.startsWith("role:")) {
|
|
3383
|
+
setImpersonation([id.slice("role:".length)]);
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
}
|
|
3387
|
+
) : /* @__PURE__ */ jsx14(
|
|
3388
|
+
NavigationMenu,
|
|
3389
|
+
{
|
|
3390
|
+
items: runningMenuItems,
|
|
3391
|
+
onSelect: (id) => {
|
|
3392
|
+
if (id === "scenario") setShowScenarioPicker(true);
|
|
3393
|
+
else if (id === "impersonate") setShowRolePicker(true);
|
|
3394
|
+
else if (id === "sync") resync();
|
|
3395
|
+
else if (id === "stop") stop();
|
|
3396
|
+
else onNavigate(id);
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
),
|
|
3400
|
+
/* @__PURE__ */ jsx14(TabBar, { tabs: TABS, activeTab })
|
|
3401
|
+
] });
|
|
3402
|
+
}
|
|
3403
|
+
function AppInfoHeader({ appConfig }) {
|
|
3404
|
+
return /* @__PURE__ */ jsxs13(
|
|
3405
|
+
Box13,
|
|
3406
|
+
{
|
|
3407
|
+
flexDirection: "column",
|
|
3408
|
+
paddingX: 1,
|
|
3409
|
+
paddingY: 1,
|
|
3410
|
+
borderStyle: "round",
|
|
3411
|
+
borderColor: "gray",
|
|
3412
|
+
children: [
|
|
3413
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", children: appConfig.name }),
|
|
3414
|
+
appConfig.description && /* @__PURE__ */ jsx14(Text14, { color: "gray", children: appConfig.description }),
|
|
3415
|
+
!appConfig.appId && /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: '\u26A0 No "appId" field in mindstudio.json \u2014 add your app ID to start a dev session' })
|
|
3416
|
+
]
|
|
3417
|
+
}
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3420
|
+
function InfoTab({
|
|
3421
|
+
appConfig,
|
|
3422
|
+
session,
|
|
3423
|
+
devPort,
|
|
3424
|
+
proxyPort,
|
|
3425
|
+
devServerPhase,
|
|
3426
|
+
requests,
|
|
3427
|
+
syncResult,
|
|
3428
|
+
scenarioResult,
|
|
3429
|
+
roleOverride,
|
|
3430
|
+
contentHeight
|
|
3431
|
+
}) {
|
|
3432
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
3433
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Session" }),
|
|
3434
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3435
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: "Session ID: " }),
|
|
3436
|
+
/* @__PURE__ */ jsx14(Text14, { children: session?.sessionId ?? "..." })
|
|
3437
|
+
] }),
|
|
3438
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3439
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: "Release ID: " }),
|
|
3440
|
+
/* @__PURE__ */ jsx14(Text14, { children: session?.releaseId ?? "..." })
|
|
3441
|
+
] }),
|
|
3442
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3443
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: "Branch: " }),
|
|
3444
|
+
/* @__PURE__ */ jsx14(Text14, { children: session?.branch ?? "..." })
|
|
3445
|
+
] }),
|
|
3446
|
+
session?.user && /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3447
|
+
/* @__PURE__ */ jsx14(Text14, { color: "gray", children: "User: " }),
|
|
3448
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3449
|
+
session.user.name,
|
|
3450
|
+
" (",
|
|
3451
|
+
session.user.email,
|
|
3452
|
+
")"
|
|
3453
|
+
] })
|
|
3454
|
+
] }),
|
|
3455
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "App URL" }) }),
|
|
3456
|
+
/* @__PURE__ */ jsx14(Text14, { color: "cyan", bold: true, children: session?.previewUrl ?? session?.webInterfaceUrl ?? "..." }),
|
|
3457
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Dev Server" }) }),
|
|
3458
|
+
devPort !== null ? /* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3459
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3460
|
+
"localhost:",
|
|
3461
|
+
devPort
|
|
3462
|
+
] }),
|
|
3463
|
+
devServerPhase === "running" ? /* @__PURE__ */ jsx14(Text14, { color: "green", children: " \u25CF running" }) : devServerPhase === "starting" ? /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: " \u25CB starting" }) : /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3464
|
+
" \u25CB ",
|
|
3465
|
+
devServerPhase
|
|
3466
|
+
] })
|
|
3467
|
+
] }) : /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "Backend-only mode (no frontend)" }),
|
|
3468
|
+
roleOverride && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
3469
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Impersonation" }),
|
|
3470
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "yellow", children: [
|
|
3471
|
+
" \u25CF Active: ",
|
|
3472
|
+
roleOverride.join(", ")
|
|
3473
|
+
] })
|
|
3474
|
+
] }),
|
|
3475
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Databases" }) }),
|
|
3476
|
+
(session?.databases ?? []).length === 0 ? /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "No databases" }) : session?.databases.map((db) => /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
3477
|
+
/* @__PURE__ */ jsxs13(Text14, { children: [
|
|
3478
|
+
" ",
|
|
3479
|
+
/* @__PURE__ */ jsx14(Text14, { color: "cyan", children: db.name })
|
|
3480
|
+
] }),
|
|
3481
|
+
db.tables.map((table) => /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3482
|
+
" ",
|
|
3483
|
+
table.name
|
|
3484
|
+
] }, table.name))
|
|
3485
|
+
] }, db.id)),
|
|
3486
|
+
syncResult && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
3487
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Schema Sync" }),
|
|
3488
|
+
syncResult.created.length > 0 && /* @__PURE__ */ jsxs13(Text14, { color: "green", children: [
|
|
3489
|
+
" \u2713 Created: ",
|
|
3490
|
+
syncResult.created.join(", ")
|
|
3491
|
+
] }),
|
|
3492
|
+
syncResult.altered.length > 0 && /* @__PURE__ */ jsxs13(Text14, { color: "yellow", children: [
|
|
3493
|
+
" \u2713 Altered: ",
|
|
3494
|
+
syncResult.altered.join(", ")
|
|
3495
|
+
] }),
|
|
3496
|
+
syncResult.errors.map((err, i) => /* @__PURE__ */ jsxs13(Text14, { color: "red", children: [
|
|
3497
|
+
" \u2716 ",
|
|
3498
|
+
err
|
|
3499
|
+
] }, i)),
|
|
3500
|
+
syncResult.created.length === 0 && syncResult.altered.length === 0 && syncResult.errors.length === 0 && /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: " No changes" })
|
|
3501
|
+
] }),
|
|
3502
|
+
scenarioResult && /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
3503
|
+
/* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Last Scenario" }),
|
|
3504
|
+
scenarioResult.success ? /* @__PURE__ */ jsxs13(Text14, { color: "green", children: [
|
|
3505
|
+
' \u2713 "',
|
|
3506
|
+
scenarioResult.name ?? scenarioResult.id,
|
|
3507
|
+
'" applied'
|
|
3508
|
+
] }) : /* @__PURE__ */ jsxs13(Text14, { color: "red", children: [
|
|
3509
|
+
' \u2716 "',
|
|
3510
|
+
scenarioResult.name ?? scenarioResult.id,
|
|
3511
|
+
'" failed: ',
|
|
3512
|
+
scenarioResult.error
|
|
3513
|
+
] }),
|
|
3514
|
+
scenarioResult.roles.length > 0 && /* @__PURE__ */ jsxs13(Text14, { color: "gray", dimColor: true, children: [
|
|
3515
|
+
" Roles: ",
|
|
3516
|
+
scenarioResult.roles.join(", ")
|
|
3517
|
+
] })
|
|
3518
|
+
] }),
|
|
3519
|
+
/* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, color: "white", underline: true, children: "Recent Requests" }) }),
|
|
3520
|
+
requests.length === 0 ? /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "No requests yet" }) : requests.slice(-5).map((req) => /* @__PURE__ */ jsxs13(Box13, { gap: 1, children: [
|
|
3521
|
+
req.status === "completed" && /* @__PURE__ */ jsx14(Text14, { color: "green", children: "\u2713" }),
|
|
3522
|
+
req.status === "failed" && /* @__PURE__ */ jsx14(Text14, { color: "red", children: "\u2716" }),
|
|
3523
|
+
req.status === "processing" && /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "\u25CF" }),
|
|
3524
|
+
/* @__PURE__ */ jsx14(Text14, { children: req.method ?? "unknown" }),
|
|
3525
|
+
req.duration != null && /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3526
|
+
req.duration,
|
|
3527
|
+
"ms"
|
|
3528
|
+
] }),
|
|
3529
|
+
req.status === "failed" && req.error && /* @__PURE__ */ jsx14(Text14, { color: "red", wrap: "truncate", children: req.error.split("\n")[0] })
|
|
3530
|
+
] }, req.id))
|
|
3531
|
+
] });
|
|
3532
|
+
}
|
|
3533
|
+
function MethodsTab({
|
|
3534
|
+
appConfig,
|
|
3535
|
+
contentHeight
|
|
3536
|
+
}) {
|
|
3537
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
3538
|
+
/* @__PURE__ */ jsxs13(Text14, { bold: true, color: "white", underline: true, children: [
|
|
3539
|
+
"Methods (",
|
|
3540
|
+
appConfig.methods.length,
|
|
3541
|
+
")"
|
|
3542
|
+
] }),
|
|
3543
|
+
appConfig.methods.slice(0, contentHeight - 2).map((method) => /* @__PURE__ */ jsxs13(Box13, { gap: 1, children: [
|
|
3544
|
+
/* @__PURE__ */ jsx14(Text14, { color: "cyan", children: method.export }),
|
|
3545
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "gray", dimColor: true, children: [
|
|
3546
|
+
"\u2192 ",
|
|
3547
|
+
method.id
|
|
3548
|
+
] }),
|
|
3549
|
+
/* @__PURE__ */ jsxs13(Text14, { color: "gray", dimColor: true, children: [
|
|
3550
|
+
"(",
|
|
3551
|
+
method.path,
|
|
3552
|
+
")"
|
|
3553
|
+
] })
|
|
3554
|
+
] }, method.id)),
|
|
3555
|
+
appConfig.methods.length === 0 && /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "No methods defined" })
|
|
3556
|
+
] });
|
|
3557
|
+
}
|
|
3558
|
+
function DevServerTab({
|
|
3559
|
+
devPort,
|
|
3560
|
+
phase,
|
|
3561
|
+
outputLines,
|
|
3562
|
+
error,
|
|
3563
|
+
contentHeight
|
|
3564
|
+
}) {
|
|
3565
|
+
const visibleLines = contentHeight - 3;
|
|
3566
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, marginTop: 1, children: [
|
|
3567
|
+
/* @__PURE__ */ jsxs13(Text14, { bold: true, color: "white", underline: true, children: [
|
|
3568
|
+
"Dev Server",
|
|
3569
|
+
phase === "running" ? /* @__PURE__ */ jsx14(Text14, { color: "green", children: " \u25CF running" }) : phase === "starting" ? /* @__PURE__ */ jsx14(Text14, { color: "yellow", children: " \u25CB starting" }) : phase === "error" ? /* @__PURE__ */ jsx14(Text14, { color: "red", children: " \u2716 error" }) : /* @__PURE__ */ jsxs13(Text14, { color: "gray", children: [
|
|
3570
|
+
" \u25CB ",
|
|
3571
|
+
phase
|
|
3572
|
+
] })
|
|
3573
|
+
] }),
|
|
3574
|
+
devPort !== null && /* @__PURE__ */ jsxs13(Text14, { color: "gray", dimColor: true, children: [
|
|
3575
|
+
"localhost:",
|
|
3576
|
+
devPort
|
|
3577
|
+
] }),
|
|
3578
|
+
devPort === null && /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "Backend-only mode (no frontend)" }),
|
|
3579
|
+
error && /* @__PURE__ */ jsx14(Text14, { color: "red", children: error }),
|
|
3580
|
+
outputLines.slice(-visibleLines).map((line, i) => /* @__PURE__ */ jsx14(Text14, { color: "gray", wrap: "truncate", children: line }, i)),
|
|
3581
|
+
outputLines.length === 0 && phase !== "idle" && !error && /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "Waiting for output..." }),
|
|
3582
|
+
outputLines.length === 0 && phase === "idle" && !error && /* @__PURE__ */ jsx14(Text14, { color: "gray", dimColor: true, children: "Dev server not started" })
|
|
3583
|
+
] });
|
|
3584
|
+
}
|
|
3585
|
+
|
|
2445
3586
|
// src/tui/App.tsx
|
|
2446
|
-
import { Fragment as Fragment5, jsx as
|
|
3587
|
+
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2447
3588
|
var MODEL_TYPE_MAP = {
|
|
2448
3589
|
text: "llm_chat",
|
|
2449
3590
|
image: "image_generation",
|
|
2450
3591
|
video: "video_generation"
|
|
2451
3592
|
};
|
|
2452
|
-
function App({ runner }) {
|
|
3593
|
+
function App({ runner, appConfig }) {
|
|
2453
3594
|
const { exit } = useApp();
|
|
2454
|
-
const { stdout } =
|
|
3595
|
+
const { stdout } = useStdout8();
|
|
2455
3596
|
const {
|
|
2456
3597
|
status: connectionStatus,
|
|
2457
3598
|
environment,
|
|
@@ -2477,31 +3618,31 @@ function App({ runner }) {
|
|
|
2477
3618
|
refresh: refreshSynced
|
|
2478
3619
|
} = useSyncedModels(connectionStatus);
|
|
2479
3620
|
const shouldOnboard = getApiKey() === void 0 || getUserId() === void 0;
|
|
2480
|
-
const [page, setPage] =
|
|
2481
|
-
shouldOnboard ? "onboarding" : "dashboard"
|
|
3621
|
+
const [page, setPage] = useState21(
|
|
3622
|
+
shouldOnboard ? "onboarding" : appConfig ? "dev" : "dashboard"
|
|
2482
3623
|
);
|
|
2483
|
-
const [syncStatus, setSyncStatus] =
|
|
3624
|
+
const [syncStatus, setSyncStatus] = useState21(
|
|
2484
3625
|
"idle"
|
|
2485
3626
|
);
|
|
2486
|
-
const syncTimerRef =
|
|
2487
|
-
const lastSyncPayloadRef =
|
|
2488
|
-
|
|
3627
|
+
const syncTimerRef = useRef12(void 0);
|
|
3628
|
+
const lastSyncPayloadRef = useRef12("");
|
|
3629
|
+
useEffect20(() => {
|
|
2489
3630
|
if (connectionStatus === "not_authenticated") {
|
|
2490
3631
|
setPage("onboarding");
|
|
2491
3632
|
}
|
|
2492
3633
|
}, [connectionStatus]);
|
|
2493
|
-
|
|
3634
|
+
useEffect20(() => {
|
|
2494
3635
|
if (page === "dashboard") {
|
|
2495
3636
|
refreshAll();
|
|
2496
3637
|
}
|
|
2497
3638
|
}, [page]);
|
|
2498
|
-
|
|
3639
|
+
useEffect20(() => {
|
|
2499
3640
|
if (connectionStatus === "connected" && syncedModels.length > 0) {
|
|
2500
3641
|
runner.start(syncedModels);
|
|
2501
3642
|
}
|
|
2502
3643
|
}, [connectionStatus, syncedModels, runner]);
|
|
2503
|
-
|
|
2504
|
-
const refreshAll =
|
|
3644
|
+
useEffect20(() => () => runner.stop(), [runner]);
|
|
3645
|
+
const refreshAll = useCallback12(
|
|
2505
3646
|
async (silent = false) => {
|
|
2506
3647
|
if (!silent) setSyncStatus("syncing");
|
|
2507
3648
|
const [discoveredModels] = await Promise.all([
|
|
@@ -2531,21 +3672,21 @@ function App({ runner }) {
|
|
|
2531
3672
|
},
|
|
2532
3673
|
[refreshProviders, refreshModels, refreshSynced]
|
|
2533
3674
|
);
|
|
2534
|
-
|
|
3675
|
+
useEffect20(() => {
|
|
2535
3676
|
if (connectionStatus !== "connected" || page !== "dashboard") return;
|
|
2536
3677
|
const interval = setInterval(() => refreshAll(true), 1500);
|
|
2537
3678
|
return () => clearInterval(interval);
|
|
2538
3679
|
}, [connectionStatus, page, refreshAll]);
|
|
2539
|
-
const handleQuit =
|
|
3680
|
+
const handleQuit = useCallback12(() => {
|
|
2540
3681
|
runner.stop();
|
|
2541
3682
|
exit();
|
|
2542
3683
|
}, [runner, exit]);
|
|
2543
|
-
const handleOnboardingComplete =
|
|
3684
|
+
const handleOnboardingComplete = useCallback12(() => {
|
|
2544
3685
|
retryConnection();
|
|
2545
3686
|
refreshAll();
|
|
2546
3687
|
setPage("dashboard");
|
|
2547
3688
|
}, [retryConnection, refreshAll]);
|
|
2548
|
-
const handleNavigate =
|
|
3689
|
+
const handleNavigate = useCallback12(
|
|
2549
3690
|
(id) => {
|
|
2550
3691
|
switch (id) {
|
|
2551
3692
|
case "interfaces":
|
|
@@ -2557,6 +3698,12 @@ function App({ runner }) {
|
|
|
2557
3698
|
case "setup":
|
|
2558
3699
|
setPage("setup");
|
|
2559
3700
|
break;
|
|
3701
|
+
case "dev":
|
|
3702
|
+
setPage("dev");
|
|
3703
|
+
break;
|
|
3704
|
+
case "dashboard":
|
|
3705
|
+
setPage("dashboard");
|
|
3706
|
+
break;
|
|
2560
3707
|
case "refresh":
|
|
2561
3708
|
refreshAll();
|
|
2562
3709
|
break;
|
|
@@ -2570,7 +3717,7 @@ function App({ runner }) {
|
|
|
2570
3717
|
const subpageMenuItems = [
|
|
2571
3718
|
{ id: "back", label: "Back", description: "Return to dashboard" }
|
|
2572
3719
|
];
|
|
2573
|
-
const handleSubpageNavigate =
|
|
3720
|
+
const handleSubpageNavigate = useCallback12(
|
|
2574
3721
|
(id) => {
|
|
2575
3722
|
if (id === "back") {
|
|
2576
3723
|
setPage("dashboard");
|
|
@@ -2580,11 +3727,11 @@ function App({ runner }) {
|
|
|
2580
3727
|
},
|
|
2581
3728
|
[handleNavigate]
|
|
2582
3729
|
);
|
|
2583
|
-
const [termSize, setTermSize] =
|
|
3730
|
+
const [termSize, setTermSize] = useState21({
|
|
2584
3731
|
rows: stdout?.rows ?? 24,
|
|
2585
3732
|
columns: stdout?.columns ?? 80
|
|
2586
3733
|
});
|
|
2587
|
-
|
|
3734
|
+
useEffect20(() => {
|
|
2588
3735
|
if (!stdout) return;
|
|
2589
3736
|
const onResize = () => {
|
|
2590
3737
|
stdout.write("\x1B[2J\x1B[H");
|
|
@@ -2597,8 +3744,8 @@ function App({ runner }) {
|
|
|
2597
3744
|
}, [stdout]);
|
|
2598
3745
|
const termHeight = termSize.rows - 4;
|
|
2599
3746
|
const compactHeader = termSize.rows <= 45 || termSize.columns <= 90;
|
|
2600
|
-
return /* @__PURE__ */
|
|
2601
|
-
/* @__PURE__ */
|
|
3747
|
+
return /* @__PURE__ */ jsx15(Box14, { flexDirection: "column", height: termHeight, overflow: "hidden", children: page === "onboarding" ? /* @__PURE__ */ jsx15(OnboardingPage, { onComplete: handleOnboardingComplete }) : /* @__PURE__ */ jsxs14(Fragment5, { children: [
|
|
3748
|
+
/* @__PURE__ */ jsx15(
|
|
2602
3749
|
Header,
|
|
2603
3750
|
{
|
|
2604
3751
|
connection: connectionStatus,
|
|
@@ -2609,7 +3756,7 @@ function App({ runner }) {
|
|
|
2609
3756
|
hasActiveRequest: activeRequestCount > 0
|
|
2610
3757
|
}
|
|
2611
3758
|
),
|
|
2612
|
-
page === "dashboard" && /* @__PURE__ */
|
|
3759
|
+
page === "dashboard" && /* @__PURE__ */ jsx15(
|
|
2613
3760
|
DashboardPage,
|
|
2614
3761
|
{
|
|
2615
3762
|
requests,
|
|
@@ -2625,8 +3772,8 @@ function App({ runner }) {
|
|
|
2625
3772
|
onNavigate: handleNavigate
|
|
2626
3773
|
}
|
|
2627
3774
|
),
|
|
2628
|
-
page === "setup" && /* @__PURE__ */
|
|
2629
|
-
page === "interfaces" && /* @__PURE__ */
|
|
3775
|
+
page === "setup" && /* @__PURE__ */ jsx15(SetupPage, { onBack: () => setPage("dashboard") }),
|
|
3776
|
+
page === "interfaces" && /* @__PURE__ */ jsx15(
|
|
2630
3777
|
InterfacesPage,
|
|
2631
3778
|
{
|
|
2632
3779
|
onBack: () => setPage("dashboard"),
|
|
@@ -2635,12 +3782,12 @@ function App({ runner }) {
|
|
|
2635
3782
|
refresh: editorSessions.refresh
|
|
2636
3783
|
}
|
|
2637
3784
|
),
|
|
2638
|
-
page
|
|
2639
|
-
|
|
2640
|
-
NavigationMenu,
|
|
3785
|
+
page === "dev" && appConfig && /* @__PURE__ */ jsx15(
|
|
3786
|
+
DevPage,
|
|
2641
3787
|
{
|
|
2642
|
-
|
|
2643
|
-
|
|
3788
|
+
appConfig,
|
|
3789
|
+
onNavigate: handleNavigate,
|
|
3790
|
+
termHeight
|
|
2644
3791
|
}
|
|
2645
3792
|
)
|
|
2646
3793
|
] }) });
|
|
@@ -2656,7 +3803,7 @@ function getInstallMethod() {
|
|
|
2656
3803
|
return "npm";
|
|
2657
3804
|
}
|
|
2658
3805
|
function getCurrentVersion() {
|
|
2659
|
-
return "0.5.
|
|
3806
|
+
return "0.5.8";
|
|
2660
3807
|
}
|
|
2661
3808
|
async function fetchLatestVersion() {
|
|
2662
3809
|
try {
|
|
@@ -2702,15 +3849,15 @@ async function checkForUpdate() {
|
|
|
2702
3849
|
}
|
|
2703
3850
|
|
|
2704
3851
|
// src/tui/components/UpdatePrompt.tsx
|
|
2705
|
-
import { useState as
|
|
2706
|
-
import { Box as
|
|
3852
|
+
import { useState as useState22, useEffect as useEffect21, useMemo as useMemo9 } from "react";
|
|
3853
|
+
import { Box as Box15, Text as Text15, useInput as useInput8, useStdout as useStdout9 } from "ink";
|
|
2707
3854
|
import chalk4 from "chalk";
|
|
2708
|
-
import { jsx as
|
|
3855
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2709
3856
|
var SHIMMER_SPEED2 = 35;
|
|
2710
3857
|
function useShimmerLogo2() {
|
|
2711
|
-
const [frame, setFrame] =
|
|
2712
|
-
const lines =
|
|
2713
|
-
const totalChars =
|
|
3858
|
+
const [frame, setFrame] = useState22(0);
|
|
3859
|
+
const lines = useMemo9(() => LogoString.split("\n"), []);
|
|
3860
|
+
const totalChars = useMemo9(() => {
|
|
2714
3861
|
let count = 0;
|
|
2715
3862
|
for (const line of lines) {
|
|
2716
3863
|
for (const ch of line) {
|
|
@@ -2720,13 +3867,13 @@ function useShimmerLogo2() {
|
|
|
2720
3867
|
return count;
|
|
2721
3868
|
}, [lines]);
|
|
2722
3869
|
const cycleLength = totalChars + 40;
|
|
2723
|
-
|
|
3870
|
+
useEffect21(() => {
|
|
2724
3871
|
const interval = setInterval(() => {
|
|
2725
3872
|
setFrame((f) => (f + 1) % cycleLength);
|
|
2726
3873
|
}, SHIMMER_SPEED2);
|
|
2727
3874
|
return () => clearInterval(interval);
|
|
2728
3875
|
}, [cycleLength]);
|
|
2729
|
-
return
|
|
3876
|
+
return useMemo9(() => {
|
|
2730
3877
|
const sweepPos = frame;
|
|
2731
3878
|
const holdEnd = totalChars + 20;
|
|
2732
3879
|
let charIdx = 0;
|
|
@@ -2769,19 +3916,19 @@ function UpdatePrompt({
|
|
|
2769
3916
|
latestVersion,
|
|
2770
3917
|
onChoice
|
|
2771
3918
|
}) {
|
|
2772
|
-
const { stdout } =
|
|
3919
|
+
const { stdout } = useStdout9();
|
|
2773
3920
|
const shimmerLogo = useShimmerLogo2();
|
|
2774
|
-
|
|
3921
|
+
useInput8(() => {
|
|
2775
3922
|
onChoice(true);
|
|
2776
3923
|
});
|
|
2777
3924
|
const termHeight = (stdout?.rows ?? 24) - 4;
|
|
2778
|
-
return /* @__PURE__ */
|
|
2779
|
-
/* @__PURE__ */
|
|
2780
|
-
/* @__PURE__ */
|
|
2781
|
-
/* @__PURE__ */
|
|
2782
|
-
/* @__PURE__ */
|
|
2783
|
-
/* @__PURE__ */
|
|
2784
|
-
/* @__PURE__ */
|
|
3925
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", height: termHeight, children: [
|
|
3926
|
+
/* @__PURE__ */ jsx16(Box15, { flexGrow: 1 }),
|
|
3927
|
+
/* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", alignItems: "center", children: [
|
|
3928
|
+
/* @__PURE__ */ jsx16(Text15, { children: shimmerLogo }),
|
|
3929
|
+
/* @__PURE__ */ jsx16(Box15, { flexDirection: "column", alignItems: "center", marginTop: 2, children: /* @__PURE__ */ jsx16(Text15, { bold: true, color: "white", children: "MindStudio Local Tunnel" }) }),
|
|
3930
|
+
/* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", alignItems: "center", children: [
|
|
3931
|
+
/* @__PURE__ */ jsxs15(Text15, { color: "gray", children: [
|
|
2785
3932
|
"Update required ",
|
|
2786
3933
|
"\u2022",
|
|
2787
3934
|
" v",
|
|
@@ -2791,19 +3938,19 @@ function UpdatePrompt({
|
|
|
2791
3938
|
" v",
|
|
2792
3939
|
latestVersion
|
|
2793
3940
|
] }),
|
|
2794
|
-
/* @__PURE__ */
|
|
3941
|
+
/* @__PURE__ */ jsx16(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text15, { color: "cyan", bold: true, children: "Press any key to update" }) })
|
|
2795
3942
|
] })
|
|
2796
3943
|
] }),
|
|
2797
|
-
/* @__PURE__ */
|
|
3944
|
+
/* @__PURE__ */ jsx16(Box15, { flexGrow: 1 })
|
|
2798
3945
|
] });
|
|
2799
3946
|
}
|
|
2800
3947
|
|
|
2801
3948
|
// src/tui/index.tsx
|
|
2802
|
-
import { jsx as
|
|
3949
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
2803
3950
|
async function promptForUpdate(currentVersion, latestVersion) {
|
|
2804
3951
|
return new Promise((resolve) => {
|
|
2805
3952
|
const { unmount } = render(
|
|
2806
|
-
/* @__PURE__ */
|
|
3953
|
+
/* @__PURE__ */ jsx17(
|
|
2807
3954
|
UpdatePrompt,
|
|
2808
3955
|
{
|
|
2809
3956
|
currentVersion,
|
|
@@ -2886,14 +4033,16 @@ async function startTUI() {
|
|
|
2886
4033
|
}
|
|
2887
4034
|
console.clear();
|
|
2888
4035
|
}
|
|
4036
|
+
const appConfig = detectAppConfig(process.cwd());
|
|
2889
4037
|
const runner = new TunnelRunner();
|
|
2890
|
-
const { waitUntilExit } = render(
|
|
2891
|
-
|
|
2892
|
-
|
|
4038
|
+
const { waitUntilExit } = render(
|
|
4039
|
+
/* @__PURE__ */ jsx17(App, { runner, appConfig: appConfig ?? void 0 }),
|
|
4040
|
+
{ exitOnCtrlC: true }
|
|
4041
|
+
);
|
|
2893
4042
|
await waitUntilExit();
|
|
2894
4043
|
runner.stop();
|
|
2895
4044
|
}
|
|
2896
4045
|
export {
|
|
2897
4046
|
startTUI
|
|
2898
4047
|
};
|
|
2899
|
-
//# sourceMappingURL=tui-
|
|
4048
|
+
//# sourceMappingURL=tui-4PJCFILV.js.map
|