onveloz 0.0.0-beta.22 → 0.0.0-beta.23

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.
@@ -0,0 +1,1028 @@
1
+ import { a as statusLabels, i as TERMINAL_STATUSES, l as info, n as isHiddenMessage, o as getClient, r as parseBuildLine, t as cleanDisplayLine, u as success } from "./index.mjs";
2
+ import chalk from "chalk";
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { Box, Static, Text, render, useApp } from "ink";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region ../../packages/api/src/lib/build-steps.ts
8
+ const TIMESTAMP_RE = /^\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]\s*/;
9
+ const STEP_PREFIX_RE = /^#(\d+)\s+(.*)/;
10
+ const TIMING_PREFIX_RE = /^(\d+\.\d+)\s*(.*)/;
11
+ const DONE_RE = /^DONE\s+([\d.]+s?)$/;
12
+ const STAGE_RE = /^\[([\w-]+)\s+(\d+)\/\d+\]/;
13
+ function parseStageInfo(title) {
14
+ const match = title.match(STAGE_RE);
15
+ if (!match) return null;
16
+ return {
17
+ stage: match[1].toLowerCase(),
18
+ substep: parseInt(match[2], 10)
19
+ };
20
+ }
21
+ /** Derive stage order from first occurrence in the step list (preserves Dockerfile order). */
22
+ function buildStageOrder(steps) {
23
+ const order = /* @__PURE__ */ new Map();
24
+ for (const step of steps) {
25
+ const info$1 = parseStageInfo(step.title);
26
+ if (info$1 && !order.has(info$1.stage)) order.set(info$1.stage, order.size);
27
+ }
28
+ return order;
29
+ }
30
+ const RUNTIME_LINE_PATTERNS = [
31
+ /^[\u26A0\u{1F680}]/u,
32
+ /^\{.*"(?:level|msg|pid)"/,
33
+ /^Error:\s/,
34
+ /^\s+at\s+/,
35
+ /^errorno:/i,
36
+ /^code:\s+'/,
37
+ /^syscall:\s+'/,
38
+ /Server (?:running|started)/i
39
+ ];
40
+ function isRuntimeLine(content) {
41
+ return RUNTIME_LINE_PATTERNS.some((p) => p.test(content.trim()));
42
+ }
43
+ function parseBuildSteps(rawText) {
44
+ const rawLines = rawText.split("\n");
45
+ let currentPhase = 0;
46
+ let maxStepInPhase = 0;
47
+ const phaseStepMap = /* @__PURE__ */ new Map();
48
+ const allSteps = [];
49
+ let currentGeneralStep = null;
50
+ for (const raw of rawLines) {
51
+ const trimmed = raw.trim();
52
+ if (!trimmed) continue;
53
+ const tsMatch = trimmed.match(TIMESTAMP_RE);
54
+ const timestamp = tsMatch?.[1] ?? null;
55
+ const line = tsMatch ? trimmed.slice(tsMatch[0].length) : trimmed;
56
+ if (!line.trim()) continue;
57
+ const stepMatch = line.match(STEP_PREFIX_RE);
58
+ if (!stepMatch) {
59
+ if (!currentGeneralStep) {
60
+ currentGeneralStep = {
61
+ stepNumber: null,
62
+ title: "Configuração",
63
+ duration: null,
64
+ status: "done",
65
+ lines: [],
66
+ phase: currentPhase,
67
+ startedAt: timestamp
68
+ };
69
+ allSteps.push(currentGeneralStep);
70
+ }
71
+ currentGeneralStep.lines.push({
72
+ content: line,
73
+ elapsed: null,
74
+ timestamp
75
+ });
76
+ continue;
77
+ }
78
+ currentGeneralStep = null;
79
+ const stepNum = parseInt(stepMatch[1], 10);
80
+ let content = stepMatch[2];
81
+ if (content.trim() === "...") continue;
82
+ const existingKey = `${currentPhase}-${stepNum}`;
83
+ const existing = phaseStepMap.get(existingKey);
84
+ if (existing && (existing.status === "done" || existing.status === "cached") && stepNum < maxStepInPhase) {
85
+ currentPhase++;
86
+ maxStepInPhase = 0;
87
+ }
88
+ if (stepNum > maxStepInPhase) maxStepInPhase = stepNum;
89
+ const key = `${currentPhase}-${stepNum}`;
90
+ let step = phaseStepMap.get(key);
91
+ if (!step) {
92
+ step = {
93
+ stepNumber: stepNum,
94
+ title: "",
95
+ duration: null,
96
+ status: "running",
97
+ lines: [],
98
+ phase: currentPhase,
99
+ startedAt: timestamp
100
+ };
101
+ phaseStepMap.set(key, step);
102
+ allSteps.push(step);
103
+ }
104
+ const doneMatch = content.match(DONE_RE);
105
+ if (doneMatch) {
106
+ step.duration = doneMatch[1];
107
+ step.status = "done";
108
+ continue;
109
+ }
110
+ if (content.trim() === "CACHED") {
111
+ step.status = "cached";
112
+ step.duration = "cached";
113
+ continue;
114
+ }
115
+ let elapsed = null;
116
+ const timingMatch = content.match(TIMING_PREFIX_RE);
117
+ if (timingMatch) {
118
+ elapsed = parseFloat(timingMatch[1]);
119
+ content = timingMatch[2];
120
+ }
121
+ if (!content.trim()) continue;
122
+ if (!step.title) step.title = content.trim();
123
+ if (step.lines.length > 0 || content.trim() !== step.title) step.lines.push({
124
+ content: content.trim(),
125
+ elapsed,
126
+ timestamp
127
+ });
128
+ }
129
+ const numberedWithIndex = [];
130
+ const structure = [];
131
+ for (let i = 0; i < allSteps.length; i++) {
132
+ const step = allSteps[i];
133
+ if (step.stepNumber === null) structure.push({
134
+ type: "general",
135
+ origIdx: i
136
+ });
137
+ else if (step.title.trim() !== "") {
138
+ structure.push({
139
+ type: "numbered",
140
+ origIdx: i
141
+ });
142
+ numberedWithIndex.push({
143
+ step,
144
+ origIdx: i
145
+ });
146
+ }
147
+ }
148
+ const stageOrder = buildStageOrder(numberedWithIndex.map((n) => n.step));
149
+ numberedWithIndex.sort((a, b) => {
150
+ if (a.step.phase !== b.step.phase) return a.step.phase - b.step.phase;
151
+ if (a.step.startedAt && b.step.startedAt) {
152
+ const tsDiff = new Date(a.step.startedAt).getTime() - new Date(b.step.startedAt).getTime();
153
+ if (tsDiff !== 0) return tsDiff;
154
+ }
155
+ const aInfo = parseStageInfo(a.step.title);
156
+ const bInfo = parseStageInfo(b.step.title);
157
+ const aOrder = aInfo ? stageOrder.get(aInfo.stage) ?? 99 : 99;
158
+ const bOrder = bInfo ? stageOrder.get(bInfo.stage) ?? 99 : 99;
159
+ if (aOrder !== bOrder) return aOrder - bOrder;
160
+ return (aInfo?.substep ?? a.step.stepNumber ?? 0) - (bInfo?.substep ?? b.step.stepNumber ?? 0);
161
+ });
162
+ const result = [];
163
+ let nIdx = 0;
164
+ let generalCount = 0;
165
+ const totalGenerals = structure.filter((s) => s.type === "general").length;
166
+ for (const entry of structure) {
167
+ if (entry.type === "numbered") {
168
+ result.push(numberedWithIndex[nIdx].step);
169
+ nIdx++;
170
+ continue;
171
+ }
172
+ generalCount++;
173
+ const generalStep = allSteps[entry.origIdx];
174
+ if (generalCount === totalGenerals && totalGenerals > 1) {
175
+ const deployLines = [];
176
+ const healthLines = [];
177
+ let hitRuntime = false;
178
+ for (const line of generalStep.lines) {
179
+ if (!hitRuntime && isRuntimeLine(line.content)) hitRuntime = true;
180
+ if (hitRuntime) healthLines.push(line);
181
+ else deployLines.push(line);
182
+ }
183
+ if (deployLines.length > 0) result.push({
184
+ stepNumber: null,
185
+ title: "Finalização",
186
+ duration: null,
187
+ status: "done",
188
+ lines: deployLines,
189
+ phase: generalStep.phase,
190
+ startedAt: deployLines[0]?.timestamp ?? generalStep.startedAt
191
+ });
192
+ if (healthLines.length > 0) result.push({
193
+ stepNumber: null,
194
+ title: "Verificação de saúde",
195
+ duration: null,
196
+ status: "done",
197
+ lines: healthLines,
198
+ phase: generalStep.phase,
199
+ startedAt: healthLines[0]?.timestamp ?? null
200
+ });
201
+ } else result.push(generalStep);
202
+ }
203
+ return result;
204
+ }
205
+
206
+ //#endregion
207
+ //#region src/components/deploy-tui.tsx
208
+ const BRAND_COLOR = "#FF4D00";
209
+ const BAR_WIDTH = 20;
210
+ const SPINNER_FRAMES = [
211
+ "⠋",
212
+ "⠙",
213
+ "⠹",
214
+ "⠸",
215
+ "⠼",
216
+ "⠴",
217
+ "⠦",
218
+ "⠧",
219
+ "⠇",
220
+ "⠏"
221
+ ];
222
+ const MAX_OUTPUT_LINES = 5;
223
+ /** Regex to extract stage info from BuildStep titles like `[stage-0 3/11] COPY ...` */
224
+ const STAGE_TITLE_RE = /^\[([\w-]+)\s+(\d+)\/(\d+)\]\s+(.+)$/;
225
+ function buildStepsToStages(steps) {
226
+ const stages = /* @__PURE__ */ new Map();
227
+ const platformMessages = [];
228
+ const stepDetails = /* @__PURE__ */ new Map();
229
+ const displayOrder = [];
230
+ const seenStages = /* @__PURE__ */ new Set();
231
+ for (const step of steps) {
232
+ if (step.stepNumber === null) {
233
+ for (const line of step.lines) {
234
+ const cleaned = cleanDisplayLine(line.content);
235
+ if (cleaned) platformMessages.push(cleaned);
236
+ }
237
+ continue;
238
+ }
239
+ const match = step.title.match(STAGE_TITLE_RE);
240
+ if (!match) {
241
+ if (step.title && !/^\[(?:depot|internal)\]/i.test(step.title)) displayOrder.push({
242
+ kind: "extra",
243
+ step
244
+ });
245
+ continue;
246
+ }
247
+ const stageName = match[1];
248
+ const substep = parseInt(match[2], 10);
249
+ const total = parseInt(match[3], 10);
250
+ const command = match[4];
251
+ let stage = stages.get(stageName);
252
+ if (!stage) {
253
+ stage = {
254
+ name: stageName,
255
+ total,
256
+ steps: /* @__PURE__ */ new Map(),
257
+ stepNumMap: /* @__PURE__ */ new Map(),
258
+ cachedStepNums: /* @__PURE__ */ new Set(),
259
+ doneStepNums: /* @__PURE__ */ new Set()
260
+ };
261
+ stages.set(stageName, stage);
262
+ }
263
+ if (!seenStages.has(stageName)) {
264
+ seenStages.add(stageName);
265
+ displayOrder.push({
266
+ kind: "stage",
267
+ name: stageName
268
+ });
269
+ }
270
+ stage.steps.set(substep, command);
271
+ stage.stepNumMap.set(step.stepNumber, substep);
272
+ stage.total = Math.max(stage.total, total);
273
+ if (step.status === "cached") stage.cachedStepNums.add(step.stepNumber);
274
+ else if (step.status === "done") stage.doneStepNums.add(step.stepNumber);
275
+ stepDetails.set(`${stageName}-${substep}`, {
276
+ duration: step.duration !== "cached" ? step.duration : null,
277
+ status: step.status,
278
+ lastLines: step.lines.slice(-MAX_OUTPUT_LINES).map((l) => l.content),
279
+ lineCount: step.lines.length
280
+ });
281
+ }
282
+ return {
283
+ stages,
284
+ platformMessages,
285
+ displayOrder,
286
+ stepDetails
287
+ };
288
+ }
289
+ function AnimatedSpinner({ color = "cyan" }) {
290
+ const [frame, setFrame] = useState(0);
291
+ useEffect(() => {
292
+ const timer = setInterval(() => {
293
+ setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
294
+ }, 80);
295
+ return () => clearInterval(timer);
296
+ }, []);
297
+ return /* @__PURE__ */ jsx(Text, {
298
+ color,
299
+ children: SPINNER_FRAMES[frame]
300
+ });
301
+ }
302
+ function ProgressBar({ filled, total, allCached, allDone }) {
303
+ const ratio = total > 0 ? filled / total : 0;
304
+ const filledChars = Math.round(ratio * BAR_WIDTH);
305
+ const emptyChars = BAR_WIDTH - filledChars;
306
+ const counter = `${filled}/${total}`;
307
+ if (allCached) return /* @__PURE__ */ jsxs(Text, {
308
+ color: BRAND_COLOR,
309
+ children: [
310
+ "━".repeat(BAR_WIDTH),
311
+ " ",
312
+ counter,
313
+ " ✓ veloz cache"
314
+ ]
315
+ });
316
+ if (allDone) return /* @__PURE__ */ jsxs(Text, {
317
+ color: "green",
318
+ children: [
319
+ "━".repeat(BAR_WIDTH),
320
+ " ",
321
+ counter,
322
+ " ✓"
323
+ ]
324
+ });
325
+ return /* @__PURE__ */ jsxs(Text, { children: [
326
+ /* @__PURE__ */ jsx(Text, {
327
+ color: "cyan",
328
+ children: "━".repeat(filledChars)
329
+ }),
330
+ /* @__PURE__ */ jsx(Text, {
331
+ dimColor: true,
332
+ children: "─".repeat(emptyChars)
333
+ }),
334
+ /* @__PURE__ */ jsxs(Text, {
335
+ dimColor: true,
336
+ children: [" ", counter]
337
+ })
338
+ ] });
339
+ }
340
+ function getStageStats(stage) {
341
+ let finishedCount = 0;
342
+ let cachedCount = 0;
343
+ for (const bkNum of stage.stepNumMap.keys()) if (stage.cachedStepNums.has(bkNum)) {
344
+ finishedCount++;
345
+ cachedCount++;
346
+ } else if (stage.doneStepNums.has(bkNum)) finishedCount++;
347
+ const allFinished = stage.total > 0 && finishedCount >= stage.total;
348
+ return {
349
+ finishedCount,
350
+ cachedCount,
351
+ allFinished,
352
+ allCached: allFinished && cachedCount >= stage.total,
353
+ allDone: allFinished && cachedCount < stage.total
354
+ };
355
+ }
356
+ function getStepStatus(stepNum, stage) {
357
+ for (const [bkNum, dockerStep] of stage.stepNumMap.entries()) if (dockerStep === stepNum) {
358
+ if (stage.cachedStepNums.has(bkNum)) return "cached";
359
+ if (stage.doneStepNums.has(bkNum)) return "done";
360
+ return "running";
361
+ }
362
+ return "pending";
363
+ }
364
+ function StepStatusIcon({ status, spinnerFrame }) {
365
+ switch (status) {
366
+ case "cached": return /* @__PURE__ */ jsx(Text, {
367
+ color: BRAND_COLOR,
368
+ children: "✓"
369
+ });
370
+ case "done": return /* @__PURE__ */ jsx(Text, {
371
+ color: "green",
372
+ children: "✓"
373
+ });
374
+ case "running": return /* @__PURE__ */ jsx(Text, {
375
+ color: "cyan",
376
+ children: SPINNER_FRAMES[spinnerFrame]
377
+ });
378
+ case "pending": return /* @__PURE__ */ jsx(Text, {
379
+ dimColor: true,
380
+ children: "·"
381
+ });
382
+ }
383
+ }
384
+ function friendlyExtraTitle(title) {
385
+ if (/^exporting to image/i.test(title)) return "Código compilado, distribuindo no sistema da Veloz";
386
+ if (/^exporting to client/i.test(title)) return "Exportando resultado";
387
+ return title;
388
+ }
389
+ function BuildDashboard({ serviceName, stages, displayOrder, committedCount, platformMessages, stepDetails, spinnerText }) {
390
+ const [spinnerFrame, setSpinnerFrame] = useState(0);
391
+ useEffect(() => {
392
+ const timer = setInterval(() => {
393
+ setSpinnerFrame((f) => (f + 1) % SPINNER_FRAMES.length);
394
+ }, 80);
395
+ return () => clearInterval(timer);
396
+ }, []);
397
+ const visibleItems = displayOrder.slice(committedCount);
398
+ const showHeader = committedCount === 0;
399
+ const stageNames = displayOrder.filter((d) => d.kind === "stage").map((d) => d.name);
400
+ const maxNameLen = Math.max(...stageNames.map((n) => n.length), 4);
401
+ const allStagesComplete = stageNames.length > 0 && stageNames.every((name) => {
402
+ const stage = stages.get(name);
403
+ return stage ? getStageStats(stage).allFinished : false;
404
+ });
405
+ const hasRunningExtra = displayOrder.some((d) => d.kind === "extra" && d.step.status === "running");
406
+ const allComplete = displayOrder.length > 0 && displayOrder.every((item) => {
407
+ if (item.kind === "stage") {
408
+ const stage = stages.get(item.name);
409
+ return stage ? getStageStats(stage).allFinished : false;
410
+ }
411
+ return item.step.status === "done" || item.step.status === "cached";
412
+ });
413
+ let bottomSpinner = spinnerText;
414
+ if (allComplete) bottomSpinner = "Finalizando build...";
415
+ else if (allStagesComplete && hasRunningExtra) bottomSpinner = "Distribuindo...";
416
+ return /* @__PURE__ */ jsxs(Box, {
417
+ flexDirection: "column",
418
+ paddingLeft: 2,
419
+ children: [
420
+ showHeader && /* @__PURE__ */ jsxs(Fragment, { children: [
421
+ /* @__PURE__ */ jsxs(Text, {
422
+ bold: true,
423
+ color: "cyan",
424
+ children: ["BUILD ", serviceName ? /* @__PURE__ */ jsxs(Text, {
425
+ dimColor: true,
426
+ children: [
427
+ "(",
428
+ serviceName,
429
+ ")"
430
+ ]
431
+ }) : null]
432
+ }),
433
+ platformMessages.map((msg, i) => /* @__PURE__ */ jsxs(Text, { children: [" ", msg] }, `pm-${i}`)),
434
+ displayOrder.length > 0 && /* @__PURE__ */ jsx(Text, { children: "" })
435
+ ] }),
436
+ visibleItems.map((item, idx) => {
437
+ if (item.kind === "stage") {
438
+ const stage = stages.get(item.name);
439
+ const stats = getStageStats(stage);
440
+ const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
441
+ return /* @__PURE__ */ jsxs(Box, {
442
+ flexDirection: "column",
443
+ children: [
444
+ /* @__PURE__ */ jsxs(Box, { children: [
445
+ /* @__PURE__ */ jsx(Text, {
446
+ bold: true,
447
+ children: item.name.padEnd(maxNameLen)
448
+ }),
449
+ /* @__PURE__ */ jsx(Text, { children: " " }),
450
+ /* @__PURE__ */ jsx(ProgressBar, {
451
+ filled: stats.finishedCount,
452
+ total: stage.total,
453
+ allCached: stats.allCached,
454
+ allDone: stats.allDone
455
+ })
456
+ ] }),
457
+ sortedSteps.map(([stepNum, command]) => {
458
+ const status$1 = getStepStatus(stepNum, stage);
459
+ const detail = stepDetails.get(`${item.name}-${stepNum}`);
460
+ const isDone$1 = status$1 === "done" || status$1 === "cached";
461
+ const isRunning$1 = status$1 === "running";
462
+ return /* @__PURE__ */ jsxs(Box, {
463
+ flexDirection: "column",
464
+ children: [/* @__PURE__ */ jsxs(Box, { children: [
465
+ /* @__PURE__ */ jsx(Text, { children: " " }),
466
+ /* @__PURE__ */ jsx(StepStatusIcon, {
467
+ status: status$1,
468
+ spinnerFrame
469
+ }),
470
+ /* @__PURE__ */ jsxs(Text, {
471
+ dimColor: isDone$1,
472
+ children: [
473
+ " ",
474
+ command,
475
+ status$1 === "cached" ? /* @__PURE__ */ jsx(Text, {
476
+ color: BRAND_COLOR,
477
+ children: " (veloz cache)"
478
+ }) : isDone$1 && detail?.duration ? /* @__PURE__ */ jsxs(Text, {
479
+ dimColor: true,
480
+ children: [" ", detail.duration]
481
+ }) : null
482
+ ]
483
+ })
484
+ ] }), isRunning$1 && detail && detail.lastLines.length > 0 && /* @__PURE__ */ jsx(Box, {
485
+ flexDirection: "column",
486
+ children: detail.lastLines.map((line, li) => /* @__PURE__ */ jsxs(Text, {
487
+ dimColor: true,
488
+ children: [" ", line]
489
+ }, li))
490
+ })]
491
+ }, stepNum);
492
+ }),
493
+ idx < visibleItems.length - 1 && /* @__PURE__ */ jsx(Text, { children: "" })
494
+ ]
495
+ }, item.name);
496
+ }
497
+ const step = item.step;
498
+ const isDone = step.status === "done" || step.status === "cached";
499
+ const isRunning = step.status === "running";
500
+ const status = step.status === "error" ? "running" : step.status;
501
+ const title = friendlyExtraTitle(step.title);
502
+ const lastLines = step.lines.slice(-MAX_OUTPUT_LINES).map((l) => l.content);
503
+ return /* @__PURE__ */ jsxs(Box, {
504
+ flexDirection: "column",
505
+ children: [/* @__PURE__ */ jsxs(Box, { children: [
506
+ /* @__PURE__ */ jsx(Text, { children: " " }),
507
+ /* @__PURE__ */ jsx(StepStatusIcon, {
508
+ status,
509
+ spinnerFrame
510
+ }),
511
+ /* @__PURE__ */ jsxs(Text, {
512
+ dimColor: isDone,
513
+ children: [
514
+ " ",
515
+ title,
516
+ isDone && step.duration && step.duration !== "cached" ? /* @__PURE__ */ jsxs(Text, {
517
+ dimColor: true,
518
+ children: [" ", step.duration]
519
+ }) : null
520
+ ]
521
+ })
522
+ ] }), isRunning && lastLines.length > 0 && /* @__PURE__ */ jsx(Box, {
523
+ flexDirection: "column",
524
+ children: lastLines.map((line, li) => /* @__PURE__ */ jsxs(Text, {
525
+ dimColor: true,
526
+ children: [" ", line]
527
+ }, li))
528
+ })]
529
+ }, `extra-${idx}`);
530
+ }),
531
+ /* @__PURE__ */ jsxs(Box, {
532
+ marginTop: 1,
533
+ children: [/* @__PURE__ */ jsx(AnimatedSpinner, {}), /* @__PURE__ */ jsxs(Text, { children: [" ", bottomSpinner] })]
534
+ })
535
+ ]
536
+ });
537
+ }
538
+ function DeployTUIApp({ stream, serviceName, resultRef }) {
539
+ const { exit } = useApp();
540
+ const [, forceUpdate] = useState(0);
541
+ const [staticLines, setStaticLines] = useState([]);
542
+ const [shouldExit, setShouldExit] = useState(false);
543
+ const stateRef = useRef({
544
+ stages: /* @__PURE__ */ new Map(),
545
+ displayOrder: [],
546
+ platformMessages: [],
547
+ stepDetails: /* @__PURE__ */ new Map(),
548
+ phase: "waiting",
549
+ finalStatus: "",
550
+ committedCount: 0,
551
+ headerCommitted: false
552
+ });
553
+ useEffect(() => {
554
+ if (shouldExit) exit();
555
+ }, [shouldExit, exit]);
556
+ useEffect(() => {
557
+ const state$1 = stateRef.current;
558
+ const allLogs = [];
559
+ let accumulatedBuildLogs = "";
560
+ let runtimeLogId = 0;
561
+ function applyBuildParse() {
562
+ const result = buildStepsToStages(parseBuildSteps(accumulatedBuildLogs));
563
+ state$1.stages = result.stages;
564
+ state$1.displayOrder = result.displayOrder;
565
+ state$1.platformMessages = result.platformMessages;
566
+ state$1.stepDetails = result.stepDetails;
567
+ }
568
+ /** Check if a display item is fully complete */
569
+ function isItemComplete(item) {
570
+ if (item.kind === "stage") {
571
+ const stage = state$1.stages.get(item.name);
572
+ return stage ? getStageStats(stage).allFinished : false;
573
+ }
574
+ return item.step.status === "done" || item.step.status === "cached";
575
+ }
576
+ /** Progressively commit completed leading items to Static for scrolling */
577
+ function commitCompleted() {
578
+ const newItems = [];
579
+ if (!state$1.headerCommitted && state$1.displayOrder.length > 0) {
580
+ state$1.headerCommitted = true;
581
+ newItems.push({
582
+ id: "build-header",
583
+ node: /* @__PURE__ */ jsxs(Text, {
584
+ bold: true,
585
+ color: "cyan",
586
+ children: [" BUILD ", serviceName ? /* @__PURE__ */ jsxs(Text, {
587
+ dimColor: true,
588
+ children: [
589
+ "(",
590
+ serviceName,
591
+ ")"
592
+ ]
593
+ }) : null]
594
+ })
595
+ });
596
+ for (const [i, msg] of state$1.platformMessages.entries()) newItems.push({
597
+ id: `platform-${i}`,
598
+ node: /* @__PURE__ */ jsxs(Text, { children: [" ", msg] })
599
+ });
600
+ newItems.push({
601
+ id: "header-gap",
602
+ node: /* @__PURE__ */ jsx(Text, { children: "" })
603
+ });
604
+ }
605
+ const stageNames = state$1.displayOrder.filter((d) => d.kind === "stage").map((d) => d.name);
606
+ const maxNameLen = Math.max(...stageNames.map((n) => n.length), 4);
607
+ for (let i = state$1.committedCount; i < state$1.displayOrder.length; i++) {
608
+ const item = state$1.displayOrder[i];
609
+ if (!isItemComplete(item)) break;
610
+ if (item.kind === "stage") {
611
+ const stage = state$1.stages.get(item.name);
612
+ const stats = getStageStats(stage);
613
+ const counter = `${stats.finishedCount}/${stage.total}`;
614
+ const barNode = stats.allCached ? /* @__PURE__ */ jsxs(Text, {
615
+ color: BRAND_COLOR,
616
+ children: [
617
+ "━".repeat(BAR_WIDTH),
618
+ " ",
619
+ counter,
620
+ " ✓ veloz cache"
621
+ ]
622
+ }) : /* @__PURE__ */ jsxs(Text, {
623
+ color: "green",
624
+ children: [
625
+ "━".repeat(BAR_WIDTH),
626
+ " ",
627
+ counter,
628
+ " ✓"
629
+ ]
630
+ });
631
+ newItems.push({
632
+ id: `stage-${item.name}`,
633
+ node: /* @__PURE__ */ jsxs(Box, { children: [
634
+ /* @__PURE__ */ jsxs(Text, {
635
+ bold: true,
636
+ children: [" ", item.name.padEnd(maxNameLen)]
637
+ }),
638
+ /* @__PURE__ */ jsx(Text, { children: " " }),
639
+ barNode
640
+ ] })
641
+ });
642
+ const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
643
+ for (const [stepNum, command] of sortedSteps) {
644
+ const stepStatus = getStepStatus(stepNum, stage);
645
+ const detail = state$1.stepDetails.get(`${item.name}-${stepNum}`);
646
+ const icon = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
647
+ color: BRAND_COLOR,
648
+ children: "✓"
649
+ }) : /* @__PURE__ */ jsx(Text, {
650
+ color: "green",
651
+ children: "✓"
652
+ });
653
+ const suffix = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
654
+ color: BRAND_COLOR,
655
+ children: " (veloz cache)"
656
+ }) : detail?.duration ? /* @__PURE__ */ jsxs(Text, {
657
+ dimColor: true,
658
+ children: [" ", detail.duration]
659
+ }) : null;
660
+ newItems.push({
661
+ id: `step-${item.name}-${stepNum}`,
662
+ node: /* @__PURE__ */ jsxs(Text, { children: [
663
+ " ",
664
+ icon,
665
+ " ",
666
+ /* @__PURE__ */ jsx(Text, {
667
+ dimColor: true,
668
+ children: command
669
+ }),
670
+ suffix
671
+ ] })
672
+ });
673
+ }
674
+ } else {
675
+ const step = item.step;
676
+ const title = friendlyExtraTitle(step.title);
677
+ const duration = step.duration && step.duration !== "cached" ? ` ${step.duration}` : "";
678
+ newItems.push({
679
+ id: `extra-${i}`,
680
+ node: /* @__PURE__ */ jsxs(Text, { children: [
681
+ " ",
682
+ /* @__PURE__ */ jsx(Text, {
683
+ color: "green",
684
+ children: "✓"
685
+ }),
686
+ " ",
687
+ /* @__PURE__ */ jsxs(Text, {
688
+ dimColor: true,
689
+ children: [title, duration]
690
+ })
691
+ ] })
692
+ });
693
+ }
694
+ newItems.push({
695
+ id: `gap-${i}`,
696
+ node: /* @__PURE__ */ jsx(Text, { children: "" })
697
+ });
698
+ state$1.committedCount = i + 1;
699
+ }
700
+ if (newItems.length > 0) setStaticLines((prev) => [...prev, ...newItems]);
701
+ }
702
+ /** Force-commit all remaining items (even if not complete) — used at phase transitions */
703
+ function commitAllRemaining() {
704
+ const newItems = [];
705
+ const stageNames = state$1.displayOrder.filter((d) => d.kind === "stage").map((d) => d.name);
706
+ const maxNameLen = Math.max(...stageNames.map((n) => n.length), 4);
707
+ for (let i = state$1.committedCount; i < state$1.displayOrder.length; i++) {
708
+ const item = state$1.displayOrder[i];
709
+ if (item.kind === "stage") {
710
+ const stage = state$1.stages.get(item.name);
711
+ const stats = getStageStats(stage);
712
+ const counter = `${stats.finishedCount}/${stage.total}`;
713
+ const barNode = stats.allCached ? /* @__PURE__ */ jsxs(Text, {
714
+ color: BRAND_COLOR,
715
+ children: [
716
+ "━".repeat(BAR_WIDTH),
717
+ " ",
718
+ counter,
719
+ " ✓ veloz cache"
720
+ ]
721
+ }) : stats.allDone ? /* @__PURE__ */ jsxs(Text, {
722
+ color: "green",
723
+ children: [
724
+ "━".repeat(BAR_WIDTH),
725
+ " ",
726
+ counter,
727
+ " ✓"
728
+ ]
729
+ }) : /* @__PURE__ */ jsxs(Text, { children: [
730
+ /* @__PURE__ */ jsx(Text, {
731
+ color: "cyan",
732
+ children: "━".repeat(Math.round(stats.finishedCount / Math.max(stage.total, 1) * BAR_WIDTH))
733
+ }),
734
+ /* @__PURE__ */ jsx(Text, {
735
+ dimColor: true,
736
+ children: "─".repeat(BAR_WIDTH - Math.round(stats.finishedCount / Math.max(stage.total, 1) * BAR_WIDTH))
737
+ }),
738
+ /* @__PURE__ */ jsxs(Text, {
739
+ dimColor: true,
740
+ children: [" ", counter]
741
+ })
742
+ ] });
743
+ newItems.push({
744
+ id: `stage-${item.name}`,
745
+ node: /* @__PURE__ */ jsxs(Box, { children: [
746
+ /* @__PURE__ */ jsxs(Text, {
747
+ bold: true,
748
+ children: [" ", item.name.padEnd(maxNameLen)]
749
+ }),
750
+ /* @__PURE__ */ jsx(Text, { children: " " }),
751
+ barNode
752
+ ] })
753
+ });
754
+ const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
755
+ for (const [stepNum, command] of sortedSteps) {
756
+ const stepStatus = getStepStatus(stepNum, stage);
757
+ const detail = state$1.stepDetails.get(`${item.name}-${stepNum}`);
758
+ const icon = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
759
+ color: BRAND_COLOR,
760
+ children: "✓"
761
+ }) : stepStatus === "done" || stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
762
+ color: "green",
763
+ children: "✓"
764
+ }) : /* @__PURE__ */ jsx(Text, {
765
+ dimColor: true,
766
+ children: "·"
767
+ });
768
+ const suffix = stepStatus === "cached" ? /* @__PURE__ */ jsx(Text, {
769
+ color: BRAND_COLOR,
770
+ children: " (veloz cache)"
771
+ }) : detail?.duration ? /* @__PURE__ */ jsxs(Text, {
772
+ dimColor: true,
773
+ children: [" ", detail.duration]
774
+ }) : null;
775
+ newItems.push({
776
+ id: `step-${item.name}-${stepNum}`,
777
+ node: /* @__PURE__ */ jsxs(Text, { children: [
778
+ " ",
779
+ icon,
780
+ " ",
781
+ /* @__PURE__ */ jsx(Text, {
782
+ dimColor: true,
783
+ children: command
784
+ }),
785
+ suffix
786
+ ] })
787
+ });
788
+ }
789
+ } else {
790
+ const step = item.step;
791
+ const isDone = step.status === "done" || step.status === "cached";
792
+ const title = friendlyExtraTitle(step.title);
793
+ const duration = isDone && step.duration && step.duration !== "cached" ? ` ${step.duration}` : "";
794
+ const icon = isDone ? /* @__PURE__ */ jsx(Text, {
795
+ color: "green",
796
+ children: "✓"
797
+ }) : /* @__PURE__ */ jsx(Text, {
798
+ dimColor: true,
799
+ children: "·"
800
+ });
801
+ newItems.push({
802
+ id: `extra-${i}`,
803
+ node: /* @__PURE__ */ jsxs(Text, { children: [
804
+ " ",
805
+ icon,
806
+ " ",
807
+ /* @__PURE__ */ jsxs(Text, {
808
+ dimColor: true,
809
+ children: [title, duration]
810
+ })
811
+ ] })
812
+ });
813
+ }
814
+ newItems.push({
815
+ id: `gap-${i}`,
816
+ node: /* @__PURE__ */ jsx(Text, { children: "" })
817
+ });
818
+ }
819
+ state$1.committedCount = state$1.displayOrder.length;
820
+ return newItems;
821
+ }
822
+ const consume = async () => {
823
+ try {
824
+ for await (const event of stream) if (event.type === "status") {
825
+ state$1.finalStatus = event.content;
826
+ switch (event.content) {
827
+ case "BUILDING":
828
+ state$1.phase = "building";
829
+ forceUpdate((n) => n + 1);
830
+ break;
831
+ case "DEPLOYING":
832
+ if (accumulatedBuildLogs) applyBuildParse();
833
+ for (const stage of state$1.stages.values()) if (stage.steps.size > 0 && stage.steps.size < stage.total) stage.total = stage.steps.size;
834
+ commitCompleted();
835
+ if (state$1.committedCount < state$1.displayOrder.length) {
836
+ const remaining = commitAllRemaining();
837
+ if (remaining.length > 0) setStaticLines((prev) => [...prev, ...remaining]);
838
+ }
839
+ state$1.phase = "deploying";
840
+ forceUpdate((n) => n + 1);
841
+ break;
842
+ case "LIVE":
843
+ setStaticLines((prev) => [...prev, {
844
+ id: "live-status",
845
+ node: /* @__PURE__ */ jsxs(Text, {
846
+ color: "green",
847
+ children: [" ", "✓ Publicado"]
848
+ })
849
+ }]);
850
+ state$1.phase = "live";
851
+ forceUpdate((n) => n + 1);
852
+ break;
853
+ default: if (TERMINAL_STATUSES.has(event.content)) {
854
+ if (accumulatedBuildLogs) applyBuildParse();
855
+ if (state$1.phase === "building" || state$1.phase === "waiting") {
856
+ commitCompleted();
857
+ if (state$1.committedCount < state$1.displayOrder.length) {
858
+ const remaining = commitAllRemaining();
859
+ if (remaining.length > 0) setStaticLines((prev) => [...prev, ...remaining]);
860
+ }
861
+ }
862
+ state$1.phase = "failed";
863
+ forceUpdate((n) => n + 1);
864
+ }
865
+ }
866
+ } else if (event.type === "log") {
867
+ const lines = event.content.split("\n");
868
+ allLogs.push(...lines);
869
+ if (state$1.phase === "deploying" || state$1.phase === "live") {
870
+ for (const line of lines) {
871
+ const trimmed = line.trim();
872
+ if (!trimmed) continue;
873
+ const parsed = parseBuildLine(trimmed);
874
+ let text = null;
875
+ if (parsed.kind === "platform") text = parsed.message;
876
+ else if (parsed.kind === "other" && parsed.text) text = parsed.text;
877
+ else if (parsed.kind === "output") text = parsed.text;
878
+ if (text && !isHiddenMessage(text)) {
879
+ const logId = runtimeLogId++;
880
+ const newItems = [];
881
+ if (logId === 0) newItems.push({
882
+ id: "runtime-header",
883
+ node: /* @__PURE__ */ jsx(Text, {
884
+ bold: true,
885
+ color: "cyan",
886
+ children: "\n RUNTIME"
887
+ })
888
+ });
889
+ newItems.push({
890
+ id: `log-${logId}`,
891
+ node: /* @__PURE__ */ jsxs(Text, { children: [" ", text] })
892
+ });
893
+ setStaticLines((prev) => [...prev, ...newItems]);
894
+ }
895
+ }
896
+ continue;
897
+ }
898
+ if (state$1.phase === "waiting") state$1.phase = "building";
899
+ if (accumulatedBuildLogs && !accumulatedBuildLogs.endsWith("\n")) accumulatedBuildLogs += "\n";
900
+ accumulatedBuildLogs += event.content;
901
+ applyBuildParse();
902
+ commitCompleted();
903
+ forceUpdate((n) => n + 1);
904
+ }
905
+ } catch {}
906
+ resultRef.current = {
907
+ status: state$1.finalStatus,
908
+ logs: allLogs.filter((l) => l.trim())
909
+ };
910
+ setShouldExit(true);
911
+ };
912
+ consume();
913
+ }, [
914
+ stream,
915
+ serviceName,
916
+ resultRef,
917
+ exit,
918
+ setShouldExit
919
+ ]);
920
+ const state = stateRef.current;
921
+ const showBuild = state.phase === "waiting" || state.phase === "building";
922
+ const showDeploySpinner = state.phase === "deploying";
923
+ return /* @__PURE__ */ jsxs(Box, {
924
+ flexDirection: "column",
925
+ children: [
926
+ /* @__PURE__ */ jsx(Static, {
927
+ items: staticLines,
928
+ children: (line) => /* @__PURE__ */ jsx(Box, { children: line.node }, line.id)
929
+ }),
930
+ showBuild && /* @__PURE__ */ jsx(BuildDashboard, {
931
+ serviceName,
932
+ stages: state.stages,
933
+ displayOrder: state.displayOrder,
934
+ committedCount: state.committedCount,
935
+ platformMessages: state.platformMessages,
936
+ stepDetails: state.stepDetails,
937
+ spinnerText: state.phase === "waiting" ? "Aguardando início do build..." : "Compilando..."
938
+ }),
939
+ showDeploySpinner && /* @__PURE__ */ jsxs(Box, {
940
+ paddingLeft: 2,
941
+ children: [/* @__PURE__ */ jsx(AnimatedSpinner, {}), /* @__PURE__ */ jsx(Text, { children: " Publicando..." })]
942
+ })
943
+ ]
944
+ });
945
+ }
946
+ async function fetchDeployUrls(client, serviceId) {
947
+ try {
948
+ return (await client.domains.list({ serviceId })).map((d) => `https://${d.domain}`);
949
+ } catch {
950
+ return [];
951
+ }
952
+ }
953
+ function getFailureHints(status) {
954
+ switch (status) {
955
+ case "BUILD_FAILED": return [
956
+ "Verifique os logs de build acima para erros de compilação",
957
+ "Teste o build localmente: rode o comando de build do seu projeto",
958
+ "Use 'veloz config show' para verificar as configurações"
959
+ ];
960
+ case "DEPLOY_FAILED": return [
961
+ "O build passou mas o serviço falhou ao iniciar",
962
+ "Verifique se a porta configurada está correta: 'veloz config show'",
963
+ "Veja os logs de runtime: 'veloz logs -f'"
964
+ ];
965
+ case "CANCELLED": return ["Deploy cancelado. Execute 'veloz deploy' para tentar novamente."];
966
+ default: return ["Execute 'veloz logs -f' para mais detalhes."];
967
+ }
968
+ }
969
+ async function renderDeployTUI(deploymentId, serviceId, serviceName) {
970
+ const client = await getClient();
971
+ const stream = await client.logs.streamBuildLogs({ deploymentId });
972
+ const resultRef = { current: {
973
+ status: "",
974
+ logs: []
975
+ } };
976
+ const instance = render(/* @__PURE__ */ jsx(DeployTUIApp, {
977
+ stream,
978
+ serviceName,
979
+ resultRef
980
+ }), {
981
+ exitOnCtrlC: false,
982
+ patchConsole: false
983
+ });
984
+ try {
985
+ await instance.waitUntilExit();
986
+ } catch {
987
+ instance.unmount();
988
+ }
989
+ if (!resultRef.current.status) try {
990
+ const d = await client.deployments.get({ deploymentId });
991
+ resultRef.current.status = d.status;
992
+ try {
993
+ const logs = await client.logs.getBuildLogs({ deploymentId });
994
+ if (logs.buildLogs) resultRef.current.logs.push(...logs.buildLogs.split("\n"));
995
+ } catch {}
996
+ } catch {}
997
+ const urls = resultRef.current.status === "LIVE" ? await fetchDeployUrls(client, serviceId) : [];
998
+ const finalStatus = resultRef.current.status;
999
+ if (finalStatus === "LIVE") {
1000
+ success(serviceName ? `Deploy de ${chalk.bold(serviceName)} concluído! Serviço ativo.` : "Deploy concluído! Serviço ativo.");
1001
+ for (const url of urls) info(chalk.bold(url));
1002
+ } else if (TERMINAL_STATUSES.has(finalStatus)) {
1003
+ const label = statusLabels[finalStatus] ?? finalStatus;
1004
+ const hints = getFailureHints(finalStatus);
1005
+ const allLogs = resultRef.current.logs;
1006
+ if (allLogs.length > 0) {
1007
+ process.stderr.write("\n");
1008
+ process.stderr.write(chalk.red(` ${"─".repeat(50)}`) + "\n");
1009
+ process.stderr.write(chalk.red.bold(" Logs de build:") + "\n");
1010
+ process.stderr.write(chalk.red(` ${"─".repeat(50)}`) + "\n");
1011
+ for (const line of allLogs) if (line.trim()) process.stderr.write(chalk.dim(` ${line}`) + "\n");
1012
+ process.stderr.write(chalk.red(` ${"─".repeat(50)}`) + "\n");
1013
+ }
1014
+ const errorMsg = serviceName ? `Deploy de ${chalk.bold(serviceName)} finalizou: ${label}` : `Deploy finalizou: ${label}`;
1015
+ process.stderr.write(chalk.red(`\n✗ ${errorMsg}`) + "\n");
1016
+ for (const hint of hints) process.stderr.write(chalk.yellow(` → ${hint}`) + "\n");
1017
+ process.exit(1);
1018
+ }
1019
+ return {
1020
+ status: finalStatus,
1021
+ logs: resultRef.current.logs.filter((l) => l.trim()),
1022
+ urls,
1023
+ serviceName
1024
+ };
1025
+ }
1026
+
1027
+ //#endregion
1028
+ export { renderDeployTUI };
package/dist/index.mjs CHANGED
@@ -943,7 +943,7 @@ const requireAuth = middleware(async (_c, next) => {
943
943
  console.error("Não autorizado. Defina VELOZ_API_KEY ou execute `veloz login`.");
944
944
  process.exit(1);
945
945
  }
946
- const { performLogin: performLogin$1 } = await import("./login-jxR-lGEN.mjs");
946
+ const { performLogin: performLogin$1 } = await import("./login-DEQ5zMzL.mjs");
947
947
  await performLogin$1();
948
948
  }
