@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.
- package/api/integration.js +37 -60
- package/api/serverless.js +39 -21
- package/commands/login.js +4 -4
- package/commands/serverless.js +4 -4
- package/helper/secure-storage.js +45 -15
- package/helper/serverless.js +6 -6
- package/package.json +1 -1
package/api/integration.js
CHANGED
|
@@ -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
|
-
|
|
27
|
-
|
|
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
|
|
34
|
+
return token ? { "x-boltic-token": token } : {};
|
|
45
35
|
};
|
|
46
36
|
|
|
47
|
-
const ensureAuthenticatedOrExit =
|
|
48
|
-
|
|
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
|
-
|
|
65
|
-
const authHeaders =
|
|
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
|
-
|
|
91
|
-
const authHeaders =
|
|
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
|
-
|
|
123
|
-
const authHeaders =
|
|
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
|
-
|
|
144
|
-
const authHeaders =
|
|
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
|
-
|
|
171
|
-
const authHeaders =
|
|
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
|
-
|
|
198
|
-
const authHeaders =
|
|
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
|
-
|
|
223
|
-
const authHeaders =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
281
|
-
const authHeaders =
|
|
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
|
-
|
|
306
|
-
const authHeaders =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
353
|
-
const authHeaders =
|
|
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
|
-
|
|
373
|
-
const authHeaders =
|
|
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
|
-
|
|
403
|
-
const authHeaders =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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("
|
|
237
|
+
await storeSecret("token", pat);
|
|
238
238
|
await storeSecret("account_id", accountId);
|
|
239
239
|
console.log(
|
|
240
240
|
chalk.green(
|
|
241
|
-
"\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
|
|
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("
|
|
274
|
+
await storeSecret("token", pat);
|
|
275
275
|
await storeSecret("account_id", accountId);
|
|
276
276
|
console.log(
|
|
277
277
|
chalk.green(
|
package/commands/serverless.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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("
|
|
2353
|
+
process.stdout.write(chalk.dim("."));
|
|
2354
2354
|
}
|
|
2355
2355
|
|
|
2356
2356
|
// Check if we've reached a terminal state
|
package/helper/secure-storage.js
CHANGED
|
@@ -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
|
-
|
|
16
|
-
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
50
|
-
*
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 () => {
|
package/helper/serverless.js
CHANGED
|
@@ -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: "
|
|
17
|
+
nodejs: "20",
|
|
18
18
|
python: "3",
|
|
19
|
-
golang: "1.
|
|
20
|
-
java: "
|
|
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.
|
|
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>
|
|
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/
|
|
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.
|
|
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": {
|