@boltic/cli 1.0.37 ā 1.0.39
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 +69 -127
- package/cli.js +69 -2
- package/commands/login.js +127 -1
- package/package.json +1 -1
- package/utils/integration.js +11 -0
package/api/integration.js
CHANGED
|
@@ -3,6 +3,7 @@ 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";
|
|
6
7
|
import { logApi } from "../helper/verbose.js";
|
|
7
8
|
|
|
8
9
|
const getHttpsAgentForUrl = (baseUrl) => {
|
|
@@ -16,14 +17,39 @@ const getHttpsAgentForUrl = (baseUrl) => {
|
|
|
16
17
|
) {
|
|
17
18
|
return new https.Agent({ rejectUnauthorized: false });
|
|
18
19
|
}
|
|
19
|
-
} catch
|
|
20
|
+
} catch {
|
|
20
21
|
// ignore URL parse errors and fall back to default agent
|
|
21
22
|
}
|
|
22
23
|
return undefined;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
-
|
|
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
|
+
}
|
|
41
|
+
if (session) {
|
|
42
|
+
headers.Cookie = session;
|
|
43
|
+
}
|
|
44
|
+
return headers;
|
|
45
|
+
};
|
|
46
|
+
|
|
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) {
|
|
27
53
|
console.error(
|
|
28
54
|
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
29
55
|
);
|
|
@@ -31,7 +57,12 @@ const getIntegrationGroups = async (apiUrl, accountId, token, session) => {
|
|
|
31
57
|
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
32
58
|
process.exit(1); // Exit the CLI with an error code
|
|
33
59
|
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getIntegrationGroups = async (apiUrl, accountId, token, session) => {
|
|
34
63
|
try {
|
|
64
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
65
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
35
66
|
const axiosOptions = {
|
|
36
67
|
method: "get",
|
|
37
68
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integration-groups`,
|
|
@@ -41,8 +72,7 @@ const getIntegrationGroups = async (apiUrl, accountId, token, session) => {
|
|
|
41
72
|
},
|
|
42
73
|
headers: {
|
|
43
74
|
"Content-Type": "application/json",
|
|
44
|
-
|
|
45
|
-
Cookie: session,
|
|
75
|
+
...authHeaders,
|
|
46
76
|
},
|
|
47
77
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
48
78
|
};
|
|
@@ -56,15 +86,9 @@ const getIntegrationGroups = async (apiUrl, accountId, token, session) => {
|
|
|
56
86
|
};
|
|
57
87
|
|
|
58
88
|
const listAllIntegrations = async (apiUrl, token, accountId, session) => {
|
|
59
|
-
if (!token || !session || !accountId) {
|
|
60
|
-
console.error(
|
|
61
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
62
|
-
);
|
|
63
|
-
console.log("\nš¹ Please log in first using:");
|
|
64
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
65
|
-
process.exit(1); // Exit the CLI with an error code
|
|
66
|
-
}
|
|
67
89
|
try {
|
|
90
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
91
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
68
92
|
const axiosOptions = {
|
|
69
93
|
method: "get",
|
|
70
94
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations`,
|
|
@@ -74,8 +98,7 @@ const listAllIntegrations = async (apiUrl, token, accountId, session) => {
|
|
|
74
98
|
},
|
|
75
99
|
headers: {
|
|
76
100
|
"Content-Type": "application/json",
|
|
77
|
-
|
|
78
|
-
Cookie: session,
|
|
101
|
+
...authHeaders,
|
|
79
102
|
},
|
|
80
103
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
81
104
|
};
|
|
@@ -95,23 +118,16 @@ const saveIntegration = async (
|
|
|
95
118
|
session,
|
|
96
119
|
integration
|
|
97
120
|
) => {
|
|
98
|
-
if (!token || !session || !accountId) {
|
|
99
|
-
console.error(
|
|
100
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
101
|
-
);
|
|
102
|
-
console.log("\nš¹ Please log in first using:");
|
|
103
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
104
|
-
process.exit(1); // Exit the CLI with an error code
|
|
105
|
-
}
|
|
106
121
|
try {
|
|
122
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
123
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
107
124
|
const response = await axios({
|
|
108
125
|
method: "post",
|
|
109
126
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations`,
|
|
110
127
|
data: integration,
|
|
111
128
|
headers: {
|
|
112
129
|
"Content-Type": "application/json",
|
|
113
|
-
|
|
114
|
-
Cookie: session,
|
|
130
|
+
...authHeaders,
|
|
115
131
|
},
|
|
116
132
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
117
133
|
});
|
|
@@ -122,24 +138,17 @@ const saveIntegration = async (
|
|
|
122
138
|
};
|
|
123
139
|
|
|
124
140
|
const editIntegration = async (apiUrl, token, accountId, session, payload) => {
|
|
125
|
-
if (!token || !session || !accountId) {
|
|
126
|
-
console.error(
|
|
127
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
128
|
-
);
|
|
129
|
-
console.log("\nš¹ Please log in first using:");
|
|
130
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
131
|
-
process.exit(1); // Exit the CLI with an error code
|
|
132
|
-
}
|
|
133
141
|
const { id } = payload;
|
|
134
142
|
try {
|
|
143
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
144
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
135
145
|
const response = await axios({
|
|
136
146
|
method: "post",
|
|
137
147
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${id}/edit`,
|
|
138
148
|
data: payload,
|
|
139
149
|
headers: {
|
|
140
150
|
"Content-Type": "application/json",
|
|
141
|
-
|
|
142
|
-
Cookie: session,
|
|
151
|
+
...authHeaders,
|
|
143
152
|
},
|
|
144
153
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
145
154
|
});
|
|
@@ -157,15 +166,9 @@ const updateIntegration = async (
|
|
|
157
166
|
session,
|
|
158
167
|
integration
|
|
159
168
|
) => {
|
|
160
|
-
if (!token || !session || !accountId) {
|
|
161
|
-
console.error(
|
|
162
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
163
|
-
);
|
|
164
|
-
console.log("\nš¹ Please log in first using:");
|
|
165
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
166
|
-
process.exit(1); // Exit the CLI with an error code
|
|
167
|
-
}
|
|
168
169
|
try {
|
|
170
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
171
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
169
172
|
const { id, ...rest } = integration;
|
|
170
173
|
const response = await axios({
|
|
171
174
|
method: "patch",
|
|
@@ -173,8 +176,7 @@ const updateIntegration = async (
|
|
|
173
176
|
data: rest,
|
|
174
177
|
headers: {
|
|
175
178
|
"Content-Type": "application/json",
|
|
176
|
-
|
|
177
|
-
Cookie: session,
|
|
179
|
+
...authHeaders,
|
|
178
180
|
},
|
|
179
181
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
180
182
|
});
|
|
@@ -191,23 +193,15 @@ const getIntegrationById = async (
|
|
|
191
193
|
session,
|
|
192
194
|
integrationId
|
|
193
195
|
) => {
|
|
194
|
-
if (!token || !session || !accountId) {
|
|
195
|
-
console.error(
|
|
196
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
197
|
-
);
|
|
198
|
-
console.log("\nš¹ Please log in first using:");
|
|
199
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
200
|
-
process.exit(1); // Exit the CLI with an error code
|
|
201
|
-
}
|
|
202
|
-
|
|
203
196
|
try {
|
|
197
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
198
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
204
199
|
const response = await axios({
|
|
205
200
|
method: "get",
|
|
206
201
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${integrationId}`,
|
|
207
202
|
headers: {
|
|
208
203
|
"Content-Type": "application/json",
|
|
209
|
-
|
|
210
|
-
Cookie: session,
|
|
204
|
+
...authHeaders,
|
|
211
205
|
},
|
|
212
206
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
213
207
|
});
|
|
@@ -224,23 +218,15 @@ const getAuthenticationByIntegrationId = async (
|
|
|
224
218
|
session,
|
|
225
219
|
integrationId
|
|
226
220
|
) => {
|
|
227
|
-
if (!token || !session || !accountId) {
|
|
228
|
-
console.error(
|
|
229
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
230
|
-
);
|
|
231
|
-
console.log("\nš¹ Please log in first using:");
|
|
232
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
233
|
-
process.exit(1); // Exit the CLI with an error code
|
|
234
|
-
}
|
|
235
|
-
|
|
236
221
|
try {
|
|
222
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
223
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
237
224
|
const response = await axios({
|
|
238
225
|
method: "get",
|
|
239
226
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}integrations/${integrationId}/authentication`,
|
|
240
227
|
headers: {
|
|
241
228
|
"Content-Type": "application/json",
|
|
242
|
-
|
|
243
|
-
Cookie: session,
|
|
229
|
+
...authHeaders,
|
|
244
230
|
},
|
|
245
231
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
246
232
|
});
|
|
@@ -290,22 +276,15 @@ const getConfigurationByIntegrationId = async (
|
|
|
290
276
|
accountId,
|
|
291
277
|
integrationId
|
|
292
278
|
) => {
|
|
293
|
-
if (!token || !session || !accountId) {
|
|
294
|
-
console.error(
|
|
295
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
296
|
-
);
|
|
297
|
-
console.log("\nš¹ Please log in first using:");
|
|
298
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
299
|
-
process.exit(1); // Exit the CLI with an error code
|
|
300
|
-
}
|
|
301
279
|
try {
|
|
280
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
281
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
302
282
|
const response = await axios({
|
|
303
283
|
method: "get",
|
|
304
284
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}integrations/${integrationId}/configuration`,
|
|
305
285
|
headers: {
|
|
306
286
|
"Content-Type": "application/json",
|
|
307
|
-
|
|
308
|
-
Cookie: session,
|
|
287
|
+
...authHeaders,
|
|
309
288
|
},
|
|
310
289
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
311
290
|
});
|
|
@@ -322,23 +301,16 @@ const syncIntegration = async (
|
|
|
322
301
|
session,
|
|
323
302
|
integration
|
|
324
303
|
) => {
|
|
325
|
-
if (!token || !session || !accountId) {
|
|
326
|
-
console.error(
|
|
327
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
328
|
-
);
|
|
329
|
-
console.log("\nš¹ Please log in first using:");
|
|
330
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
331
|
-
process.exit(1); // Exit the CLI with an error code
|
|
332
|
-
}
|
|
333
304
|
try {
|
|
305
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
306
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
334
307
|
const response = await axios({
|
|
335
308
|
method: "post",
|
|
336
309
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${integration.integration_id}/deploy`,
|
|
337
310
|
data: integration,
|
|
338
311
|
headers: {
|
|
339
312
|
"Content-Type": "application/json",
|
|
340
|
-
|
|
341
|
-
Cookie: session,
|
|
313
|
+
...authHeaders,
|
|
342
314
|
},
|
|
343
315
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
344
316
|
});
|
|
@@ -355,15 +327,8 @@ const sendIntegrationForReview = async (
|
|
|
355
327
|
session,
|
|
356
328
|
integration
|
|
357
329
|
) => {
|
|
358
|
-
if (!token || !session || !accountId) {
|
|
359
|
-
console.error(
|
|
360
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
361
|
-
);
|
|
362
|
-
console.log("\nš¹ Please log in first using:");
|
|
363
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
364
|
-
process.exit(1); // Exit the CLI with an error code
|
|
365
|
-
}
|
|
366
330
|
try {
|
|
331
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
367
332
|
const response = await axios({
|
|
368
333
|
method: "post",
|
|
369
334
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integration-reviews`,
|
|
@@ -383,24 +348,16 @@ const sendIntegrationForReview = async (
|
|
|
383
348
|
|
|
384
349
|
const purgeCache = async (apiUrl, token, accountId, session, integration) => {
|
|
385
350
|
const { integration_id } = integration;
|
|
386
|
-
if (!token || !session || !accountId) {
|
|
387
|
-
console.error(
|
|
388
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
389
|
-
);
|
|
390
|
-
console.log("\nš¹ Please log in first using:");
|
|
391
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
392
|
-
process.exit(1); // Exit the CLI with an error code
|
|
393
|
-
}
|
|
394
|
-
|
|
395
351
|
try {
|
|
352
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
353
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
396
354
|
const response = await axios({
|
|
397
355
|
method: "post",
|
|
398
356
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${integration_id}/cache`,
|
|
399
357
|
data: {},
|
|
400
358
|
headers: {
|
|
401
359
|
"Content-Type": "application/json",
|
|
402
|
-
|
|
403
|
-
Cookie: session,
|
|
360
|
+
...authHeaders,
|
|
404
361
|
},
|
|
405
362
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
406
363
|
});
|
|
@@ -411,22 +368,15 @@ const purgeCache = async (apiUrl, token, accountId, session, integration) => {
|
|
|
411
368
|
};
|
|
412
369
|
|
|
413
370
|
const pullIntegration = async (apiUrl, token, accountId, session, id) => {
|
|
414
|
-
if (!token || !session || !accountId) {
|
|
415
|
-
console.error(
|
|
416
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
417
|
-
);
|
|
418
|
-
console.log("\nš¹ Please log in first using:");
|
|
419
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
420
|
-
process.exit(1); // Exit the CLI with an error code
|
|
421
|
-
}
|
|
422
371
|
try {
|
|
372
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
373
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
423
374
|
const response = await axios({
|
|
424
375
|
method: "get",
|
|
425
376
|
url: `${apiUrl}/service/panel/automation/v1.0/${accountId}/integrations/${id}/pull`,
|
|
426
377
|
headers: {
|
|
427
378
|
"Content-Type": "application/json",
|
|
428
|
-
|
|
429
|
-
Cookie: session,
|
|
379
|
+
...authHeaders,
|
|
430
380
|
},
|
|
431
381
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
432
382
|
});
|
|
@@ -444,20 +394,13 @@ const uploadFileToCloud = async (
|
|
|
444
394
|
session,
|
|
445
395
|
filePath
|
|
446
396
|
) => {
|
|
447
|
-
if (!token || !session || !accountId) {
|
|
448
|
-
console.error(
|
|
449
|
-
"\x1b[31mError:\x1b[0m Authentication credentials are required."
|
|
450
|
-
);
|
|
451
|
-
console.log("\nš¹ Please log in first using:");
|
|
452
|
-
console.log("\x1b[32m$ boltic login\x1b[0m\n");
|
|
453
|
-
process.exit(1);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
397
|
if (!fs.existsSync(filePath)) {
|
|
457
398
|
throw new Error("File does not exist: " + filePath);
|
|
458
399
|
}
|
|
459
400
|
|
|
460
401
|
try {
|
|
402
|
+
await ensureAuthenticatedOrExit(accountId, token, session);
|
|
403
|
+
const authHeaders = await buildAuthHeaders(token, session);
|
|
461
404
|
const form = new FormData();
|
|
462
405
|
form.append("files", fs.createReadStream(filePath));
|
|
463
406
|
|
|
@@ -467,8 +410,7 @@ const uploadFileToCloud = async (
|
|
|
467
410
|
{
|
|
468
411
|
headers: {
|
|
469
412
|
...form.getHeaders(),
|
|
470
|
-
|
|
471
|
-
Cookie: session,
|
|
413
|
+
...authHeaders,
|
|
472
414
|
},
|
|
473
415
|
httpsAgent: getHttpsAgentForUrl(apiUrl),
|
|
474
416
|
}
|
package/cli.js
CHANGED
|
@@ -16,8 +16,75 @@ const createCLI = (consoleUrl, apiUrl, serviceName, env) => {
|
|
|
16
16
|
const commands = {
|
|
17
17
|
login: {
|
|
18
18
|
description: "Authenticate the user and save access token",
|
|
19
|
-
action: async () =>
|
|
20
|
-
|
|
19
|
+
action: async (args) => {
|
|
20
|
+
// Support PAT-based login via flags, e.g.:
|
|
21
|
+
// boltic login --pat XXXXX --account_id YYYYYY
|
|
22
|
+
// boltic login --pat=XXXXX --account-id=YYYYYY
|
|
23
|
+
let patFromArg;
|
|
24
|
+
let accountIdFromArg;
|
|
25
|
+
let hasPatFlag = false;
|
|
26
|
+
let hasAccountIdFlag = false;
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < args.length; i++) {
|
|
29
|
+
const arg = args[i];
|
|
30
|
+
|
|
31
|
+
if (arg === "--pat") {
|
|
32
|
+
hasPatFlag = true;
|
|
33
|
+
if (
|
|
34
|
+
i + 1 < args.length &&
|
|
35
|
+
!args[i + 1].startsWith("--")
|
|
36
|
+
) {
|
|
37
|
+
patFromArg = args[i + 1];
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (arg === "--account_id" || arg === "--account-id") {
|
|
44
|
+
hasAccountIdFlag = true;
|
|
45
|
+
if (
|
|
46
|
+
i + 1 < args.length &&
|
|
47
|
+
!args[i + 1].startsWith("--")
|
|
48
|
+
) {
|
|
49
|
+
accountIdFromArg = args[i + 1];
|
|
50
|
+
i++;
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (arg.startsWith("--pat=")) {
|
|
56
|
+
hasPatFlag = true;
|
|
57
|
+
patFromArg = arg.split("=")[1];
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
arg.startsWith("--account_id=") ||
|
|
63
|
+
arg.startsWith("--account-id=")
|
|
64
|
+
) {
|
|
65
|
+
hasAccountIdFlag = true;
|
|
66
|
+
accountIdFromArg = arg.split("=")[1];
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If any PAT-related flag is present, delegate to PAT login handler.
|
|
72
|
+
// `handlePatLogin` will decide whether to prompt based on which values are provided.
|
|
73
|
+
if (
|
|
74
|
+
hasPatFlag ||
|
|
75
|
+
hasAccountIdFlag ||
|
|
76
|
+
patFromArg ||
|
|
77
|
+
accountIdFromArg
|
|
78
|
+
) {
|
|
79
|
+
await AuthCommands.handlePatLogin(
|
|
80
|
+
patFromArg,
|
|
81
|
+
accountIdFromArg
|
|
82
|
+
);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await AuthCommands.handleLogin(consoleUrl, apiUrl, env);
|
|
87
|
+
},
|
|
21
88
|
},
|
|
22
89
|
integration: {
|
|
23
90
|
description: "Manage integrations (create, list)",
|
package/commands/login.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import open from "open";
|
|
3
|
+
import readline from "readline";
|
|
3
4
|
import { v4 as uuidv4 } from "uuid";
|
|
4
5
|
import { getCliBearerToken, getCliSession } from "../api/login.js";
|
|
5
6
|
import { getCurrentEnv } from "../helper/env.js";
|
|
@@ -28,9 +29,73 @@ const execute = async (args) => {
|
|
|
28
29
|
return;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
// Special handling for `boltic login` to support PAT-based login via flags:
|
|
33
|
+
// boltic login --pat XXXXX --account_id YYYYYY
|
|
34
|
+
if (subCommand === "login") {
|
|
35
|
+
const options = args.slice(1);
|
|
36
|
+
let patFromArg;
|
|
37
|
+
let accountIdFromArg;
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < options.length; i++) {
|
|
40
|
+
const arg = options[i];
|
|
41
|
+
|
|
42
|
+
if (arg === "--pat" && i + 1 < options.length) {
|
|
43
|
+
patFromArg = options[i + 1];
|
|
44
|
+
i++;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
(arg === "--account_id" || arg === "--account-id") &&
|
|
50
|
+
i + 1 < options.length
|
|
51
|
+
) {
|
|
52
|
+
accountIdFromArg = options[i + 1];
|
|
53
|
+
i++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (arg.startsWith("--pat=")) {
|
|
58
|
+
patFromArg = arg.split("=")[1];
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
arg.startsWith("--account_id=") ||
|
|
64
|
+
arg.startsWith("--account-id=")
|
|
65
|
+
) {
|
|
66
|
+
accountIdFromArg = arg.split("=")[1];
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If PAT flags are provided, use PAT-based login. Otherwise, fall back to browser-based login.
|
|
72
|
+
if (patFromArg || accountIdFromArg) {
|
|
73
|
+
await handlePatLogin(patFromArg, accountIdFromArg);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await handleLogin();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
31
81
|
await commands[subCommand].action(args.slice(1));
|
|
32
82
|
};
|
|
33
83
|
|
|
84
|
+
// Prompt user for input from the terminal
|
|
85
|
+
async function askQuestion(query) {
|
|
86
|
+
const rl = readline.createInterface({
|
|
87
|
+
input: process.stdin,
|
|
88
|
+
output: process.stdout,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return new Promise((resolve) => {
|
|
92
|
+
rl.question(query, (answer) => {
|
|
93
|
+
rl.close();
|
|
94
|
+
resolve(answer);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
34
99
|
// Show available login commands
|
|
35
100
|
function showHelp() {
|
|
36
101
|
console.log(chalk.cyan("\nLogin Commands:\n"));
|
|
@@ -161,6 +226,67 @@ async function handleLogin() {
|
|
|
161
226
|
);
|
|
162
227
|
}
|
|
163
228
|
|
|
229
|
+
// Handle PAT-based login command
|
|
230
|
+
async function handlePatLogin(patFromArg, accountIdFromArg) {
|
|
231
|
+
let pat = patFromArg && patFromArg.trim();
|
|
232
|
+
let accountId = accountIdFromArg && accountIdFromArg.trim();
|
|
233
|
+
|
|
234
|
+
// If both values are provided via CLI flags, do not prompt at all.
|
|
235
|
+
if (pat && accountId) {
|
|
236
|
+
try {
|
|
237
|
+
await storeSecret("pat", pat);
|
|
238
|
+
await storeSecret("account_id", accountId);
|
|
239
|
+
console.log(
|
|
240
|
+
chalk.green(
|
|
241
|
+
"\nā
PAT token and Account ID stored securely. They will be used for future organization-related requests.\n"
|
|
242
|
+
)
|
|
243
|
+
);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(
|
|
246
|
+
chalk.red(
|
|
247
|
+
`\nā Failed to store PAT credentials: ${error.message || error}\n`
|
|
248
|
+
)
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (!pat) {
|
|
255
|
+
console.log(chalk.cyan("\nš Personal Access Token (PAT) login\n"));
|
|
256
|
+
pat = (await askQuestion("Enter your PAT token: ")).trim();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!pat) {
|
|
260
|
+
console.log(chalk.red("\nā PAT token cannot be empty.\n"));
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (!accountId) {
|
|
265
|
+
accountId = (await askQuestion("Enter your Account ID: ")).trim();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!accountId) {
|
|
269
|
+
console.log(chalk.red("\nā Account ID cannot be empty.\n"));
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
await storeSecret("pat", pat);
|
|
275
|
+
await storeSecret("account_id", accountId);
|
|
276
|
+
console.log(
|
|
277
|
+
chalk.green(
|
|
278
|
+
"\nā
PAT token and Account ID stored securely. They will be used for future organization-related requests.\n"
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error(
|
|
283
|
+
chalk.red(
|
|
284
|
+
`\nā Failed to store PAT credentials: ${error.message || error}\n`
|
|
285
|
+
)
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
164
290
|
// Handle logout command
|
|
165
291
|
async function handleLogout() {
|
|
166
292
|
await deleteAllSecrets();
|
|
@@ -170,4 +296,4 @@ async function handleLogout() {
|
|
|
170
296
|
);
|
|
171
297
|
}
|
|
172
298
|
|
|
173
|
-
export default { execute, handleLogin, handleLogout };
|
|
299
|
+
export default { execute, handleLogin, handlePatLogin, handleLogout };
|
package/package.json
CHANGED
package/utils/integration.js
CHANGED
|
@@ -218,6 +218,17 @@ async function getSvgFilePath() {
|
|
|
218
218
|
if (pickedFile && fs.existsSync(pickedFile)) {
|
|
219
219
|
return pickedFile;
|
|
220
220
|
}
|
|
221
|
+
|
|
222
|
+
// Fallback: ask user to manually input the SVG path
|
|
223
|
+
const manualPath = await input({
|
|
224
|
+
message: "Enter the path to your SVG icon file:",
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (!manualPath || !fs.existsSync(manualPath)) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return manualPath;
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
export { getSvgFilePath, pickSvgFile };
|