949
949
  await next();
@@ -4299,236 +4299,19 @@ function parseBuildLine(raw) {
4299
4299
  text: trimmed
4300
4300
  };
4301
4301
  }
4302
- const BAR_WIDTH = 20;
4303
- const BRAND = chalk.rgb(255, 77, 0);
4304
- function renderProgressBar(filled, total, allCached, allDone) {
4305
- const ratio = total > 0 ? filled / total : 0;
4306
- const filledChars = Math.round(ratio * BAR_WIDTH);
4307
- const emptyChars = BAR_WIDTH - filledChars;
4308
- const counter = `${filled}/${total}`;
4309
- if (allCached) return `${BRAND("━".repeat(BAR_WIDTH))} ${BRAND(`${counter} ◆ cached`)}`;
4310
- if (allDone) return `${chalk.green("━".repeat(BAR_WIDTH))} ${chalk.green(`${counter} ✓`)}`;
4311
- return chalk.cyan("━".repeat(filledChars)) + chalk.dim("─".repeat(emptyChars)) + ` ${chalk.dim(counter)}`;
4312
- }
4313
- const SPINNER_FRAMES = [
4314
- "⠋",
4315
- "⠙",
4316
- "⠹",
4317
- "⠸",
4318
- "⠼",
4319
- "⠴",
4320
- "⠦",
4321
- "⠧",
4322
- "⠇",
4323
- "⠏"
4324
- ];
4325
- /**
4326
- * Dashboard-style renderer for compact TTY mode.
4327
- * Redraws the entire build progress block on each update.
4328
- * Includes an integrated spinner that animates via setInterval.
4329
- */
4330
- var BuildProgressRenderer = class {
4331
- stages = /* @__PURE__ */ new Map();
4332
- stageOrder = [];
4333
- platformMessages = [];
4334
- renderLineCount = 0;
4335
- phase = "waiting";
4336
- runtimeHeaderPrinted = false;
4337
- serviceName;
4338
- spinnerFrame = 0;
4339
- spinnerInterval = null;
4340
- spinnerText = "Aguardando início do build...";
4341
- /** External ora spinner reference — used to pause/resume during runtime log output */
4342
- externalSpinner = null;
4343
- /** Pending render scheduled via setTimeout for batching rapid updates */
4344
- pendingRender = null;
4345
- constructor(serviceName) {
4346
- this.serviceName = serviceName;
4347
- this.startSpinner();
4348
- }
4349
- setExternalSpinner(spinner$1) {
4350
- this.externalSpinner = spinner$1;
4351
- }
4352
- startSpinner() {
4353
- if (this.spinnerInterval) return;
4354
- this.spinnerInterval = setInterval(() => {
4355
- this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
4356
- this.render();
4357
- }, 120);
4358
- }
4359
- stopSpinner() {
4360
- if (this.spinnerInterval) {
4361
- clearInterval(this.spinnerInterval);
4362
- this.spinnerInterval = null;
4363
- }
4364
- if (this.pendingRender) {
4365
- clearTimeout(this.pendingRender);
4366
- this.pendingRender = null;
4367
- }
4368
- }
4369
- setBuilding() {
4370
- this.phase = "building";
4371
- this.spinnerText = "Compilando...";
4372
- }
4373
- switchToRuntime() {
4374
- if (this.phase === "runtime") return;
4375
- for (const stage of this.stages.values()) if (stage.steps.size > 0 && stage.steps.size < stage.total) stage.total = stage.steps.size;
4376
- this.stopSpinner();
4377
- this.render();
4378
- this.renderLineCount = 0;
4379
- this.phase = "runtime";
4380
- }
4381
- processLine(raw) {
4382
- const trimmed = raw.trim();
4383
- if (!trimmed) return;
4384
- if (this.phase === "runtime") {
4385
- this.printRuntimeLine(trimmed);
4386
- return;
4387
- }
4388
- if (this.phase === "waiting") {
4389
- this.phase = "building";
4390
- this.spinnerText = "Compilando...";
4391
- }
4392
- const parsed = parseBuildLine(trimmed);
4393
- switch (parsed.kind) {
4394
- case "step": {
4395
- let stage = this.stages.get(parsed.stage);
4396
- if (!stage) {
4397
- stage = {
4398
- name: parsed.stage,
4399
- total: parsed.total,
4400
- steps: /* @__PURE__ */ new Map(),
4401
- stepNumMap: /* @__PURE__ */ new Map(),
4402
- cachedStepNums: /* @__PURE__ */ new Set(),
4403
- doneStepNums: /* @__PURE__ */ new Set()
4404
- };
4405
- this.stages.set(parsed.stage, stage);
4406
- this.stageOrder.push(parsed.stage);
4407
- }
4408
- stage.steps.set(parsed.step, parsed.command);
4409
- stage.stepNumMap.set(parsed.stepNum, parsed.step);
4410
- stage.total = Math.max(stage.total, parsed.total);
4411
- break;
4412
- }
4413
- case "cached":
4414
- for (const stage of this.stages.values()) if (stage.stepNumMap.has(parsed.stepNum)) {
4415
- stage.cachedStepNums.add(parsed.stepNum);
4416
- break;
4417
- }
4418
- break;
4419
- case "done":
4420
- for (const stage of this.stages.values()) if (stage.stepNumMap.has(parsed.stepNum)) {
4421
- stage.doneStepNums.add(parsed.stepNum);
4422
- break;
4423
- }
4424
- break;
4425
- case "platform": {
4426
- const cleaned = cleanDisplayLine(parsed.message);
4427
- if (cleaned) this.platformMessages.push(cleaned);
4428
- break;
4429
- }
4430
- case "output":
4431
- case "other": break;
4432
- }
4433
- this.scheduleRender();
4434
- }
4435
- printRuntimeLine(line) {
4436
- const parsed = parseBuildLine(line);
4437
- let text = null;
4438
- if (parsed.kind === "platform") text = parsed.message;
4439
- else if (parsed.kind === "other" && parsed.text) text = parsed.text;
4440
- else if (parsed.kind === "output") text = parsed.text;
4441
- if (!text) return;
4442
- if (isHiddenMessage(text)) return;
4443
- if (this.externalSpinner) this.externalSpinner.clear();
4444
- if (!this.runtimeHeaderPrinted) {
4445
- this.runtimeHeaderPrinted = true;
4446
- console.log(chalk.cyan.bold(`\n RUNTIME`));
4447
- }
4448
- console.log(` ${text}`);
4449
- if (this.externalSpinner) this.externalSpinner.render();
4450
- }
4451
- isStageComplete(stage) {
4452
- return stage.steps.size >= stage.total;
4453
- }
4454
- /**
4455
- * Schedule a render on the next tick — batches rapid processLine() calls
4456
- * so we don't re-render for every single log line in a burst.
4457
- */
4458
- scheduleRender() {
4459
- if (this.pendingRender) return;
4460
- this.pendingRender = setTimeout(() => {
4461
- this.pendingRender = null;
4462
- this.render();
4463
- }, 0);
4464
- }
4465
- render() {
4466
- const buf = [];
4467
- if (this.renderLineCount > 0) buf.push(`\x1b[${this.renderLineCount}A\x1b[J`);
4468
- let lines = 0;
4469
- const label = this.serviceName ? `BUILD ${chalk.dim(`(${this.serviceName})`)}` : "BUILD";
4470
- buf.push(`${chalk.cyan.bold(` ${label}`)}\n`);
4471
- lines++;
4472
- for (const msg of this.platformMessages) {
4473
- buf.push(` ${msg}\n`);
4474
- lines++;
4475
- }
4476
- if (this.stageOrder.length > 0) {
4477
- buf.push("\n");
4478
- lines++;
4479
- }
4480
- const maxNameLen = Math.max(...this.stageOrder.map((n) => n.length), 4);
4481
- for (let i = 0; i < this.stageOrder.length; i++) {
4482
- const stageName = this.stageOrder[i];
4483
- const stage = this.stages.get(stageName);
4484
- const complete = this.isStageComplete(stage);
4485
- const allCached = complete && stage.cachedStepNums.size === stage.steps.size;
4486
- const allDone = complete && !allCached;
4487
- const bar = renderProgressBar(stage.steps.size, stage.total, allCached, allDone);
4488
- const paddedName = chalk.bold(stageName.padEnd(maxNameLen));
4489
- buf.push(` ${paddedName} ${bar}\n`);
4490
- lines++;
4491
- const sortedSteps = [...stage.steps.entries()].sort((a, b) => a[0] - b[0]);
4492
- const spinnerChar = SPINNER_FRAMES[this.spinnerFrame];
4493
- for (const [stepNum, command] of sortedSteps) {
4494
- let stepStatus = "";
4495
- for (const [bkNum, dockerStep] of stage.stepNumMap.entries()) if (dockerStep === stepNum) {
4496
- if (stage.cachedStepNums.has(bkNum)) stepStatus = ` ${BRAND("◆")}`;
4497
- else if (stage.doneStepNums.has(bkNum)) stepStatus = ` ${chalk.green("✓")}`;
4498
- else stepStatus = ` ${chalk.cyan(spinnerChar)}`;
4499
- break;
4500
- }
4501
- buf.push(` ${command}${stepStatus}\n`);
4502
- lines++;
4503
- }
4504
- if (i < this.stageOrder.length - 1) {
4505
- buf.push("\n");
4506
- lines++;
4507
- }
4508
- }
4509
- if (this.spinnerInterval) {
4510
- if (!(this.stageOrder.length > 0 && this.stageOrder.every((name) => this.isStageComplete(this.stages.get(name))))) {
4511
- const frame = SPINNER_FRAMES[this.spinnerFrame];
4512
- buf.push(`\n ${chalk.cyan(frame)} ${this.spinnerText}\n`);
4513
- lines += 2;
4514
- }
4515
- }
4516
- process.stdout.write(buf.join(""));
4517
- this.renderLineCount = lines;
4518
- }
4519
- };
4520
4302
  async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
