@solidactions/cli 0.7.0 → 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/README.md +68 -25
- package/dist/commands/ai-examples.js +1 -1
- package/dist/commands/deploy.js +26 -27
- package/dist/commands/env-delete.js +6 -22
- package/dist/commands/env-list.js +4 -14
- package/dist/commands/env-map.js +29 -17
- package/dist/commands/env-pull.js +6 -16
- package/dist/commands/env-push.js +5 -16
- package/dist/commands/env-set.js +41 -27
- package/dist/commands/init.js +21 -5
- package/dist/commands/project-list.js +52 -0
- package/dist/commands/{logs-build.js → project-logs.js} +3 -10
- package/dist/commands/pull.js +32 -22
- package/dist/commands/run-list.js +186 -0
- package/dist/commands/{run.js → run-start.js} +4 -15
- package/dist/commands/run-view.js +255 -0
- package/dist/commands/schedule-delete.js +4 -14
- package/dist/commands/schedule-list.js +3 -10
- package/dist/commands/schedule-set.js +48 -12
- package/dist/commands/{webhooks.js → webhook-list.js} +7 -14
- package/dist/commands/workspaces.js +89 -0
- package/dist/index.js +130 -98
- package/dist/utils/api.js +129 -0
- package/package.json +4 -4
- package/dist/commands/logs.js +0 -119
- package/dist/commands/runs.js +0 -97
- package/dist/commands/steps.js +0 -215
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,10 +19,11 @@ 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
|
|
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");
|
|
26
|
+
const workspaces_1 = require("./commands/workspaces");
|
|
26
27
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
27
28
|
const pkg = require('../package.json');
|
|
28
29
|
const program = new commander_1.Command();
|
|
@@ -31,7 +32,7 @@ program
|
|
|
31
32
|
.description('SolidActions CLI - Deploy and manage workflow automation')
|
|
32
33
|
.version(pkg.version);
|
|
33
34
|
// =============================================================================
|
|
34
|
-
//
|
|
35
|
+
// Top-level commands
|
|
35
36
|
// =============================================================================
|
|
36
37
|
program
|
|
37
38
|
.command('init')
|
|
@@ -39,6 +40,7 @@ program
|
|
|
39
40
|
.argument('<api-key>', 'Your SolidActions API key')
|
|
40
41
|
.option('--dev', 'Use local development server (http://localhost:8000)')
|
|
41
42
|
.option('--host <url>', 'Custom API host URL')
|
|
43
|
+
.option('--workspace <name-or-id>', 'Set workspace by name, slug, or ID (skips interactive prompt)')
|
|
42
44
|
.action((apiKey, options) => {
|
|
43
45
|
(0, init_1.init)(apiKey, options);
|
|
44
46
|
});
|
|
@@ -54,90 +56,97 @@ program
|
|
|
54
56
|
.action(() => {
|
|
55
57
|
(0, init_1.whoami)();
|
|
56
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
|
+
});
|
|
57
67
|
// =============================================================================
|
|
58
|
-
//
|
|
68
|
+
// project <subcommand>
|
|
59
69
|
// =============================================================================
|
|
60
|
-
program
|
|
70
|
+
const project = program.command('project').description('Manage projects');
|
|
71
|
+
project
|
|
61
72
|
.command('deploy')
|
|
62
73
|
.description('Deploy a project to SolidActions')
|
|
63
74
|
.argument('<project-name>', 'Project name (will be created if it doesn\'t exist)')
|
|
64
75
|
.argument('[path]', 'Source directory to deploy (defaults to current directory)')
|
|
65
76
|
.option('-e, --env <environment>', 'Target environment (production/staging/dev)', 'dev')
|
|
66
77
|
.option('--create', 'Create environment project if it doesn\'t exist')
|
|
78
|
+
.option('--config-only', 'Sync YAML env declarations without building/deploying')
|
|
67
79
|
.action((projectName, path, options) => {
|
|
68
80
|
(0, deploy_1.deploy)(projectName, path, options);
|
|
69
81
|
});
|
|
70
|
-
|
|
82
|
+
project
|
|
71
83
|
.command('pull')
|
|
72
84
|
.description('Pull project source from SolidActions')
|
|
73
85
|
.argument('<project-name>', 'Project name')
|
|
74
86
|
.argument('[path]', 'Destination directory (defaults to current directory)')
|
|
75
|
-
.
|
|
76
|
-
|
|
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)();
|
|
77
103
|
});
|
|
78
104
|
// =============================================================================
|
|
79
|
-
//
|
|
105
|
+
// run <subcommand>
|
|
80
106
|
// =============================================================================
|
|
81
|
-
program
|
|
82
|
-
|
|
107
|
+
const runCmd = program.command('run').description('Manage workflow runs');
|
|
108
|
+
runCmd
|
|
109
|
+
.command('start')
|
|
83
110
|
.description('Trigger a workflow run')
|
|
84
111
|
.argument('<project>', 'Project name')
|
|
85
112
|
.argument('<workflow>', 'Workflow name')
|
|
86
113
|
.option('-e, --env <environment>', 'Environment (production/staging/dev)', 'dev')
|
|
87
114
|
.option('-i, --input <json>', 'JSON input for the workflow')
|
|
88
115
|
.option('-w, --wait', 'Wait for the workflow to complete')
|
|
89
|
-
.action((
|
|
90
|
-
(0,
|
|
116
|
+
.action((projectName, workflow, options) => {
|
|
117
|
+
(0, run_start_1.run)(projectName, workflow, options);
|
|
91
118
|
});
|
|
92
|
-
|
|
93
|
-
.command('
|
|
119
|
+
runCmd
|
|
120
|
+
.command('list')
|
|
94
121
|
.description('List recent workflow runs')
|
|
95
122
|
.argument('[project]', 'Filter by project name')
|
|
96
123
|
.option('-l, --limit <number>', 'Number of runs to show', parseInt)
|
|
97
|
-
.
|
|
98
|
-
(
|
|
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);
|
|
99
132
|
});
|
|
100
|
-
|
|
101
|
-
.command('
|
|
102
|
-
.description('
|
|
103
|
-
.argument('<file>', 'Workflow file to run (e.g., src/simple-steps.ts)')
|
|
104
|
-
.option('-i, --input <json>', 'JSON input for the workflow', '{}')
|
|
105
|
-
.action((file, options) => {
|
|
106
|
-
(0, dev_1.dev)(file, options);
|
|
107
|
-
});
|
|
108
|
-
// =============================================================================
|
|
109
|
-
// Logs
|
|
110
|
-
// =============================================================================
|
|
111
|
-
program
|
|
112
|
-
.command('logs')
|
|
113
|
-
.description('View logs for a workflow run')
|
|
133
|
+
runCmd
|
|
134
|
+
.command('view')
|
|
135
|
+
.description('View details, timeline, steps, or logs for a workflow run')
|
|
114
136
|
.argument('<run-id>', 'Run ID')
|
|
115
|
-
.option('
|
|
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')
|
|
116
141
|
.action((runId, options) => {
|
|
117
|
-
(0,
|
|
118
|
-
});
|
|
119
|
-
program
|
|
120
|
-
.command('logs:build')
|
|
121
|
-
.description('View build/deployment logs for a project')
|
|
122
|
-
.argument('<project>', 'Project name')
|
|
123
|
-
.action((project) => {
|
|
124
|
-
(0, logs_build_1.logsBuild)(project);
|
|
125
|
-
});
|
|
126
|
-
program
|
|
127
|
-
.command('steps')
|
|
128
|
-
.description('View step outputs and timing for a workflow run')
|
|
129
|
-
.argument('<run-id>', 'Run ID')
|
|
130
|
-
.option('-s, --step <name>', 'Show output for a specific step')
|
|
131
|
-
.option('--json', 'Output as JSON (for piping/redirection)')
|
|
132
|
-
.option('-f, --follow', 'Follow step progress for in-progress runs')
|
|
133
|
-
.action((runId, options) => {
|
|
134
|
-
(0, steps_1.steps)(runId, options);
|
|
142
|
+
(0, run_view_1.runView)(runId, options);
|
|
135
143
|
});
|
|
136
144
|
// =============================================================================
|
|
137
|
-
//
|
|
145
|
+
// env <subcommand>
|
|
138
146
|
// =============================================================================
|
|
139
|
-
program
|
|
140
|
-
|
|
147
|
+
const env = program.command('env').description('Manage environment variables');
|
|
148
|
+
env
|
|
149
|
+
.command('set')
|
|
141
150
|
.description('Set an environment variable (create or update, global or project)')
|
|
142
151
|
.argument('<key-or-project>', 'Variable key (global) or project name')
|
|
143
152
|
.argument('<value-or-key>', 'Variable value (global) or variable key (project)')
|
|
@@ -149,19 +158,20 @@ program
|
|
|
149
158
|
.option('--staging-inherit', 'Staging inherits from production (global only)')
|
|
150
159
|
.option('--dev-inherit', 'Dev inherits from production (global only)')
|
|
151
160
|
.option('--dev-inherit-staging', 'Dev inherits from staging (global only)')
|
|
161
|
+
.option('-y, --yes', 'Skip overwrite confirmation')
|
|
152
162
|
.action((keyOrProject, valueOrKey, value, options) => {
|
|
153
163
|
(0, env_set_1.envSet)(keyOrProject, valueOrKey, value, options);
|
|
154
164
|
});
|
|
155
|
-
|
|
156
|
-
.command('
|
|
165
|
+
env
|
|
166
|
+
.command('list')
|
|
157
167
|
.description('List environment variables')
|
|
158
168
|
.argument('[project]', 'Project name (omit for global variables)')
|
|
159
169
|
.option('-e, --env <environment>', 'Filter by environment (production/staging/dev)')
|
|
160
|
-
.action((
|
|
161
|
-
(0, env_list_1.envList)(
|
|
170
|
+
.action((projectName, options) => {
|
|
171
|
+
(0, env_list_1.envList)(projectName, options);
|
|
162
172
|
});
|
|
163
|
-
|
|
164
|
-
.command('
|
|
173
|
+
env
|
|
174
|
+
.command('delete')
|
|
165
175
|
.description('Delete an environment variable')
|
|
166
176
|
.argument('<key-or-project>', 'Variable key (global) or project name')
|
|
167
177
|
.argument('[key]', 'Variable key (if first arg is project)')
|
|
@@ -169,28 +179,29 @@ program
|
|
|
169
179
|
.action((keyOrProject, key, options) => {
|
|
170
180
|
(0, env_delete_1.envDelete)(keyOrProject, key, options);
|
|
171
181
|
});
|
|
172
|
-
|
|
173
|
-
.command('
|
|
182
|
+
env
|
|
183
|
+
.command('map')
|
|
174
184
|
.description('Map a global variable to a project-specific key')
|
|
175
185
|
.argument('<project>', 'Project name')
|
|
176
186
|
.argument('<key>', 'Project-specific variable name')
|
|
177
187
|
.argument('<global-key>', 'Global variable name to map from')
|
|
178
|
-
.
|
|
179
|
-
|
|
188
|
+
.option('-y, --yes', 'Skip overwrite confirmation')
|
|
189
|
+
.action((projectName, key, globalKey, options) => {
|
|
190
|
+
(0, env_map_1.envMap)(projectName, key, globalKey, options);
|
|
180
191
|
});
|
|
181
|
-
|
|
182
|
-
.command('
|
|
192
|
+
env
|
|
193
|
+
.command('pull')
|
|
183
194
|
.description('Pull resolved environment variables to a local file')
|
|
184
195
|
.argument('<project>', 'Project name')
|
|
185
196
|
.option('-e, --env <environment>', 'Environment (production/staging/dev)', 'dev')
|
|
186
197
|
.option('-o, --output <file>', 'Output file path (defaults to .env or .env.{environment})')
|
|
187
198
|
.option('-y, --yes', 'Skip confirmation for secrets')
|
|
188
199
|
.option('--update-oauth', 'Only pull OAuth tokens and merge into existing .env file')
|
|
189
|
-
.action((
|
|
190
|
-
(0, env_pull_1.envPull)(
|
|
200
|
+
.action((projectName, options) => {
|
|
201
|
+
(0, env_pull_1.envPull)(projectName, options);
|
|
191
202
|
});
|
|
192
|
-
|
|
193
|
-
.command('
|
|
203
|
+
env
|
|
204
|
+
.command('push')
|
|
194
205
|
.description('Push environment variables from .env file to a project')
|
|
195
206
|
.argument('<project>', 'Project name')
|
|
196
207
|
.argument('[path]', 'Source directory with solidactions.yaml and .env file', '.')
|
|
@@ -198,61 +209,82 @@ program
|
|
|
198
209
|
.option('--new-only', 'Only push new or empty variables (skip existing)')
|
|
199
210
|
.option('--include-undeclared', 'Also push vars not declared in solidactions.yaml')
|
|
200
211
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
201
|
-
.action((
|
|
202
|
-
(0, env_push_1.envPush)(
|
|
212
|
+
.action((projectName, path, options) => {
|
|
213
|
+
(0, env_push_1.envPush)(projectName, path, options);
|
|
203
214
|
});
|
|
204
215
|
// =============================================================================
|
|
205
|
-
//
|
|
216
|
+
// schedule <subcommand>
|
|
206
217
|
// =============================================================================
|
|
207
|
-
program
|
|
208
|
-
|
|
218
|
+
const schedule = program.command('schedule').description('Manage cron schedules');
|
|
219
|
+
schedule
|
|
220
|
+
.command('set')
|
|
209
221
|
.description('Set a cron schedule for a workflow')
|
|
210
222
|
.argument('<project>', 'Project name')
|
|
211
223
|
.argument('<cron>', 'Cron expression (e.g., "0 9 * * *" for daily at 9am)')
|
|
212
224
|
.option('-w, --workflow <name>', 'Workflow name (if project has multiple)')
|
|
213
225
|
.option('-i, --input <json>', 'JSON input to pass to scheduled runs')
|
|
214
|
-
.
|
|
215
|
-
|
|
226
|
+
.option('-y, --yes', 'Skip confirmation if schedule already exists')
|
|
227
|
+
.action((projectName, cron, options) => {
|
|
228
|
+
(0, schedule_set_1.scheduleSet)(projectName, cron, options);
|
|
216
229
|
});
|
|
217
|
-
|
|
218
|
-
.command('
|
|
230
|
+
schedule
|
|
231
|
+
.command('list')
|
|
219
232
|
.description('List schedules for a project')
|
|
220
233
|
.argument('<project>', 'Project name')
|
|
221
|
-
.action((
|
|
222
|
-
(0, schedule_list_1.scheduleList)(
|
|
234
|
+
.action((projectName) => {
|
|
235
|
+
(0, schedule_list_1.scheduleList)(projectName);
|
|
223
236
|
});
|
|
224
|
-
|
|
225
|
-
.command('
|
|
237
|
+
schedule
|
|
238
|
+
.command('delete')
|
|
226
239
|
.description('Delete a schedule')
|
|
227
240
|
.argument('<project>', 'Project name')
|
|
228
241
|
.argument('<schedule-id>', 'Schedule ID')
|
|
229
242
|
.option('-y, --yes', 'Skip confirmation prompt')
|
|
230
|
-
.action((
|
|
231
|
-
(0, schedule_delete_1.scheduleDelete)(
|
|
243
|
+
.action((projectName, scheduleId, options) => {
|
|
244
|
+
(0, schedule_delete_1.scheduleDelete)(projectName, scheduleId, options);
|
|
232
245
|
});
|
|
233
246
|
// =============================================================================
|
|
234
|
-
//
|
|
247
|
+
// webhook <subcommand>
|
|
235
248
|
// =============================================================================
|
|
236
|
-
program
|
|
237
|
-
|
|
249
|
+
const webhook = program.command('webhook').description('Manage webhooks');
|
|
250
|
+
webhook
|
|
251
|
+
.command('list')
|
|
238
252
|
.description('List webhook URLs for a project')
|
|
239
253
|
.argument('<project>', 'Project name')
|
|
240
254
|
.option('-e, --env <environment>', 'Environment (production/staging/dev)')
|
|
241
255
|
.option('--show-secrets', 'Show webhook secrets')
|
|
242
|
-
.action((
|
|
243
|
-
(0,
|
|
256
|
+
.action((projectName, options) => {
|
|
257
|
+
(0, webhook_list_1.webhookList)(projectName, options);
|
|
244
258
|
});
|
|
245
259
|
// =============================================================================
|
|
246
|
-
//
|
|
260
|
+
// workspace <subcommand>
|
|
247
261
|
// =============================================================================
|
|
248
|
-
program
|
|
249
|
-
|
|
262
|
+
const workspace = program.command('workspace').description('Manage workspaces');
|
|
263
|
+
workspace
|
|
264
|
+
.command('list')
|
|
265
|
+
.description('List your workspaces across all organizations')
|
|
266
|
+
.action(() => {
|
|
267
|
+
(0, workspaces_1.workspacesList)();
|
|
268
|
+
});
|
|
269
|
+
workspace
|
|
270
|
+
.command('set')
|
|
271
|
+
.description('Set the active workspace for CLI operations')
|
|
272
|
+
.argument('<workspace-id>', 'Workspace ID, slug, or name')
|
|
273
|
+
.action((workspaceId) => {
|
|
274
|
+
(0, workspaces_1.workspaceSet)(workspaceId);
|
|
275
|
+
});
|
|
276
|
+
// =============================================================================
|
|
277
|
+
// ai <subcommand>
|
|
278
|
+
// =============================================================================
|
|
279
|
+
const ai = program.command('ai').description('AI helper tools');
|
|
280
|
+
ai
|
|
281
|
+
.command('init')
|
|
250
282
|
.description('Install AI helper documentation (CLAUDE.md or AGENTS.md) for AI-assisted workflow development')
|
|
251
283
|
.option('--claude', 'Use CLAUDE.md (for Claude Code)')
|
|
252
284
|
.option('--agents', 'Use AGENTS.md (for Cursor, Windsurf, etc.)')
|
|
253
285
|
.action((options) => { (0, ai_init_1.aiInit)(options); });
|
|
254
|
-
|
|
255
|
-
.command('
|
|
286
|
+
ai
|
|
287
|
+
.command('examples')
|
|
256
288
|
.description('Install example workflows for AI reference')
|
|
257
289
|
.argument('[names...]', 'Example names to install (omit for interactive selector)')
|
|
258
290
|
.option('--all', 'Install all available examples')
|
|
@@ -0,0 +1,129 @@
|
|
|
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.getApiHeaders = getApiHeaders;
|
|
7
|
+
exports.ensureWorkspaceSelected = ensureWorkspaceSelected;
|
|
8
|
+
exports.requireConfig = requireConfig;
|
|
9
|
+
exports.requireConfigWithWorkspace = requireConfigWithWorkspace;
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const readline_1 = __importDefault(require("readline"));
|
|
13
|
+
const init_1 = require("../commands/init");
|
|
14
|
+
/**
|
|
15
|
+
* Get standard API headers including X-Workspace-Id if configured.
|
|
16
|
+
*/
|
|
17
|
+
function getApiHeaders(config, contentType) {
|
|
18
|
+
const headers = {
|
|
19
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
20
|
+
'Accept': 'application/json',
|
|
21
|
+
};
|
|
22
|
+
if (contentType) {
|
|
23
|
+
headers['Content-Type'] = contentType;
|
|
24
|
+
}
|
|
25
|
+
if (config.workspaceId) {
|
|
26
|
+
headers['X-Workspace-Id'] = config.workspaceId;
|
|
27
|
+
}
|
|
28
|
+
return headers;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Ensure a workspace is selected. If not configured, fetches workspaces and auto-selects
|
|
32
|
+
* (if only one) or prompts the user to choose.
|
|
33
|
+
*
|
|
34
|
+
* Returns the config with workspaceId set, or exits if no workspaces available.
|
|
35
|
+
*/
|
|
36
|
+
async function ensureWorkspaceSelected(config) {
|
|
37
|
+
if (config.workspaceId) {
|
|
38
|
+
return config;
|
|
39
|
+
}
|
|
40
|
+
// Fetch workspaces from API (this endpoint doesn't require X-Workspace-Id)
|
|
41
|
+
let workspaces;
|
|
42
|
+
try {
|
|
43
|
+
const response = await axios_1.default.get(`${config.host}/api/v1/workspaces`, {
|
|
44
|
+
headers: {
|
|
45
|
+
'Authorization': `Bearer ${config.apiKey}`,
|
|
46
|
+
'Accept': 'application/json',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
// API returns { workspaces: { "Org Name": [{ id, name, role, tenant_name, ... }] } }
|
|
50
|
+
// Flatten the grouped structure into a flat array
|
|
51
|
+
const grouped = response.data.workspaces || response.data.teams || response.data.data || response.data;
|
|
52
|
+
if (typeof grouped === 'object' && !Array.isArray(grouped)) {
|
|
53
|
+
workspaces = [];
|
|
54
|
+
for (const orgName of Object.keys(grouped)) {
|
|
55
|
+
for (const ws of grouped[orgName]) {
|
|
56
|
+
workspaces.push({
|
|
57
|
+
id: ws.id,
|
|
58
|
+
name: ws.name,
|
|
59
|
+
org_name: ws.tenant_name || orgName,
|
|
60
|
+
role: ws.role,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
workspaces = Array.isArray(grouped) ? grouped : [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error.response?.status === 401) {
|
|
71
|
+
console.error(chalk_1.default.red('Authentication failed. Run `solidactions init <api-key>` to reconfigure.'));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.error(chalk_1.default.red('Failed to fetch workspaces:'), error.response?.data?.message || error.message);
|
|
75
|
+
}
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (workspaces.length === 0) {
|
|
79
|
+
console.error(chalk_1.default.red('No workspaces found. Create a workspace at your SolidActions dashboard first.'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
let selected;
|
|
83
|
+
if (workspaces.length === 1) {
|
|
84
|
+
selected = workspaces[0];
|
|
85
|
+
console.log(chalk_1.default.gray(`Auto-selected workspace: ${selected.name}`));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Prompt user to select
|
|
89
|
+
console.log(chalk_1.default.blue('\nSelect a workspace:\n'));
|
|
90
|
+
workspaces.forEach((ws, i) => {
|
|
91
|
+
console.log(` ${chalk_1.default.white(`${i + 1}.`)} ${ws.name} ${chalk_1.default.gray(`(${ws.org_name}, ${ws.role})`)}`);
|
|
92
|
+
});
|
|
93
|
+
console.log('');
|
|
94
|
+
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
95
|
+
const answer = await new Promise((resolve) => {
|
|
96
|
+
rl.question(chalk_1.default.blue('Enter number: '), resolve);
|
|
97
|
+
});
|
|
98
|
+
rl.close();
|
|
99
|
+
const index = parseInt(answer, 10) - 1;
|
|
100
|
+
if (isNaN(index) || index < 0 || index >= workspaces.length) {
|
|
101
|
+
console.error(chalk_1.default.red('Invalid selection.'));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
selected = workspaces[index];
|
|
105
|
+
}
|
|
106
|
+
// Save workspace selection to config
|
|
107
|
+
config.workspaceId = selected.id;
|
|
108
|
+
(0, init_1.saveConfig)(config);
|
|
109
|
+
console.log(chalk_1.default.green(`Workspace set: ${selected.name}`));
|
|
110
|
+
return config;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get config and ensure it's initialized. Exits if not.
|
|
114
|
+
*/
|
|
115
|
+
function requireConfig() {
|
|
116
|
+
const config = (0, init_1.getConfig)();
|
|
117
|
+
if (!config?.apiKey) {
|
|
118
|
+
console.error(chalk_1.default.red('Not initialized. Run `solidactions init <api-key>` first.'));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
return config;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get config with workspace selected. Use this for any command that needs workspace context.
|
|
125
|
+
*/
|
|
126
|
+
async function requireConfigWithWorkspace() {
|
|
127
|
+
const config = requireConfig();
|
|
128
|
+
return ensureWorkspaceSelected(config);
|
|
129
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidactions/cli",
|
|
3
|
-
"version": "0.
|
|
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
|
}
|
package/dist/commands/logs.js
DELETED
|
@@ -1,119 +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 init_1 = require("./init");
|
|
10
|
-
async function logs(runId, options) {
|
|
11
|
-
const config = (0, init_1.getConfig)();
|
|
12
|
-
if (!config?.apiKey) {
|
|
13
|
-
console.error(chalk_1.default.red('Not initialized. Run "solidactions init <api-key>" first.'));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
console.log(chalk_1.default.blue(`Fetching logs for run: ${runId}...`));
|
|
17
|
-
try {
|
|
18
|
-
// Get the run status first
|
|
19
|
-
const runResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}`, {
|
|
20
|
-
headers: {
|
|
21
|
-
'Authorization': `Bearer ${config.apiKey}`,
|
|
22
|
-
'Accept': 'application/json',
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
const runData = runResponse.data;
|
|
26
|
-
console.log(chalk_1.default.gray(`Status: ${runData.status}`));
|
|
27
|
-
console.log(chalk_1.default.gray('---'));
|
|
28
|
-
// Get the logs for this run
|
|
29
|
-
const logsResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}/logs`, {
|
|
30
|
-
headers: {
|
|
31
|
-
'Authorization': `Bearer ${config.apiKey}`,
|
|
32
|
-
'Accept': 'application/json',
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
const errors = logsResponse.data.errors || [];
|
|
36
|
-
if (errors.length > 0) {
|
|
37
|
-
for (const err of errors) {
|
|
38
|
-
console.log(chalk_1.default.red(`Worker ${err.worker}: ${err.error}`));
|
|
39
|
-
}
|
|
40
|
-
console.log(chalk_1.default.gray('---'));
|
|
41
|
-
}
|
|
42
|
-
const logData = logsResponse.data.logs || '';
|
|
43
|
-
let printed = displayLogs(logData);
|
|
44
|
-
if (options.follow && runData.status === 'running') {
|
|
45
|
-
console.log(chalk_1.default.gray('\n--- Following logs (Ctrl+C to stop) ---\n'));
|
|
46
|
-
const pollInterval = setInterval(async () => {
|
|
47
|
-
try {
|
|
48
|
-
const refreshResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}/logs`, {
|
|
49
|
-
headers: {
|
|
50
|
-
'Authorization': `Bearer ${config.apiKey}`,
|
|
51
|
-
'Accept': 'application/json',
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
const newLogData = refreshResponse.data.logs || '';
|
|
55
|
-
if (newLogData.length > printed) {
|
|
56
|
-
const newContent = newLogData.substring(printed);
|
|
57
|
-
printed += displayLogs(newContent);
|
|
58
|
-
}
|
|
59
|
-
// Check if run is complete
|
|
60
|
-
const runStatus = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}`, {
|
|
61
|
-
headers: {
|
|
62
|
-
'Authorization': `Bearer ${config.apiKey}`,
|
|
63
|
-
'Accept': 'application/json',
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
if (['completed', 'failed', 'acknowledged'].includes(runStatus.data.status)) {
|
|
67
|
-
clearInterval(pollInterval);
|
|
68
|
-
console.log(chalk_1.default.gray(`\n--- Run ${runStatus.data.status} ---`));
|
|
69
|
-
process.exit(runStatus.data.status === 'completed' ? 0 : 1);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
// Ignore transient errors during follow
|
|
74
|
-
}
|
|
75
|
-
}, 2000);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
if (error.response) {
|
|
80
|
-
if (error.response.status === 401) {
|
|
81
|
-
console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
|
|
82
|
-
}
|
|
83
|
-
else if (error.response.status === 404) {
|
|
84
|
-
console.error(chalk_1.default.red('Run not found.'));
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
console.error(chalk_1.default.red('Connection failed:'), error.message);
|
|
92
|
-
}
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Display log content. Handles both string logs and array-of-entry logs.
|
|
98
|
-
* Returns the number of characters printed (for follow-mode diffing).
|
|
99
|
-
*/
|
|
100
|
-
function displayLogs(logData) {
|
|
101
|
-
if (typeof logData === 'string') {
|
|
102
|
-
if (logData.trim()) {
|
|
103
|
-
console.log(logData);
|
|
104
|
-
}
|
|
105
|
-
return logData.length;
|
|
106
|
-
}
|
|
107
|
-
// Handle array format in case the API changes
|
|
108
|
-
for (const entry of logData) {
|
|
109
|
-
const message = entry.message || entry.content || '';
|
|
110
|
-
if (!message.trim())
|
|
111
|
-
continue;
|
|
112
|
-
const timestamp = entry.timestamp ? new Date(entry.timestamp).toLocaleTimeString() : '??:??:??';
|
|
113
|
-
const stream = entry.stream || 'stdout';
|
|
114
|
-
const streamIndicator = stream === 'stderr' ? chalk_1.default.red('[err]') : chalk_1.default.gray('[out]');
|
|
115
|
-
const coloredMessage = stream === 'stderr' ? chalk_1.default.red(message) : chalk_1.default.white(message);
|
|
116
|
-
console.log(`${chalk_1.default.gray(`[${timestamp}]`)} ${streamIndicator} ${coloredMessage}`);
|
|
117
|
-
}
|
|
118
|
-
return logData.length;
|
|
119
|
-
}
|