@solidactions/cli 0.7.3 → 1.0.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.
package/dist/index.js CHANGED
@@ -4,12 +4,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const commander_1 = require("commander");
5
5
  const deploy_1 = require("./commands/deploy");
6
6
  const init_1 = require("./commands/init");
7
- const logs_1 = require("./commands/logs");
8
- const logs_build_1 = require("./commands/logs-build");
9
- const steps_1 = require("./commands/steps");
10
- const run_1 = require("./commands/run");
11
- const runs_1 = require("./commands/runs");
12
7
  const pull_1 = require("./commands/pull");
8
+ const project_list_1 = require("./commands/project-list");
9
+ const project_logs_1 = require("./commands/project-logs");
10
+ const run_start_1 = require("./commands/run-start");
11
+ const run_list_1 = require("./commands/run-list");
12
+ const run_view_1 = require("./commands/run-view");
13
13
  const env_list_1 = require("./commands/env-list");
14
14
  const env_delete_1 = require("./commands/env-delete");
15
15
  const env_map_1 = require("./commands/env-map");
@@ -19,7 +19,7 @@ const env_set_1 = require("./commands/env-set");
19
19
  const schedule_set_1 = require("./commands/schedule-set");
20
20
  const schedule_list_1 = require("./commands/schedule-list");
21
21
  const schedule_delete_1 = require("./commands/schedule-delete");
22
- const webhooks_1 = require("./commands/webhooks");
22
+ const webhook_list_1 = require("./commands/webhook-list");
23
23
  const dev_1 = require("./commands/dev");
24
24
  const ai_init_1 = require("./commands/ai-init");
25
25
  const ai_examples_1 = require("./commands/ai-examples");
@@ -32,7 +32,7 @@ program
32
32
  .description('SolidActions CLI - Deploy and manage workflow automation')
33
33
  .version(pkg.version);
34
34
  // =============================================================================
35
- // Authentication & Configuration
35
+ // Top-level commands
36
36
  // =============================================================================
37
37
  program
38
38
  .command('init')
@@ -56,90 +56,97 @@ program
56
56
  .action(() => {
57
57
  (0, init_1.whoami)();
58
58
  });
59
+ program
60
+ .command('dev')
61
+ .description('Run a workflow locally using an in-memory mock server (no deploy needed)')
62
+ .argument('<file>', 'Workflow file to run (e.g., src/simple-steps.ts)')
63
+ .option('-i, --input <json>', 'JSON input for the workflow', '{}')
64
+ .action((file, options) => {
65
+ (0, dev_1.dev)(file, options);
66
+ });
59
67
  // =============================================================================
60
- // Project Management
68
+ // project <subcommand>
61
69
  // =============================================================================
62
- program
70
+ const project = program.command('project').description('Manage projects');
71
+ project
63
72
  .command('deploy')
64
73
  .description('Deploy a project to SolidActions')
65
74
  .argument('<project-name>', 'Project name (will be created if it doesn\'t exist)')
66
75
  .argument('[path]', 'Source directory to deploy (defaults to current directory)')
67
76
  .option('-e, --env <environment>', 'Target environment (production/staging/dev)', 'dev')
68
77
  .option('--create', 'Create environment project if it doesn\'t exist')
78
+ .option('--config-only', 'Sync YAML env declarations without building/deploying')
69
79
  .action((projectName, path, options) => {
70
80
  (0, deploy_1.deploy)(projectName, path, options);
71
81
  });
72
- program
82
+ project
73
83
  .command('pull')
74
84
  .description('Pull project source from SolidActions')
75
85
  .argument('<project-name>', 'Project name')
76
86
  .argument('[path]', 'Destination directory (defaults to current directory)')