4521
4303
  const client = await getClient();
4522
4304
  const isVerbose = process.env.VELOZ_VERBOSE === "true";
4523
4305
  const mcp = isMcpMode();
4524
4306
  const isTTY = !mcp && process.stdout.isTTY;
4525
4307
  const isGHA = !mcp && process.env.GITHUB_ACTIONS === "true";
4308
+ if (isTTY && !isVerbose) {
4309
+ const { renderDeployTUI } = await import("./deploy-tui-U7fsZN6x.mjs");
4310
+ return renderDeployTUI(deploymentId, serviceId, serviceName);
4311
+ }
4526
4312
  const allLogLines = [];
4527
- let buildSpinner = null;
4528
- let renderer = null;
4529
4313
  if (mcp) log(serviceName ? `[deploy] Build: ${serviceName}` : "[deploy] Build iniciando...");
4530
4314
  else if (isGHA) startGroup(serviceName ? `Build: ${serviceName}` : "Build");
4531
- else if (isTTY && !isVerbose) renderer = new BuildProgressRenderer(serviceName);
4532
4315
  else if (isTTY) {
4533
4316
  const header = serviceName ? `Build: ${chalk.bold(serviceName)}` : "Build";
4534
4317
  console.log(chalk.cyan(`\n${header}`));
@@ -4544,29 +4327,7 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
4544
4327
  const label = statusLabels[event.content] ?? event.content;
4545
4328
  finalStatus = event.content;
4546
4329
  if (mcp) log(`[deploy] Status: ${label}`);
4547
- else if (renderer) {
4548
- if (event.content === "BUILDING") renderer.setBuilding();
4549
- else if (event.content === "DEPLOYING") {
4550
- renderer.switchToRuntime();
4551
- buildSpinner = ora({
4552
- text: "Publicando...",
4553
- color: "cyan"
4554
- }).start();
4555
- renderer.setExternalSpinner(buildSpinner);
4556
- } else if (event.content === "LIVE") {
4557
- if (buildSpinner) {
4558
- renderer.setExternalSpinner(null);
4559
- buildSpinner.succeed("Publicado");
4560
- buildSpinner = null;
4561
- }
4562
- } else if (TERMINAL_STATUSES.has(event.content) && event.content !== "LIVE") {
4563
- if (buildSpinner) {
4564
- renderer.setExternalSpinner(null);
4565
- buildSpinner.fail(label);
4566
- buildSpinner = null;
4567
- }
4568
- }
4569
- } else {
4330
+ else {
4570
4331
  const icon = statusIcons[event.content] ?? chalk.yellow("●");
4571
4332
  process.stdout.write(`\n${icon} ${chalk.bold(label)}\n`);
4572
4333
  }
@@ -4575,15 +4336,9 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
4575
4336
  allLogLines.push(...lines);
4576
4337
  if (mcp) {
4577
4338
  for (const line of lines) if (line.trim()) log(`[build] ${line.trim()}`);
4578
- } else if (renderer) for (const line of lines) renderer.processLine(line);
4579
- else for (const line of lines) if (line.trim()) process.stdout.write(` ${line}\n`);
4339
+ } else for (const line of lines) if (line.trim()) process.stdout.write(` ${line}\n`);
4580
4340
  }
