@agentuity/cli 0.0.72 → 0.0.74
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/bin/cli.ts +19 -5
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +13 -9
- package/dist/auth.js.map +1 -1
- package/dist/banner.js +1 -1
- package/dist/banner.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +79 -21
- package/dist/cli.js.map +1 -1
- package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
- package/dist/cmd/ai/prompt/api.js +5 -4
- package/dist/cmd/ai/prompt/api.js.map +1 -1
- package/dist/cmd/auth/api.d.ts +2 -2
- package/dist/cmd/auth/api.d.ts.map +1 -1
- package/dist/cmd/auth/api.js +15 -14
- package/dist/cmd/auth/api.js.map +1 -1
- package/dist/cmd/auth/login.d.ts.map +1 -1
- package/dist/cmd/auth/login.js +37 -16
- package/dist/cmd/auth/login.js.map +1 -1
- package/dist/cmd/auth/ssh/api.d.ts.map +1 -1
- package/dist/cmd/auth/ssh/api.js +3 -2
- package/dist/cmd/auth/ssh/api.js.map +1 -1
- package/dist/cmd/build/ast.d.ts.map +1 -1
- package/dist/cmd/build/ast.js +76 -14
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/bundler.d.ts +3 -1
- package/dist/cmd/build/bundler.d.ts.map +1 -1
- package/dist/cmd/build/bundler.js +21 -9
- package/dist/cmd/build/bundler.js.map +1 -1
- package/dist/cmd/build/format-schema.d.ts +6 -0
- package/dist/cmd/build/format-schema.d.ts.map +1 -0
- package/dist/cmd/build/format-schema.js +60 -0
- package/dist/cmd/build/format-schema.js.map +1 -0
- package/dist/cmd/build/index.d.ts.map +1 -1
- package/dist/cmd/build/index.js +13 -0
- package/dist/cmd/build/index.js.map +1 -1
- package/dist/cmd/build/plugin.d.ts.map +1 -1
- package/dist/cmd/build/plugin.js +123 -32
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/build/route-discovery.d.ts +50 -0
- package/dist/cmd/build/route-discovery.d.ts.map +1 -0
- package/dist/cmd/build/route-discovery.js +143 -0
- package/dist/cmd/build/route-discovery.js.map +1 -0
- package/dist/cmd/build/route-registry.d.ts.map +1 -1
- package/dist/cmd/build/route-registry.js +25 -10
- package/dist/cmd/build/route-registry.js.map +1 -1
- package/dist/cmd/cloud/deploy.d.ts.map +1 -1
- package/dist/cmd/cloud/deploy.js +8 -6
- package/dist/cmd/cloud/deploy.js.map +1 -1
- package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
- package/dist/cmd/cloud/deployment/show.js +34 -10
- package/dist/cmd/cloud/deployment/show.js.map +1 -1
- package/dist/cmd/dev/agents.d.ts.map +1 -1
- package/dist/cmd/dev/agents.js +2 -2
- package/dist/cmd/dev/agents.js.map +1 -1
- package/dist/cmd/dev/index.d.ts.map +1 -1
- package/dist/cmd/dev/index.js +21 -0
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/dev/sync.d.ts.map +1 -1
- package/dist/cmd/dev/sync.js +2 -2
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/cmd/project/download.d.ts.map +1 -1
- package/dist/cmd/project/download.js +16 -2
- package/dist/cmd/project/download.js.map +1 -1
- package/dist/cmd/project/list.d.ts.map +1 -1
- package/dist/cmd/project/list.js +2 -10
- package/dist/cmd/project/list.js.map +1 -1
- package/dist/cmd/project/show.d.ts.map +1 -1
- package/dist/cmd/project/show.js +8 -7
- package/dist/cmd/project/show.js.map +1 -1
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +14 -2
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/steps.d.ts +20 -30
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +339 -184
- package/dist/steps.js.map +1 -1
- package/dist/tui/box.d.ts.map +1 -1
- package/dist/tui/box.js +8 -4
- package/dist/tui/box.js.map +1 -1
- package/dist/tui/prompt.d.ts.map +1 -1
- package/dist/tui/prompt.js +7 -2
- package/dist/tui/prompt.js.map +1 -1
- package/dist/tui.d.ts +20 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +90 -18
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/auth.ts +13 -10
- package/src/banner.ts +1 -1
- package/src/cli.ts +89 -27
- package/src/cmd/ai/prompt/api.ts +5 -4
- package/src/cmd/auth/api.ts +20 -22
- package/src/cmd/auth/login.ts +36 -17
- package/src/cmd/auth/ssh/api.ts +5 -9
- package/src/cmd/build/ast.ts +88 -14
- package/src/cmd/build/bundler.ts +32 -11
- package/src/cmd/build/format-schema.ts +66 -0
- package/src/cmd/build/index.ts +14 -0
- package/src/cmd/build/plugin.ts +146 -36
- package/src/cmd/build/route-discovery.ts +197 -0
- package/src/cmd/build/route-registry.ts +26 -10
- package/src/cmd/cloud/deploy.ts +19 -6
- package/src/cmd/cloud/deployment/show.ts +42 -10
- package/src/cmd/dev/agents.ts +2 -10
- package/src/cmd/dev/index.ts +25 -0
- package/src/cmd/dev/sync.ts +2 -12
- package/src/cmd/project/download.ts +16 -2
- package/src/cmd/project/list.ts +2 -9
- package/src/cmd/project/show.ts +8 -6
- package/src/cmd/project/template-flow.ts +21 -2
- package/src/config.ts +10 -0
- package/src/index.ts +2 -2
- package/src/steps.ts +397 -229
- package/src/tui/box.ts +8 -4
- package/src/tui/prompt.ts +7 -4
- package/src/tui.ts +125 -20
package/dist/steps.js
CHANGED
|
@@ -1,61 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Steps UI Component v2 - Clean state-driven implementation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Key principle: Render entire step list from state on every update cycle.
|
|
5
|
+
* Track total lines rendered to calculate cursor movement.
|
|
6
6
|
*/
|
|
7
7
|
import { ValidationInputError, ValidationOutputError } from '@agentuity/server';
|
|
8
|
-
/**
|
|
9
|
-
* Get the appropriate exit function (Bun.exit or process.exit)
|
|
10
|
-
*/
|
|
11
|
-
function getExitFn() {
|
|
12
|
-
const bunExit = globalThis.Bun?.exit;
|
|
13
|
-
return typeof bunExit === 'function' ? bunExit : process.exit.bind(process);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Install interrupt handlers (SIGINT/SIGTERM + TTY raw mode for Ctrl+C)
|
|
17
|
-
*/
|
|
18
|
-
function installInterruptHandlers(onInterrupt) {
|
|
19
|
-
const cleanupFns = [];
|
|
20
|
-
const sigHandler = () => onInterrupt();
|
|
21
|
-
process.on('SIGINT', sigHandler);
|
|
22
|
-
process.on('SIGTERM', sigHandler);
|
|
23
|
-
cleanupFns.push(() => {
|
|
24
|
-
process.off('SIGINT', sigHandler);
|
|
25
|
-
process.off('SIGTERM', sigHandler);
|
|
26
|
-
});
|
|
27
|
-
// TTY raw mode fallback for Bun/Windows/inconsistent SIGINT delivery
|
|
28
|
-
const stdin = process.stdin;
|
|
29
|
-
if (stdin && stdin.isTTY) {
|
|
30
|
-
const onData = (buf) => {
|
|
31
|
-
// Ctrl+C is ASCII ETX (0x03)
|
|
32
|
-
if (buf.length === 1 && buf[0] === 0x03)
|
|
33
|
-
onInterrupt();
|
|
34
|
-
};
|
|
35
|
-
try {
|
|
36
|
-
stdin.setRawMode?.(true);
|
|
37
|
-
}
|
|
38
|
-
catch {
|
|
39
|
-
// ignore if not supported
|
|
40
|
-
}
|
|
41
|
-
stdin.resume?.();
|
|
42
|
-
stdin.on('data', onData);
|
|
43
|
-
cleanupFns.push(() => {
|
|
44
|
-
stdin.off?.('data', onData);
|
|
45
|
-
stdin.pause?.();
|
|
46
|
-
try {
|
|
47
|
-
stdin.setRawMode?.(false);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
// ignore if setRawMode fails
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
return () => {
|
|
55
|
-
for (const fn of cleanupFns.splice(0))
|
|
56
|
-
fn();
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
8
|
// Spinner frames
|
|
60
9
|
const FRAMES = ['◐', '◓', '◑', '◒'];
|
|
61
10
|
// Icons
|
|
@@ -65,7 +14,7 @@ const ICONS = {
|
|
|
65
14
|
error: '✗',
|
|
66
15
|
pending: '☐',
|
|
67
16
|
};
|
|
68
|
-
// Color definitions
|
|
17
|
+
// Color definitions
|
|
69
18
|
const COLORS = {
|
|
70
19
|
cyan: { light: '\x1b[36m', dark: '\x1b[96m' },
|
|
71
20
|
blue: { light: '\x1b[34m', dark: '\x1b[94m' },
|
|
@@ -78,12 +27,8 @@ const COLORS = {
|
|
|
78
27
|
strikethrough: '\x1b[9m',
|
|
79
28
|
reset: '\x1b[0m',
|
|
80
29
|
};
|
|
81
|
-
// Spinner color sequence
|
|
82
30
|
const SPINNER_COLORS = ['cyan', 'blue', 'magenta', 'cyan'];
|
|
83
|
-
|
|
84
|
-
export function setStepsColorScheme(scheme) {
|
|
85
|
-
currentColorScheme = scheme;
|
|
86
|
-
}
|
|
31
|
+
const currentColorScheme = process.env.CI ? 'light' : 'dark';
|
|
87
32
|
function getColor(colorKey) {
|
|
88
33
|
const color = COLORS[colorKey];
|
|
89
34
|
if (typeof color === 'string') {
|
|
@@ -94,42 +39,80 @@ function getColor(colorKey) {
|
|
|
94
39
|
/**
|
|
95
40
|
* Helper functions for creating step outcomes
|
|
96
41
|
*/
|
|
97
|
-
export const stepSuccess = () => ({ status: 'success' });
|
|
98
|
-
export const stepSkipped = (reason) => ({
|
|
99
|
-
|
|
42
|
+
export const stepSuccess = (output) => ({ status: 'success', output });
|
|
43
|
+
export const stepSkipped = (reason, output) => ({
|
|
44
|
+
status: 'skipped',
|
|
45
|
+
reason,
|
|
46
|
+
output,
|
|
47
|
+
});
|
|
48
|
+
export const stepError = (message, cause, output) => ({
|
|
100
49
|
status: 'error',
|
|
101
50
|
message,
|
|
102
51
|
cause,
|
|
52
|
+
output,
|
|
103
53
|
});
|
|
104
54
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* Each step runs its callback while showing a spinner animation.
|
|
108
|
-
* Steps can complete with success, skipped, or error status.
|
|
109
|
-
* Exits with code 1 if any step errors.
|
|
110
|
-
*
|
|
111
|
-
* When there's no TTY or log level is debug/trace, uses plain output instead of TUI.
|
|
55
|
+
* Render a single step line (without output box)
|
|
112
56
|
*/
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
57
|
+
function renderStepLine(step, spinner) {
|
|
58
|
+
const grayColor = getColor('gray');
|
|
59
|
+
const greenColor = getColor('green');
|
|
60
|
+
const yellowColor = getColor('yellow');
|
|
61
|
+
const redColor = getColor('red');
|
|
62
|
+
const cyanColor = getColor('cyan');
|
|
63
|
+
if (step.status === 'success') {
|
|
64
|
+
return `${greenColor}${ICONS.success}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${step.label}${COLORS.reset}`;
|
|
65
|
+
}
|
|
66
|
+
else if (step.status === 'skipped') {
|
|
67
|
+
const reason = step.skipReason ? ` ${grayColor}(${step.skipReason})${COLORS.reset}` : '';
|
|
68
|
+
return `${yellowColor}${ICONS.skipped}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${step.label}${COLORS.reset}${reason}`;
|
|
69
|
+
}
|
|
70
|
+
else if (step.status === 'error') {
|
|
71
|
+
return `${redColor}${ICONS.error}${COLORS.reset} ${step.label}`;
|
|
72
|
+
}
|
|
73
|
+
else if (step.status === 'running' && spinner) {
|
|
74
|
+
const progressIndicator = step.progress !== undefined
|
|
75
|
+
? ` ${cyanColor}${Math.floor(step.progress)}%${COLORS.reset}`
|
|
76
|
+
: '';
|
|
77
|
+
return `${spinner} ${step.label}${progressIndicator}`;
|
|
128
78
|
}
|
|
129
79
|
else {
|
|
130
|
-
|
|
80
|
+
return `${grayColor}${ICONS.pending}${COLORS.reset} ${step.label}`;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Render all steps from state, including output boxes
|
|
85
|
+
* Returns the rendered output and total line count
|
|
86
|
+
*/
|
|
87
|
+
function renderAllSteps(state, runningStepIndex, spinner) {
|
|
88
|
+
const lines = [];
|
|
89
|
+
let totalLines = 0;
|
|
90
|
+
const grayColor = getColor('gray');
|
|
91
|
+
for (let i = 0; i < state.length; i++) {
|
|
92
|
+
const step = state[i];
|
|
93
|
+
const isRunning = i === runningStepIndex;
|
|
94
|
+
const stepSpinner = isRunning && spinner ? spinner : undefined;
|
|
95
|
+
// Render step line
|
|
96
|
+
lines.push(renderStepLine(step, stepSpinner));
|
|
97
|
+
totalLines++;
|
|
98
|
+
// Render output box if present
|
|
99
|
+
if (step.output && step.output.length > 0) {
|
|
100
|
+
lines.push(`${grayColor}╭─ Output${COLORS.reset}`);
|
|
101
|
+
totalLines++;
|
|
102
|
+
for (const line of step.output) {
|
|
103
|
+
lines.push(`${grayColor}│${COLORS.reset} ${line}`);
|
|
104
|
+
totalLines++;
|
|
105
|
+
}
|
|
106
|
+
lines.push(`${grayColor}╰─${COLORS.reset}`);
|
|
107
|
+
totalLines++;
|
|
108
|
+
// Don't add blank line here - the '\n' we append during write creates separation
|
|
109
|
+
}
|
|
131
110
|
}
|
|
111
|
+
return { output: lines.join('\n'), totalLines };
|
|
132
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Print validation issues (for ValidationInputError/ValidationOutputError)
|
|
115
|
+
*/
|
|
133
116
|
function printValidationIssues(issues) {
|
|
134
117
|
const errorColor = getColor('red');
|
|
135
118
|
console.error(`${errorColor}Validation details:${COLORS.reset}`);
|
|
@@ -141,15 +124,127 @@ function printValidationIssues(issues) {
|
|
|
141
124
|
}
|
|
142
125
|
}
|
|
143
126
|
/**
|
|
144
|
-
*
|
|
127
|
+
* Global pause state and tracking
|
|
128
|
+
*/
|
|
129
|
+
let isPaused = false;
|
|
130
|
+
let getTotalLinesFn = null;
|
|
131
|
+
let forceRerenderFn = null;
|
|
132
|
+
export function isStepUIPaused() {
|
|
133
|
+
return isPaused;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Internal function to set pause capability
|
|
137
|
+
*/
|
|
138
|
+
function enablePauseResume(getTotalLines, forceRerender) {
|
|
139
|
+
getTotalLinesFn = getTotalLines;
|
|
140
|
+
forceRerenderFn = forceRerender;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Pause step rendering for interactive input
|
|
144
|
+
* Returns resume function
|
|
145
|
+
*/
|
|
146
|
+
export function pauseStepUI() {
|
|
147
|
+
if (!process.stdout.isTTY || !getTotalLinesFn) {
|
|
148
|
+
return () => { }; // No-op if not TTY or not in step context
|
|
149
|
+
}
|
|
150
|
+
isPaused = true;
|
|
151
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
152
|
+
const originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
153
|
+
// Intercept writes during pause (unused but prevents issues with interactive prompts)
|
|
154
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
155
|
+
process.stdout.write = ((chunk, ..._args) => {
|
|
156
|
+
return originalStdoutWrite(chunk);
|
|
157
|
+
});
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
159
|
+
process.stderr.write = ((chunk, ..._args) => {
|
|
160
|
+
return originalStderrWrite(chunk);
|
|
161
|
+
});
|
|
162
|
+
// Show cursor and add newline for separation
|
|
163
|
+
process.stdout.write('\x1B[?25h');
|
|
164
|
+
process.stdout.write('\n');
|
|
165
|
+
// Return resume function
|
|
166
|
+
return () => {
|
|
167
|
+
isPaused = false;
|
|
168
|
+
// Restore original write functions
|
|
169
|
+
process.stdout.write = originalStdoutWrite;
|
|
170
|
+
process.stderr.write = originalStderrWrite;
|
|
171
|
+
// Restore cursor to saved position (where steps began)
|
|
172
|
+
process.stdout.write('\x1B[u'); // Restore cursor position
|
|
173
|
+
process.stdout.write('\x1B[0J'); // Clear from saved position to end of screen
|
|
174
|
+
process.stdout.write('\x1B[?25l'); // Hide cursor
|
|
175
|
+
// Force immediate re-render (cursor already at step start)
|
|
176
|
+
if (forceRerenderFn) {
|
|
177
|
+
forceRerenderFn(true);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get exit function (Bun.exit or process.exit)
|
|
145
183
|
*/
|
|
146
|
-
|
|
184
|
+
function getExitFn() {
|
|
185
|
+
const bunExit = globalThis.Bun?.exit;
|
|
186
|
+
return typeof bunExit === 'function' ? bunExit : process.exit.bind(process);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Install interrupt handlers (SIGINT/SIGTERM + raw mode)
|
|
190
|
+
*/
|
|
191
|
+
function installInterruptHandlers(onInterrupt) {
|
|
192
|
+
const cleanupFns = [];
|
|
193
|
+
const sigHandler = () => onInterrupt();
|
|
194
|
+
process.on('SIGINT', sigHandler);
|
|
195
|
+
process.on('SIGTERM', sigHandler);
|
|
196
|
+
cleanupFns.push(() => {
|
|
197
|
+
process.off('SIGINT', sigHandler);
|
|
198
|
+
process.off('SIGTERM', sigHandler);
|
|
199
|
+
});
|
|
200
|
+
// TTY raw mode fallback
|
|
201
|
+
const stdin = process.stdin;
|
|
202
|
+
if (stdin && stdin.isTTY) {
|
|
203
|
+
const onData = (buf) => {
|
|
204
|
+
if (buf.length === 1 && buf[0] === 0x03)
|
|
205
|
+
onInterrupt();
|
|
206
|
+
};
|
|
207
|
+
try {
|
|
208
|
+
stdin.setRawMode?.(true);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Ignore errors
|
|
212
|
+
}
|
|
213
|
+
stdin.resume?.();
|
|
214
|
+
stdin.on('data', onData);
|
|
215
|
+
cleanupFns.push(() => {
|
|
216
|
+
stdin.off?.('data', onData);
|
|
217
|
+
stdin.pause?.();
|
|
218
|
+
try {
|
|
219
|
+
stdin.setRawMode?.(false);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Ignore errors
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return () => {
|
|
227
|
+
for (const fn of cleanupFns.splice(0))
|
|
228
|
+
fn();
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Run steps with TUI (animated mode)
|
|
233
|
+
*/
|
|
234
|
+
async function runStepsTUI(steps) {
|
|
235
|
+
// Initialize state
|
|
236
|
+
const state = steps.map((s) => ({
|
|
237
|
+
label: s.label,
|
|
238
|
+
status: 'pending',
|
|
239
|
+
}));
|
|
240
|
+
let totalLinesFromLastRender = 0;
|
|
241
|
+
let interrupted = false;
|
|
242
|
+
let activeInterval = null;
|
|
243
|
+
let currentStepIndex = -1;
|
|
244
|
+
let currentFrameIndex = 0;
|
|
147
245
|
// Hide cursor
|
|
148
246
|
process.stdout.write('\x1B[?25l');
|
|
149
|
-
//
|
|
150
|
-
let activeInterval = null;
|
|
151
|
-
let interrupted = false;
|
|
152
|
-
// Set up Ctrl+C handler for graceful exit
|
|
247
|
+
// Set up interrupt handler
|
|
153
248
|
const exit = getExitFn();
|
|
154
249
|
const onInterrupt = () => {
|
|
155
250
|
if (interrupted)
|
|
@@ -161,116 +256,204 @@ async function runStepsTUI(state) {
|
|
|
161
256
|
exit(130);
|
|
162
257
|
};
|
|
163
258
|
const restoreInterrupts = installInterruptHandlers(onInterrupt);
|
|
259
|
+
// Force re-render function
|
|
260
|
+
const forceRerender = (skipMove = false) => {
|
|
261
|
+
if (currentStepIndex < 0 || currentStepIndex >= state.length)
|
|
262
|
+
return;
|
|
263
|
+
const colorKey = SPINNER_COLORS[currentFrameIndex % SPINNER_COLORS.length];
|
|
264
|
+
const color = getColor(colorKey);
|
|
265
|
+
const spinner = `${color}${COLORS.bold}${FRAMES[currentFrameIndex % FRAMES.length]}${COLORS.reset}`;
|
|
266
|
+
const rendered = renderAllSteps(state, currentStepIndex, spinner);
|
|
267
|
+
// Optionally move up, then to column 0
|
|
268
|
+
if (!skipMove && totalLinesFromLastRender > 0) {
|
|
269
|
+
process.stdout.write(`\x1B[${totalLinesFromLastRender}A`);
|
|
270
|
+
process.stdout.write('\x1B[0G');
|
|
271
|
+
}
|
|
272
|
+
process.stdout.write('\x1B[0J');
|
|
273
|
+
process.stdout.write(rendered.output + '\n');
|
|
274
|
+
totalLinesFromLastRender = rendered.totalLines;
|
|
275
|
+
};
|
|
164
276
|
try {
|
|
277
|
+
// Enable pause/resume capability
|
|
278
|
+
enablePauseResume(() => totalLinesFromLastRender, forceRerender);
|
|
279
|
+
// Save cursor position BEFORE rendering steps
|
|
280
|
+
process.stdout.write('\x1B[s');
|
|
165
281
|
// Initial render
|
|
166
|
-
|
|
167
|
-
|
|
282
|
+
const initialRender = renderAllSteps(state, -1);
|
|
283
|
+
process.stdout.write(initialRender.output + '\n');
|
|
284
|
+
totalLinesFromLastRender = initialRender.totalLines;
|
|
285
|
+
// Execute steps
|
|
286
|
+
for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
|
|
168
287
|
if (interrupted)
|
|
169
288
|
break;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
289
|
+
currentStepIndex = stepIndex;
|
|
290
|
+
currentFrameIndex = 0;
|
|
291
|
+
const step = steps[stepIndex];
|
|
292
|
+
const stepState = state[stepIndex];
|
|
293
|
+
stepState.status = 'running';
|
|
173
294
|
// Start spinner animation
|
|
174
295
|
activeInterval = setInterval(() => {
|
|
175
|
-
|
|
296
|
+
if (isPaused)
|
|
297
|
+
return;
|
|
298
|
+
const colorKey = SPINNER_COLORS[currentFrameIndex % SPINNER_COLORS.length];
|
|
176
299
|
const color = getColor(colorKey);
|
|
177
|
-
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
300
|
+
const spinner = `${color}${COLORS.bold}${FRAMES[currentFrameIndex % FRAMES.length]}${COLORS.reset}`;
|
|
301
|
+
// Render all steps from state
|
|
302
|
+
const rendered = renderAllSteps(state, currentStepIndex, spinner);
|
|
303
|
+
// Move to start, clear, render
|
|
304
|
+
if (totalLinesFromLastRender > 0) {
|
|
305
|
+
process.stdout.write(`\x1B[${totalLinesFromLastRender}A`); // Move up
|
|
306
|
+
process.stdout.write('\x1B[0G'); // Move to column 0 (using absolute positioning)
|
|
307
|
+
}
|
|
308
|
+
process.stdout.write('\x1B[0J'); // Clear from cursor to end
|
|
309
|
+
process.stdout.write(rendered.output + '\n');
|
|
310
|
+
totalLinesFromLastRender = rendered.totalLines;
|
|
311
|
+
currentFrameIndex++;
|
|
182
312
|
}, 120);
|
|
183
|
-
//
|
|
313
|
+
// Progress callback
|
|
184
314
|
const progressCallback = (progress) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
315
|
+
if (isPaused)
|
|
316
|
+
return;
|
|
317
|
+
stepState.progress = Math.min(100, Math.max(0, progress));
|
|
318
|
+
// Render all steps from state
|
|
319
|
+
const colorKey = SPINNER_COLORS[currentFrameIndex % SPINNER_COLORS.length];
|
|
320
|
+
const color = getColor(colorKey);
|
|
321
|
+
const spinner = `${color}${COLORS.bold}${FRAMES[currentFrameIndex % FRAMES.length]}${COLORS.reset}`;
|
|
322
|
+
const rendered = renderAllSteps(state, currentStepIndex, spinner);
|
|
323
|
+
// Move to start, clear, render
|
|
324
|
+
if (totalLinesFromLastRender > 0) {
|
|
325
|
+
process.stdout.write(`\x1B[${totalLinesFromLastRender}A`);
|
|
326
|
+
process.stdout.write('\x1B[0G');
|
|
327
|
+
}
|
|
328
|
+
process.stdout.write('\x1B[0J');
|
|
329
|
+
process.stdout.write(rendered.output + '\n');
|
|
330
|
+
totalLinesFromLastRender = rendered.totalLines;
|
|
189
331
|
};
|
|
332
|
+
// Run the step
|
|
190
333
|
try {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
334
|
+
const outcome = await step.run({ progress: progressCallback });
|
|
335
|
+
// Update state from outcome
|
|
336
|
+
if (outcome.status === 'success') {
|
|
337
|
+
stepState.status = 'success';
|
|
338
|
+
stepState.output = outcome.output;
|
|
339
|
+
}
|
|
340
|
+
else if (outcome.status === 'skipped') {
|
|
341
|
+
stepState.status = 'skipped';
|
|
342
|
+
stepState.skipReason = outcome.reason;
|
|
343
|
+
stepState.output = outcome.output;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
stepState.status = 'error';
|
|
347
|
+
stepState.errorMessage = outcome.message;
|
|
348
|
+
stepState.errorCause = outcome.cause;
|
|
349
|
+
stepState.output = outcome.output;
|
|
350
|
+
}
|
|
194
351
|
}
|
|
195
352
|
catch (err) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
cause: err instanceof Error ? err : undefined,
|
|
200
|
-
};
|
|
353
|
+
stepState.status = 'error';
|
|
354
|
+
stepState.errorMessage = err instanceof Error ? err.message : String(err);
|
|
355
|
+
stepState.errorCause = err instanceof Error ? err : undefined;
|
|
201
356
|
}
|
|
357
|
+
// Stop spinner
|
|
202
358
|
if (activeInterval) {
|
|
203
359
|
clearInterval(activeInterval);
|
|
204
360
|
activeInterval = null;
|
|
205
361
|
}
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
362
|
+
// Final render with outcome
|
|
363
|
+
stepState.progress = undefined;
|
|
364
|
+
const finalRender = renderAllSteps(state, -1);
|
|
365
|
+
if (totalLinesFromLastRender > 0) {
|
|
366
|
+
process.stdout.write(`\x1B[${totalLinesFromLastRender}A`);
|
|
367
|
+
process.stdout.write('\x1B[0G');
|
|
368
|
+
}
|
|
369
|
+
process.stdout.write('\x1B[0J');
|
|
370
|
+
process.stdout.write(finalRender.output + '\n');
|
|
371
|
+
totalLinesFromLastRender = finalRender.totalLines;
|
|
372
|
+
// Handle errors
|
|
373
|
+
if (stepState.status === 'error') {
|
|
212
374
|
const errorColor = getColor('red');
|
|
213
|
-
console.error(`\n${errorColor}Error: ${
|
|
214
|
-
if (
|
|
215
|
-
|
|
216
|
-
printValidationIssues(
|
|
375
|
+
console.error(`\n${errorColor}Error: ${stepState.errorMessage}${COLORS.reset}`);
|
|
376
|
+
if (stepState.errorCause instanceof ValidationInputError ||
|
|
377
|
+
stepState.errorCause instanceof ValidationOutputError) {
|
|
378
|
+
printValidationIssues(stepState.errorCause.issues);
|
|
217
379
|
}
|
|
218
380
|
console.error('');
|
|
219
381
|
process.stdout.write('\x1B[?25h'); // Show cursor
|
|
220
382
|
process.exit(1);
|
|
221
383
|
}
|
|
222
384
|
}
|
|
223
|
-
// Show cursor
|
|
385
|
+
// Show cursor
|
|
224
386
|
process.stdout.write('\x1B[?25h');
|
|
225
387
|
}
|
|
226
388
|
catch (err) {
|
|
227
|
-
// Ensure cursor is shown even if something goes wrong
|
|
228
389
|
process.stdout.write('\x1B[?25h');
|
|
229
390
|
throw err;
|
|
230
391
|
}
|
|
231
392
|
finally {
|
|
232
|
-
// Remove signal/TTY handlers
|
|
233
393
|
restoreInterrupts();
|
|
394
|
+
getTotalLinesFn = null; // Clear pause capability
|
|
395
|
+
forceRerenderFn = null;
|
|
234
396
|
}
|
|
235
397
|
}
|
|
236
398
|
/**
|
|
237
|
-
* Run steps in plain mode (no
|
|
399
|
+
* Run steps in plain mode (no animations)
|
|
238
400
|
*/
|
|
239
|
-
async function runStepsPlain(
|
|
401
|
+
async function runStepsPlain(steps) {
|
|
240
402
|
const grayColor = getColor('gray');
|
|
241
403
|
const greenColor = getColor('green');
|
|
242
404
|
const yellowColor = getColor('yellow');
|
|
243
405
|
const redColor = getColor('red');
|
|
244
|
-
for (const step of
|
|
245
|
-
|
|
406
|
+
for (const step of steps) {
|
|
407
|
+
let outcome;
|
|
246
408
|
try {
|
|
247
|
-
|
|
248
|
-
step.outcome = outcome;
|
|
409
|
+
outcome = await step.run({ progress: () => { } });
|
|
249
410
|
}
|
|
250
411
|
catch (err) {
|
|
251
|
-
|
|
412
|
+
outcome = {
|
|
252
413
|
status: 'error',
|
|
253
414
|
message: err instanceof Error ? err.message : String(err),
|
|
254
415
|
cause: err instanceof Error ? err : undefined,
|
|
255
416
|
};
|
|
256
417
|
}
|
|
257
|
-
// Print final state
|
|
258
|
-
if (
|
|
418
|
+
// Print final state
|
|
419
|
+
if (outcome.status === 'success') {
|
|
259
420
|
console.log(`${greenColor}${ICONS.success}${COLORS.reset} ${step.label}`);
|
|
421
|
+
if (outcome.output && outcome.output.length > 0) {
|
|
422
|
+
console.log(`${grayColor}╭─ Output${COLORS.reset}`);
|
|
423
|
+
for (const line of outcome.output) {
|
|
424
|
+
console.log(`${grayColor}│${COLORS.reset} ${line}`);
|
|
425
|
+
}
|
|
426
|
+
console.log(`${grayColor}╰─${COLORS.reset}`);
|
|
427
|
+
console.log('');
|
|
428
|
+
}
|
|
260
429
|
}
|
|
261
|
-
else if (
|
|
262
|
-
const reason =
|
|
263
|
-
? ` ${grayColor}(${step.outcome.reason})${COLORS.reset}`
|
|
264
|
-
: '';
|
|
430
|
+
else if (outcome.status === 'skipped') {
|
|
431
|
+
const reason = outcome.reason ? ` ${grayColor}(${outcome.reason})${COLORS.reset}` : '';
|
|
265
432
|
console.log(`${yellowColor}${ICONS.skipped}${COLORS.reset} ${step.label}${reason}`);
|
|
433
|
+
if (outcome.output && outcome.output.length > 0) {
|
|
434
|
+
console.log(`${grayColor}╭─ Output${COLORS.reset}`);
|
|
435
|
+
for (const line of outcome.output) {
|
|
436
|
+
console.log(`${grayColor}│${COLORS.reset} ${line}`);
|
|
437
|
+
}
|
|
438
|
+
console.log(`${grayColor}╰─${COLORS.reset}`);
|
|
439
|
+
console.log('');
|
|
440
|
+
}
|
|
266
441
|
}
|
|
267
|
-
else
|
|
442
|
+
else {
|
|
268
443
|
console.log(`${redColor}${ICONS.error}${COLORS.reset} ${step.label}`);
|
|
444
|
+
if (outcome.output && outcome.output.length > 0) {
|
|
445
|
+
console.log(`${grayColor}╭─ Output${COLORS.reset}`);
|
|
446
|
+
for (const line of outcome.output) {
|
|
447
|
+
console.log(`${grayColor}│${COLORS.reset} ${line}`);
|
|
448
|
+
}
|
|
449
|
+
console.log(`${grayColor}╰─${COLORS.reset}`);
|
|
450
|
+
console.log('');
|
|
451
|
+
}
|
|
269
452
|
const errorColor = getColor('red');
|
|
270
|
-
console.error(`\n${errorColor}Error: ${
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
printValidationIssues(
|
|
453
|
+
console.error(`\n${errorColor}Error: ${outcome.message}${COLORS.reset}`);
|
|
454
|
+
if (outcome.cause instanceof ValidationInputError ||
|
|
455
|
+
outcome.cause instanceof ValidationOutputError) {
|
|
456
|
+
printValidationIssues(outcome.cause.issues);
|
|
274
457
|
}
|
|
275
458
|
console.error('');
|
|
276
459
|
process.exit(1);
|
|
@@ -278,43 +461,15 @@ async function runStepsPlain(state) {
|
|
|
278
461
|
}
|
|
279
462
|
}
|
|
280
463
|
/**
|
|
281
|
-
*
|
|
282
|
-
*/
|
|
283
|
-
function renderProgress(progress) {
|
|
284
|
-
const cyanColor = getColor('cyan');
|
|
285
|
-
const percentage = `${Math.floor(progress)}%`;
|
|
286
|
-
return ` ${cyanColor}${percentage}${COLORS.reset}`;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Render all steps as a multiline string
|
|
464
|
+
* Run a series of steps with animated progress
|
|
290
465
|
*/
|
|
291
|
-
function
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (s.outcome?.status === 'success') {
|
|
300
|
-
lines.push(`${greenColor}${ICONS.success}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${s.label}${COLORS.reset}`);
|
|
301
|
-
}
|
|
302
|
-
else if (s.outcome?.status === 'skipped') {
|
|
303
|
-
const reason = s.outcome.reason ? ` ${grayColor}(${s.outcome.reason})${COLORS.reset}` : '';
|
|
304
|
-
lines.push(`${yellowColor}${ICONS.skipped}${COLORS.reset} ${grayColor}${COLORS.strikethrough}${s.label}${COLORS.reset}${reason}`);
|
|
305
|
-
}
|
|
306
|
-
else if (s.outcome?.status === 'error') {
|
|
307
|
-
lines.push(`${redColor}${ICONS.error}${COLORS.reset} ${s.label}`);
|
|
308
|
-
}
|
|
309
|
-
else if (i === activeIndex && spinner) {
|
|
310
|
-
// Only show progress for active step with spinner
|
|
311
|
-
const progressIndicator = s.progress !== undefined ? renderProgress(s.progress) : '';
|
|
312
|
-
lines.push(`${spinner} ${s.label}${progressIndicator}`);
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
lines.push(`${grayColor}${ICONS.pending}${COLORS.reset} ${s.label}`);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
return lines.join('\n');
|
|
466
|
+
export async function runSteps(steps, logLevel) {
|
|
467
|
+
const useTUI = process.stdout.isTTY && (!logLevel || ['info', 'warn', 'error'].includes(logLevel));
|
|
468
|
+
if (useTUI) {
|
|
469
|
+
await runStepsTUI(steps);
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
await runStepsPlain(steps);
|
|
473
|
+
}
|
|
319
474
|
}
|
|
320
475
|
//# sourceMappingURL=steps.js.map
|