77
- .action((projectName, path) => {
78
- (0, pull_1.pull)(projectName, path);
87
+ .option('-y, --yes', 'Skip overwrite confirmation')
88
+ .action((projectName, path, options) => {
89
+ (0, pull_1.pull)(projectName, path, options);
90
+ });
91
+ project
92
+ .command('logs')
93
+ .description('View build/deployment logs for a project')
94
+ .argument('<project>', 'Project name')
95
+ .action((projectName) => {
96
+ (0, project_logs_1.logsBuild)(projectName);
97
+ });
98
+ project
99
+ .command('list')
100
+ .description('List all projects')
101
+ .action(() => {
102
+ (0, project_list_1.projectList)();
79
103
  });
80
104
  // =============================================================================
81
- // Workflow Execution
105
+ // run <subcommand>
82
106
  // =============================================================================
83
- program
84
- .command('run')
107
+ const runCmd = program.command('run').description('Manage workflow runs');
108
+ runCmd
109
+ .command('start')
85
110
  .description('Trigger a workflow run')
86
111
  .argument('<project>', 'Project name')
87
112
  .argument('<workflow>', 'Workflow name')
88
113
  .option('-e, --env <environment>', 'Environment (production/staging/dev)', 'dev')
89
114
  .option('-i, --input <json>', 'JSON input for the workflow')
90
115
  .option('-w, --wait', 'Wait for the workflow to complete')
91
- .action((project, workflow, options) => {
92
- (0, run_1.run)(project, workflow, options);
116
+ .action((projectName, workflow, options) => {
117
+ (0, run_start_1.run)(projectName, workflow, options);
93
118
  });
94
- program
95
- .command('runs')
119
+ runCmd
120
+ .command('list')
96
121
  .description('List recent workflow runs')
97
122
  .argument('[project]', 'Filter by project name')
98
123
  .option('-l, --limit <number>', 'Number of runs to show', parseInt)
99
- .action((project, options) => {
100
- (0, runs_1.runs)(project, options);
101
- });
102
- program
103
- .command('dev')
104
- .description('Run a workflow locally using an in-memory mock server (no deploy needed)')
105
- .argument('<file>', 'Workflow file to run (e.g., src/simple-steps.ts)')
106
- .option('-i, --input <json>', 'JSON input for the workflow', '{}')
107
- .action((file, options) => {
108
- (0, dev_1.dev)(file, options);
109
- });
110
- // =============================================================================
111
- // Logs
112
- // =============================================================================
113
- program
114
- .command('logs')
115
- .description('View logs for a workflow run')
116
- .argument('<run-id>', 'Run ID')
117
- .option('-f, --follow', 'Follow log output (tail -f style)')
118
- .action((runId, options) => {
119
- (0, logs_1.logs)(runId, options);
120
- });
121
- program
122
- .command('logs:build')
123
- .description('View build/deployment logs for a project')
124
- .argument('<project>', 'Project name')
125
- .action((project) => {
126
- (0, logs_build_1.logsBuild)(project);
124
+ .option('--offset <number>', 'Skip first N runs (pagination)', parseInt)
125
+ .option('--status <status>', 'Filter by status (completed, failed, queued, running)')
126
+ .option('--since <duration>', 'Filter to runs since (e.g., 1h, 30m, 2d, 1w)')
127
+ .option('--workflow <name>', 'Filter by workflow name')
128
+ .option('--detailed', 'Include timeline, steps, and logs per run (default limit: 5)')
129
+ .option('--json', 'Output as JSON')
130
+ .action((projectName, options) => {
131
+ (0, run_list_1.runs)(projectName, options);
127
132
  });
128
- program
129
- .command('steps')
130
- .description('View step outputs and timing for a workflow run')
133
+ runCmd
134
+ .command('view')
135
+ .description('View details, timeline, steps, or logs for a workflow run')
131
136
  .argument('<run-id>', 'Run ID')
132
- .option('-s, --step <name>', 'Show output for a specific step')
133
- .option('--json', 'Output as JSON (for piping/redirection)')
134
- .option('-f, --follow', 'Follow step progress for in-progress runs')
137
+ .option('--timeline', 'Show only timeline data')
138
+ .option('--steps', 'Show only step data')
139
+ .option('--logs', 'Show raw logs')
140
+ .option('--json', 'Output as JSON')
135
141
  .action((runId, options) => {
136
- (0, steps_1.steps)(runId, options);
142
+ (0, run_view_1.runView)(runId, options);
137
143
  });
138
144
  // =============================================================================
139
- // Environment Variables
145
+ // env <subcommand>
140
146
  // =============================================================================
141
- program
142
- .command('env:set')
147
+ const env = program.command('env').description('Manage environment variables');
148
+ env
149
+ .command('set')
143
150
  .description('Set an environment variable (create or update, global or project)')
144
151
  .argument('<key-or-project>', 'Variable key (global) or project name')
145
152
  .argument('<value-or-key>', 'Variable value (global) or variable key (project)')
@@ -151,19 +158,20 @@ program
151
158
  .option('--staging-inherit', 'Staging inherits from production (global only)')
152
159
  .option('--dev-inherit', 'Dev inherits from production (global only)')
153
160
  .option('--dev-inherit-staging', 'Dev inherits from staging (global only)')
161
+ .option('-y, --yes', 'Skip overwrite confirmation')
154
162
  .action((keyOrProject, valueOrKey, value, options) => {
155
163
  (0, env_set_1.envSet)(keyOrProject, valueOrKey, value, options);
156
164
  });
157
- program
158
- .command('env:list')
165
+ env
166
+ .command('list')
159
167
  .description('List environment variables')
160
168
  .argument('[project]', 'Project name (omit for global variables)')
161
169
  .option('-e, --env <environment>', 'Filter by environment (production/staging/dev)')
162
- .action((project, options) => {
163
- (0, env_list_1.envList)(project, options);
170
+ .action((projectName, options) => {
171
+ (0, env_list_1.envList)(projectName, options);
164
172
  });
165
- program
166
- .command('env:delete')
173
+ env
174
+ .command('delete')
167
175
  .description('Delete an environment variable')
168
176
  .argument('<key-or-project>', 'Variable key (global) or project name')
169
177
  .argument('[key]', 'Variable key (if first arg is project)')
@@ -171,28 +179,29 @@ program
171
179
  .action((keyOrProject, key, options) => {
172
180
  (0, env_delete_1.envDelete)(keyOrProject, key, options);
173
181
  });
174
- program
175
- .command('env:map')
182
+ env
183
+ .command('map')
176
184
  .description('Map a global variable to a project-specific key')
177
185
  .argument('<project>', 'Project name')
178
186
  .argument('<key>', 'Project-specific variable name')
179
187
  .argument('<global-key>', 'Global variable name to map from')
180
- .action((project, key, globalKey) => {
181
- (0, env_map_1.envMap)(project, key, globalKey);
188
+ .option('-y, --yes', 'Skip overwrite confirmation')
189
+ .action((projectName, key, globalKey, options) => {
190
+ (0, env_map_1.envMap)(projectName, key, globalKey, options);
182
191
  });
183
- program
184
- .command('env:pull')
192
+ env
193
+ .command('pull')
185
194
  .description('Pull resolved environment variables to a local file')
186
195
  .argument('<project>', 'Project name')
187
196
  .option('-e, --env <environment>', 'Environment (production/staging/dev)', 'dev')
188
197
  .option('-o, --output <file>', 'Output file path (defaults to .env or .env.{environment})')
189
198
  .option('-y, --yes', 'Skip confirmation for secrets')
190
199
  .option('--update-oauth', 'Only pull OAuth tokens and merge into existing .env file')
191
- .action((project, options) => {
192
- (0, env_pull_1.envPull)(project, options);
200
+ .action((projectName, options) => {
201
+ (0, env_pull_1.envPull)(projectName, options);
193
202
  });
194
- program
195
- .command('env:push')
203
+ env
204
+ .command('push')
196
205
  .description('Push environment variables from .env file to a project')
197
206
  .argument('<project>', 'Project name')
198
207
  .argument('[path]', 'Source directory with solidactions.yaml and .env file', '.')
@@ -200,77 +209,82 @@ program
200
209
  .option('--new-only', 'Only push new or empty variables (skip existing)')
201
210
  .option('--include-undeclared', 'Also push vars not declared in solidactions.yaml')
202
211
  .option('-y, --yes', 'Skip confirmation prompt')
203
- .action((project, path, options) => {
204
- (0, env_push_1.envPush)(project, path, options);
212
+ .action((projectName, path, options) => {
213
+ (0, env_push_1.envPush)(projectName, path, options);
205
214
  });
206
215
  // =============================================================================
207
- // Scheduling
216
+ // schedule <subcommand>
208
217
  // =============================================================================
209
- program
210
- .command('schedule:set')
218
+ const schedule = program.command('schedule').description('Manage cron schedules');
219
+ schedule
220
+ .command('set')
211
221
  .description('Set a cron schedule for a workflow')
212
222
  .argument('<project>', 'Project name')
213
223
  .argument('<cron>', 'Cron expression (e.g., "0 9 * * *" for daily at 9am)')
214
224
  .option('-w, --workflow <name>', 'Workflow name (if project has multiple)')
215
225
  .option('-i, --input <json>', 'JSON input to pass to scheduled runs')
216
- .action((project, cron, options) => {
217
- (0, schedule_set_1.scheduleSet)(project, cron, options);
226
+ .option('-y, --yes', 'Skip confirmation if schedule already exists')
227
+ .action((projectName, cron, options) => {
228
+ (0, schedule_set_1.scheduleSet)(projectName, cron, options);
218
229
  });
219
- program
220
- .command('schedule:list')
230
+ schedule
231
+ .command('list')
221
232
  .description('List schedules for a project')
222
233
  .argument('<project>', 'Project name')
223
- .action((project) => {
224
- (0, schedule_list_1.scheduleList)(project);
234
+ .action((projectName) => {
235
+ (0, schedule_list_1.scheduleList)(projectName);
225
236
  });
226
- program
227
- .command('schedule:delete')
237
+ schedule
238
+ .command('delete')
228
239
  .description('Delete a schedule')
229
240
  .argument('<project>', 'Project name')
230
241
  .argument('<schedule-id>', 'Schedule ID')
231
242
  .option('-y, --yes', 'Skip confirmation prompt')
232
- .action((project, scheduleId, options) => {
233
- (0, schedule_delete_1.scheduleDelete)(project, scheduleId, options);
243
+ .action((projectName, scheduleId, options) => {
244
+ (0, schedule_delete_1.scheduleDelete)(projectName, scheduleId, options);
234
245
  });
235
246
  // =============================================================================
236
- // Webhooks
247
+ // webhook <subcommand>
237
248
  // =============================================================================
238
- program
239
- .command('webhooks')
249
+ const webhook = program.command('webhook').description('Manage webhooks');
250
+ webhook
251
+ .command('list')
240
252
  .description('List webhook URLs for a project')
241
253
  .argument('<project>', 'Project name')
242
254
  .option('-e, --env <environment>', 'Environment (production/staging/dev)')
243
255
  .option('--show-secrets', 'Show webhook secrets')
244
- .action((project, options) => {
245
- (0, webhooks_1.webhookList)(project, options);
256
+ .action((projectName, options) => {
257
+ (0, webhook_list_1.webhookList)(projectName, options);
246
258
  });
247
259
  // =============================================================================
248
- // Workspaces
260
+ // workspace <subcommand>
249
261
  // =============================================================================
250
- program
251
- .command('workspaces')
262
+ const workspace = program.command('workspace').description('Manage workspaces');
263
+ workspace
264
+ .command('list')
252
265
  .description('List your workspaces across all organizations')
253
266
  .action(() => {
254
267
  (0, workspaces_1.workspacesList)();
255
268
  });
256
- program
257
- .command('workspace:set')
269
+ workspace
270
+ .command('set')
258
271
  .description('Set the active workspace for CLI operations')
259
272
  .argument('<workspace-id>', 'Workspace ID, slug, or name')
260
273
  .action((workspaceId) => {
261
274
  (0, workspaces_1.workspaceSet)(workspaceId);
262
275
  });
263
276
  // =============================================================================
264
- // AI Helper
277
+ // ai <subcommand>
265
278
  // =============================================================================
266
- program
267
- .command('ai:init')
279
+ const ai = program.command('ai').description('AI helper tools');
280
+ ai
281
+ .command('init')
268
282
  .description('Install AI helper documentation (CLAUDE.md or AGENTS.md) for AI-assisted workflow development')
269
283
  .option('--claude', 'Use CLAUDE.md (for Claude Code)')
270
284
  .option('--agents', 'Use AGENTS.md (for Cursor, Windsurf, etc.)')
271
285
  .action((options) => { (0, ai_init_1.aiInit)(options); });
272
- program
273
- .command('ai:examples')
286
+ ai
287
+ .command('examples')
274
288
  .description('Install example workflows for AI reference')
275
289
  .argument('[names...]', 'Example names to install (omit for interactive selector)')
276
290
  .option('--all', 'Install all available examples')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidactions/cli",
3
- "version": "0.7.3",
3
+ "version": "1.0.0",
4
4
  "description": "SolidActions CLI - Deploy and manage workflow automation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -36,7 +36,6 @@
36
36
  "prepublishOnly": "npm run build"
37
37
  },
38
38
  "dependencies": {
39
- "adm-zip": "^0.5.16",
40
39
  "archiver": "^7.0.0",
41
40
  "axios": "^1.6.0",
42
41
  "chalk": "^4.1.2",
@@ -45,15 +44,16 @@
45
44
  "form-data": "^4.0.0",
46
45
  "fs-extra": "^11.0.0",
47
46
  "js-yaml": "^4.1.0",
48
- "prompts": "^2.4.2"
47
+ "prompts": "^2.4.2",
48
+ "tar": "^7.5.12"
49
49
  },
50
50
  "devDependencies": {
51
- "@types/adm-zip": "^0.5.7",
52
51
  "@types/archiver": "^6.0.0",
53
52
  "@types/fs-extra": "^11.0.0",
54
53
  "@types/js-yaml": "^4.0.9",
55
54
  "@types/node": "^20.0.0",
56
55
  "@types/prompts": "^2.4.9",
56
+ "@types/tar": "^6.1.13",
57
57
  "typescript": "^5.0.0"
58
58
  }
59
59
  }
@@ -1,103 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.logs = logs;
7
- const axios_1 = __importDefault(require("axios"));
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const api_1 = require("../utils/api");
10
- async function logs(runId, options) {
11
- const config = await (0, api_1.requireConfigWithWorkspace)();
12
- console.log(chalk_1.default.blue(`Fetching logs for run: ${runId}...`));
13
- try {
14
- // Get the run status first
15
- const runResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}`, {
16
- headers: (0, api_1.getApiHeaders)(config),
17
- });
18
- const runData = runResponse.data;
19
- console.log(chalk_1.default.gray(`Status: ${runData.status}`));
20
- console.log(chalk_1.default.gray('---'));
21
- // Get the logs for this run
22
- const logsResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}/logs`, {
23
- headers: (0, api_1.getApiHeaders)(config),
24
- });
25
- const errors = logsResponse.data.errors || [];
26
- if (errors.length > 0) {
27
- for (const err of errors) {
28
- console.log(chalk_1.default.red(`Worker ${err.worker}: ${err.error}`));
29
- }
30
- console.log(chalk_1.default.gray('---'));
31
- }
32
- const logData = logsResponse.data.logs || '';
33
- let printed = displayLogs(logData);
34
- if (options.follow && runData.status === 'running') {
35
- console.log(chalk_1.default.gray('\n--- Following logs (Ctrl+C to stop) ---\n'));
36
- const pollInterval = setInterval(async () => {
37
- try {
38
- const refreshResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}/logs`, {
39
- headers: (0, api_1.getApiHeaders)(config),
40
- });
41
- const newLogData = refreshResponse.data.logs || '';
42
- if (newLogData.length > printed) {
43
- const newContent = newLogData.substring(printed);
44
- printed += displayLogs(newContent);
45
- }
46
- // Check if run is complete
47
- const runStatus = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}`, {
48
- headers: (0, api_1.getApiHeaders)(config),
49
- });
50
- if (['completed', 'failed', 'acknowledged'].includes(runStatus.data.status)) {
51
- clearInterval(pollInterval);
52
- console.log(chalk_1.default.gray(`\n--- Run ${runStatus.data.status} ---`));
53
- process.exit(runStatus.data.status === 'completed' ? 0 : 1);
54
- }
55
- }
56
- catch {
57
- // Ignore transient errors during follow
58
- }
59
- }, 2000);
60
- }
61
- }
62
- catch (error) {
63
- if (error.response) {
64
- if (error.response.status === 401) {
65
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
66
- }
67
- else if (error.response.status === 404) {
68
- console.error(chalk_1.default.red('Run not found.'));
69
- }
70
- else {
71
- console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
72
- }
73
- }
74
- else {
75
- console.error(chalk_1.default.red('Connection failed:'), error.message);
76
- }
77
- process.exit(1);
78
- }
79
- }
80
- /**
81
- * Display log content. Handles both string logs and array-of-entry logs.
82
- * Returns the number of characters printed (for follow-mode diffing).
83
- */
84
- function displayLogs(logData) {
85
- if (typeof logData === 'string') {
86
- if (logData.trim()) {
87
- console.log(logData);
88
- }
89
- return logData.length;
90
- }
91
- // Handle array format in case the API changes
92
- for (const entry of logData) {
93
- const message = entry.message || entry.content || '';
94
- if (!message.trim())
95
- continue;
96
- const timestamp = entry.timestamp ? new Date(entry.timestamp).toLocaleTimeString() : '??:??:??';
97
- const stream = entry.stream || 'stdout';
98
- const streamIndicator = stream === 'stderr' ? chalk_1.default.red('[err]') : chalk_1.default.gray('[out]');
99
- const coloredMessage = stream === 'stderr' ? chalk_1.default.red(message) : chalk_1.default.white(message);
100
- console.log(`${chalk_1.default.gray(`[${timestamp}]`)} ${streamIndicator} ${coloredMessage}`);
101
- }
102
- return logData.length;
103
- }
@@ -1,90 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.runs = runs;
7
- const axios_1 = __importDefault(require("axios"));
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const api_1 = require("../utils/api");
10
- async function runs(projectName, options = {}) {
11
- const config = await (0, api_1.requireConfigWithWorkspace)();
12
- const limit = options.limit || 20;
13
- console.log(chalk_1.default.blue(projectName ? `Recent runs for "${projectName}":` : 'Recent runs:'));
14
- try {
15
- const params = { limit };
16
- if (projectName) {
17
- params.project = projectName;
18
- }
19
- const response = await axios_1.default.get(`${config.host}/api/v1/runs`, {
20
- headers: (0, api_1.getApiHeaders)(config),
21
- params,
22
- });
23
- const runsList = response.data.data || response.data;
24
- if (!runsList || runsList.length === 0) {
25
- console.log(chalk_1.default.gray('No runs found.'));
26
- return;
27
- }
28
- console.log('');
29
- console.log(chalk_1.default.gray('ID'.padEnd(38) + 'WORKFLOW'.padEnd(25) + 'STATUS'.padEnd(12) + 'STARTED'.padEnd(22) + 'DURATION'));
30
- console.log(chalk_1.default.gray('-'.repeat(110)));
31
- for (const run of runsList) {
32
- const id = run.id || '?';
33
- const workflow = run.workflow?.name || run.workflow_name || '?';
34
- const status = run.status || '?';
35
- const startedAt = run.started_at ? new Date(run.started_at).toLocaleString() : '-';
36
- const duration = calculateDuration(run.started_at, run.completed_at);
37
- const statusColor = getStatusColor(status);
38
- console.log(chalk_1.default.gray(id.toString().padEnd(38)) +
39
- workflow.padEnd(25) +
40
- statusColor(status.padEnd(12)) +
41
- chalk_1.default.gray(startedAt.padEnd(22)) +
42
- chalk_1.default.gray(duration));
43
- }
44
- console.log('');
45
- console.log(chalk_1.default.gray(`Showing ${runsList.length} run(s)`));
46
- }
47
- catch (error) {
48
- if (error.response) {
49
- if (error.response.status === 401) {
50
- console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
51
- }
52
- else {
53
- console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
54
- }
55
- }
56
- else {
57
- console.error(chalk_1.default.red('Connection failed:'), error.message);
58
- }
59
- process.exit(1);
60
- }
61
- }
62
- function calculateDuration(startedAt, completedAt) {
63
- if (!startedAt)
64
- return '-';
65
- const start = new Date(startedAt).getTime();
66
- const end = completedAt ? new Date(completedAt).getTime() : Date.now();
67
- const durationMs = end - start;
68
- if (durationMs < 1000)
69
- return `${durationMs}ms`;
70
- if (durationMs < 60000)
71
- return `${Math.round(durationMs / 1000)}s`;
72
- if (durationMs < 3600000)
73
- return `${Math.round(durationMs / 60000)}m`;
74
- return `${Math.round(durationMs / 3600000)}h`;
75
- }
76
- function getStatusColor(status) {
77
- switch (status.toLowerCase()) {
78
- case 'completed':
79
- return chalk_1.default.green;
80
- case 'running':
81
- return chalk_1.default.blue;
82
- case 'pending':
83
- case 'queued':
84
- return chalk_1.default.yellow;
85
- case 'failed':
86
- return chalk_1.default.red;
87
- default:
88
- return chalk_1.default.gray;
89
- }
90
- }