@solidactions/cli 0.7.3 → 1.1.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 +11 -1
- package/dist/commands/env-map.js +25 -2
- package/dist/commands/env-pull.js +2 -2
- package/dist/commands/env-push.js +1 -1
- package/dist/commands/env-set.js +35 -2
- package/dist/commands/init.js +4 -4
- package/dist/commands/project-list.js +52 -0
- package/dist/commands/pull.js +29 -12
- package/dist/commands/run-list.js +186 -0
- package/dist/commands/run-view.js +267 -0
- package/dist/commands/schedule-set.js +45 -1
- package/dist/commands/{webhooks.js → webhook-list.js} +4 -4
- package/dist/commands/workspaces.js +1 -1
- package/dist/index.js +117 -103
- package/package.json +4 -4
- package/dist/commands/logs.js +0 -103
- package/dist/commands/runs.js +0 -90
- package/dist/commands/steps.js +0 -202
- /package/dist/commands/{logs-build.js → project-logs.js} +0 -0
- /package/dist/commands/{run.js → run-start.js} +0 -0
package/README.md
CHANGED
|
@@ -11,39 +11,82 @@ npm install -g @solidactions/cli
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
# Initialize
|
|
15
|
-
solidactions init <api-key>
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
solidactions
|
|
14
|
+
solidactions init <api-key> # Initialize (prompts for workspace)
|
|
15
|
+
solidactions init <api-key> --workspace <name> # Initialize with workspace
|
|
16
|
+
solidactions project deploy <project> <path> # Deploy a project
|
|
17
|
+
solidactions run start <project> <workflow> # Trigger a workflow
|
|
18
|
+
solidactions run view <run-id> # Inspect a run
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Commands
|
|
22
22
|
|
|
23
|
+
Use `solidactions <command> --help` for full flag details on any command.
|
|
24
|
+
|
|
25
|
+
### Top-level
|
|
26
|
+
|
|
23
27
|
| Command | Description |
|
|
24
28
|
|---------|-------------|
|
|
25
|
-
| `init <api-key>` | Initialize CLI with
|
|
29
|
+
| `init <api-key>` | Initialize CLI with API key |
|
|
26
30
|
| `logout` | Remove saved credentials |
|
|
27
31
|
| `whoami` | Show current configuration |
|
|
28
|
-
| `
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
|
42
|
-
|
|
43
|
-
| `
|
|
44
|
-
| `
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
| `dev <file>` | Run a workflow locally (no deploy needed) |
|
|
33
|
+
|
|
34
|
+
### project
|
|
35
|
+
|
|
36
|
+
| Command | Key Flags | Description |
|
|
37
|
+
|---------|-----------|-------------|
|
|
38
|
+
| `project deploy <name> [path]` | `-e`, `--create`, `--config-only` | Deploy or sync config only |
|
|
39
|
+
| `project pull <name> [path]` | `-y` | Pull source (warns before overwriting) |
|
|
40
|
+
| `project logs <name>` | | View build logs |
|
|
41
|
+
| `project list` | | List all projects |
|
|
42
|
+
|
|
43
|
+
### run
|
|
44
|
+
|
|
45
|
+
| Command | Key Flags | Description |
|
|
46
|
+
|---------|-----------|-------------|
|
|
47
|
+
| `run start <project> <workflow>` | `-e`, `-i`, `-w` | Trigger a workflow run |
|
|
48
|
+
| `run list [project]` | `--json`, `--detailed`, `--status`, `--since`, `--workflow`, `--limit`, `--offset` | List and filter runs |
|
|
49
|
+
| `run view <run-id>` | `--json`, `--timeline`, `--steps`, `--logs` | Inspect a run |
|
|
50
|
+
|
|
51
|
+
### env
|
|
52
|
+
|
|
53
|
+
| Command | Key Flags | Description |
|
|
54
|
+
|---------|-----------|-------------|
|
|
55
|
+
| `env set <key> <value>` | `-s`, `-y`, `--staging-value`, `--dev-value` | Set global variable (warns before overwriting) |
|
|
56
|
+
| `env set <project> <key> <value>` | `-e`, `-s`, `-y` | Set project variable (warns before overwriting) |
|
|
57
|
+
| `env list [project]` | `-e` | List variables |
|
|
58
|
+
| `env delete <key-or-project> [key]` | `-y` | Delete a variable |
|
|
59
|
+
| `env map <project> <key> <global-key>` | `-y` | Map global to project key (warns before overwriting) |
|
|
60
|
+
| `env pull <project>` | `-e`, `-o`, `-y`, `--update-oauth` | Pull resolved env vars to .env file |
|
|
61
|
+
| `env push <project> [path]` | `-e`, `-y`, `--new-only`, `--include-undeclared` | Push .env values to project |
|
|
62
|
+
|
|
63
|
+
### schedule
|
|
64
|
+
|
|
65
|
+
| Command | Key Flags | Description |
|
|
66
|
+
|---------|-----------|-------------|
|
|
67
|
+
| `schedule set <project> <cron>` | `-w`, `-i`, `-y` | Set cron schedule (warns if exists) |
|
|
68
|
+
| `schedule list <project>` | | List schedules |
|
|
69
|
+
| `schedule delete <project> <id>` | `-y` | Delete a schedule |
|
|
70
|
+
|
|
71
|
+
### webhook
|
|
72
|
+
|
|
73
|
+
| Command | Key Flags | Description |
|
|
74
|
+
|---------|-----------|-------------|
|
|
75
|
+
| `webhook list <project>` | `-e`, `--show-secrets` | List webhook URLs |
|
|
76
|
+
|
|
77
|
+
### workspace
|
|
78
|
+
|
|
79
|
+
| Command | Description |
|
|
80
|
+
|---------|-------------|
|
|
81
|
+
| `workspace list` | List all workspaces |
|
|
82
|
+
| `workspace set <id>` | Set active workspace (by ID, slug, or name) |
|
|
83
|
+
|
|
84
|
+
### ai
|
|
85
|
+
|
|
86
|
+
| Command | Key Flags | Description |
|
|
87
|
+
|---------|-----------|-------------|
|
|
88
|
+
| `ai init` | `--claude`, `--agents` | Install AI helper docs |
|
|
89
|
+
| `ai examples [names...]` | `--all`, `--overwrite` | Install example workflows |
|
|
47
90
|
|
|
48
91
|
## Development
|
|
49
92
|
|
|
@@ -83,7 +83,7 @@ async function aiExamples(names, options = {}) {
|
|
|
83
83
|
// Detect AI helper file and upsert examples reference
|
|
84
84
|
const aiHelperFile = (0, markers_1.findAiHelperFile)();
|
|
85
85
|
if (!aiHelperFile) {
|
|
86
|
-
console.log(chalk_1.default.yellow('No AI helper file found. Run "solidactions ai
|
|
86
|
+
console.log(chalk_1.default.yellow('No AI helper file found. Run "solidactions ai init" first to create one.'));
|
|
87
87
|
}
|
|
88
88
|
else {
|
|
89
89
|
// List ALL installed examples (existing + newly installed)
|
package/dist/commands/deploy.js
CHANGED
|
@@ -191,6 +191,16 @@ async function deploy(projectName, sourcePath, options = {}) {
|
|
|
191
191
|
process.exit(1);
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
|
+
// --config-only: sync YAML env declarations without building
|
|
195
|
+
if (options.configOnly) {
|
|
196
|
+
if (!yamlConfig) {
|
|
197
|
+
console.error(chalk_1.default.red('Cannot sync config: solidactions.yaml not found or invalid.'));
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
await pushYamlDeclarations(config, projectSlug, yamlConfig);
|
|
201
|
+
console.log(chalk_1.default.green(`✓ Config synced for ${projectSlug}${envLabel}`));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
194
204
|
const archivePath = path_1.default.join(sourceDir, '.steps-deploy.tar.gz');
|
|
195
205
|
const output = fs_1.default.createWriteStream(archivePath);
|
|
196
206
|
const archive = (0, archiver_1.default)('tar', { gzip: true, gzipOptions: { level: 9 } });
|
|
@@ -302,7 +312,7 @@ async function deploy(projectName, sourcePath, options = {}) {
|
|
|
302
312
|
});
|
|
303
313
|
// Dockerfile always at archive root, referencing tenantcode/
|
|
304
314
|
const universalDockerfile = [
|
|
305
|
-
'FROM node:
|
|
315
|
+
'FROM node:24-alpine',
|
|
306
316
|
'WORKDIR /app',
|
|
307
317
|
'COPY tenantcode/package.json tenantcode/package-lock.json* ./',
|
|
308
318
|
'RUN npm install',
|
package/dist/commands/env-map.js
CHANGED
|
@@ -6,8 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.envMap = envMap;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
10
|
const api_1 = require("../utils/api");
|
|
10
|
-
async function envMap(projectName, projectKey, globalKey) {
|
|
11
|
+
async function envMap(projectName, projectKey, globalKey, options = {}) {
|
|
11
12
|
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
12
13
|
console.log(chalk_1.default.blue(`Mapping global variable "${globalKey}" to project key "${projectKey}" in "${projectName}"...`));
|
|
13
14
|
try {
|
|
@@ -19,9 +20,31 @@ async function envMap(projectName, projectKey, globalKey) {
|
|
|
19
20
|
const globalVar = variables.find((v) => v.key === globalKey);
|
|
20
21
|
if (!globalVar) {
|
|
21
22
|
console.error(chalk_1.default.red(`Global variable "${globalKey}" not found.`));
|
|
22
|
-
console.log(chalk_1.default.gray('Create it with: solidactions env
|
|
23
|
+
console.log(chalk_1.default.gray('Create it with: solidactions env set ' + globalKey + ' <value>'));
|
|
23
24
|
process.exit(1);
|
|
24
25
|
}
|
|
26
|
+
// Check if project key already has a mapping
|
|
27
|
+
if (!options.yes) {
|
|
28
|
+
const mappingsResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/variable-mappings`, { headers: (0, api_1.getApiHeaders)(config) });
|
|
29
|
+
const mappings = mappingsResponse.data || [];
|
|
30
|
+
const existing = mappings.find((m) => m.env_name === projectKey);
|
|
31
|
+
if (existing && (existing.has_value || existing.global_variable_id)) {
|
|
32
|
+
const currentSource = existing.global_variable_id
|
|
33
|
+
? `mapped to global "${existing.global_variable_key || 'unknown'}"`
|
|
34
|
+
: 'has a local value';
|
|
35
|
+
console.log(chalk_1.default.yellow(`"${projectKey}" already ${currentSource} in "${projectName}".`));
|
|
36
|
+
const confirm = await (0, prompts_1.default)({
|
|
37
|
+
type: 'confirm',
|
|
38
|
+
name: 'proceed',
|
|
39
|
+
message: 'Overwrite mapping?',
|
|
40
|
+
initial: false,
|
|
41
|
+
});
|
|
42
|
+
if (!confirm.proceed) {
|
|
43
|
+
console.log(chalk_1.default.gray('Cancelled.'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
25
48
|
// Create the mapping
|
|
26
49
|
await axios_1.default.post(`${config.host}/api/v1/projects/${projectName}/variable-mappings`, {
|
|
27
50
|
project_key: projectKey,
|
|
@@ -147,7 +147,7 @@ async function envPull(projectName, options = {}) {
|
|
|
147
147
|
// Full pull: Build .env file content
|
|
148
148
|
const lines = [];
|
|
149
149
|
lines.push(`# Environment variables for ${projectName} (${environment})`);
|
|
150
|
-
lines.push(`# Generated by solidactions env
|
|
150
|
+
lines.push(`# Generated by solidactions env pull on ${new Date().toISOString()}`);
|
|
151
151
|
lines.push('');
|
|
152
152
|
let count = 0;
|
|
153
153
|
let secretCount = 0;
|
|
@@ -208,7 +208,7 @@ async function envPull(projectName, options = {}) {
|
|
|
208
208
|
else if (error.response.status === 404) {
|
|
209
209
|
console.error(chalk_1.default.red(`Project "${projectSlug}" not found.`));
|
|
210
210
|
if (environment !== 'production') {
|
|
211
|
-
console.log(chalk_1.default.gray(`Try deploying with: solidactions deploy ${projectName} --env ${environment} --create`));
|
|
211
|
+
console.log(chalk_1.default.gray(`Try deploying with: solidactions project deploy ${projectName} --env ${environment} --create`));
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
else {
|
|
@@ -92,7 +92,7 @@ async function envPush(projectName, sourcePath, options = {}) {
|
|
|
92
92
|
}
|
|
93
93
|
else if (error.response?.status === 404) {
|
|
94
94
|
console.error(chalk_1.default.red(`Project "${projectSlug}" not found.`));
|
|
95
|
-
console.log(chalk_1.default.gray(`Deploy first with: solidactions deploy ${projectName} --env ${environment}` + (environment !== 'production' ? ' --create' : '')));
|
|
95
|
+
console.log(chalk_1.default.gray(`Deploy first with: solidactions project deploy ${projectName} --env ${environment}` + (environment !== 'production' ? ' --create' : '')));
|
|
96
96
|
process.exit(1);
|
|
97
97
|
}
|
|
98
98
|
throw error;
|
package/dist/commands/env-set.js
CHANGED
|
@@ -6,13 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.envSet = envSet;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
10
|
const api_1 = require("../utils/api");
|
|
10
11
|
async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
|
|
11
12
|
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
12
13
|
// Detect mode based on arguments
|
|
13
14
|
const isProjectMode = valueIfProject !== undefined;
|
|
14
15
|
if (isProjectMode) {
|
|
15
|
-
// Project mode: solidactions env
|
|
16
|
+
// Project mode: solidactions env set <project> <key> <value>
|
|
16
17
|
const projectName = keyOrProject;
|
|
17
18
|
const key = valueOrKey;
|
|
18
19
|
const value = valueIfProject;
|
|
@@ -24,6 +25,25 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
|
|
|
24
25
|
// Auto-detect secrets
|
|
25
26
|
const isSecret = options.secret || /secret|key|token|password|credential/i.test(key);
|
|
26
27
|
try {
|
|
28
|
+
// Check if variable already has a value
|
|
29
|
+
if (!options.yes) {
|
|
30
|
+
const mappingsResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings?reveal=true`, { headers: (0, api_1.getApiHeaders)(config) });
|
|
31
|
+
const mappings = mappingsResponse.data || [];
|
|
32
|
+
const existing = mappings.find((m) => m.env_name === key);
|
|
33
|
+
if (existing && existing.has_value) {
|
|
34
|
+
console.log(chalk_1.default.yellow(`Variable "${key}" already has a value in "${projectName}" (${environment}).`));
|
|
35
|
+
const confirm = await (0, prompts_1.default)({
|
|
36
|
+
type: 'confirm',
|
|
37
|
+
name: 'proceed',
|
|
38
|
+
message: 'Overwrite?',
|
|
39
|
+
initial: false,
|
|
40
|
+
});
|
|
41
|
+
if (!confirm.proceed) {
|
|
42
|
+
console.log(chalk_1.default.gray('Cancelled.'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
27
47
|
// Use bulk endpoint for upsert
|
|
28
48
|
const response = await axios_1.default.post(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings/bulk`, {
|
|
29
49
|
variables: [{
|
|
@@ -60,7 +80,7 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
|
|
|
60
80
|
}
|
|
61
81
|
}
|
|
62
82
|
else {
|
|
63
|
-
// Global mode: solidactions env
|
|
83
|
+
// Global mode: solidactions env set <key> <value>
|
|
64
84
|
const key = keyOrProject;
|
|
65
85
|
const value = valueOrKey;
|
|
66
86
|
const isSecret = options.secret || false;
|
|
@@ -98,6 +118,19 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
|
|
|
98
118
|
const existing = variables.find((v) => v.key === key);
|
|
99
119
|
let action;
|
|
100
120
|
if (existing) {
|
|
121
|
+
if (!options.yes) {
|
|
122
|
+
console.log(chalk_1.default.yellow(`Global variable "${key}" already exists.`));
|
|
123
|
+
const confirm = await (0, prompts_1.default)({
|
|
124
|
+
type: 'confirm',
|
|
125
|
+
name: 'proceed',
|
|
126
|
+
message: 'Overwrite?',
|
|
127
|
+
initial: false,
|
|
128
|
+
});
|
|
129
|
+
if (!confirm.proceed) {
|
|
130
|
+
console.log(chalk_1.default.gray('Cancelled.'));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
101
134
|
await axios_1.default.put(`${config.host}/api/v1/variables/${existing.id}`, body, {
|
|
102
135
|
headers: (0, api_1.getApiHeaders)(config, 'application/json'),
|
|
103
136
|
});
|
package/dist/commands/init.js
CHANGED
|
@@ -83,13 +83,13 @@ async function init(apiKey, options) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
catch {
|
|
86
|
-
console.log(chalk_1.default.yellow('Could not set workspace. Run `solidactions workspace
|
|
86
|
+
console.log(chalk_1.default.yellow('Could not set workspace. Run `solidactions workspace set` later.'));
|
|
87
87
|
}
|
|
88
88
|
console.log('');
|
|
89
89
|
console.log(chalk_1.default.blue('Quick start:'));
|
|
90
|
-
console.log(chalk_1.default.gray(' solidactions deploy <
|
|
91
|
-
console.log(chalk_1.default.gray(' solidactions run <
|
|
92
|
-
console.log(chalk_1.default.gray(' solidactions
|
|
90
|
+
console.log(chalk_1.default.gray(' solidactions project deploy <name> Deploy current directory'));
|
|
91
|
+
console.log(chalk_1.default.gray(' solidactions run start <proj> <wf> Run a workflow'));
|
|
92
|
+
console.log(chalk_1.default.gray(' solidactions run list List recent runs'));
|
|
93
93
|
}
|
|
94
94
|
async function logout() {
|
|
95
95
|
if (fs_1.default.existsSync(CONFIG_FILE)) {
|
|
@@ -0,0 +1,52 @@
|
|
|
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.projectList = projectList;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const api_1 = require("../utils/api");
|
|
10
|
+
async function projectList() {
|
|
11
|
+
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
12
|
+
console.log(chalk_1.default.blue('Projects:\n'));
|
|
13
|
+
try {
|
|
14
|
+
const response = await axios_1.default.get(`${config.host}/api/v1/projects`, {
|
|
15
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
16
|
+
});
|
|
17
|
+
const projects = response.data.data || [];
|
|
18
|
+
if (projects.length === 0) {
|
|
19
|
+
console.log(chalk_1.default.gray('No projects found.'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.log(chalk_1.default.gray('NAME'.padEnd(25) + 'STATUS'.padEnd(15) + 'SNAPSHOT'.padEnd(30) + 'ENVIRONMENTS'));
|
|
23
|
+
console.log(chalk_1.default.gray('-'.repeat(100)));
|
|
24
|
+
for (const project of projects) {
|
|
25
|
+
const name = (project.name || '?').padEnd(25);
|
|
26
|
+
const status = project.status || '?';
|
|
27
|
+
const statusColor = status === 'ready' ? chalk_1.default.green : status === 'building' ? chalk_1.default.yellow : chalk_1.default.gray;
|
|
28
|
+
const snapshot = (project.snapshot_name || '-').padEnd(30);
|
|
29
|
+
const envs = (project.environments || [project.environment || 'production']).join(', ');
|
|
30
|
+
console.log(name +
|
|
31
|
+
statusColor(status.padEnd(15)) +
|
|
32
|
+
chalk_1.default.gray(snapshot) +
|
|
33
|
+
chalk_1.default.gray(envs));
|
|
34
|
+
}
|
|
35
|
+
console.log('');
|
|
36
|
+
console.log(chalk_1.default.gray(`${projects.length} project(s)`));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error.response) {
|
|
40
|
+
if (error.response.status === 401) {
|
|
41
|
+
console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error(chalk_1.default.red('Connection failed:'), error.message);
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
package/dist/commands/pull.js
CHANGED
|
@@ -6,30 +6,47 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.pull = pull;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const zlib_1 = require("zlib");
|
|
10
|
+
const promises_1 = require("stream/promises");
|
|
11
|
+
const stream_1 = require("stream");
|
|
12
|
+
const tar_1 = require("tar");
|
|
9
13
|
const axios_1 = __importDefault(require("axios"));
|
|
10
14
|
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const
|
|
15
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
12
16
|
const api_1 = require("../utils/api");
|
|
13
|
-
async function pull(projectName, destPath) {
|
|
17
|
+
async function pull(projectName, destPath, options = {}) {
|
|
14
18
|
const config = await (0, api_1.requireConfigWithWorkspace)();
|
|
15
19
|
const destination = destPath ? path_1.default.resolve(destPath) : process.cwd();
|
|
20
|
+
// Warn if destination has existing files
|
|
21
|
+
if (fs_1.default.existsSync(destination)) {
|
|
22
|
+
const entries = fs_1.default.readdirSync(destination);
|
|
23
|
+
if (entries.length > 0 && !options.yes) {
|
|
24
|
+
console.log(chalk_1.default.yellow(`Destination "${destination}" is not empty (${entries.length} items).`));
|
|
25
|
+
console.log(chalk_1.default.yellow('Pulling will overwrite existing files.'));
|
|
26
|
+
const response = await (0, prompts_1.default)({
|
|
27
|
+
type: 'confirm',
|
|
28
|
+
name: 'proceed',
|
|
29
|
+
message: 'Continue?',
|
|
30
|
+
initial: false,
|
|
31
|
+
});
|
|
32
|
+
if (!response.proceed) {
|
|
33
|
+
console.log(chalk_1.default.gray('Cancelled.'));
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
16
38
|
console.log(chalk_1.default.blue(`Pulling project "${projectName}"...`));
|
|
17
39
|
try {
|
|
18
40
|
const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/source`, {
|
|
19
41
|
headers: { ...(0, api_1.getApiHeaders)(config), 'Accept': 'application/octet-stream' },
|
|
20
42
|
responseType: 'arraybuffer',
|
|
21
43
|
});
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
// Write the zip to a temp file
|
|
25
|
-
fs_1.default.writeFileSync(tempZipPath, zipBuffer);
|
|
26
|
-
console.log(chalk_1.default.gray(`Downloaded ${zipBuffer.length} bytes`));
|
|
44
|
+
const buffer = Buffer.from(response.data);
|
|
45
|
+
console.log(chalk_1.default.gray(`Downloaded ${buffer.length} bytes`));
|
|
27
46
|
console.log(chalk_1.default.yellow(`Extracting to ${destination}...`));
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
// Clean up temp file
|
|
32
|
-
fs_1.default.unlinkSync(tempZipPath);
|
|
47
|
+
fs_1.default.mkdirSync(destination, { recursive: true });
|
|
48
|
+
const readable = stream_1.Readable.from(buffer);
|
|
49
|
+
await (0, promises_1.pipeline)(readable, (0, zlib_1.createGunzip)(), (0, tar_1.extract)({ cwd: destination, strip: 0 }));
|
|
33
50
|
console.log(chalk_1.default.green(`Project "${projectName}" pulled successfully!`));
|
|
34
51
|
}
|
|
35
52
|
catch (error) {
|
|
@@ -0,0 +1,186 @@
|
|
|
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
|
+
// Default limit is lower for --detailed (more data per run)
|
|
13
|
+
const defaultLimit = options.detailed ? 5 : 20;
|
|
14
|
+
const limit = options.limit || defaultLimit;
|
|
15
|
+
try {
|
|
16
|
+
const params = { limit };
|
|
17
|
+
if (projectName)
|
|
18
|
+
params.project = projectName;
|
|
19
|
+
if (options.offset)
|
|
20
|
+
params.offset = options.offset;
|
|
21
|
+
if (options.status)
|
|
22
|
+
params.status = options.status;
|
|
23
|
+
if (options.since)
|
|
24
|
+
params.since = parseSince(options.since);
|
|
25
|
+
if (options.workflow)
|
|
26
|
+
params.workflow = options.workflow;
|
|
27
|
+
if (options.detailed)
|
|
28
|
+
params.detailed = '1';
|
|
29
|
+
const response = await axios_1.default.get(`${config.host}/api/v1/runs`, {
|
|
30
|
+
headers: (0, api_1.getApiHeaders)(config),
|
|
31
|
+
params,
|
|
32
|
+
});
|
|
33
|
+
const runsList = response.data.data || response.data;
|
|
34
|
+
if (!runsList || runsList.length === 0) {
|
|
35
|
+
if (options.json) {
|
|
36
|
+
console.log('[]');
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.log(chalk_1.default.gray('No runs found.'));
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (options.json) {
|
|
44
|
+
console.log(JSON.stringify(runsList, null, 2));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Human-readable output
|
|
48
|
+
if (options.detailed) {
|
|
49
|
+
displayDetailedList(runsList, projectName);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
displaySummaryTable(runsList, projectName);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error.response) {
|
|
57
|
+
if (error.response.status === 401) {
|
|
58
|
+
console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.error(chalk_1.default.red('Connection failed:'), error.message);
|
|
66
|
+
}
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ─── Display modes ─────────────────────────────────────────────────────────
|
|
71
|
+
function displaySummaryTable(runsList, projectName) {
|
|
72
|
+
const header = projectName ? `Recent runs for "${projectName}":` : 'Recent runs:';
|
|
73
|
+
console.log(chalk_1.default.blue(header));
|
|
74
|
+
console.log('');
|
|
75
|
+
console.log(chalk_1.default.gray('ID'.padEnd(8) + 'WORKFLOW'.padEnd(25) + 'STATUS'.padEnd(12) + 'TRIGGERED'.padEnd(22) + 'TRIGGERED BY'));
|
|
76
|
+
console.log(chalk_1.default.gray('-'.repeat(90)));
|
|
77
|
+
for (const run of runsList) {
|
|
78
|
+
const id = String(run.id || '?').padEnd(8);
|
|
79
|
+
const workflow = truncate(run.workflow_name || '?', 24).padEnd(25);
|
|
80
|
+
const status = run.execution_status || run.status || '?';
|
|
81
|
+
const statusColor = getStatusColor(status);
|
|
82
|
+
const triggeredAt = run.triggered_at ? new Date(run.triggered_at).toLocaleString() : '-';
|
|
83
|
+
const triggeredBy = run.triggered_by || '-';
|
|
84
|
+
console.log(chalk_1.default.gray(id) +
|
|
85
|
+
workflow +
|
|
86
|
+
statusColor(status.padEnd(12)) +
|
|
87
|
+
chalk_1.default.gray(triggeredAt.padEnd(22)) +
|
|
88
|
+
chalk_1.default.gray(triggeredBy));
|
|
89
|
+
}
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log(chalk_1.default.gray(`Showing ${runsList.length} run(s)`));
|
|
92
|
+
}
|
|
93
|
+
function displayDetailedList(runsList, projectName) {
|
|
94
|
+
const header = projectName ? `Runs for "${projectName}" (detailed):` : 'Runs (detailed):';
|
|
95
|
+
console.log(chalk_1.default.blue(header));
|
|
96
|
+
for (const run of runsList) {
|
|
97
|
+
const status = run.execution_status || run.status || '?';
|
|
98
|
+
const statusColor = getStatusColor(status);
|
|
99
|
+
const exitStr = run.exit_code !== null && run.exit_code !== undefined ? ` (exit ${run.exit_code})` : '';
|
|
100
|
+
console.log('');
|
|
101
|
+
console.log(chalk_1.default.bold(` Run #${run.id}`) + chalk_1.default.gray(` — ${run.workflow_name || '?'} (${run.project_name || '?'})`));
|
|
102
|
+
console.log(` Status: ${statusColor(status)}${chalk_1.default.gray(exitStr)}`);
|
|
103
|
+
console.log(` Trigger: ${chalk_1.default.gray(run.triggered_by || '-')}`);
|
|
104
|
+
// Timeline
|
|
105
|
+
if (run.timeline) {
|
|
106
|
+
const formatTs = (iso) => iso ? new Date(iso).toLocaleString() : '-';
|
|
107
|
+
console.log(` Triggered: ${chalk_1.default.gray(formatTs(run.timeline.triggered))}`);
|
|
108
|
+
console.log(` Started: ${chalk_1.default.gray(formatTs(run.timeline.started))}`);
|
|
109
|
+
console.log(` Completed: ${chalk_1.default.gray(formatTs(run.timeline.completed))}`);
|
|
110
|
+
}
|
|
111
|
+
// Steps summary
|
|
112
|
+
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)`)}`);
|
|
116
|
+
}
|
|
117
|
+
// Logs snippet (first errors or last 3 lines)
|
|
118
|
+
if (run.logs && typeof run.logs === 'string' && run.logs.trim()) {
|
|
119
|
+
const lines = run.logs.trim().split('\n');
|
|
120
|
+
const errorLines = lines.filter((l) => /error|fail|exception/i.test(l));
|
|
121
|
+
if (errorLines.length > 0) {
|
|
122
|
+
console.log(` Errors:`);
|
|
123
|
+
for (const line of errorLines.slice(0, 3)) {
|
|
124
|
+
console.log(chalk_1.default.red(` ${truncate(line, 80)}`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(chalk_1.default.gray(`Showing ${runsList.length} run(s)`));
|
|
131
|
+
}
|
|
132
|
+
// ─── Utility ───────────────────────────────────────────────────────────────
|
|
133
|
+
/**
|
|
134
|
+
* Parse --since value into epoch ms.
|
|
135
|
+
* Accepts: "1h", "30m", "2d", "1w", or ISO date string.
|
|
136
|
+
*/
|
|
137
|
+
function parseSince(since) {
|
|
138
|
+
const match = since.match(/^(\d+)(m|h|d|w)$/);
|
|
139
|
+
if (match) {
|
|
140
|
+
const value = parseInt(match[1]);
|
|
141
|
+
const unit = match[2];
|
|
142
|
+
const multipliers = {
|
|
143
|
+
m: 60 * 1000,
|
|
144
|
+
h: 60 * 60 * 1000,
|
|
145
|
+
d: 24 * 60 * 60 * 1000,
|
|
146
|
+
w: 7 * 24 * 60 * 60 * 1000,
|
|
147
|
+
};
|
|
148
|
+
const ms = Date.now() - (value * multipliers[unit]);
|
|
149
|
+
return String(ms);
|
|
150
|
+
}
|
|
151
|
+
// Assume ISO date string — let the backend parse it
|
|
152
|
+
return since;
|
|
153
|
+
}
|
|
154
|
+
function formatDuration(ms) {
|
|
155
|
+
if (ms === null || ms === undefined)
|
|
156
|
+
return '-';
|
|
157
|
+
if (ms < 1000)
|
|
158
|
+
return `${ms}ms`;
|
|
159
|
+
if (ms < 60000)
|
|
160
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
161
|
+
if (ms < 3600000)
|
|
162
|
+
return `${Math.round(ms / 60000)}m`;
|
|
163
|
+
return `${Math.round(ms / 3600000)}h`;
|
|
164
|
+
}
|
|
165
|
+
function truncate(str, max) {
|
|
166
|
+
if (str.length <= max)
|
|
167
|
+
return str;
|
|
168
|
+
return str.substring(0, max - 3) + '...';
|
|
169
|
+
}
|
|
170
|
+
function getStatusColor(status) {
|
|
171
|
+
switch (status?.toLowerCase()) {
|
|
172
|
+
case 'completed':
|
|
173
|
+
case 'success':
|
|
174
|
+
return chalk_1.default.green;
|
|
175
|
+
case 'running':
|
|
176
|
+
return chalk_1.default.blue;
|
|
177
|
+
case 'pending':
|
|
178
|
+
case 'queued':
|
|
179
|
+
return chalk_1.default.yellow;
|
|
180
|
+
case 'failed':
|
|
181
|
+
case 'error':
|
|
182
|
+
return chalk_1.default.red;
|
|
183
|
+
default:
|
|
184
|
+
return chalk_1.default.gray;
|
|
185
|
+
}
|
|
186
|
+
}
|