everything-dev 1.27.0 → 1.28.1
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/dist/cli/infra.cjs +1 -1
- package/dist/cli/infra.mjs +1 -1
- package/dist/cli/init.cjs +34 -9
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +2 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +2 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +34 -9
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +28 -24
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +27 -24
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +40 -3
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +40 -3
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli.cjs +187 -12
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +186 -11
- package/dist/cli.mjs.map +1 -1
- package/dist/config.cjs +1 -0
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +1 -0
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +1 -1
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +38 -34
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +38 -34
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +1 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/dev-session.cjs +0 -1
- package/dist/dev-session.mjs +1 -1
- package/dist/index.cjs +0 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +0 -1
- package/dist/near-cli.cjs +1 -1
- package/dist/near-cli.mjs +1 -1
- package/dist/orchestrator.cjs +1 -1
- package/dist/orchestrator.mjs +1 -1
- package/dist/plugin.cjs +183 -151
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +67 -34
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +66 -34
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +173 -142
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.d.cts +34 -0
- package/dist/service-descriptor.d.cts.map +1 -0
- package/dist/service-descriptor.d.mts +36 -0
- package/dist/service-descriptor.d.mts.map +1 -0
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/dist/utils/run.cjs +9 -20
- package/dist/utils/run.cjs.map +1 -1
- package/dist/utils/run.mjs +9 -20
- package/dist/utils/run.mjs.map +1 -1
- package/package.json +2 -2
- package/src/api-contract.ts +0 -623
- package/src/app.ts +0 -193
- package/src/cli/catalog.ts +0 -49
- package/src/cli/framework-version.ts +0 -61
- package/src/cli/help.ts +0 -13
- package/src/cli/infra.ts +0 -190
- package/src/cli/init.ts +0 -1145
- package/src/cli/parse.ts +0 -147
- package/src/cli/prompts.ts +0 -135
- package/src/cli/snapshot.ts +0 -46
- package/src/cli/status.ts +0 -99
- package/src/cli/sync.ts +0 -429
- package/src/cli/timing.ts +0 -63
- package/src/cli/upgrade.ts +0 -869
- package/src/cli.ts +0 -516
- package/src/components/dev-view.tsx +0 -352
- package/src/components/streaming-view.ts +0 -177
- package/src/config.ts +0 -893
- package/src/contract.meta.ts +0 -140
- package/src/contract.ts +0 -326
- package/src/dev-logs.ts +0 -92
- package/src/dev-session.ts +0 -283
- package/src/fastkv.ts +0 -181
- package/src/index.ts +0 -8
- package/src/integrity.ts +0 -138
- package/src/internal/manifest-normalizer.ts +0 -290
- package/src/merge.ts +0 -187
- package/src/mf.ts +0 -147
- package/src/near-cli.ts +0 -259
- package/src/network.ts +0 -3
- package/src/orchestrator.ts +0 -493
- package/src/plugin.ts +0 -1799
- package/src/sdk.ts +0 -14
- package/src/service-descriptor.ts +0 -281
- package/src/shared.ts +0 -249
- package/src/sidebar.ts +0 -140
- package/src/types.ts +0 -330
- package/src/ui/head.ts +0 -83
- package/src/ui/index.ts +0 -5
- package/src/ui/metadata.ts +0 -95
- package/src/ui/router.ts +0 -88
- package/src/ui/runtime.ts +0 -42
- package/src/ui/types.ts +0 -65
- package/src/utils/banner.ts +0 -21
- package/src/utils/linkify.ts +0 -11
- package/src/utils/path-match.ts +0 -16
- package/src/utils/run.ts +0 -31
- package/src/utils/save-config.ts +0 -20
- package/src/utils/theme.ts +0 -39
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
import { Box, render, Text, useApp, useInput } from "ink";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import type { SourceMode } from "../types";
|
|
4
|
-
import { linkify } from "../utils/linkify";
|
|
5
|
-
import { colors, divider, frames, gradients, icons } from "../utils/theme";
|
|
6
|
-
|
|
7
|
-
const PLUGIN_PREFIX = "plugin:";
|
|
8
|
-
|
|
9
|
-
export type ProcessStatus = "pending" | "starting" | "ready" | "error";
|
|
10
|
-
|
|
11
|
-
export interface ProcessState {
|
|
12
|
-
name: string;
|
|
13
|
-
status: ProcessStatus;
|
|
14
|
-
port: number;
|
|
15
|
-
message?: string;
|
|
16
|
-
source?: SourceMode;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface LogEntry {
|
|
20
|
-
id: string;
|
|
21
|
-
source: string;
|
|
22
|
-
line: string;
|
|
23
|
-
timestamp: number;
|
|
24
|
-
isError?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface DevViewProps {
|
|
28
|
-
processes: ProcessState[];
|
|
29
|
-
logs: LogEntry[];
|
|
30
|
-
description: string;
|
|
31
|
-
proxyTarget?: string;
|
|
32
|
-
onExit?: () => Promise<void> | void;
|
|
33
|
-
onExportLogs?: () => Promise<void> | void;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function StatusIcon({ status }: { status: ProcessStatus }) {
|
|
37
|
-
switch (status) {
|
|
38
|
-
case "pending":
|
|
39
|
-
return <Text color="gray">{icons.pending}</Text>;
|
|
40
|
-
case "starting":
|
|
41
|
-
return <Text color="#00ffff">{icons.scan}</Text>;
|
|
42
|
-
case "ready":
|
|
43
|
-
return <Text color="#00ff41">{icons.ok}</Text>;
|
|
44
|
-
case "error":
|
|
45
|
-
return <Text color="#ff3366">{icons.err}</Text>;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function getServiceColor(name: string): string {
|
|
50
|
-
if (name.startsWith(PLUGIN_PREFIX)) return "#ffaa00";
|
|
51
|
-
return name === "host" ? "#00ffff" : name === "ui" ? "#ff00ff" : "#0080ff";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function getDisplayName(name: string): string {
|
|
55
|
-
return name.startsWith(PLUGIN_PREFIX)
|
|
56
|
-
? name.slice(PLUGIN_PREFIX.length).toUpperCase()
|
|
57
|
-
: name.toUpperCase();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function isPlugin(name: string): boolean {
|
|
61
|
-
return name.startsWith(PLUGIN_PREFIX);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function getSectionedProcesses(processes: ProcessState[]): Array<{
|
|
65
|
-
key: string;
|
|
66
|
-
title: string;
|
|
67
|
-
processes: ProcessState[];
|
|
68
|
-
}> {
|
|
69
|
-
const plugins = processes.filter((p) => isPlugin(p.name));
|
|
70
|
-
const services = processes.filter((p) => !isPlugin(p.name));
|
|
71
|
-
const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];
|
|
72
|
-
if (plugins.length > 0) sections.push({ key: "plugins", title: "PLUGINS", processes: plugins });
|
|
73
|
-
if (services.length > 0)
|
|
74
|
-
sections.push({ key: "services", title: "SERVICES", processes: services });
|
|
75
|
-
return sections;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function getColumnWidths(processes: ProcessState[]): { name: number; source: number } {
|
|
79
|
-
const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));
|
|
80
|
-
const source = Math.max(10, ...processes.map((p) => (p.source ? `(${p.source})`.length : 0)));
|
|
81
|
-
return { name, source };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function ProcessRow({
|
|
85
|
-
proc,
|
|
86
|
-
nameWidth,
|
|
87
|
-
sourceWidth,
|
|
88
|
-
}: {
|
|
89
|
-
proc: ProcessState;
|
|
90
|
-
nameWidth: number;
|
|
91
|
-
sourceWidth: number;
|
|
92
|
-
}) {
|
|
93
|
-
const color = getServiceColor(proc.name);
|
|
94
|
-
const isRemote = proc.source === "remote";
|
|
95
|
-
const isHost = proc.name === "host";
|
|
96
|
-
const showPort = proc.port > 0 && (isHost || !isRemote);
|
|
97
|
-
const portStr = showPort ? `:${proc.port}` : "";
|
|
98
|
-
const sourceLabel = proc.source ? ` (${proc.source})` : "";
|
|
99
|
-
|
|
100
|
-
const statusText =
|
|
101
|
-
proc.status === "pending"
|
|
102
|
-
? "waiting"
|
|
103
|
-
: proc.status === "starting"
|
|
104
|
-
? "starting"
|
|
105
|
-
: proc.status === "ready"
|
|
106
|
-
? isRemote && !isHost
|
|
107
|
-
? "loaded"
|
|
108
|
-
: "running"
|
|
109
|
-
: "failed";
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<Box>
|
|
113
|
-
<Text>{" "}</Text>
|
|
114
|
-
<StatusIcon status={proc.status} />
|
|
115
|
-
<Text> </Text>
|
|
116
|
-
<Text color={color} bold>
|
|
117
|
-
{getDisplayName(proc.name).padEnd(nameWidth)}
|
|
118
|
-
</Text>
|
|
119
|
-
<Text color="gray">{sourceLabel.padEnd(sourceWidth)}</Text>
|
|
120
|
-
<Text color={proc.status === "ready" ? "#00ff41" : "gray"}>{statusText}</Text>
|
|
121
|
-
{showPort && <Text color="#00ffff"> {portStr}</Text>}
|
|
122
|
-
</Box>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function SectionHeader({ title }: { title: string }) {
|
|
127
|
-
return (
|
|
128
|
-
<Box marginBottom={0} marginTop={1}>
|
|
129
|
-
<Text color="#00ffff" bold>
|
|
130
|
-
{title}
|
|
131
|
-
</Text>
|
|
132
|
-
</Box>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function LogLine({ entry }: { entry: LogEntry }) {
|
|
137
|
-
const color = getServiceColor(entry.source);
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<Box>
|
|
141
|
-
<Text color={color}>[{entry.source}]</Text>
|
|
142
|
-
<Text color={entry.isError ? "#ff3366" : undefined}> {linkify(entry.line)}</Text>
|
|
143
|
-
</Box>
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function truncateUrl(url: string, maxLen: number): string {
|
|
148
|
-
if (url.length <= maxLen) return url;
|
|
149
|
-
try {
|
|
150
|
-
const parsed = new URL(url);
|
|
151
|
-
const host = parsed.host;
|
|
152
|
-
if (host.length > maxLen - 10) {
|
|
153
|
-
return `${host.slice(0, maxLen - 13)}...`;
|
|
154
|
-
}
|
|
155
|
-
return host;
|
|
156
|
-
} catch {
|
|
157
|
-
return `${url.slice(0, maxLen - 3)}...`;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function DevView({
|
|
162
|
-
processes,
|
|
163
|
-
logs,
|
|
164
|
-
description,
|
|
165
|
-
proxyTarget,
|
|
166
|
-
onExit,
|
|
167
|
-
onExportLogs,
|
|
168
|
-
}: DevViewProps) {
|
|
169
|
-
const { exit } = useApp();
|
|
170
|
-
const [isShuttingDown, setIsShuttingDown] = useState(false);
|
|
171
|
-
|
|
172
|
-
useInput((input, key) => {
|
|
173
|
-
if (isShuttingDown) return;
|
|
174
|
-
|
|
175
|
-
if (input === "q" || (key.ctrl && input === "c")) {
|
|
176
|
-
setIsShuttingDown(true);
|
|
177
|
-
Promise.resolve(onExit?.()).then(() => {
|
|
178
|
-
exit();
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
if (input === "l") {
|
|
182
|
-
setIsShuttingDown(true);
|
|
183
|
-
Promise.resolve(onExportLogs?.()).then(() => {
|
|
184
|
-
exit();
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const readyCount = processes.filter((p) => p.status === "ready").length;
|
|
190
|
-
const total = processes.length;
|
|
191
|
-
const allReady = readyCount === total;
|
|
192
|
-
const hostProcess = processes.find((p) => p.name === "host");
|
|
193
|
-
const hostPort = hostProcess?.port || 3000;
|
|
194
|
-
const recentLogs = logs.slice(-12);
|
|
195
|
-
const sectionedProcesses = getSectionedProcesses(processes);
|
|
196
|
-
const columnWidths = getColumnWidths(processes);
|
|
197
|
-
|
|
198
|
-
return (
|
|
199
|
-
<Box flexDirection="column">
|
|
200
|
-
<Box marginBottom={0}>
|
|
201
|
-
<Text color="#00ffff">{frames.top(52)}</Text>
|
|
202
|
-
</Box>
|
|
203
|
-
<Box>
|
|
204
|
-
<Text>
|
|
205
|
-
{" "}
|
|
206
|
-
{icons.run} {gradients.cyber(description.toUpperCase())}
|
|
207
|
-
</Text>
|
|
208
|
-
</Box>
|
|
209
|
-
<Box marginBottom={1}>
|
|
210
|
-
<Text color="#00ffff">{frames.bottom(52)}</Text>
|
|
211
|
-
</Box>
|
|
212
|
-
|
|
213
|
-
{allReady && (
|
|
214
|
-
<Box marginBottom={1} flexDirection="column">
|
|
215
|
-
<Box>
|
|
216
|
-
<Text color="#00ff41">
|
|
217
|
-
{" "}
|
|
218
|
-
{icons.app} APP READY
|
|
219
|
-
</Text>
|
|
220
|
-
</Box>
|
|
221
|
-
<Box>
|
|
222
|
-
<Text color="#00ff41" bold>
|
|
223
|
-
{" "}
|
|
224
|
-
{icons.arrow} http://localhost:{hostPort}
|
|
225
|
-
</Text>
|
|
226
|
-
</Box>
|
|
227
|
-
</Box>
|
|
228
|
-
)}
|
|
229
|
-
|
|
230
|
-
{proxyTarget && (
|
|
231
|
-
<Box marginBottom={1}>
|
|
232
|
-
<Text color="#ffaa00">
|
|
233
|
-
{" "}
|
|
234
|
-
{icons.arrow} API PROXY → {truncateUrl(proxyTarget, 38)}
|
|
235
|
-
</Text>
|
|
236
|
-
</Box>
|
|
237
|
-
)}
|
|
238
|
-
|
|
239
|
-
<Box marginTop={0} marginBottom={0}>
|
|
240
|
-
<Text>{colors.dim(divider(52))}</Text>
|
|
241
|
-
</Box>
|
|
242
|
-
|
|
243
|
-
{sectionedProcesses.map((section) => (
|
|
244
|
-
<Box key={section.key} flexDirection="column">
|
|
245
|
-
<SectionHeader title={section.title} />
|
|
246
|
-
{section.processes.map((proc) => (
|
|
247
|
-
<ProcessRow
|
|
248
|
-
key={proc.name}
|
|
249
|
-
proc={proc}
|
|
250
|
-
nameWidth={columnWidths.name}
|
|
251
|
-
sourceWidth={columnWidths.source}
|
|
252
|
-
/>
|
|
253
|
-
))}
|
|
254
|
-
</Box>
|
|
255
|
-
))}
|
|
256
|
-
|
|
257
|
-
<Box marginTop={1} marginBottom={0}>
|
|
258
|
-
<Text>{colors.dim(divider(52))}</Text>
|
|
259
|
-
</Box>
|
|
260
|
-
|
|
261
|
-
<Box marginTop={0}>
|
|
262
|
-
<Text color={allReady ? "#00ff41" : "#00ffff"}>
|
|
263
|
-
{" "}
|
|
264
|
-
{allReady
|
|
265
|
-
? `${icons.ok} All ${total} services running`
|
|
266
|
-
: `${icons.scan} ${readyCount}/${total} ready`}
|
|
267
|
-
</Text>
|
|
268
|
-
<Text color="gray">
|
|
269
|
-
{" "}
|
|
270
|
-
{icons.dot} q quit {icons.dot} l logs
|
|
271
|
-
</Text>
|
|
272
|
-
</Box>
|
|
273
|
-
|
|
274
|
-
{recentLogs.length > 0 && (
|
|
275
|
-
<>
|
|
276
|
-
<Box marginTop={1} marginBottom={0}>
|
|
277
|
-
<Text>{colors.dim(divider(52))}</Text>
|
|
278
|
-
</Box>
|
|
279
|
-
<Box flexDirection="column" marginTop={0}>
|
|
280
|
-
{recentLogs.map((entry) => (
|
|
281
|
-
<LogLine key={entry.id} entry={entry} />
|
|
282
|
-
))}
|
|
283
|
-
</Box>
|
|
284
|
-
</>
|
|
285
|
-
)}
|
|
286
|
-
</Box>
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export interface DevViewHandle {
|
|
291
|
-
updateProcess: (name: string, status: ProcessStatus, message?: string) => void;
|
|
292
|
-
addLog: (source: string, line: string, isError?: boolean) => void;
|
|
293
|
-
unmount: () => void;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function renderDevView(
|
|
297
|
-
initialProcesses: ProcessState[],
|
|
298
|
-
description: string,
|
|
299
|
-
env: Record<string, string>,
|
|
300
|
-
onExit?: () => Promise<void> | void,
|
|
301
|
-
onExportLogs?: () => Promise<void> | void,
|
|
302
|
-
): DevViewHandle {
|
|
303
|
-
let processes = [...initialProcesses];
|
|
304
|
-
let logs: LogEntry[] = [];
|
|
305
|
-
let rerender: (() => void) | null = null;
|
|
306
|
-
const proxyTarget = env.API_PROXY;
|
|
307
|
-
let logSeq = 0;
|
|
308
|
-
let lastLogKey: string | null = null;
|
|
309
|
-
|
|
310
|
-
const updateProcess = (name: string, status: ProcessStatus, message?: string) => {
|
|
311
|
-
processes = processes.map((p) => (p.name === name ? { ...p, status, message } : p));
|
|
312
|
-
rerender?.();
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const addLog = (source: string, line: string, isError = false) => {
|
|
316
|
-
const nextKey = `${source}:${isError ? "1" : "0"}:${line}`;
|
|
317
|
-
if (nextKey === lastLogKey) return;
|
|
318
|
-
lastLogKey = nextKey;
|
|
319
|
-
|
|
320
|
-
logs = [
|
|
321
|
-
...logs,
|
|
322
|
-
{ id: `${Date.now()}-${++logSeq}`, source, line, timestamp: Date.now(), isError },
|
|
323
|
-
];
|
|
324
|
-
if (logs.length > 100) logs = logs.slice(-100);
|
|
325
|
-
rerender?.();
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
function DevViewWrapper() {
|
|
329
|
-
const [, forceUpdate] = useState(0);
|
|
330
|
-
|
|
331
|
-
useEffect(() => {
|
|
332
|
-
rerender = () => forceUpdate((n: number) => n + 1);
|
|
333
|
-
return () => {
|
|
334
|
-
rerender = null;
|
|
335
|
-
};
|
|
336
|
-
}, []);
|
|
337
|
-
|
|
338
|
-
return (
|
|
339
|
-
<DevView
|
|
340
|
-
processes={processes}
|
|
341
|
-
logs={logs}
|
|
342
|
-
description={description}
|
|
343
|
-
proxyTarget={proxyTarget}
|
|
344
|
-
onExit={onExit}
|
|
345
|
-
onExportLogs={onExportLogs}
|
|
346
|
-
/>
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const { unmount } = render(<DevViewWrapper />);
|
|
351
|
-
return { updateProcess, addLog, unmount };
|
|
352
|
-
}
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { linkify } from "../utils/linkify";
|
|
3
|
-
import { colors, icons } from "../utils/theme";
|
|
4
|
-
import type { ProcessState, ProcessStatus } from "./dev-view";
|
|
5
|
-
|
|
6
|
-
const orange = chalk.hex("#ffaa00");
|
|
7
|
-
const PLUGIN_PREFIX = "plugin:";
|
|
8
|
-
|
|
9
|
-
export interface StreamingViewHandle {
|
|
10
|
-
updateProcess: (name: string, status: ProcessStatus, message?: string) => void;
|
|
11
|
-
addLog: (source: string, line: string, isError?: boolean) => void;
|
|
12
|
-
unmount: () => Promise<void> | void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const getTimestamp = (): string => {
|
|
16
|
-
const now = new Date();
|
|
17
|
-
return `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, "0")}:${now.getSeconds().toString().padStart(2, "0")}`;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const write = (text: string) => process.stdout.write(`${text}\n`);
|
|
21
|
-
|
|
22
|
-
const getServiceColor = (name: string): ((s: string) => string) => {
|
|
23
|
-
if (name.startsWith(PLUGIN_PREFIX)) return orange;
|
|
24
|
-
if (name === "host") return colors.cyan;
|
|
25
|
-
if (name === "ui" || name === "ui-ssr") return colors.magenta;
|
|
26
|
-
if (name === "api") return colors.blue;
|
|
27
|
-
return colors.white;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const getDisplayName = (name: string): string => {
|
|
31
|
-
return name.startsWith(PLUGIN_PREFIX)
|
|
32
|
-
? name.slice(PLUGIN_PREFIX.length).toUpperCase()
|
|
33
|
-
: name.toUpperCase();
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const isPlugin = (name: string): boolean => name.startsWith(PLUGIN_PREFIX);
|
|
37
|
-
|
|
38
|
-
const getSectionedProcesses = (processes: ProcessState[]) => {
|
|
39
|
-
const plugins = processes.filter((p) => isPlugin(p.name));
|
|
40
|
-
const services = processes.filter((p) => !isPlugin(p.name));
|
|
41
|
-
const sections: Array<{ key: string; title: string; processes: ProcessState[] }> = [];
|
|
42
|
-
if (plugins.length > 0) sections.push({ key: "plugins", title: "PLUGINS", processes: plugins });
|
|
43
|
-
if (services.length > 0)
|
|
44
|
-
sections.push({ key: "services", title: "SERVICES", processes: services });
|
|
45
|
-
return sections;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const getColumnWidths = (processes: ProcessState[]) => {
|
|
49
|
-
const name = Math.max(6, ...processes.map((p) => getDisplayName(p.name).length));
|
|
50
|
-
const source = Math.max(10, ...processes.map((p) => (p.source ? ` (${p.source})`.length : 0)));
|
|
51
|
-
return { name, source };
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const getStatusIcon = (status: ProcessStatus): string => {
|
|
55
|
-
switch (status) {
|
|
56
|
-
case "pending":
|
|
57
|
-
return icons.pending;
|
|
58
|
-
case "starting":
|
|
59
|
-
return icons.scan;
|
|
60
|
-
case "ready":
|
|
61
|
-
return icons.ok;
|
|
62
|
-
case "error":
|
|
63
|
-
return icons.err;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export function renderStreamingView(
|
|
68
|
-
initialProcesses: ProcessState[],
|
|
69
|
-
description: string,
|
|
70
|
-
env: Record<string, string>,
|
|
71
|
-
onExit?: () => Promise<void> | void,
|
|
72
|
-
): StreamingViewHandle {
|
|
73
|
-
const processes = new Map<string, ProcessState>();
|
|
74
|
-
for (const p of initialProcesses) {
|
|
75
|
-
processes.set(p.name, { ...p });
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let allReadyPrinted = false;
|
|
79
|
-
const hostProcess = initialProcesses.find((p) => p.name === "host");
|
|
80
|
-
const hostPort = hostProcess?.port || 3000;
|
|
81
|
-
const proxyTarget = env.API_PROXY;
|
|
82
|
-
const sectionedProcesses = getSectionedProcesses(initialProcesses);
|
|
83
|
-
const columnWidths = getColumnWidths(initialProcesses);
|
|
84
|
-
const lastLogBySource = new Map<string, string>();
|
|
85
|
-
|
|
86
|
-
const headerLines: string[] = [
|
|
87
|
-
"",
|
|
88
|
-
colors.cyan(`${"─".repeat(52)}`),
|
|
89
|
-
` ${icons.run} ${colors.cyan(description.toUpperCase())}`,
|
|
90
|
-
colors.cyan(`${"─".repeat(52)}`),
|
|
91
|
-
"",
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
if (proxyTarget) {
|
|
95
|
-
headerLines.push(orange(` ${icons.arrow} API PROXY → ${proxyTarget}`), "");
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
for (const section of sectionedProcesses) {
|
|
99
|
-
headerLines.push(colors.cyan(` ${section.title}`));
|
|
100
|
-
for (const proc of section.processes) {
|
|
101
|
-
const color = getServiceColor(proc.name);
|
|
102
|
-
const sourceLabel = proc.source ? ` (${proc.source})` : "";
|
|
103
|
-
headerLines.push(
|
|
104
|
-
`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${getDisplayName(proc.name).padEnd(columnWidths.name)}]`)} ${icons.pending} waiting${sourceLabel.padEnd(columnWidths.source)}`,
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
headerLines.push("");
|
|
108
|
-
}
|
|
109
|
-
console.log(headerLines.join("\n"));
|
|
110
|
-
|
|
111
|
-
const checkAllReady = () => {
|
|
112
|
-
if (allReadyPrinted) return;
|
|
113
|
-
const allReady = Array.from(processes.values()).every((p) => p.status === "ready");
|
|
114
|
-
if (allReady) {
|
|
115
|
-
allReadyPrinted = true;
|
|
116
|
-
const readyLines = [
|
|
117
|
-
"",
|
|
118
|
-
colors.dim(`${"─".repeat(52)}`),
|
|
119
|
-
colors.green(`${icons.ok} All ${processes.size} services ready`),
|
|
120
|
-
colors.green(`${icons.arrow} http://localhost:${hostPort}`),
|
|
121
|
-
colors.dim(`${"─".repeat(52)}`),
|
|
122
|
-
"",
|
|
123
|
-
];
|
|
124
|
-
console.log(readyLines.join("\n"));
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
const updateProcess = (name: string, status: ProcessStatus, message?: string) => {
|
|
129
|
-
const proc = processes.get(name);
|
|
130
|
-
if (!proc) return;
|
|
131
|
-
|
|
132
|
-
proc.status = status;
|
|
133
|
-
if (message) proc.message = message;
|
|
134
|
-
|
|
135
|
-
const color = getServiceColor(name);
|
|
136
|
-
const icon = getStatusIcon(status);
|
|
137
|
-
const displayName = getDisplayName(name).padEnd(columnWidths.name);
|
|
138
|
-
const sourceLabel = proc?.source ? ` (${proc.source})` : "";
|
|
139
|
-
const isRemote = proc?.source === "remote";
|
|
140
|
-
const isHost = name === "host";
|
|
141
|
-
const showPort = proc.port > 0 && (isHost || !isRemote) && status === "ready";
|
|
142
|
-
const statusText =
|
|
143
|
-
status === "ready"
|
|
144
|
-
? isRemote && !isHost
|
|
145
|
-
? "loaded"
|
|
146
|
-
: "running"
|
|
147
|
-
: status === "starting"
|
|
148
|
-
? "starting"
|
|
149
|
-
: status === "error"
|
|
150
|
-
? "failed"
|
|
151
|
-
: "waiting";
|
|
152
|
-
const portStr = showPort ? ` :${proc.port}` : "";
|
|
153
|
-
|
|
154
|
-
write(
|
|
155
|
-
`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${displayName}]`)} ${status === "ready" ? colors.green(icon) : status === "error" ? colors.error(icon) : icon} ${statusText}${sourceLabel.padEnd(columnWidths.source)}${portStr}`,
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
checkAllReady();
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const addLog = (source: string, line: string, isError = false) => {
|
|
162
|
-
const lastLine = lastLogBySource.get(source);
|
|
163
|
-
const nextLine = `${isError ? "ERR" : "OUT"}:${line}`;
|
|
164
|
-
if (lastLine === nextLine) return;
|
|
165
|
-
lastLogBySource.set(source, nextLine);
|
|
166
|
-
|
|
167
|
-
const color = getServiceColor(source);
|
|
168
|
-
const logColor = isError ? colors.error : colors.dim;
|
|
169
|
-
write(
|
|
170
|
-
`${colors.dim(`[${getTimestamp()}]`)} ${color(`[${source.toUpperCase()}]`)} ${colors.dim("│")} ${logColor(linkify(line))}`,
|
|
171
|
-
);
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const unmount = () => onExit?.();
|
|
175
|
-
|
|
176
|
-
return { updateProcess, addLog, unmount };
|
|
177
|
-
}
|