@solidactions/cli 0.7.0 → 0.7.3

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.
@@ -11,8 +11,8 @@ const axios_1 = __importDefault(require("axios"));
11
11
  const form_data_1 = __importDefault(require("form-data"));
12
12
  const chalk_1 = __importDefault(require("chalk"));
13
13
  const js_yaml_1 = __importDefault(require("js-yaml"));
14
- const init_1 = require("./init");
15
14
  const env_1 = require("../utils/env");
15
+ const api_1 = require("../utils/api");
16
16
  /**
17
17
  * Validate project structure before deployment.
18
18
  * Checks for required files and SDK dependency.
@@ -89,7 +89,7 @@ function validateProject(sourceDir) {
89
89
  * Push YAML env declarations to the project.
90
90
  * This registers all YAML-declared vars and their mappings.
91
91
  */
92
- async function pushYamlDeclarations(host, apiKey, projectSlug, yamlConfig) {
92
+ async function pushYamlDeclarations(config, projectSlug, yamlConfig) {
93
93
  const parsedVars = (0, env_1.parseYamlEnvVars)(yamlConfig);
94
94
  if (parsedVars.length === 0) {
95
95
  return;
@@ -102,12 +102,8 @@ async function pushYamlDeclarations(host, apiKey, projectSlug, yamlConfig) {
102
102
  source: 'yaml',
103
103
  }));
104
104
  try {
105
- await axios_1.default.post(`${host}/api/v1/projects/${projectSlug}/variable-mappings/sync-yaml`, { declarations }, {
106
- headers: {
107
- 'Authorization': `Bearer ${apiKey}`,
108
- 'Accept': 'application/json',
109
- 'Content-Type': 'application/json',
110
- },
105
+ await axios_1.default.post(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings/sync-yaml`, { declarations }, {
106
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
111
107
  });
112
108
  console.log(chalk_1.default.gray(`Synced ${declarations.length} YAML env declarations`));
113
109
  }
@@ -116,11 +112,7 @@ async function pushYamlDeclarations(host, apiKey, projectSlug, yamlConfig) {
116
112
  }
117
113
  }
118
114
  async function deploy(projectName, sourcePath, options = {}) {
119
- const config = (0, init_1.getConfig)();
120
- if (!config?.apiKey) {
121
- console.error(chalk_1.default.red('Not initialized. Run `solidactions init <api-key>` first.'));
122
- process.exit(1);
123
- }
115
+ const config = await (0, api_1.requireConfigWithWorkspace)();
124
116
  const sourceDir = sourcePath ? path_1.default.resolve(sourcePath) : process.cwd();
125
117
  const environment = options.env || 'dev';
126
118
  if (!fs_1.default.existsSync(sourceDir)) {
@@ -165,10 +157,7 @@ async function deploy(projectName, sourcePath, options = {}) {
165
157
  ? projectName
166
158
  : `${projectName}-${environment}`;
167
159
  const checkResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${lookupSlug}`, {
168
- headers: {
169
- 'Authorization': `Bearer ${config.apiKey}`,
170
- 'Accept': 'application/json',
171
- },
160
+ headers: (0, api_1.getApiHeaders)(config),
172
161
  });
173
162
  projectSlug = checkResponse.data.slug || checkResponse.data.name;
174
163
  }
@@ -187,11 +176,7 @@ async function deploy(projectName, sourcePath, options = {}) {
187
176
  slug: projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-') + (environment !== 'production' ? `-${environment}` : ''),
188
177
  environment: environment,
189
178
  }, {
190
- headers: {
191
- 'Authorization': `Bearer ${config.apiKey}`,
192
- 'Accept': 'application/json',
193
- 'Content-Type': 'application/json',
194
- },
179
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
195
180
  });
196
181
  projectSlug = createResponse.data.slug || createResponse.data.name;
197
182
  console.log(chalk_1.default.green(`Project "${projectName}"${envLabel} created.`));
@@ -218,8 +203,7 @@ async function deploy(projectName, sourcePath, options = {}) {
218
203
  await axios_1.default.post(`${config.host}/api/v1/projects/${projectSlug}/deploy`, form, {
219
204
  headers: {
220
205
  ...form.getHeaders(),
221
- 'Accept': 'application/json',
222
- 'Authorization': `Bearer ${config.apiKey}`,
206
+ ...(0, api_1.getApiHeaders)(config),
223
207
  },
224
208
  maxContentLength: Infinity,
225
209
  maxBodyLength: Infinity
@@ -228,13 +212,13 @@ async function deploy(projectName, sourcePath, options = {}) {
228
212
  console.log(chalk_1.default.yellow('Waiting for build to complete...\n'));
229
213
  // Poll for completion
230
214
  let attempts = 0;
231
- const maxAttempts = 120; // 2 minutes timeout
215
+ const maxAttempts = 600; // 10 minutes of inactivity timeout
232
216
  let lastLogLength = 0;
233
217
  const poll = setInterval(async () => {
234
218
  try {
235
219
  attempts++;
236
220
  const statusRes = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}`, {
237
- headers: { 'Authorization': `Bearer ${config.apiKey}` }
221
+ headers: (0, api_1.getApiHeaders)(config),
238
222
  });
239
223
  const { status, build_log } = statusRes.data;
240
224
  // Stream new log content
@@ -245,13 +229,18 @@ async function deploy(projectName, sourcePath, options = {}) {
245
229
  process.stdout.write('\n');
246
230
  }
247
231
  lastLogLength = build_log.length;
232
+ attempts = 0; // Reset timeout — build is still progressing
233
+ }
234
+ // Periodic waiting indicator when no logs yet
235
+ if (attempts > 0 && attempts % 15 === 0 && lastLogLength === 0) {
236
+ console.log(chalk_1.default.gray('Still waiting for build to start...'));
248
237
  }
249
238
  if (status === 'deployed') {
250
239
  clearInterval(poll);
251
240
  console.log(chalk_1.default.green(`\n✓ Deployed to ${projectSlug}${envLabel}!`));
252
241
  // Always sync YAML declarations (registers env vars and their mappings)
253
242
  if (yamlConfig) {
254
- await pushYamlDeclarations(config.host, config.apiKey, projectSlug, yamlConfig);
243
+ await pushYamlDeclarations(config, projectSlug, yamlConfig);
255
244
  }
256
245
  fs_1.default.unlinkSync(archivePath);
257
246
  process.exit(0);
@@ -7,13 +7,9 @@ exports.envDelete = envDelete;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
9
  const prompts_1 = __importDefault(require("prompts"));
10
- const init_1 = require("./init");
10
+ const api_1 = require("../utils/api");
11
11
  async function envDelete(keyOrProject, keyIfProject, options = {}) {
12
- const config = (0, init_1.getConfig)();
13
- if (!config?.apiKey) {
14
- console.error(chalk_1.default.red('Not initialized. Run "solidactions init <api-key>" first.'));
15
- process.exit(1);
16
- }
12
+ const config = await (0, api_1.requireConfigWithWorkspace)();
17
13
  // Determine mode: if keyIfProject is provided, it's project mapping delete
18
14
  const isProjectMode = keyIfProject !== undefined;
19
15
  const projectName = isProjectMode ? keyOrProject : undefined;
@@ -24,10 +20,7 @@ async function envDelete(keyOrProject, keyIfProject, options = {}) {
24
20
  console.log(chalk_1.default.blue(`Deleting variable mapping "${key}" from project "${projectName}"...`));
25
21
  // First, get the mapping to find its ID
26
22
  const listResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/variable-mappings`, {
27
- headers: {
28
- 'Authorization': `Bearer ${config.apiKey}`,
29
- 'Accept': 'application/json',
30
- },
23
+ headers: (0, api_1.getApiHeaders)(config),
31
24
  });
32
25
  const mappings = listResponse.data || [];
33
26
  const mapping = mappings.find((m) => m.env_name === key);
@@ -52,10 +45,7 @@ async function envDelete(keyOrProject, keyIfProject, options = {}) {
52
45
  }
53
46
  // Delete the mapping
54
47
  await axios_1.default.delete(`${config.host}/api/v1/projects/${projectName}/variable-mappings/${mapping.id}`, {
55
- headers: {
56
- 'Authorization': `Bearer ${config.apiKey}`,
57
- 'Accept': 'application/json',
58
- },
48
+ headers: (0, api_1.getApiHeaders)(config),
59
49
  });
60
50
  if (mapping.is_yaml_declared) {
61
51
  console.log(chalk_1.default.green(`Variable mapping "${key}" cleared successfully.`));
@@ -69,10 +59,7 @@ async function envDelete(keyOrProject, keyIfProject, options = {}) {
69
59
  console.log(chalk_1.default.blue(`Deleting global variable "${key}"...`));
70
60
  // First, get the variable to find its ID
71
61
  const listResponse = await axios_1.default.get(`${config.host}/api/v1/variables`, {
72
- headers: {
73
- 'Authorization': `Bearer ${config.apiKey}`,
74
- 'Accept': 'application/json',
75
- },
62
+ headers: (0, api_1.getApiHeaders)(config),
76
63
  });
77
64
  const variables = listResponse.data?.data || [];
78
65
  const variable = variables.find((v) => v.key === key);
@@ -95,10 +82,7 @@ async function envDelete(keyOrProject, keyIfProject, options = {}) {
95
82
  }
96
83
  // Delete the variable
97
84
  await axios_1.default.delete(`${config.host}/api/v1/variables/${variable.id}`, {
98
- headers: {
99
- 'Authorization': `Bearer ${config.apiKey}`,
100
- 'Accept': 'application/json',
101
- },
85
+ headers: (0, api_1.getApiHeaders)(config),
102
86
  });
103
87
  console.log(chalk_1.default.green(`Global variable "${key}" deleted successfully.`));
104
88
  }
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.envList = envList;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  /**
11
11
  * Format a value for display, with inherited badge if applicable.
12
12
  */
@@ -27,11 +27,7 @@ function formatEnvValue(value, source, isSecret) {
27
27
  return displayValue;
28
28
  }
29
29
  async function envList(projectName, options = {}) {
30
- const config = (0, init_1.getConfig)();
31
- if (!config?.apiKey) {
32
- console.error(chalk_1.default.red('Not initialized. Run "solidactions init <api-key>" first.'));
33
- process.exit(1);
34
- }
30
+ const config = await (0, api_1.requireConfigWithWorkspace)();
35
31
  try {
36
32
  if (projectName) {
37
33
  // List project variable mappings
@@ -41,10 +37,7 @@ async function envList(projectName, options = {}) {
41
37
  : `${projectName}-${environment}`;
42
38
  console.log(chalk_1.default.blue(`Environment variables for project "${projectName}" (${environment}):`));
43
39
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings`, {
44
- headers: {
45
- 'Authorization': `Bearer ${config.apiKey}`,
46
- 'Accept': 'application/json',
47
- },
40
+ headers: (0, api_1.getApiHeaders)(config),
48
41
  });
49
42
  const mappings = response.data || [];
50
43
  if (mappings.length === 0) {
@@ -99,10 +92,7 @@ async function envList(projectName, options = {}) {
99
92
  // List global variables with per-environment values
100
93
  console.log(chalk_1.default.blue('Global environment variables:'));
101
94
  const response = await axios_1.default.get(`${config.host}/api/v1/variables`, {
102
- headers: {
103
- 'Authorization': `Bearer ${config.apiKey}`,
104
- 'Accept': 'application/json',
105
- },
95
+ headers: (0, api_1.getApiHeaders)(config),
106
96
  });
107
97
  const variables = response.data?.data || [];
108
98
  if (variables.length === 0) {
@@ -6,21 +6,14 @@ 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 init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function envMap(projectName, projectKey, globalKey) {
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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  console.log(chalk_1.default.blue(`Mapping global variable "${globalKey}" to project key "${projectKey}" in "${projectName}"...`));
17
13
  try {
18
14
  // First, get the global variable ID by key
19
15
  const varsResponse = await axios_1.default.get(`${config.host}/api/v1/variables`, {
20
- headers: {
21
- 'Authorization': `Bearer ${config.apiKey}`,
22
- 'Accept': 'application/json',
23
- },
16
+ headers: (0, api_1.getApiHeaders)(config),
24
17
  });
25
18
  const variables = varsResponse.data.data || varsResponse.data;
26
19
  const globalVar = variables.find((v) => v.key === globalKey);
@@ -34,11 +27,7 @@ async function envMap(projectName, projectKey, globalKey) {
34
27
  project_key: projectKey,
35
28
  global_variable_id: globalVar.id,
36
29
  }, {
37
- headers: {
38
- 'Authorization': `Bearer ${config.apiKey}`,
39
- 'Accept': 'application/json',
40
- 'Content-Type': 'application/json',
41
- },
30
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
42
31
  });
43
32
  console.log(chalk_1.default.green(`Variable mapping created!`));
44
33
  console.log(chalk_1.default.gray(` ${globalKey} -> ${projectKey} (in ${projectName})`));
@@ -9,7 +9,7 @@ const path_1 = __importDefault(require("path"));
9
9
  const axios_1 = __importDefault(require("axios"));
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const readline_1 = __importDefault(require("readline"));
12
- const init_1 = require("./init");
12
+ const api_1 = require("../utils/api");
13
13
  /**
14
14
  * Prompt the user for confirmation (unless --yes flag is set).
15
15
  */
@@ -26,11 +26,7 @@ async function confirm(message) {
26
26
  });
27
27
  }
28
28
  async function envPull(projectName, options = {}) {
29
- const config = (0, init_1.getConfig)();
30
- if (!config?.apiKey) {
31
- console.error(chalk_1.default.red('Not initialized. Run "solidactions init <api-key>" first.'));
32
- process.exit(1);
33
- }
29
+ const config = await (0, api_1.requireConfigWithWorkspace)();
34
30
  const environment = options.env || 'dev';
35
31
  // Build the project slug for lookup
36
32
  const projectSlug = environment === 'production'
@@ -43,10 +39,7 @@ async function envPull(projectName, options = {}) {
43
39
  try {
44
40
  // First, check if there are any secrets
45
41
  const checkResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings?resolve_oauth=true`, {
46
- headers: {
47
- 'Authorization': `Bearer ${config.apiKey}`,
48
- 'Accept': 'application/json',
49
- },
42
+ headers: (0, api_1.getApiHeaders)(config),
50
43
  });
51
44
  const mappings = checkResponse.data || [];
52
45
  if (mappings.length === 0) {
@@ -65,10 +58,7 @@ async function envPull(projectName, options = {}) {
65
58
  }
66
59
  // Now fetch with reveal=true to get actual values
67
60
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings?reveal=true&resolve_oauth=true`, {
68
- headers: {
69
- 'Authorization': `Bearer ${config.apiKey}`,
70
- 'Accept': 'application/json',
71
- },
61
+ headers: (0, api_1.getApiHeaders)(config),
72
62
  });
73
63
  const variables = response.data || [];
74
64
  // --update-oauth: Only pull OAuth tokens and merge into existing .env
@@ -9,14 +9,10 @@ const path_1 = __importDefault(require("path"));
9
9
  const axios_1 = __importDefault(require("axios"));
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const prompts_1 = __importDefault(require("prompts"));
12
- const init_1 = require("./init");
12
+ const api_1 = require("../utils/api");
13
13
  const env_1 = require("../utils/env");
14
14
  async function envPush(projectName, sourcePath, options = {}) {
15
- const config = (0, init_1.getConfig)();
16
- if (!config?.apiKey) {
17
- console.error(chalk_1.default.red('Not initialized. Run "solidactions init <api-key>" first.'));
18
- process.exit(1);
19
- }
15
+ const config = await (0, api_1.requireConfigWithWorkspace)();
20
16
  const sourceDir = path_1.default.resolve(sourcePath);
21
17
  const environment = options.env || 'dev';
22
18
  // Read solidactions.yaml
@@ -85,10 +81,7 @@ async function envPush(projectName, sourcePath, options = {}) {
85
81
  let serverMappings = [];
86
82
  try {
87
83
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings`, {
88
- headers: {
89
- 'Authorization': `Bearer ${config.apiKey}`,
90
- 'Accept': 'application/json',
91
- },
84
+ headers: (0, api_1.getApiHeaders)(config),
92
85
  });
93
86
  serverMappings = response.data || [];
94
87
  }
@@ -191,11 +184,7 @@ async function envPush(projectName, sourcePath, options = {}) {
191
184
  }));
192
185
  try {
193
186
  const response = await axios_1.default.post(`${config.host}/api/v1/projects/${projectSlug}/variable-mappings/bulk`, { variables }, {
194
- headers: {
195
- 'Authorization': `Bearer ${config.apiKey}`,
196
- 'Accept': 'application/json',
197
- 'Content-Type': 'application/json',
198
- },
187
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
199
188
  });
200
189
  const { created, updated } = response.data;
201
190
  console.log(chalk_1.default.green(`\n✓ Pushed ${toPush.length} variable(s) to ${projectSlug}`));
@@ -6,13 +6,9 @@ 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 init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function envSet(keyOrProject, valueOrKey, valueIfProject, 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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  // Detect mode based on arguments
17
13
  const isProjectMode = valueIfProject !== undefined;
18
14
  if (isProjectMode) {
@@ -36,11 +32,7 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
36
32
  is_secret: isSecret,
37
33
  }]
38
34
  }, {
39
- headers: {
40
- 'Authorization': `Bearer ${config.apiKey}`,
41
- 'Accept': 'application/json',
42
- 'Content-Type': 'application/json',
43
- },
35
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
44
36
  });
45
37
  const { created, updated } = response.data;
46
38
  const action = created > 0 ? 'created' : 'updated';
@@ -100,31 +92,20 @@ async function envSet(keyOrProject, valueOrKey, valueIfProject, options = {}) {
100
92
  try {
101
93
  // Check if variable already exists
102
94
  const getResponse = await axios_1.default.get(`${config.host}/api/v1/variables`, {
103
- headers: {
104
- 'Authorization': `Bearer ${config.apiKey}`,
105
- 'Accept': 'application/json',
106
- },
95
+ headers: (0, api_1.getApiHeaders)(config),
107
96
  });
108
97
  const variables = getResponse.data?.data || [];
109
98
  const existing = variables.find((v) => v.key === key);
110
99
  let action;
111
100
  if (existing) {
112
101
  await axios_1.default.put(`${config.host}/api/v1/variables/${existing.id}`, body, {
113
- headers: {
114
- 'Authorization': `Bearer ${config.apiKey}`,
115
- 'Accept': 'application/json',
116
- 'Content-Type': 'application/json',
117
- },
102
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
118
103
  });
119
104
  action = 'updated';
120
105
  }
121
106
  else {
122
107
  await axios_1.default.post(`${config.host}/api/v1/variables`, body, {
123
- headers: {
124
- 'Authorization': `Bearer ${config.apiKey}`,
125
- 'Accept': 'application/json',
126
- 'Content-Type': 'application/json',
127
- },
108
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
128
109
  });
129
110
  action = 'created';
130
111
  }
@@ -13,6 +13,8 @@ const fs_1 = __importDefault(require("fs"));
13
13
  const path_1 = __importDefault(require("path"));
14
14
  const os_1 = __importDefault(require("os"));
15
15
  const chalk_1 = __importDefault(require("chalk"));
16
+ const api_1 = require("../utils/api");
17
+ const workspaces_1 = require("./workspaces");
16
18
  const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.solidactions');
17
19
  const CONFIG_FILE = path_1.default.join(CONFIG_DIR, 'config.json');
18
20
  function getConfig() {
@@ -63,13 +65,27 @@ async function init(apiKey, options) {
63
65
  console.log(chalk_1.default.blue(`Initializing SolidActions CLI...`));
64
66
  console.log(chalk_1.default.gray(`Host: ${host}`));
65
67
  // Save the configuration
66
- saveConfig({
68
+ const config = {
67
69
  host,
68
70
  apiKey: apiKey.trim(),
69
- });
71
+ };
72
+ saveConfig(config);
70
73
  console.log(chalk_1.default.green('CLI initialized successfully!'));
71
74
  console.log(chalk_1.default.gray(`Configuration saved to ${CONFIG_FILE}`));
72
75
  console.log('');
76
+ // Set workspace — explicit flag or interactive prompt
77
+ try {
78
+ if (options.workspace) {
79
+ await (0, workspaces_1.workspaceSet)(options.workspace);
80
+ }
81
+ else {
82
+ await (0, api_1.ensureWorkspaceSelected)(config);
83
+ }
84
+ }
85
+ catch {
86
+ console.log(chalk_1.default.yellow('Could not set workspace. Run `solidactions workspace:set` later.'));
87
+ }
88
+ console.log('');
73
89
  console.log(chalk_1.default.blue('Quick start:'));
74
90
  console.log(chalk_1.default.gray(' solidactions deploy <project-name> Deploy current directory'));
75
91
  console.log(chalk_1.default.gray(' solidactions run <project> <workflow> Run a workflow'));
@@ -6,20 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logsBuild = logsBuild;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function logsBuild(projectName) {
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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  console.log(chalk_1.default.blue(`Fetching build logs for project "${projectName}"...`));
17
13
  try {
18
14
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/build-log`, {
19
- headers: {
20
- 'Authorization': `Bearer ${config.apiKey}`,
21
- 'Accept': 'application/json',
22
- },
15
+ headers: (0, api_1.getApiHeaders)(config),
23
16
  });
24
17
  const buildLog = response.data.build_log || response.data;
25
18
  if (!buildLog || buildLog.length === 0) {
@@ -6,31 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.logs = logs;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  console.log(chalk_1.default.blue(`Fetching logs for run: ${runId}...`));
17
13
  try {
18
14
  // Get the run status first
19
15
  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
- },
16
+ headers: (0, api_1.getApiHeaders)(config),
24
17
  });
25
18
  const runData = runResponse.data;
26
19
  console.log(chalk_1.default.gray(`Status: ${runData.status}`));
27
20
  console.log(chalk_1.default.gray('---'));
28
21
  // Get the logs for this run
29
22
  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
- },
23
+ headers: (0, api_1.getApiHeaders)(config),
34
24
  });
35
25
  const errors = logsResponse.data.errors || [];
36
26
  if (errors.length > 0) {
@@ -46,10 +36,7 @@ async function logs(runId, options) {
46
36
  const pollInterval = setInterval(async () => {
47
37
  try {
48
38
  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
- },
39
+ headers: (0, api_1.getApiHeaders)(config),
53
40
  });
54
41
  const newLogData = refreshResponse.data.logs || '';
55
42
  if (newLogData.length > printed) {
@@ -58,10 +45,7 @@ async function logs(runId, options) {
58
45
  }
59
46
  // Check if run is complete
60
47
  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
- },
48
+ headers: (0, api_1.getApiHeaders)(config),
65
49
  });
66
50
  if (['completed', 'failed', 'acknowledged'].includes(runStatus.data.status)) {
67
51
  clearInterval(pollInterval);
@@ -9,21 +9,14 @@ const path_1 = __importDefault(require("path"));
9
9
  const axios_1 = __importDefault(require("axios"));
10
10
  const chalk_1 = __importDefault(require("chalk"));
11
11
  const adm_zip_1 = __importDefault(require("adm-zip"));
12
- const init_1 = require("./init");
12
+ const api_1 = require("../utils/api");
13
13
  async function pull(projectName, destPath) {
14
- const config = (0, init_1.getConfig)();
15
- if (!config?.apiKey) {
16
- console.error(chalk_1.default.red('Not initialized. Run `solidactions init <api-key>` first.'));
17
- process.exit(1);
18
- }
14
+ const config = await (0, api_1.requireConfigWithWorkspace)();
19
15
  const destination = destPath ? path_1.default.resolve(destPath) : process.cwd();
20
16
  console.log(chalk_1.default.blue(`Pulling project "${projectName}"...`));
21
17
  try {
22
18
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/source`, {
23
- headers: {
24
- 'Authorization': `Bearer ${config.apiKey}`,
25
- 'Accept': 'application/octet-stream',
26
- },
19
+ headers: { ...(0, api_1.getApiHeaders)(config), 'Accept': 'application/octet-stream' },
27
20
  responseType: 'arraybuffer',
28
21
  });
29
22
  const zipBuffer = Buffer.from(response.data);
@@ -6,13 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.run = run;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function run(projectName, workflowName, 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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  const environment = options.env || 'dev';
17
13
  const projectSlug = environment === 'production'
18
14
  ? projectName
@@ -30,11 +26,7 @@ async function run(projectName, workflowName, options) {
30
26
  }
31
27
  try {
32
28
  const response = await axios_1.default.post(`${config.host}/api/v1/projects/${projectSlug}/workflows/${workflowName}/trigger`, { input: inputData }, {
33
- headers: {
34
- 'Authorization': `Bearer ${config.apiKey}`,
35
- 'Accept': 'application/json',
36
- 'Content-Type': 'application/json',
37
- },
29
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
38
30
  });
39
31
  const runData = response.data.run || response.data;
40
32
  console.log(chalk_1.default.green(`Workflow triggered! Run ID: ${runData.id}`));
@@ -46,10 +38,7 @@ async function run(projectName, workflowName, options) {
46
38
  try {
47
39
  attempts++;
48
40
  const statusResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runData.id}`, {
49
- headers: {
50
- 'Authorization': `Bearer ${config.apiKey}`,
51
- 'Accept': 'application/json',
52
- },
41
+ headers: (0, api_1.getApiHeaders)(config),
53
42
  });
54
43
  const status = statusResponse.data.status;
55
44
  if (status === 'completed') {
@@ -6,13 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runs = runs;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function runs(projectName, 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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  const limit = options.limit || 20;
17
13
  console.log(chalk_1.default.blue(projectName ? `Recent runs for "${projectName}":` : 'Recent runs:'));
18
14
  try {
@@ -21,10 +17,7 @@ async function runs(projectName, options = {}) {
21
17
  params.project = projectName;
22
18
  }
23
19
  const response = await axios_1.default.get(`${config.host}/api/v1/runs`, {
24
- headers: {
25
- 'Authorization': `Bearer ${config.apiKey}`,
26
- 'Accept': 'application/json',
27
- },
20
+ headers: (0, api_1.getApiHeaders)(config),
28
21
  params,
29
22
  });
30
23
  const runsList = response.data.data || response.data;
@@ -7,21 +7,14 @@ exports.scheduleDelete = scheduleDelete;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
9
  const prompts_1 = __importDefault(require("prompts"));
10
- const init_1 = require("./init");
10
+ const api_1 = require("../utils/api");
11
11
  async function scheduleDelete(projectName, scheduleId, options = {}) {
12
- const config = (0, init_1.getConfig)();
13
- if (!config?.apiKey) {
14
- console.error(chalk_1.default.red('Not initialized. Run "solidactions init <api-key>" first.'));
15
- process.exit(1);
16
- }
12
+ const config = await (0, api_1.requireConfigWithWorkspace)();
17
13
  console.log(chalk_1.default.blue(`Deleting schedule ${scheduleId} from project "${projectName}"...`));
18
14
  try {
19
15
  // First, get the schedule details for confirmation
20
16
  const listResponse = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/schedules`, {
21
- headers: {
22
- 'Authorization': `Bearer ${config.apiKey}`,
23
- 'Accept': 'application/json',
24
- },
17
+ headers: (0, api_1.getApiHeaders)(config),
25
18
  });
26
19
  const schedules = listResponse.data || [];
27
20
  const schedule = schedules.find((s) => s.id?.toString() === scheduleId);
@@ -44,10 +37,7 @@ async function scheduleDelete(projectName, scheduleId, options = {}) {
44
37
  }
45
38
  // Delete the schedule
46
39
  await axios_1.default.delete(`${config.host}/api/v1/projects/${projectName}/schedules/${scheduleId}`, {
47
- headers: {
48
- 'Authorization': `Bearer ${config.apiKey}`,
49
- 'Accept': 'application/json',
50
- },
40
+ headers: (0, api_1.getApiHeaders)(config),
51
41
  });
52
42
  console.log(chalk_1.default.green(`Schedule ${scheduleId} deleted successfully.`));
53
43
  console.log(chalk_1.default.gray(` Workflow: ${schedule.workflow_name || schedule.workflow_slug}`));
@@ -6,20 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.scheduleList = scheduleList;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function scheduleList(projectName) {
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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  console.log(chalk_1.default.blue(`Schedules for project "${projectName}":`));
17
13
  try {
18
14
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectName}/schedules`, {
19
- headers: {
20
- 'Authorization': `Bearer ${config.apiKey}`,
21
- 'Accept': 'application/json',
22
- },
15
+ headers: (0, api_1.getApiHeaders)(config),
23
16
  });
24
17
  const schedules = response.data || [];
25
18
  if (schedules.length === 0) {
@@ -6,13 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.scheduleSet = scheduleSet;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function scheduleSet(projectName, cron, 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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  console.log(chalk_1.default.blue(`Setting schedule for project "${projectName}"...`));
17
13
  // Parse input JSON if provided
18
14
  let inputData;
@@ -36,11 +32,7 @@ async function scheduleSet(projectName, cron, options) {
36
32
  payload.input = inputData;
37
33
  }
38
34
  await axios_1.default.post(`${config.host}/api/v1/projects/${projectName}/schedules`, payload, {
39
- headers: {
40
- 'Authorization': `Bearer ${config.apiKey}`,
41
- 'Accept': 'application/json',
42
- 'Content-Type': 'application/json',
43
- },
35
+ headers: (0, api_1.getApiHeaders)(config, 'application/json'),
44
36
  });
45
37
  console.log(chalk_1.default.green(`Schedule set successfully!`));
46
38
  console.log(chalk_1.default.gray(` Cron: ${cron}`));
@@ -6,13 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.steps = steps;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function steps(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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  // Flag validation: --follow and --step are incompatible
17
13
  if (options.follow && options.step) {
18
14
  console.error(chalk_1.default.red('Cannot use --follow with --step.'));
@@ -20,10 +16,7 @@ async function steps(runId, options) {
20
16
  }
21
17
  try {
22
18
  const response = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}/steps`, {
23
- headers: {
24
- 'Authorization': `Bearer ${config.apiKey}`,
25
- 'Accept': 'application/json',
26
- },
19
+ headers: (0, api_1.getApiHeaders)(config),
27
20
  });
28
21
  const data = response.data;
29
22
  // --json mode: output raw JSON and exit
@@ -68,16 +61,10 @@ async function steps(runId, options) {
68
61
  const pollInterval = setInterval(async () => {
69
62
  try {
70
63
  const refreshResponse = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}/steps`, {
71
- headers: {
72
- 'Authorization': `Bearer ${config.apiKey}`,
73
- 'Accept': 'application/json',
74
- },
64
+ headers: (0, api_1.getApiHeaders)(config),
75
65
  });
76
66
  const runStatus = await axios_1.default.get(`${config.host}/api/v1/runs/${runId}`, {
77
- headers: {
78
- 'Authorization': `Bearer ${config.apiKey}`,
79
- 'Accept': 'application/json',
80
- },
67
+ headers: (0, api_1.getApiHeaders)(config),
81
68
  });
82
69
  console.clear();
83
70
  displayAllSteps(runId, refreshResponse.data);
@@ -6,13 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.webhookList = webhookList;
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const chalk_1 = __importDefault(require("chalk"));
9
- const init_1 = require("./init");
9
+ const api_1 = require("../utils/api");
10
10
  async function webhookList(projectName, 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
- }
11
+ const config = await (0, api_1.requireConfigWithWorkspace)();
16
12
  const environment = options.env || 'dev';
17
13
  const projectSlug = environment === 'production' ? projectName : `${projectName}-${environment}`;
18
14
  console.log(chalk_1.default.blue(`Webhooks for project "${projectName}"${environment !== 'production' ? ` (${environment})` : ''}:`));
@@ -22,10 +18,7 @@ async function webhookList(projectName, options = {}) {
22
18
  params.show_secrets = 'true';
23
19
  }
24
20
  const response = await axios_1.default.get(`${config.host}/api/v1/projects/${projectSlug}/webhooks`, {
25
- headers: {
26
- 'Authorization': `Bearer ${config.apiKey}`,
27
- 'Accept': 'application/json',
28
- },
21
+ headers: (0, api_1.getApiHeaders)(config),
29
22
  params,
30
23
  });
31
24
  const webhooks = response.data || [];
@@ -0,0 +1,89 @@
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.workspacesList = workspacesList;
7
+ exports.workspaceSet = workspaceSet;
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const chalk_1 = __importDefault(require("chalk"));
10
+ const api_1 = require("../utils/api");
11
+ const init_1 = require("./init");
12
+ async function workspacesList() {
13
+ const config = (0, api_1.requireConfig)();
14
+ try {
15
+ const response = await axios_1.default.get(`${config.host}/api/v1/workspaces`, {
16
+ headers: {
17
+ 'Authorization': `Bearer ${config.apiKey}`,
18
+ 'Accept': 'application/json',
19
+ },
20
+ });
21
+ // API returns { workspaces: { "Org Name": [{ id, name, role, tenant_name, ... }] } }
22
+ const grouped = response.data.workspaces || response.data.data || response.data;
23
+ let hasWorkspaces = false;
24
+ console.log(chalk_1.default.blue(`\nYour workspaces:\n`));
25
+ if (typeof grouped === 'object' && !Array.isArray(grouped)) {
26
+ for (const orgName of Object.keys(grouped)) {
27
+ console.log(` ${chalk_1.default.white(orgName)}`);
28
+ for (const ws of grouped[orgName]) {
29
+ hasWorkspaces = true;
30
+ const current = config.workspaceId === ws.id ? chalk_1.default.green(' ← current') : '';
31
+ console.log(` ${chalk_1.default.white(ws.name)} ${chalk_1.default.gray(`(${ws.role})`)}${current}`);
32
+ console.log(chalk_1.default.gray(` ID: ${ws.id}`));
33
+ }
34
+ }
35
+ }
36
+ else if (Array.isArray(grouped)) {
37
+ for (const ws of grouped) {
38
+ hasWorkspaces = true;
39
+ const current = config.workspaceId === ws.id ? chalk_1.default.green(' ← current') : '';
40
+ console.log(` ${chalk_1.default.white(ws.name)} ${chalk_1.default.gray(`(${ws.org_name || ''}, ${ws.role})`)}${current}`);
41
+ console.log(chalk_1.default.gray(` ID: ${ws.id}`));
42
+ }
43
+ }
44
+ if (!hasWorkspaces) {
45
+ console.log(chalk_1.default.yellow('No workspaces found.'));
46
+ return;
47
+ }
48
+ console.log('');
49
+ }
50
+ catch (error) {
51
+ console.error(chalk_1.default.red('Failed to list workspaces:'), error.response?.data?.message || error.message);
52
+ process.exit(1);
53
+ }
54
+ }
55
+ async function workspaceSet(workspaceId) {
56
+ const config = (0, api_1.requireConfig)();
57
+ // Validate the workspace exists and user has access
58
+ try {
59
+ const response = await axios_1.default.get(`${config.host}/api/v1/workspaces`, {
60
+ headers: {
61
+ 'Authorization': `Bearer ${config.apiKey}`,
62
+ 'Accept': 'application/json',
63
+ },
64
+ });
65
+ // Flatten grouped response
66
+ const grouped = response.data.workspaces || response.data.data || response.data;
67
+ let allWorkspaces = [];
68
+ if (typeof grouped === 'object' && !Array.isArray(grouped)) {
69
+ for (const orgWorkspaces of Object.values(grouped)) {
70
+ allWorkspaces.push(...orgWorkspaces);
71
+ }
72
+ }
73
+ else if (Array.isArray(grouped)) {
74
+ allWorkspaces = grouped;
75
+ }
76
+ const workspace = allWorkspaces.find((w) => w.id === workspaceId || w.slug === workspaceId || w.name === workspaceId);
77
+ if (!workspace) {
78
+ console.error(chalk_1.default.red(`Workspace "${workspaceId}" not found. Run \`solidactions workspaces\` to list available workspaces.`));
79
+ process.exit(1);
80
+ }
81
+ config.workspaceId = workspace.id;
82
+ (0, init_1.saveConfig)(config);
83
+ console.log(chalk_1.default.green(`Workspace set to: ${workspace.name} (${workspace.id})`));
84
+ }
85
+ catch (error) {
86
+ console.error(chalk_1.default.red('Failed to set workspace:'), error.response?.data?.message || error.message);
87
+ process.exit(1);
88
+ }
89
+ }
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ const webhooks_1 = require("./commands/webhooks");
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();
@@ -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
  });
@@ -243,6 +245,22 @@ program
243
245
  (0, webhooks_1.webhookList)(project, options);
244
246
  });
245
247
  // =============================================================================
248
+ // Workspaces
249
+ // =============================================================================
250
+ program
251
+ .command('workspaces')
252
+ .description('List your workspaces across all organizations')
253
+ .action(() => {
254
+ (0, workspaces_1.workspacesList)();
255
+ });
256
+ program
257
+ .command('workspace:set')
258
+ .description('Set the active workspace for CLI operations')
259
+ .argument('<workspace-id>', 'Workspace ID, slug, or name')
260
+ .action((workspaceId) => {
261
+ (0, workspaces_1.workspaceSet)(workspaceId);
262
+ });
263
+ // =============================================================================
246
264
  // AI Helper
247
265
  // =============================================================================
248
266
  program
@@ -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.7.0",
3
+ "version": "0.7.3",
4
4
  "description": "SolidActions CLI - Deploy and manage workflow automation",
5
5
  "main": "dist/index.js",
6
6
  "bin": {