@solidactions/cli 1.1.0 → 1.2.0

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.
@@ -26,6 +26,8 @@ async function runs(projectName, options = {}) {
26
26
  params.workflow = options.workflow;
27
27
  if (options.detailed)
28
28
  params.detailed = '1';
29
+ if (options.hasErrors)
30
+ params.has_errors = '1';
29
31
  const response = await axios_1.default.get(`${config.host}/api/v1/runs`, {
30
32
  headers: (0, api_1.getApiHeaders)(config),
31
33
  params,
@@ -97,8 +99,10 @@ function displayDetailedList(runsList, projectName) {
97
99
  const status = run.execution_status || run.status || '?';
98
100
  const statusColor = getStatusColor(status);
99
101
  const exitStr = run.exit_code !== null && run.exit_code !== undefined ? ` (exit ${run.exit_code})` : '';
102
+ const isSilentFailure = hasRunErrors(run) && isSuccessStatus(status);
100
103
  console.log('');
101
- console.log(chalk_1.default.bold(` Run #${run.id}`) + chalk_1.default.gray(` — ${run.workflow_name || '?'} (${run.project_name || '?'})`));
104
+ const silentTag = isSilentFailure ? chalk_1.default.yellow(' [DEGRADED]') : '';
105
+ console.log(chalk_1.default.bold(` Run #${run.id}`) + chalk_1.default.gray(` — ${run.workflow_name || '?'} (${run.project_name || '?'})`) + silentTag);
102
106
  console.log(` Status: ${statusColor(status)}${chalk_1.default.gray(exitStr)}`);
103
107
  console.log(` Trigger: ${chalk_1.default.gray(run.triggered_by || '-')}`);
104
108
  // Timeline
@@ -108,18 +112,38 @@ function displayDetailedList(runsList, projectName) {
108
112
  console.log(` Started: ${chalk_1.default.gray(formatTs(run.timeline.started))}`);
109
113
  console.log(` Completed: ${chalk_1.default.gray(formatTs(run.timeline.completed))}`);
110
114
  }
111
- // Steps summary
115
+ // Steps detail
112
116
  if (run.steps && run.steps.length > 0) {
113
- const completed = run.steps.filter((s) => s.completed_at || s.completed_at_epoch_ms).length;
114
- const totalDuration = run.steps.reduce((sum, s) => sum + (s.duration_ms || 0), 0);
115
- console.log(` Steps: ${chalk_1.default.gray(`${completed}/${run.steps.length} completed (${formatDuration(totalDuration)} total)`)}`);
117
+ displayStepsDetail(run.steps);
118
+ }
119
+ // Workflow output
120
+ if (run.output) {
121
+ const outputStr = JSON.stringify(unwrapOutput(run.output), null, 2);
122
+ const lines = outputStr.split('\n');
123
+ if (lines.length === 1) {
124
+ console.log(` Output: ${chalk_1.default.gray(truncate(outputStr, 60))}`);
125
+ }
126
+ else {
127
+ console.log(` Output:`);
128
+ for (const line of lines.slice(0, 5)) {
129
+ console.log(chalk_1.default.gray(` ${line}`));
130
+ }
131
+ if (lines.length > 5) {
132
+ console.log(chalk_1.default.gray(` ... (${lines.length - 5} more lines)`));
133
+ }
134
+ }
135
+ }
136
+ // Workflow error
137
+ if (run.error) {
138
+ const errMsg = typeof run.error === 'string' ? run.error : JSON.stringify(run.error);
139
+ console.log(` ${chalk_1.default.bold.red('Error:')} ${chalk_1.default.red(truncate(errMsg, 60))}`);
116
140
  }
117
141
  // Logs snippet (first errors or last 3 lines)
118
142
  if (run.logs && typeof run.logs === 'string' && run.logs.trim()) {
119
143
  const lines = run.logs.trim().split('\n');
120
144
  const errorLines = lines.filter((l) => /error|fail|exception/i.test(l));
121
145
  if (errorLines.length > 0) {
122
- console.log(` Errors:`);
146
+ console.log(` Logs:`);
123
147
  for (const line of errorLines.slice(0, 3)) {
124
148
  console.log(chalk_1.default.red(` ${truncate(line, 80)}`));
125
149
  }
@@ -129,7 +153,91 @@ function displayDetailedList(runsList, projectName) {
129
153
  console.log('');
130
154
  console.log(chalk_1.default.gray(`Showing ${runsList.length} run(s)`));
131
155
  }
156
+ function displayStepsDetail(steps) {
157
+ const completed = steps.filter((s) => s.completed_at || s.completed_at_epoch_ms).length;
158
+ const totalDuration = steps.reduce((sum, s) => sum + (s.duration_ms || 0), 0);
159
+ const errorCount = steps.filter((s) => s.error || s.status === 'error' || s.status === 'failed').length;
160
+ const retryCount = steps.filter((s) => s.retried || s.status === 'retried').length;
161
+ let summary = `${completed}/${steps.length} completed (${formatDuration(totalDuration)} total)`;
162
+ if (errorCount > 0)
163
+ summary += chalk_1.default.red(` · ${errorCount} errored`);
164
+ if (retryCount > 0)
165
+ summary += chalk_1.default.yellow(` · ${retryCount} retried`);
166
+ console.log(` Steps: ${chalk_1.default.gray(summary)}`);
167
+ // Per-step detail table
168
+ console.log(chalk_1.default.gray(` ${'NAME'.padEnd(24)}${'STATUS'.padEnd(12)}${'DURATION'.padEnd(10)}OUTPUT`));
169
+ console.log(chalk_1.default.gray(` ${'─'.repeat(70)}`));
170
+ for (const step of steps) {
171
+ const name = truncate(step.name || '?', 23).padEnd(24);
172
+ const stepStatus = getStepStatus(step);
173
+ const stepStatusColor = getStepStatusColor(stepStatus);
174
+ const duration = formatDuration(step.duration_ms);
175
+ const output = step.output ? truncate(JSON.stringify(unwrapOutput(step.output)), 40) : '-';
176
+ console.log(` ${name}${stepStatusColor(stepStatus.padEnd(12))}${chalk_1.default.gray(duration.padEnd(10))}${chalk_1.default.gray(output)}`);
177
+ if (step.error) {
178
+ const errMsg = typeof step.error === 'string' ? step.error : JSON.stringify(step.error);
179
+ console.log(chalk_1.default.red(` error: ${truncate(errMsg, 70)}`));
180
+ }
181
+ }
182
+ }
132
183
  // ─── Utility ───────────────────────────────────────────────────────────────
184
+ function unwrapOutput(output) {
185
+ if (output && output.__solidactions_serializer === 'superjson' && output.json) {
186
+ return output.json;
187
+ }
188
+ return output;
189
+ }
190
+ /**
191
+ * Determine if a run has any errors (step errors, retries, or non-empty error arrays in output).
192
+ */
193
+ function hasRunErrors(run) {
194
+ // Check step-level errors
195
+ if (run.steps && run.steps.some((s) => s.error || s.status === 'error' || s.status === 'failed' || s.retried || s.status === 'retried')) {
196
+ return true;
197
+ }
198
+ // Check workflow-level error
199
+ if (run.error)
200
+ return true;
201
+ // Check output.errors array
202
+ const output = unwrapOutput(run.output);
203
+ if (output && Array.isArray(output.errors) && output.errors.length > 0)
204
+ return true;
205
+ return false;
206
+ }
207
+ function isSuccessStatus(status) {
208
+ const s = status?.toLowerCase();
209
+ return s === 'completed' || s === 'success';
210
+ }
211
+ function getStepStatus(step) {
212
+ if (step.status)
213
+ return step.status;
214
+ if (step.error)
215
+ return 'error';
216
+ if (step.completed_at || step.completed_at_epoch_ms)
217
+ return 'completed';
218
+ if (step.started_at || step.started_at_epoch_ms)
219
+ return 'running';
220
+ return 'pending';
221
+ }
222
+ function getStepStatusColor(status) {
223
+ switch (status?.toLowerCase()) {
224
+ case 'completed':
225
+ case 'success':
226
+ return chalk_1.default.green;
227
+ case 'running':
228
+ return chalk_1.default.blue;
229
+ case 'pending':
230
+ case 'queued':
231
+ return chalk_1.default.yellow;
232
+ case 'failed':
233
+ case 'error':
234
+ return chalk_1.default.red;
235
+ case 'retried':
236
+ return chalk_1.default.yellow;
237
+ default:
238
+ return chalk_1.default.gray;
239
+ }
240
+ }
133
241
  /**
134
242
  * Parse --since value into epoch ms.
135
243
  * Accepts: "1h", "30m", "2d", "1w", or ISO date string.
package/dist/index.js CHANGED
@@ -126,6 +126,7 @@ runCmd
126
126
  .option('--since <duration>', 'Filter to runs since (e.g., 1h, 30m, 2d, 1w)')
127
127
  .option('--workflow <name>', 'Filter by workflow name')
128
128
  .option('--detailed', 'Include timeline, steps, and logs per run (default limit: 5)')
129
+ .option('--has-errors', 'Show only runs with errors (step errors, retries, or degraded results)')
129
130
  .option('--json', 'Output as JSON')
130
131
  .action((projectName, options) => {
131
132
  (0, run_list_1.runs)(projectName, options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidactions/cli",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "SolidActions CLI - Deploy and manage workflow automation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {