@solidactions/cli 0.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 ADDED
@@ -0,0 +1,56 @@
1
+ # SolidActions CLI
2
+
3
+ Deploy and manage workflow automation with SolidActions.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @solidactions/cli
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Initialize with your API key
15
+ solidactions init <api-key>
16
+
17
+ # Deploy a project
18
+ solidactions deploy <project-name> <path>
19
+ ```
20
+
21
+ ## Commands
22
+
23
+ | Command | Description |
24
+ |---------|-------------|
25
+ | `init <api-key>` | Initialize CLI with your API key |
26
+ | `logout` | Remove saved credentials |
27
+ | `whoami` | Show current configuration |
28
+ | `deploy <project> [path]` | Deploy a project to SolidActions |
29
+ | `pull <project> [path]` | Pull project source from SolidActions |
30
+ | `run <project> <workflow>` | Trigger a workflow run |
31
+ | `runs [project]` | List recent workflow runs |
32
+ | `logs <run-id>` | View logs for a workflow run |
33
+ | `logs:build <project>` | View build/deployment logs |
34
+ | `env:create <key> <value>` | Create a global environment variable |
35
+ | `env:list [project]` | List environment variables |
36
+ | `env:delete <key>` | Delete an environment variable |
37
+ | `env:map <project> <key> <global-key>` | Map a global variable to a project |
38
+ | `env:pull <project>` | Pull resolved env vars to a local file |
39
+ | `schedule:set <project> <cron>` | Set a cron schedule for a workflow |
40
+ | `schedule:list <project>` | List schedules for a project |
41
+ | `schedule:delete <project> <id>` | Delete a schedule |
42
+
43
+ See [docs/cli.md](docs/cli.md) for full documentation.
44
+
45
+ ## Development
46
+
47
+ ```bash
48
+ git clone https://github.com/SolidActions/solidactions-cli.git
49
+ cd solidactions-cli
50
+ npm install
51
+ npm run build
52
+ ```
53
+
54
+ ## License
55
+
56
+ MIT
@@ -0,0 +1,454 @@
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.deploy = deploy;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const archiver_1 = __importDefault(require("archiver"));
10
+ const axios_1 = __importDefault(require("axios"));
11
+ const form_data_1 = __importDefault(require("form-data"));
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ const js_yaml_1 = __importDefault(require("js-yaml"));
14
+ const init_1 = require("./init");
15
+ /**
16
+ * Validate project structure before deployment.
17
+ * Checks for required files and SDK dependency.
18
+ */
19
+ function validateProject(sourceDir) {
20
+ const errors = [];
21
+ const warnings = [];
22
+ // Check for solidactions.yaml
23
+ const solidactionsPath = path_1.default.join(sourceDir, 'solidactions.yaml');
24
+ if (!fs_1.default.existsSync(solidactionsPath)) {
25
+ errors.push('solidactions.yaml not found. This file defines your workflows.');
26
+ return { valid: false, errors, warnings };
27
+ }
28
+ // Check for package.json
29
+ const packageJsonPath = path_1.default.join(sourceDir, 'package.json');
30
+ if (!fs_1.default.existsSync(packageJsonPath)) {
31
+ errors.push('package.json not found. Initialize with: npm init');
32
+ return { valid: false, errors, warnings };
33
+ }
34
+ // Read and validate package.json
35
+ const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
36
+ const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
37
+ // Check for SDK - either @solidactions/sdk or bundled sdk folder
38
+ const hasSdkPackage = '@solidactions/sdk' in allDeps;
39
+ const hasBundledSdk = fs_1.default.existsSync(path_1.default.join(sourceDir, 'sdk'));
40
+ if (!hasSdkPackage && !hasBundledSdk) {
41
+ errors.push('Missing SDK in package.json dependencies. Run: npm install @solidactions/sdk');
42
+ }
43
+ // Check for TypeScript config if .ts files are used
44
+ const srcDir = path_1.default.join(sourceDir, 'src');
45
+ const hasTypeScriptFiles = fs_1.default.existsSync(srcDir) &&
46
+ fs_1.default.readdirSync(srcDir).some(f => f.endsWith('.ts') || f.endsWith('.tsx'));
47
+ if (hasTypeScriptFiles) {
48
+ const tsconfigPath = path_1.default.join(sourceDir, 'tsconfig.json');
49
+ if (!fs_1.default.existsSync(tsconfigPath)) {
50
+ warnings.push('TypeScript files detected but tsconfig.json not found. Build may fail.');
51
+ }
52
+ }
53
+ // Parse and validate solidactions.yaml
54
+ try {
55
+ const configContent = fs_1.default.readFileSync(solidactionsPath, 'utf8');
56
+ const config = js_yaml_1.default.load(configContent);
57
+ if (!config || !config.workflows || !Array.isArray(config.workflows)) {
58
+ errors.push('Invalid solidactions.yaml: workflows section is required.');
59
+ }
60
+ else {
61
+ // Validate each workflow
62
+ for (const wf of config.workflows) {
63
+ if (!wf.name) {
64
+ errors.push(`Workflow missing required 'name' field.`);
65
+ continue;
66
+ }
67
+ // Must have either command: or file:
68
+ if (!wf.command && !wf.file) {
69
+ errors.push(`Workflow "${wf.name}": must specify either 'command:' or 'file:'.`);
70
+ continue;
71
+ }
72
+ // If file: is specified, verify the file exists
73
+ if (wf.file) {
74
+ const filePath = path_1.default.join(sourceDir, wf.file);
75
+ if (!fs_1.default.existsSync(filePath)) {
76
+ errors.push(`Workflow "${wf.name}": file "${wf.file}" not found.`);
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ catch (err) {
83
+ errors.push(`Failed to parse solidactions.yaml: ${err.message}`);
84
+ }
85
+ return { valid: errors.length === 0, errors, warnings };
86
+ }
87
+ /**
88
+ * Parse a .env file into a key-value map.
89
+ */
90
+ function parseEnvFile(filePath) {
91
+ const envMap = new Map();
92
+ if (!fs_1.default.existsSync(filePath)) {
93
+ return envMap;
94
+ }
95
+ const content = fs_1.default.readFileSync(filePath, 'utf8');
96
+ const lines = content.split('\n');
97
+ for (const line of lines) {
98
+ const trimmed = line.trim();
99
+ // Skip empty lines and comments
100
+ if (!trimmed || trimmed.startsWith('#')) {
101
+ continue;
102
+ }
103
+ const equalsIndex = trimmed.indexOf('=');
104
+ if (equalsIndex === -1) {
105
+ continue;
106
+ }
107
+ const key = trimmed.substring(0, equalsIndex).trim();
108
+ let value = trimmed.substring(equalsIndex + 1).trim();
109
+ // Remove surrounding quotes if present
110
+ if ((value.startsWith('"') && value.endsWith('"')) ||
111
+ (value.startsWith("'") && value.endsWith("'"))) {
112
+ value = value.slice(1, -1);
113
+ }
114
+ envMap.set(key, value);
115
+ }
116
+ return envMap;
117
+ }
118
+ /**
119
+ * Parse YAML env declarations into structured format.
120
+ * Handles the new simplified format:
121
+ * - VAR_NAME -> { key: "VAR_NAME", mappedTo: null }
122
+ * - VAR_NAME: GLOBAL -> { key: "VAR_NAME", mappedTo: "GLOBAL" }
123
+ */
124
+ function parseYamlEnvVars(config) {
125
+ const parsedVars = [];
126
+ if (!config.env || !Array.isArray(config.env)) {
127
+ return parsedVars;
128
+ }
129
+ for (const item of config.env) {
130
+ if (typeof item === 'string') {
131
+ // Simple string: - VAR_NAME (declared only, needs configuration)
132
+ parsedVars.push({ key: item, mappedTo: null });
133
+ }
134
+ else if (typeof item === 'object' && item !== null) {
135
+ // Object: - VAR_NAME: GLOBAL_NAME (mapped to global)
136
+ const keys = Object.keys(item);
137
+ if (keys.length === 1) {
138
+ const key = keys[0];
139
+ const mappedTo = item[key];
140
+ parsedVars.push({ key, mappedTo: mappedTo || null });
141
+ }
142
+ }
143
+ }
144
+ return parsedVars;
145
+ }
146
+ /**
147
+ * Extract declared variable keys from solidactions.yaml env config.
148
+ * Returns a set of env var keys that are declared in YAML.
149
+ */
150
+ function getYamlDeclaredVars(config) {
151
+ const parsedVars = parseYamlEnvVars(config);
152
+ return new Set(parsedVars.map(v => v.key));
153
+ }
154
+ /**
155
+ * Check if deployEnv is enabled in the config.
156
+ * New format has deployEnv at top level.
157
+ */
158
+ function isDeployEnvEnabled(config) {
159
+ return config.deployEnv === true;
160
+ }
161
+ /**
162
+ * Push YAML env declarations to the project.
163
+ * This registers all YAML-declared vars and their mappings.
164
+ */
165
+ async function pushYamlDeclarations(host, apiKey, projectSlug, yamlConfig) {
166
+ const parsedVars = parseYamlEnvVars(yamlConfig);
167
+ if (parsedVars.length === 0) {
168
+ return;
169
+ }
170
+ // Build the declarations array
171
+ const declarations = parsedVars.map(v => ({
172
+ env_name: v.key,
173
+ yaml_default_global_key: v.mappedTo,
174
+ source: 'yaml',
175
+ }));
176
+ try {
177
+ await axios_1.default.post(`${host}/api/v1/projects/${projectSlug}/variable-mappings/sync-yaml`, { declarations }, {
178
+ headers: {
179
+ 'Authorization': `Bearer ${apiKey}`,
180
+ 'Accept': 'application/json',
181
+ 'Content-Type': 'application/json',
182
+ },
183
+ });
184
+ console.log(chalk_1.default.gray(`Synced ${declarations.length} YAML env declarations`));
185
+ }
186
+ catch (error) {
187
+ console.error(chalk_1.default.yellow('Warning: Failed to sync YAML declarations:'), error.response?.data?.message || error.message);
188
+ }
189
+ }
190
+ /**
191
+ * Push environment variables from .env file to project.
192
+ * Only pushes vars that are declared in solidactions.yaml.
193
+ */
194
+ async function pushEnvVars(host, apiKey, projectSlug, sourceDir, environment, yamlConfig) {
195
+ // Get vars declared in YAML
196
+ const declaredVars = getYamlDeclaredVars(yamlConfig);
197
+ if (declaredVars.size === 0) {
198
+ console.log(chalk_1.default.gray('No environment variables declared in solidactions.yaml'));
199
+ return;
200
+ }
201
+ // Read the .env file for this environment
202
+ const envFileName = environment === 'production' ? '.env' : `.env.${environment}`;
203
+ const envFilePath = path_1.default.join(sourceDir, envFileName);
204
+ if (!fs_1.default.existsSync(envFilePath)) {
205
+ console.log(chalk_1.default.yellow(`⚠ ${envFileName} not found, skipping env push`));
206
+ // Warn about each missing declared var
207
+ for (const key of declaredVars) {
208
+ console.log(chalk_1.default.yellow(` ⚠ ${key}: no value (not in ${envFileName})`));
209
+ }
210
+ return;
211
+ }
212
+ // Parse the .env file
213
+ const envValues = parseEnvFile(envFilePath);
214
+ // Filter to only YAML-declared vars
215
+ const variables = [];
216
+ const missing = [];
217
+ for (const key of declaredVars) {
218
+ const value = envValues.get(key);
219
+ if (value !== undefined) {
220
+ // Mark vars containing "secret", "key", "token", "password" as secrets
221
+ const isSecret = /secret|key|token|password|credential/i.test(key);
222
+ variables.push({ key, value, is_secret: isSecret });
223
+ }
224
+ else {
225
+ missing.push(key);
226
+ }
227
+ }
228
+ // Warn about missing vars
229
+ for (const key of missing) {
230
+ console.log(chalk_1.default.yellow(`⚠ ${key}: not found in ${envFileName}`));
231
+ }
232
+ if (variables.length === 0) {
233
+ console.log(chalk_1.default.yellow('No matching environment variables to push'));
234
+ return;
235
+ }
236
+ // Push to API
237
+ try {
238
+ const response = await axios_1.default.post(`${host}/api/v1/projects/${projectSlug}/variable-mappings/bulk`, { variables }, {
239
+ headers: {
240
+ 'Authorization': `Bearer ${apiKey}`,
241
+ 'Accept': 'application/json',
242
+ 'Content-Type': 'application/json',
243
+ },
244
+ });
245
+ const { created, updated } = response.data;
246
+ console.log(chalk_1.default.green(`✓ Pushed ${variables.length} environment variables from ${envFileName}`));
247
+ if (created > 0 || updated > 0) {
248
+ console.log(chalk_1.default.gray(` (${created} created, ${updated} updated)`));
249
+ }
250
+ }
251
+ catch (error) {
252
+ console.error(chalk_1.default.red('Failed to push environment variables:'), error.response?.data?.message || error.message);
253
+ }
254
+ }
255
+ async function deploy(projectName, sourcePath, options = {}) {
256
+ const config = (0, init_1.getConfig)();
257
+ if (!config?.apiKey) {
258
+ console.error(chalk_1.default.red('Not initialized. Run `solidactions init <api-key>` first.'));
259
+ process.exit(1);
260
+ }
261
+ const sourceDir = sourcePath ? path_1.default.resolve(sourcePath) : process.cwd();
262
+ const environment = options.env || 'production';
263
+ if (!fs_1.default.existsSync(sourceDir)) {
264
+ console.error(chalk_1.default.red(`Source directory not found: ${sourceDir}`));
265
+ process.exit(1);
266
+ }
267
+ const envLabel = environment !== 'production' ? ` (${environment})` : '';
268
+ console.log(chalk_1.default.blue(`Deploying to project "${projectName}"${envLabel}...`));
269
+ console.log(chalk_1.default.gray(`Source: ${sourceDir}`));
270
+ // Validate project structure before deploying
271
+ console.log(chalk_1.default.gray('Validating project structure...'));
272
+ const validation = validateProject(sourceDir);
273
+ // Parse solidactions.yaml for env config
274
+ const solidactionsPath = path_1.default.join(sourceDir, 'solidactions.yaml');
275
+ let yamlConfig = null;
276
+ try {
277
+ const configContent = fs_1.default.readFileSync(solidactionsPath, 'utf8');
278
+ yamlConfig = js_yaml_1.default.load(configContent);
279
+ }
280
+ catch {
281
+ // Config parsing errors are handled by validateProject
282
+ }
283
+ if (validation.warnings.length > 0) {
284
+ for (const warning of validation.warnings) {
285
+ console.log(chalk_1.default.yellow(`⚠ ${warning}`));
286
+ }
287
+ }
288
+ if (!validation.valid) {
289
+ console.error(chalk_1.default.red('\nDeployment failed - validation errors:\n'));
290
+ for (const error of validation.errors) {
291
+ console.error(chalk_1.default.red(` ✗ ${error}`));
292
+ }
293
+ console.error('');
294
+ process.exit(1);
295
+ }
296
+ console.log(chalk_1.default.green('✓ Project structure validated'));
297
+ // Check if project exists, create if not
298
+ let projectSlug = projectName;
299
+ try {
300
+ // For non-production environments, append the environment to the slug for lookup
301
+ const lookupSlug = environment === 'production'
302
+ ? projectName
303
+ : `${projectName}-${environment}`;
304
+ const checkResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${lookupSlug}`, {
305
+ headers: {
306
+ 'Authorization': `Bearer ${config.apiKey}`,
307
+ 'Accept': 'application/json',
308
+ },
309
+ });
310
+ projectSlug = checkResponse.data.slug || checkResponse.data.name;
311
+ }
312
+ catch (error) {
313
+ if (error.response?.status === 404) {
314
+ // For non-production environments, check if we should create
315
+ if (environment !== 'production' && !options.create) {
316
+ console.log(chalk_1.default.yellow(`Project "${projectName}" doesn't have a ${environment} environment.`));
317
+ console.log(chalk_1.default.gray('Use --create to create it, or deploy to production first.'));
318
+ process.exit(1);
319
+ }
320
+ console.log(chalk_1.default.yellow(`Project "${projectName}"${envLabel} not found. Creating...`));
321
+ try {
322
+ const createResponse = await axios_1.default.post(`${config.host}/api/v1/projects`, {
323
+ name: projectName,
324
+ slug: projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-') + (environment !== 'production' ? `-${environment}` : ''),
325
+ environment: environment,
326
+ }, {
327
+ headers: {
328
+ 'Authorization': `Bearer ${config.apiKey}`,
329
+ 'Accept': 'application/json',
330
+ 'Content-Type': 'application/json',
331
+ },
332
+ });
333
+ projectSlug = createResponse.data.slug || createResponse.data.name;
334
+ console.log(chalk_1.default.green(`Project "${projectName}"${envLabel} created.`));
335
+ }
336
+ catch (createError) {
337
+ console.error(chalk_1.default.red('Failed to create project:'), createError.response?.data?.message || createError.message);
338
+ process.exit(1);
339
+ }
340
+ }
341
+ else {
342
+ console.error(chalk_1.default.red('Failed to check project:'), error.response?.data?.message || error.message);
343
+ process.exit(1);
344
+ }
345
+ }
346
+ const zipPath = path_1.default.join(sourceDir, '.steps-deploy.zip');
347
+ const output = fs_1.default.createWriteStream(zipPath);
348
+ const archive = (0, archiver_1.default)('zip', { zlib: { level: 9 } });
349
+ output.on('close', async () => {
350
+ console.log(chalk_1.default.gray(`Zipped ${archive.pointer()} total bytes`));
351
+ try {
352
+ const form = new form_data_1.default();
353
+ form.append('source', fs_1.default.createReadStream(zipPath));
354
+ console.log(chalk_1.default.yellow('Uploading...'));
355
+ await axios_1.default.post(`${config.host}/api/v1/projects/${projectSlug}/deploy`, form, {
356
+ headers: {
357
+ ...form.getHeaders(),
358
+ 'Accept': 'application/json',
359
+ 'Authorization': `Bearer ${config.apiKey}`,
360
+ },
361
+ maxContentLength: Infinity,
362
+ maxBodyLength: Infinity
363
+ });
364
+ console.log(chalk_1.default.green('Deployment successfully queued!'));
365
+ console.log(chalk_1.default.yellow('Waiting for build to complete...\n'));
366
+ // Poll for completion
367
+ let attempts = 0;
368
+ const maxAttempts = 120; // 2 minutes timeout
369
+ let lastLogLength = 0;
370
+ const poll = setInterval(async () => {
371
+ try {
372
+ attempts++;
373
+ const statusRes = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}`, {
374
+ headers: { 'Authorization': `Bearer ${config.apiKey}` }
375
+ });
376
+ const { status, build_log } = statusRes.data;
377
+ // Stream new log content
378
+ if (build_log && build_log.length > lastLogLength) {
379
+ const newContent = build_log.substring(lastLogLength);
380
+ process.stdout.write(chalk_1.default.gray(newContent));
381
+ if (!newContent.endsWith('\n')) {
382
+ process.stdout.write('\n');
383
+ }
384
+ lastLogLength = build_log.length;
385
+ }
386
+ if (status === 'deployed') {
387
+ clearInterval(poll);
388
+ console.log(chalk_1.default.green(`\n✓ Deployed to ${projectSlug}${envLabel}!`));
389
+ // Always sync YAML declarations (registers env vars and their mappings)
390
+ if (yamlConfig) {
391
+ await pushYamlDeclarations(config.host, config.apiKey, projectSlug, yamlConfig);
392
+ }
393
+ // Push .env values if deployEnv is enabled
394
+ if (yamlConfig && isDeployEnvEnabled(yamlConfig)) {
395
+ console.log(chalk_1.default.blue('\nPushing environment variables...'));
396
+ await pushEnvVars(config.host, config.apiKey, projectSlug, sourceDir, environment, yamlConfig);
397
+ }
398
+ fs_1.default.unlinkSync(zipPath);
399
+ process.exit(0);
400
+ }
401
+ else if (status === 'error') {
402
+ clearInterval(poll);
403
+ console.error(chalk_1.default.red('\n✗ Build Failed!'));
404
+ if (build_log) {
405
+ console.log(chalk_1.default.yellow('\n--- Full Build Log ---'));
406
+ console.log(chalk_1.default.gray(build_log));
407
+ console.log(chalk_1.default.yellow('--- End Build Log ---\n'));
408
+ }
409
+ fs_1.default.unlinkSync(zipPath);
410
+ process.exit(1);
411
+ }
412
+ else if (attempts >= maxAttempts) {
413
+ clearInterval(poll);
414
+ console.error(chalk_1.default.red('\nTimeout waiting for build. It might still finish.'));
415
+ fs_1.default.unlinkSync(zipPath);
416
+ process.exit(1);
417
+ }
418
+ }
419
+ catch {
420
+ // Ignore transient errors
421
+ }
422
+ }, 1000);
423
+ }
424
+ catch (error) {
425
+ console.error(chalk_1.default.red('Deployment failed:'));
426
+ if (error.response) {
427
+ if (error.response.status === 404) {
428
+ console.error("Project not found.");
429
+ }
430
+ else {
431
+ console.error(error.response.status, JSON.stringify(error.response.data, null, 2));
432
+ }
433
+ }
434
+ else {
435
+ console.error(error.message);
436
+ }
437
+ if (fs_1.default.existsSync(zipPath))
438
+ fs_1.default.unlinkSync(zipPath);
439
+ process.exit(1);
440
+ }
441
+ });
442
+ archive.on('error', (err) => {
443
+ throw err;
444
+ });
445
+ archive.pipe(output);
446
+ // Glob patterns to ignore
447
+ const ignore = ['node_modules/**', '.git/**', '.steps-deploy.zip', 'dist/**', 'vendor/**', '**/node_modules/**'];
448
+ archive.glob('**/*', {
449
+ cwd: sourceDir,
450
+ ignore: ignore,
451
+ dot: true
452
+ });
453
+ await archive.finalize();
454
+ }
@@ -0,0 +1,87 @@
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.envCreate = envCreate;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const chalk_1 = __importDefault(require("chalk"));
9
+ const init_1 = require("./init");
10
+ async function envCreate(key, value, 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(`Creating global variable "${key}"...`));
17
+ // Build the request body with per-environment values
18
+ const body = {
19
+ key,
20
+ production_value: value,
21
+ is_secret: options.secret || false,
22
+ };
23
+ // Handle staging value and inheritance
24
+ if (options.stagingInherit) {
25
+ body.staging_source = 'inherit_production';
26
+ }
27
+ else if (options.stagingValue !== undefined) {
28
+ body.staging_value = options.stagingValue;
29
+ body.staging_source = 'value';
30
+ }
31
+ // Handle dev value and inheritance
32
+ if (options.devInheritStaging) {
33
+ body.dev_source = 'inherit_staging';
34
+ }
35
+ else if (options.devInherit) {
36
+ body.dev_source = 'inherit_production';
37
+ }
38
+ else if (options.devValue !== undefined) {
39
+ body.dev_value = options.devValue;
40
+ body.dev_source = 'value';
41
+ }
42
+ try {
43
+ await axios_1.default.post(`${config.host}/api/v1/variables`, body, {
44
+ headers: {
45
+ 'Authorization': `Bearer ${config.apiKey}`,
46
+ 'Accept': 'application/json',
47
+ 'Content-Type': 'application/json',
48
+ },
49
+ });
50
+ const typeLabel = options.secret ? 'secret' : 'variable';
51
+ console.log(chalk_1.default.green(`Global ${typeLabel} "${key}" created successfully!`));
52
+ // Show summary of what was set
53
+ console.log(chalk_1.default.gray(` Production: ${options.secret ? '********' : value}`));
54
+ if (options.stagingValue) {
55
+ console.log(chalk_1.default.gray(` Staging: ${options.secret ? '********' : options.stagingValue}`));
56
+ }
57
+ else if (options.stagingInherit) {
58
+ console.log(chalk_1.default.gray(' Staging: (inherits from production)'));
59
+ }
60
+ if (options.devValue) {
61
+ console.log(chalk_1.default.gray(` Dev: ${options.secret ? '********' : options.devValue}`));
62
+ }
63
+ else if (options.devInheritStaging) {
64
+ console.log(chalk_1.default.gray(' Dev: (inherits from staging)'));
65
+ }
66
+ else if (options.devInherit) {
67
+ console.log(chalk_1.default.gray(' Dev: (inherits from production)'));
68
+ }
69
+ }
70
+ catch (error) {
71
+ if (error.response) {
72
+ if (error.response.status === 401) {
73
+ console.error(chalk_1.default.red('Authentication failed. Run "solidactions init <api-key>" to re-configure.'));
74
+ }
75
+ else if (error.response.status === 422) {
76
+ console.error(chalk_1.default.red('Validation error:'), error.response.data.message || error.response.data.errors);
77
+ }
78
+ else {
79
+ console.error(chalk_1.default.red(`Failed: ${error.response.status}`), error.response.data);
80
+ }
81
+ }
82
+ else {
83
+ console.error(chalk_1.default.red('Connection failed:'), error.message);
84
+ }
85
+ process.exit(1);
86
+ }
87
+ }