everything-dev 0.3.2 → 1.3.2

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