mage-remote-run 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/paas.js +8 -2
- package/lib/commands/connections.js +152 -30
- package/lib/prompts.js +11 -1
- package/package.json +1 -1
package/lib/api/paas.js
CHANGED
|
@@ -40,15 +40,21 @@ export class PaasClient extends ApiClient {
|
|
|
40
40
|
const baseURL = config.baseURL || '';
|
|
41
41
|
const url = baseURL.replace(/\/$/, '') + '/' + config.url.replace(/^\//, ''); // rudimentary join
|
|
42
42
|
|
|
43
|
+
// Determine signature method (default to HMAC-SHA256)
|
|
44
|
+
const signatureMethod = (this.config.auth.signatureMethod || 'hmac-sha256').toLowerCase();
|
|
45
|
+
const isSha1 = signatureMethod === 'hmac-sha1' || signatureMethod === 'sha1';
|
|
46
|
+
const methodStr = isSha1 ? 'HMAC-SHA1' : 'HMAC-SHA256';
|
|
47
|
+
const hashAlgo = isSha1 ? 'sha1' : 'sha256';
|
|
48
|
+
|
|
43
49
|
const oauth = OAuth({
|
|
44
50
|
consumer: {
|
|
45
51
|
key: this.config.auth.consumerKey,
|
|
46
52
|
secret: this.config.auth.consumerSecret
|
|
47
53
|
},
|
|
48
|
-
signature_method:
|
|
54
|
+
signature_method: methodStr,
|
|
49
55
|
hash_function(base_string, key) {
|
|
50
56
|
return crypto
|
|
51
|
-
.createHmac(
|
|
57
|
+
.createHmac(hashAlgo, key)
|
|
52
58
|
.update(base_string)
|
|
53
59
|
.digest('base64');
|
|
54
60
|
}
|
|
@@ -7,6 +7,22 @@ import inquirer from 'inquirer';
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { getMissingB2BModules } from '../b2b.js';
|
|
9
9
|
|
|
10
|
+
// Helper to test connection (non-interactive or one-shot)
|
|
11
|
+
async function testConnection(name, settings) {
|
|
12
|
+
console.log(chalk.blue(`\nTesting connection "${name}"...`));
|
|
13
|
+
try {
|
|
14
|
+
const client = await createClient(settings);
|
|
15
|
+
const start = Date.now();
|
|
16
|
+
await client.get('V1/store/storeViews');
|
|
17
|
+
const duration = Date.now() - start;
|
|
18
|
+
console.log(chalk.green(`✔ Connection successful! (${duration}ms)`));
|
|
19
|
+
return { success: true };
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error(chalk.red(`✖ Connection failed: ${e.message}`));
|
|
22
|
+
return { success: false, error: e };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
10
26
|
// Helper to handle interactive connection configuration and testing
|
|
11
27
|
async function configureAndTestConnection(name, initialSettings = {}) {
|
|
12
28
|
let settings = await askForProfileSettings(initialSettings);
|
|
@@ -19,18 +35,13 @@ async function configureAndTestConnection(name, initialSettings = {}) {
|
|
|
19
35
|
});
|
|
20
36
|
|
|
21
37
|
if (shouldTest) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const start = Date.now();
|
|
26
|
-
await client.get('V1/store/storeViews');
|
|
27
|
-
const duration = Date.now() - start;
|
|
28
|
-
console.log(chalk.green(`✔ Connection successful! (${duration}ms)`));
|
|
38
|
+
const result = await testConnection(name, settings);
|
|
39
|
+
|
|
40
|
+
if (result.success) {
|
|
29
41
|
lastTestError = null;
|
|
30
42
|
break; // Test passed, proceed to save
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
lastTestError = e;
|
|
43
|
+
} else {
|
|
44
|
+
lastTestError = result.error;
|
|
34
45
|
const shouldEdit = await confirm({
|
|
35
46
|
message: 'Connection failed. Do you want to change settings?',
|
|
36
47
|
default: true
|
|
@@ -345,41 +356,152 @@ export function registerConnectionCommands(program) {
|
|
|
345
356
|
//-------------------------------------------------------
|
|
346
357
|
connections.command('add')
|
|
347
358
|
.description('Configure a new connection profile')
|
|
359
|
+
.option('--name <name>', 'Profile Name')
|
|
360
|
+
.option('--type <type>', 'System Type (magento-os, mage-os, ac-on-prem, ac-cloud-paas, ac-saas)')
|
|
361
|
+
.option('--url <url>', 'Instance URL')
|
|
362
|
+
.option('--client-id <id>', 'Client ID (SaaS)')
|
|
363
|
+
.option('--client-secret <secret>', 'Client Secret (SaaS)')
|
|
364
|
+
.option('--auth-method <method>', 'Auth Method (bearer, oauth1)')
|
|
365
|
+
.option('--token <token>', 'Bearer Token')
|
|
366
|
+
.option('--consumer-key <key>', 'Consumer Key (OAuth1)')
|
|
367
|
+
.option('--consumer-secret <secret>', 'Consumer Secret (OAuth1)')
|
|
368
|
+
.option('--access-token <token>', 'Access Token (OAuth1)')
|
|
369
|
+
.option('--token-secret <secret>', 'Token Secret (OAuth1)')
|
|
370
|
+
.option('--signature-method <method>', 'Signature Method (hmac-sha256, hmac-sha1)', 'hmac-sha256')
|
|
371
|
+
.option('--active', 'Set as active profile')
|
|
372
|
+
.option('--no-test', 'Skip connection test')
|
|
348
373
|
.addHelpText('after', `
|
|
349
374
|
Examples:
|
|
375
|
+
Interactive Mode:
|
|
350
376
|
$ mage-remote-run connection add
|
|
377
|
+
|
|
378
|
+
SaaS (Non-Interactive):
|
|
379
|
+
$ mage-remote-run connection add --name "MySaaS" --type ac-saas --url "https://example.com" --client-id "id" --client-secret "secret" --active
|
|
380
|
+
|
|
381
|
+
Bearer Token (Non-Interactive):
|
|
382
|
+
$ mage-remote-run connection add --name "MyStore" --type magento-os --url "https://magento.example.com" --token "tkn"
|
|
351
383
|
`)
|
|
352
|
-
.action(async () => {
|
|
384
|
+
.action(async (options) => {
|
|
353
385
|
console.log(chalk.blue('Configure a new connection Profile'));
|
|
354
386
|
try {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
validate: value => value ? true : 'Name is required'
|
|
358
|
-
});
|
|
387
|
+
let name = options.name;
|
|
388
|
+
let settings = null;
|
|
359
389
|
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
|
|
363
|
-
|
|
390
|
+
// Non-Interactive Mode
|
|
391
|
+
if (options.type) {
|
|
392
|
+
if (!name) {
|
|
393
|
+
throw new Error('Option --name is required when using --type');
|
|
394
|
+
}
|
|
395
|
+
if (!options.url) {
|
|
396
|
+
throw new Error('Option --url is required when using --type');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
settings = {
|
|
400
|
+
type: options.type,
|
|
401
|
+
url: options.url,
|
|
402
|
+
auth: {}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
if (options.type === 'ac-saas') {
|
|
406
|
+
if (!options.clientId || !options.clientSecret) {
|
|
407
|
+
throw new Error('SaaS authentication requires --client-id and --client-secret');
|
|
408
|
+
}
|
|
409
|
+
settings.auth = {
|
|
410
|
+
clientId: options.clientId,
|
|
411
|
+
clientSecret: options.clientSecret
|
|
412
|
+
};
|
|
413
|
+
} else {
|
|
414
|
+
// Infer auth method if not provided
|
|
415
|
+
let method = options.authMethod;
|
|
416
|
+
if (!method) {
|
|
417
|
+
if (options.token) {
|
|
418
|
+
method = 'bearer';
|
|
419
|
+
} else if (options.consumerKey && options.consumerSecret) {
|
|
420
|
+
method = 'oauth1';
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (method === 'bearer') {
|
|
425
|
+
if (!options.token) {
|
|
426
|
+
throw new Error('Bearer authentication requires --token');
|
|
427
|
+
}
|
|
428
|
+
settings.auth = {
|
|
429
|
+
method: 'bearer',
|
|
430
|
+
token: options.token
|
|
431
|
+
};
|
|
432
|
+
} else if (method === 'oauth1') {
|
|
433
|
+
if (!options.consumerKey || !options.consumerSecret || !options.accessToken || !options.tokenSecret) {
|
|
434
|
+
throw new Error('OAuth1 authentication requires --consumer-key, --consumer-secret, --access-token, and --token-secret');
|
|
435
|
+
}
|
|
436
|
+
settings.auth = {
|
|
437
|
+
method: 'oauth1',
|
|
438
|
+
consumerKey: options.consumerKey,
|
|
439
|
+
consumerSecret: options.consumerSecret,
|
|
440
|
+
accessToken: options.accessToken,
|
|
441
|
+
tokenSecret: options.tokenSecret,
|
|
442
|
+
signatureMethod: options.signatureMethod
|
|
443
|
+
};
|
|
444
|
+
} else {
|
|
445
|
+
throw new Error('Invalid or missing authentication options. Use --token for Bearer or provide OAuth keys.');
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Test connection if not skipped
|
|
450
|
+
let lastTestError = null;
|
|
451
|
+
if (options.test !== false) { // --no-test sets options.test to false
|
|
452
|
+
const result = await testConnection(name, settings);
|
|
453
|
+
if (!result.success) {
|
|
454
|
+
throw new Error(`Connection test failed: ${result.error.message}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
settings = await updateProfileCapabilities(settings, lastTestError);
|
|
459
|
+
|
|
460
|
+
} else {
|
|
461
|
+
// Interactive Mode
|
|
462
|
+
name = await input({
|
|
463
|
+
message: 'Profile Name:',
|
|
464
|
+
validate: value => value ? true : 'Name is required'
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
settings = await configureAndTestConnection(name);
|
|
468
|
+
if (!settings) {
|
|
469
|
+
console.log(chalk.yellow('\nConfiguration cancelled.'));
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
364
472
|
}
|
|
365
473
|
|
|
366
474
|
await addProfile(name, settings);
|
|
367
475
|
console.log(chalk.green(`\nProfile "${name}" saved successfully!`));
|
|
368
476
|
|
|
369
|
-
// Ask to set as active if multiple exist
|
|
370
477
|
const config = await loadConfig();
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
if (setActive) {
|
|
378
|
-
config.activeProfile = name;
|
|
379
|
-
await saveConfig(config);
|
|
380
|
-
console.log(chalk.green(`Profile "${name}" set as active.`));
|
|
478
|
+
|
|
479
|
+
let setActive = false;
|
|
480
|
+
if (options.type) {
|
|
481
|
+
// Non-interactive: only set active if requested or if it's the only one (handled by addProfile implicitly for first one, but we check logic here)
|
|
482
|
+
if (options.active) {
|
|
483
|
+
setActive = true;
|
|
381
484
|
}
|
|
485
|
+
} else {
|
|
486
|
+
// Interactive
|
|
487
|
+
if (Object.keys(config.profiles).length > 1) {
|
|
488
|
+
setActive = await confirm({
|
|
489
|
+
message: 'Set this as the active profile?',
|
|
490
|
+
default: true
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// addProfile already sets active if it's the first profile.
|
|
496
|
+
// We only need to force it if requested or confirmed and it wasn't already set (e.g. multiple profiles)
|
|
497
|
+
// Actually addProfile sets it if !activeProfile.
|
|
498
|
+
|
|
499
|
+
if (setActive && config.activeProfile !== name) {
|
|
500
|
+
config.activeProfile = name;
|
|
501
|
+
await saveConfig(config);
|
|
502
|
+
console.log(chalk.green(`Profile "${name}" set as active.`));
|
|
382
503
|
}
|
|
504
|
+
|
|
383
505
|
} catch (e) {
|
|
384
506
|
if (e.name === 'ExitPromptError') {
|
|
385
507
|
console.log(chalk.yellow('\nConfiguration cancelled.'));
|
package/lib/prompts.js
CHANGED
|
@@ -81,12 +81,22 @@ export async function askForProfileSettings(defaults = {}) {
|
|
|
81
81
|
validate: value => (value || defaults.auth?.tokenSecret) ? true : 'Token Secret is required'
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
+
const signatureMethod = await select({
|
|
85
|
+
message: 'Signature Method:',
|
|
86
|
+
default: defaults.auth?.signatureMethod || 'hmac-sha256',
|
|
87
|
+
choices: [
|
|
88
|
+
{ name: 'HMAC-SHA256 (Default, Newer Versions)', value: 'hmac-sha256' },
|
|
89
|
+
{ name: 'HMAC-SHA1 (Older Versions)', value: 'hmac-sha1' }
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
|
|
84
93
|
authConfig = {
|
|
85
94
|
method: 'oauth1',
|
|
86
95
|
consumerKey,
|
|
87
96
|
consumerSecret: consumerSecret || defaults.auth?.consumerSecret,
|
|
88
97
|
accessToken: accessToken || defaults.auth?.accessToken,
|
|
89
|
-
tokenSecret: tokenSecret || defaults.auth?.tokenSecret
|
|
98
|
+
tokenSecret: tokenSecret || defaults.auth?.tokenSecret,
|
|
99
|
+
signatureMethod
|
|
90
100
|
};
|
|
91
101
|
}
|
|
92
102
|
}
|