@boltic/cli 1.0.44-dev1.1 → 1.0.44-dev1.2

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.
@@ -3,7 +3,6 @@ import FormData from "form-data";
3
3
  import fs from "fs";
4
4
  import https from "https";
5
5
  import { handleError } from "../helper/error.js";
6
- import { getSecret } from "../helper/secure-storage.js";
7
6
  import { logApi } from "../helper/verbose.js";
8
7
 
9
8
  const getHttpsAgentForUrl = (baseUrl) => {
@@ -23,33 +22,20 @@ const getHttpsAgentForUrl = (baseUrl) => {
23
22
  return undefined;
24
23
  };
25
24
 
26
- const buildAuthHeaders = async (token, session) => {
27
- const pat = await getSecret("pat");
28
-
29
- // If PAT exists, prefer PAT-based auth and do not send bearer/session
30
- if (pat && pat.trim()) {
31
- return {
32
- "x-boltic-token": pat.trim(),
33
- };
34
- }
35
-
36
- // Fallback to existing Bearer + Cookie auth
37
- const headers = {};
38
- if (token) {
39
- headers.Authorization = `Bearer ${token}`;
40
- }
25
+ // When session is absent the token is a PAT → use x-boltic-token header.
26
+ // When session is present the token is a bearer token → use Authorization + Cookie.
27
+ const buildAuthHeaders = (token, session) => {
41
28
  if (session) {
29
+ const headers = {};
30
+ if (token) headers.Authorization = `Bearer ${token}`;
42
31
  headers.Cookie = session;
32
+ return headers;
43
33
  }
44
- return headers;
34
+ return token ? { "x-boltic-token": token } : {};
45
35
  };
46
36
 
47
- const ensureAuthenticatedOrExit = async (accountId, token, session) => {
48
- const pat = await getSecret("pat");
49
- const hasPatAuth = pat && pat.trim() && accountId;
50
- const hasSessionAuth = token && session && accountId;
51
-
52
- if (!hasPatAuth && !hasSessionAuth) {
37
+ const ensureAuthenticatedOrExit = (accountId, token) => {
38
+ if (!token || !accountId) {
53
39
  console.error(
54
40
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
55
41
  );
@@ -61,8 +47,8 @@ const ensureAuthenticatedOrExit = async (accountId, token, session) => {
61
47
 
62
48
  const getIntegrationGroups = async (apiUrl, accountId, token, session) => {
63
49
  try {
64
- await ensureAuthenticatedOrExit(accountId, token, session);
65
- const authHeaders = await buildAuthHeaders(token, session);
50
+ ensureAuthenticatedOrExit(accountId, token);
51
+ const authHeaders = buildAuthHeaders(token, session);
66
52
  const axiosOptions = {
67
53
  method: "get",
68
54
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integration-groups`,
@@ -87,8 +73,8 @@ const getIntegrationGroups = async (apiUrl, accountId, token, session) => {
87
73
 
88
74
  const listAllIntegrations = async (apiUrl, token, accountId, session) => {
89
75
  try {
90
- await ensureAuthenticatedOrExit(accountId, token, session);
91
- const authHeaders = await buildAuthHeaders(token, session);
76
+ ensureAuthenticatedOrExit(accountId, token);
77
+ const authHeaders = buildAuthHeaders(token, session);
92
78
  const axiosOptions = {
93
79
  method: "get",
94
80
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations`,
@@ -119,8 +105,8 @@ const saveIntegration = async (
119
105
  integration
120
106
  ) => {
121
107
  try {
122
- await ensureAuthenticatedOrExit(accountId, token, session);
123
- const authHeaders = await buildAuthHeaders(token, session);
108
+ ensureAuthenticatedOrExit(accountId, token);
109
+ const authHeaders = buildAuthHeaders(token, session);
124
110
  const response = await axios({
125
111
  method: "post",
126
112
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations`,
@@ -140,8 +126,8 @@ const saveIntegration = async (
140
126
  const editIntegration = async (apiUrl, token, accountId, session, payload) => {
141
127
  const { id } = payload;
142
128
  try {
143
- await ensureAuthenticatedOrExit(accountId, token, session);
144
- const authHeaders = await buildAuthHeaders(token, session);
129
+ ensureAuthenticatedOrExit(accountId, token);
130
+ const authHeaders = buildAuthHeaders(token, session);
145
131
  const response = await axios({
146
132
  method: "post",
147
133
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${id}/edit`,
@@ -167,8 +153,8 @@ const updateIntegration = async (
167
153
  integration
168
154
  ) => {
169
155
  try {
170
- await ensureAuthenticatedOrExit(accountId, token, session);
171
- const authHeaders = await buildAuthHeaders(token, session);
156
+ ensureAuthenticatedOrExit(accountId, token);
157
+ const authHeaders = buildAuthHeaders(token, session);
172
158
  const { id, ...rest } = integration;
173
159
  const response = await axios({
174
160
  method: "patch",
@@ -194,8 +180,8 @@ const getIntegrationById = async (
194
180
  integrationId
195
181
  ) => {
196
182
  try {
197
- await ensureAuthenticatedOrExit(accountId, token, session);
198
- const authHeaders = await buildAuthHeaders(token, session);
183
+ ensureAuthenticatedOrExit(accountId, token);
184
+ const authHeaders = buildAuthHeaders(token, session);
199
185
  const response = await axios({
200
186
  method: "get",
201
187
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${integrationId}`,
@@ -219,8 +205,8 @@ const getAuthenticationByIntegrationId = async (
219
205
  integrationId
220
206
  ) => {
221
207
  try {
222
- await ensureAuthenticatedOrExit(accountId, token, session);
223
- const authHeaders = await buildAuthHeaders(token, session);
208
+ ensureAuthenticatedOrExit(accountId, token);
209
+ const authHeaders = buildAuthHeaders(token, session);
224
210
  const response = await axios({
225
211
  method: "get",
226
212
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}integrations/${integrationId}/authentication`,
@@ -243,14 +229,7 @@ const getWebhooksByIntegrationId = async (
243
229
  session,
244
230
  integrationId
245
231
  ) => {
246
- if (!token || !session || !accountId) {
247
- console.error(
248
- "\x1b[31mError:\x1b[0m Authentication credentials are required."
249
- );
250
- console.log("\n🔹 Please log in first using:");
251
- console.log("\x1b[32m$ boltic login\x1b[0m\n");
252
- process.exit(1); // Exit the CLI with an error code
253
- }
232
+ ensureAuthenticatedOrExit(accountId, token);
254
233
 
255
234
  try {
256
235
  const response = await axios({
@@ -258,8 +237,7 @@ const getWebhooksByIntegrationId = async (
258
237
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}integrations/${integrationId}/webhooks`,
259
238
  headers: {
260
239
  "Content-Type": "application/json",
261
- Authorization: `Bearer ${token}`,
262
- Cookie: session,
240
+ ...buildAuthHeaders(token, session),
263
241
  },
264
242
  httpsAgent: getHttpsAgentForUrl(apiUrl),
265
243
  });
@@ -277,8 +255,8 @@ const getConfigurationByIntegrationId = async (
277
255
  integrationId
278
256
  ) => {
279
257
  try {
280
- await ensureAuthenticatedOrExit(accountId, token, session);
281
- const authHeaders = await buildAuthHeaders(token, session);
258
+ ensureAuthenticatedOrExit(accountId, token);
259
+ const authHeaders = buildAuthHeaders(token, session);
282
260
  const response = await axios({
283
261
  method: "get",
284
262
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}integrations/${integrationId}/configuration`,
@@ -302,8 +280,8 @@ const syncIntegration = async (
302
280
  integration
303
281
  ) => {
304
282
  try {
305
- await ensureAuthenticatedOrExit(accountId, token, session);
306
- const authHeaders = await buildAuthHeaders(token, session);
283
+ ensureAuthenticatedOrExit(accountId, token);
284
+ const authHeaders = buildAuthHeaders(token, session);
307
285
  const response = await axios({
308
286
  method: "post",
309
287
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${integration.integration_id}/deploy`,
@@ -328,15 +306,14 @@ const sendIntegrationForReview = async (
328
306
  integration
329
307
  ) => {
330
308
  try {
331
- await ensureAuthenticatedOrExit(accountId, token, session);
309
+ ensureAuthenticatedOrExit(accountId, token);
332
310
  const response = await axios({
333
311
  method: "post",
334
312
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integration-reviews`,
335
313
  data: integration,
336
314
  headers: {
337
315
  "Content-Type": "application/json",
338
- Authorization: `Bearer ${token}`,
339
- Cookie: session,
316
+ ...buildAuthHeaders(token, session),
340
317
  },
341
318
  httpsAgent: getHttpsAgentForUrl(apiUrl),
342
319
  });
@@ -349,8 +326,8 @@ const sendIntegrationForReview = async (
349
326
  const purgeCache = async (apiUrl, token, accountId, session, integration) => {
350
327
  const { integration_id } = integration;
351
328
  try {
352
- await ensureAuthenticatedOrExit(accountId, token, session);
353
- const authHeaders = await buildAuthHeaders(token, session);
329
+ ensureAuthenticatedOrExit(accountId, token);
330
+ const authHeaders = buildAuthHeaders(token, session);
354
331
  const response = await axios({
355
332
  method: "post",
356
333
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${integration_id}/cache`,
@@ -369,8 +346,8 @@ const purgeCache = async (apiUrl, token, accountId, session, integration) => {
369
346
 
370
347
  const pullIntegration = async (apiUrl, token, accountId, session, id) => {
371
348
  try {
372
- await ensureAuthenticatedOrExit(accountId, token, session);
373
- const authHeaders = await buildAuthHeaders(token, session);
349
+ ensureAuthenticatedOrExit(accountId, token);
350
+ const authHeaders = buildAuthHeaders(token, session);
374
351
  const response = await axios({
375
352
  method: "get",
376
353
  url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${id}/pull`,
@@ -399,8 +376,8 @@ const uploadFileToCloud = async (
399
376
  }
400
377
 
401
378
  try {
402
- await ensureAuthenticatedOrExit(accountId, token, session);
403
- const authHeaders = await buildAuthHeaders(token, session);
379
+ ensureAuthenticatedOrExit(accountId, token);
380
+ const authHeaders = buildAuthHeaders(token, session);
404
381
  const form = new FormData();
405
382
  form.append("files", fs.createReadStream(filePath));
406
383
 
package/api/serverless.js CHANGED
@@ -22,6 +22,31 @@ const getHttpsAgentForUrl = (baseUrl) => {
22
22
  return undefined;
23
23
  };
24
24
 
25
+ // When session is absent the token is a PAT → use x-boltic-token header.
26
+ // When session is present the token is a bearer token → use Authorization + Cookie.
27
+ const buildAuthHeaders = (token, session) => {
28
+ if (session) {
29
+ const headers = {};
30
+ if (token) headers.Authorization = `Bearer ${token}`;
31
+ headers.Cookie = session;
32
+ return headers;
33
+ }
34
+ return token ? { "x-boltic-token": token } : {};
35
+ };
36
+
37
+ // Returns true when the user has sufficient credentials for any auth method.
38
+ // Pass accountId when the endpoint requires it; omit (undefined) when it doesn't.
39
+ // Passing null means "required but missing" → returns false.
40
+ const isAuthenticated = (token, session, accountId) => {
41
+ if (accountId !== undefined) {
42
+ // PAT auth: token + accountId (no session needed)
43
+ // Bearer auth: token + session + accountId
44
+ return !!(token && accountId);
45
+ }
46
+ // For endpoints that don't need accountId: PAT needs just token, bearer needs token+session
47
+ return !!token;
48
+ };
49
+
25
50
  const listAllServerless = async (
26
51
  apiUrl,
27
52
  token,
@@ -29,7 +54,7 @@ const listAllServerless = async (
29
54
  session,
30
55
  query = null
31
56
  ) => {
32
- if (!token || !session || !accountId) {
57
+ if (!isAuthenticated(token, session, accountId)) {
33
58
  console.error(
34
59
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
35
60
  );
@@ -56,8 +81,7 @@ const listAllServerless = async (
56
81
  params,
57
82
  headers: {
58
83
  "Content-Type": "application/json",
59
- Authorization: `Bearer ${token}`,
60
- Cookie: session,
84
+ ...buildAuthHeaders(token, session),
61
85
  },
62
86
  httpsAgent: getHttpsAgentForUrl(apiUrl),
63
87
  };
@@ -75,7 +99,7 @@ const listAllServerless = async (
75
99
  };
76
100
 
77
101
  const pullServerless = async (apiUrl, token, accountId, session, id) => {
78
- if (!token || !session || !accountId) {
102
+ if (!isAuthenticated(token, session, accountId)) {
79
103
  console.error(
80
104
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
81
105
  );
@@ -91,8 +115,7 @@ const pullServerless = async (apiUrl, token, accountId, session, id) => {
91
115
  url,
92
116
  headers: {
93
117
  "Content-Type": "application/json",
94
- Authorization: `Bearer ${token}`,
95
- Cookie: session,
118
+ ...buildAuthHeaders(token, session),
96
119
  },
97
120
  httpsAgent: getHttpsAgentForUrl(apiUrl),
98
121
  });
@@ -104,7 +127,7 @@ const pullServerless = async (apiUrl, token, accountId, session, id) => {
104
127
  };
105
128
 
106
129
  const publishServerless = async (apiUrl, token, session, payload) => {
107
- if (!token || !session) {
130
+ if (!isAuthenticated(token, session)) {
108
131
  console.error(
109
132
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
110
133
  );
@@ -119,8 +142,7 @@ const publishServerless = async (apiUrl, token, session, payload) => {
119
142
  url: `${apiUrl}/service/panel/serverless/v1.0/apps`,
120
143
  headers: {
121
144
  "Content-Type": "application/json",
122
- Authorization: `Bearer ${token}`,
123
- Cookie: session,
145
+ ...buildAuthHeaders(token, session),
124
146
  },
125
147
  data: payload,
126
148
  httpsAgent: getHttpsAgentForUrl(apiUrl),
@@ -142,7 +164,7 @@ const updateServerless = async (
142
164
  serverlessId,
143
165
  payload
144
166
  ) => {
145
- if (!token || !session) {
167
+ if (!isAuthenticated(token, session)) {
146
168
  console.error(
147
169
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
148
170
  );
@@ -157,8 +179,7 @@ const updateServerless = async (
157
179
  url: `${apiUrl}/service/panel/serverless/v1.0/apps/${serverlessId}`,
158
180
  headers: {
159
181
  "Content-Type": "application/json",
160
- Authorization: `Bearer ${token}`,
161
- Cookie: session,
182
+ ...buildAuthHeaders(token, session),
162
183
  },
163
184
  data: payload,
164
185
  httpsAgent: getHttpsAgentForUrl(apiUrl),
@@ -181,7 +202,7 @@ const getServerlessBuilds = async (
181
202
  serverlessId,
182
203
  options = {}
183
204
  ) => {
184
- if (!token || !session || !accountId) {
205
+ if (!isAuthenticated(token, session, accountId)) {
185
206
  console.error(
186
207
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
187
208
  );
@@ -201,8 +222,7 @@ const getServerlessBuilds = async (
201
222
  },
202
223
  headers: {
203
224
  "Content-Type": "application/json",
204
- Authorization: `Bearer ${token}`,
205
- Cookie: session,
225
+ ...buildAuthHeaders(token, session),
206
226
  },
207
227
  httpsAgent: getHttpsAgentForUrl(apiUrl),
208
228
  };
@@ -223,7 +243,7 @@ const getServerlessLogs = async (
223
243
  serverlessId,
224
244
  options = {}
225
245
  ) => {
226
- if (!token || !session || !accountId) {
246
+ if (!isAuthenticated(token, session, accountId)) {
227
247
  console.error(
228
248
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
229
249
  );
@@ -252,8 +272,7 @@ const getServerlessLogs = async (
252
272
  params,
253
273
  headers: {
254
274
  "Content-Type": "application/json",
255
- Authorization: `Bearer ${token}`,
256
- Cookie: session,
275
+ ...buildAuthHeaders(token, session),
257
276
  },
258
277
  httpsAgent: getHttpsAgentForUrl(apiUrl),
259
278
  };
@@ -274,7 +293,7 @@ const getBuildLogs = async (
274
293
  serverlessId,
275
294
  buildId
276
295
  ) => {
277
- if (!token || !session || !accountId) {
296
+ if (!isAuthenticated(token, session, accountId)) {
278
297
  console.error(
279
298
  "\x1b[31mError:\x1b[0m Authentication credentials are required."
280
299
  );
@@ -294,8 +313,7 @@ const getBuildLogs = async (
294
313
  },
295
314
  headers: {
296
315
  "Content-Type": "application/json",
297
- Authorization: `Bearer ${token}`,
298
- Cookie: session,
316
+ ...buildAuthHeaders(token, session),
299
317
  },
300
318
  httpsAgent: getHttpsAgentForUrl(apiUrl),
301
319
  };
package/commands/login.js CHANGED
@@ -234,17 +234,17 @@ async function handlePatLogin(patFromArg, accountIdFromArg) {
234
234
  // If both values are provided via CLI flags, do not prompt at all.
235
235
  if (pat && accountId) {
236
236
  try {
237
- await storeSecret("pat", pat);
237
+ await storeSecret("token", pat);
238
238
  await storeSecret("account_id", accountId);
239
239
  console.log(
240
240
  chalk.green(
241
- "\n✅ PAT token and Account ID stored securely. They will be used for future organization-related requests.\n"
241
+ "\n✅ Token and Account ID stored securely. They will be used for future organization-related requests.\n"
242
242
  )
243
243
  );
244
244
  } catch (error) {
245
245
  console.error(
246
246
  chalk.red(
247
- `\n❌ Failed to store PAT credentials: ${error.message || error}\n`
247
+ `\n❌ Failed to store credentials: ${error.message || error}\n`
248
248
  )
249
249
  );
250
250
  }
@@ -271,7 +271,7 @@ async function handlePatLogin(patFromArg, accountIdFromArg) {
271
271
  }
272
272
 
273
273
  try {
274
- await storeSecret("pat", pat);
274
+ await storeSecret("token", pat);
275
275
  await storeSecret("account_id", accountId);
276
276
  console.log(
277
277
  chalk.green(
@@ -267,7 +267,7 @@ async function handleCreate(args = []) {
267
267
  */
268
268
  async function checkServerlessExists(name) {
269
269
  const env = await getCurrentEnv();
270
- if (!env || !env.token || !env.session) {
270
+ if (!env || !env.token) {
271
271
  return null; // Can't check without auth, let the create call handle auth error
272
272
  }
273
273
 
@@ -379,7 +379,7 @@ async function handleCodeTypeCreate(
379
379
 
380
380
  // Get authentication credentials
381
381
  const env = await getCurrentEnv();
382
- if (!env || !env.token || !env.session) {
382
+ if (!env || !env.token) {
383
383
  console.error(chalk.red("\n❌ Not authenticated. Please login first."));
384
384
  console.log(chalk.yellow(" Run: boltic login"));
385
385
  return;
@@ -523,7 +523,7 @@ async function handleGitTypeCreate(
523
523
 
524
524
  // Get authentication credentials first
525
525
  const env = await getCurrentEnv();
526
- if (!env || !env.token || !env.session) {
526
+ if (!env || !env.token) {
527
527
  console.error(chalk.red("\n❌ Not authenticated. Please login first."));
528
528
  console.log(chalk.yellow(" Run: boltic login"));
529
529
  // Cleanup the created directory
@@ -2350,7 +2350,7 @@ async function handleStatus(args = []) {
2350
2350
  lastStatus = status;
2351
2351
  } else if (iteration % 3 === 0) {
2352
2352
  // Show a dot every 3 iterations to indicate it's still polling
2353
- process.stdout.write(chalk.dim(".\n"));
2353
+ process.stdout.write(chalk.dim("."));
2354
2354
  }
2355
2355
 
2356
2356
  // Check if we've reached a terminal state
@@ -2,8 +2,21 @@ import keytar from "keytar";
2
2
 
3
3
  const SERVICE_NAME = "boltic-cli";
4
4
 
5
+ // Mapping from credential keys to environment variable names.
6
+ // In CI/headless environments where keytar (OS keychain) is unavailable,
7
+ // these env vars are used as a fallback so commands like `boltic serverless publish`
8
+ // work without an interactive keychain daemon.
9
+ const ENV_VAR_MAP = {
10
+ token: "BOLTIC_TOKEN",
11
+ account_id: "BOLTIC_ACCOUNT_ID",
12
+ session: "BOLTIC_SESSION",
13
+ environment: "BOLTIC_ENVIRONMENT",
14
+ };
15
+
5
16
  /**
6
- * Store a secret value securely using keytar
17
+ * Store a secret value securely using keytar.
18
+ * In CI/headless environments where keytar is unavailable, logs a warning
19
+ * instead of throwing so that env-var-based auth can still be used.
7
20
  * @param {string} key - The key under which to store the secret
8
21
  * @param {string} value - The secret value to store
9
22
  * @returns {Promise<void>}
@@ -12,23 +25,31 @@ export const storeSecret = async (key, value) => {
12
25
  try {
13
26
  await keytar.setPassword(SERVICE_NAME, key, value);
14
27
  } catch (error) {
15
- console.error(`Error storing secret for ${key}:`, error.message);
16
- throw error;
28
+ // In headless/CI environments the OS keychain daemon is not available.
29
+ // Warn instead of throwing so callers can still rely on env var fallback.
30
+ console.warn(
31
+ `Warning: Could not store '${key}' in system keychain: ${error.message}`
32
+ );
33
+ console.warn(
34
+ `In CI environments, set credentials via environment variables (e.g. BOLTIC_TOKEN, BOLTIC_ACCOUNT_ID).`
35
+ );
17
36
  }
18
37
  };
19
38
 
20
39
  /**
21
- * Retrieve a secret value using keytar
40
+ * Retrieve a secret value. Tries keytar first; falls back to env vars
41
+ * (BOLTIC_TOKEN, BOLTIC_PAT, BOLTIC_ACCOUNT_ID, etc.) when keytar is unavailable.
22
42
  * @param {string} key - The key of the secret to retrieve
23
43
  * @returns {Promise<string|null>} The secret value or null if not found
24
44
  */
25
45
  export const getSecret = async (key) => {
26
46
  try {
27
- return await keytar.getPassword(SERVICE_NAME, key);
28
- } catch (error) {
29
- console.error(`Error retrieving secret for ${key}:`, error.message);
30
- return null;
47
+ const val = await keytar.getPassword(SERVICE_NAME, key);
48
+ if (val !== null) return val;
49
+ } catch {
50
+ // keytar unavailable (CI/headless) — fall through to env var
31
51
  }
52
+ return process.env[ENV_VAR_MAP[key]] || null;
32
53
  };
33
54
 
34
55
  /**
@@ -46,17 +67,26 @@ export const deleteSecret = async (key) => {
46
67
  };
47
68
 
48
69
  /**
49
- * Retrieve all secrets stored using keytar
50
- * @returns {Promise<Array<{account: string, password: string}>|null>} An array of secret objects or null if an error occurs
70
+ * Retrieve all secrets. Tries keytar first; falls back to env vars when
71
+ * keytar is unavailable (e.g. GitHub Actions, Docker containers).
72
+ * @returns {Promise<Array<{account: string, password: string}>|null>}
51
73
  */
52
-
53
74
  export const getAllSecrets = async () => {
54
75
  try {
55
- return await keytar.findCredentials(SERVICE_NAME);
56
- } catch (error) {
57
- console.error(`Error retrieving all secrets:`, error.message);
58
- return null;
76
+ const keytarSecrets = await keytar.findCredentials(SERVICE_NAME);
77
+ if (keytarSecrets && keytarSecrets.length > 0) return keytarSecrets;
78
+ } catch {
79
+ // keytar unavailable (CI/headless) — fall through to env vars
80
+ }
81
+
82
+ // Build credential list from env vars
83
+ const secrets = [];
84
+ for (const [key, envVar] of Object.entries(ENV_VAR_MAP)) {
85
+ if (process.env[envVar]) {
86
+ secrets.push({ account: key, password: process.env[envVar] });
87
+ }
59
88
  }
89
+ return secrets.length > 0 ? secrets : null;
60
90
  };
61
91
 
62
92
  export const deleteAllSecrets = async () => {
@@ -14,10 +14,10 @@ import ora from "ora";
14
14
  // Supported languages and their versions
15
15
  export const SUPPORTED_LANGUAGES = ["nodejs", "python", "golang", "java"];
16
16
  export const LANGUAGE_VERSIONS = {
17
- nodejs: "24",
17
+ nodejs: "20",
18
18
  python: "3",
19
- golang: "1.24",
20
- java: "21",
19
+ golang: "1.22",
20
+ java: "17",
21
21
  };
22
22
 
23
23
  // Handler mapping per language
@@ -935,7 +935,7 @@ public class AutogenIndex {
935
935
  function getGoModContent(appName) {
936
936
  return `module ${appName}
937
937
 
938
- go 1.24
938
+ go 1.22
939
939
  `;
940
940
  }
941
941
 
@@ -963,7 +963,7 @@ function getJavaPomXmlContent(appName) {
963
963
  <description>Boltic Serverless Function</description>
964
964
 
965
965
  <properties>
966
- <java.version>21</java.version>
966
+ <java.version>17</java.version>
967
967
  </properties>
968
968
 
969
969
  <dependencies>
@@ -1440,7 +1440,7 @@ export function displayPublishSuccessMessage(name, response) {
1440
1440
  export function getPulledBolticYamlContent(serverlessData) {
1441
1441
  const config = serverlessData.Config;
1442
1442
  const runtime = config.Runtime || "code";
1443
- const language = config.CodeOpts?.Language || "nodejs/24";
1443
+ const language = config.CodeOpts?.Language || "nodejs/20";
1444
1444
  const handler =
1445
1445
  HANDLER_MAPPING[language.split("/")[0]] || "handler.handler";
1446
1446
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boltic/cli",
3
- "version": "1.0.44-dev1.1",
3
+ "version": "1.0.44-dev1.2",
4
4
  "description": "Professional CLI for interacting with the Boltic platform — create, manage, and publish integrations, serverless functions, workflows, MCPs, and more with enterprise-grade features and a seamless developer experience",
5
5
  "main": "index.js",
6
6
  "bin": {