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