4581
4341
  } catch {
4582
- if (renderer) renderer.stopSpinner();
4583
- if (buildSpinner) {
4584
- buildSpinner.stop();
4585
- buildSpinner = null;
4586
- }
4587
4342
  try {
4588
4343
  finalStatus = (await client.deployments.get({ deploymentId })).status;
4589
4344
  try {
@@ -4593,20 +4348,11 @@ async function streamDeploymentLogs(deploymentId, serviceId, serviceName) {
4593
4348
  } catch {}
4594
4349
  }
4595
4350
  if (isGHA) endGroup();
4596
- if (renderer) renderer.stopSpinner();
4597
4351
  const urls = finalStatus === "LIVE" ? await fetchDeployUrls$1(client, serviceId) : [];
4598
4352
  if (finalStatus === "LIVE") {
4599
- if (buildSpinner) {
4600
- buildSpinner.stop();
4601
- buildSpinner = null;
4602
- }
4603
4353
  success(serviceName ? `Deploy de ${mcp ? serviceName : chalk.bold(serviceName)} concluído! Serviço ativo.` : "Deploy concluído! Serviço ativo.");
4604
4354
  if (urls.length > 0) for (const url of urls) info(mcp ? url : `${chalk.bold(url)}`);
4605
4355
  } else if (TERMINAL_STATUSES.has(finalStatus)) {
4606
- if (buildSpinner) {
4607
- buildSpinner.stop();
4608
- buildSpinner = null;
4609
- }
4610
4356
  const label = statusLabels[finalStatus] ?? finalStatus;
4611
4357
  const hints = getFailureHints$1(finalStatus);
4612
4358
  if (mcp) {
@@ -4675,7 +4421,7 @@ const LOGO_LINES = [
4675
4421
  ];
4676
4422
  const BRAND_COLOR = "#FF4D00";
4677
4423
  function getVersion() {
4678
- return "0.0.0-beta.22";
4424
+ return "0.0.0-beta.23";
4679
4425
  }
4680
4426
  function printBanner(subtitle) {
4681
4427
  const version = getVersion();
@@ -5701,7 +5447,7 @@ async function autoUpdate() {
5701
5447
  if (process.env.VELOZ_MCP === "true") return;
5702
5448
  const pm = detectPackageManager();
5703
5449
  if (!pm) return;
5704
- const currentVersion = "0.0.0-beta.22";
5450
+ const currentVersion = "0.0.0-beta.23";
5705
5451
  const latestVersion = await fetchLatestVersion();
5706
5452
  if (!latestVersion || latestVersion === currentVersion) return;
5707
5453
  const installCmd = getInstallCommand(pm, latestVersion);
@@ -7256,7 +7002,7 @@ async function pruneRemovedEntries(config, existingConfig, services, databases)
7256
7002
  //#region src/index.ts
7257
7003
  if (process.argv.includes("--mcp")) process.env.VELOZ_MCP = "true";
7258
7004
  const cli = Cli.create("veloz", {
7259
- version: "0.0.0-beta.22",
7005
+ version: "0.0.0-beta.23",
7260
7006
  description: "CLI da plataforma Veloz — deploy rápido para o Brasil",
7261
7007
  env: z.object({ VELOZ_ENV: z.string().optional().describe("Ambiente alvo (ex: preview, staging)") })
7262
7008
  });
@@ -7332,4 +7078,4 @@ registerPull(cli);
7332
7078
  cli.serve();
7333
7079
 
7334
7080
  //#endregion
7335
- export { registerLogin as n, performLogin as t };
7081
+ export { statusLabels as a, registerLogin as c, TERMINAL_STATUSES as i, info as l, isHiddenMessage as n, getClient as o, parseBuildLine as r, performLogin as s, cleanDisplayLine as t, success as u };
@@ -0,0 +1,3 @@
1
+ import { c as registerLogin, s as performLogin } from "./index.mjs";
2
+
3
+ export { performLogin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onveloz",
3
- "version": "0.0.0-beta.22",
3
+ "version": "0.0.0-beta.23",
4
4
  "description": "CLI da plataforma Veloz — deploy rápido para o Brasil",
5
5
  "keywords": [
6
6
  "brasil",
@@ -33,13 +33,16 @@
33
33
  "form-data": "^4.0.0",
34
34
  "ignore": "^5.3.0",
35
35
  "incur": "^0.3.3",
36
+ "ink": "^6.8.0",
36
37
  "ora": "^8.2.0",
38
+ "react": "^19.2.4",
37
39
  "tar": "^6.2.0",
38
40
  "ws": "^8.20.0",
39
41
  "zod": "^4.1.13"
40
42
  },
41
43
  "devDependencies": {
42
44
  "@types/node": "^22.13.14",
45
+ "@types/react": "^19.2.14",
43
46
  "@types/tar": "^6.1.11",
44
47
  "@types/ws": "^8.18.1",
45
48
  "@veloz/api": "workspace:*",
@@ -1,3 +0,0 @@
1
- import { n as registerLogin, t as performLogin } from "./index.mjs";
2
-
3
- export { performLogin };