mage-remote-run 0.1.0 → 0.2.5
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/bin/mage-remote-run.js +57 -2
- package/lib/api/paas.js +1 -1
- package/lib/api/saas.js +40 -8
- package/lib/commands/connections.js +12 -3
- package/lib/commands/customers.js +8 -8
- package/lib/commands/eav.js +2 -2
- package/lib/commands/orders.js +7 -7
- package/lib/commands/products.js +4 -4
- package/lib/commands/stores.js +10 -10
- package/lib/commands/tax.js +2 -2
- package/lib/commands/websites.js +5 -5
- package/lib/config.js +35 -0
- package/package.json +6 -2
package/bin/mage-remote-run.js
CHANGED
|
@@ -35,11 +35,63 @@ registerEavCommands(program);
|
|
|
35
35
|
registerProductsCommands(program);
|
|
36
36
|
registerTaxCommands(program);
|
|
37
37
|
|
|
38
|
+
function resolveCommandMatch(parent, token) {
|
|
39
|
+
const tokenLower = token.toLowerCase();
|
|
40
|
+
const matches = parent.commands.filter((cmd) => {
|
|
41
|
+
const name = cmd.name().toLowerCase();
|
|
42
|
+
if (name.startsWith(tokenLower)) return true;
|
|
43
|
+
const aliases = cmd.aliases ? cmd.aliases() : [];
|
|
44
|
+
return aliases.some((alias) => alias.toLowerCase().startsWith(tokenLower));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
match: matches.length === 1 ? matches[0] : null,
|
|
49
|
+
matches
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function expandCommandAbbreviations(rootCommand, argv) {
|
|
54
|
+
const expanded = [];
|
|
55
|
+
let current = rootCommand;
|
|
56
|
+
const path = [];
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
59
|
+
const token = argv[i];
|
|
60
|
+
if (token.startsWith('-')) {
|
|
61
|
+
expanded.push(token);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!current.commands || current.commands.length === 0) {
|
|
66
|
+
expanded.push(...argv.slice(i));
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const { match, matches } = resolveCommandMatch(current, token);
|
|
71
|
+
if (!match) {
|
|
72
|
+
if (matches.length > 1) {
|
|
73
|
+
const parentName = path.length > 0 ? path.join(' ') : current.name();
|
|
74
|
+
const options = matches.map((cmd) => cmd.name()).join(', ');
|
|
75
|
+
console.error(`Ambiguous command "${token}" under "${parentName}". Options: ${options}.`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
expanded.push(token);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
expanded.push(match.name());
|
|
83
|
+
current = match;
|
|
84
|
+
path.push(match.name());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return expanded;
|
|
88
|
+
}
|
|
89
|
+
|
|
38
90
|
// Check for first run (no profiles configured and no arguments or just help)
|
|
39
91
|
// We need to check args length.
|
|
40
92
|
// node script.js -> length 2.
|
|
41
93
|
// node script.js command -> length 3.
|
|
42
|
-
|
|
94
|
+
let args = process.argv.slice(2);
|
|
43
95
|
const config = await loadConfig();
|
|
44
96
|
const hasProfiles = Object.keys(config.profiles || {}).length > 0;
|
|
45
97
|
|
|
@@ -55,7 +107,10 @@ if (!hasProfiles && args.length === 0) {
|
|
|
55
107
|
// Easiest is to manually invoke program.parse with ['node', 'script', 'connection', 'add']
|
|
56
108
|
// BUT program.parse executes asynchronously usually? commander is synchronous by default but actions are async.
|
|
57
109
|
// Let's modify process.argv before parsing.
|
|
58
|
-
|
|
110
|
+
args = ['connection', 'add'];
|
|
59
111
|
}
|
|
60
112
|
|
|
113
|
+
args = expandCommandAbbreviations(program, args);
|
|
114
|
+
process.argv = [...process.argv.slice(0, 2), ...args];
|
|
115
|
+
|
|
61
116
|
program.parse(process.argv);
|
package/lib/api/paas.js
CHANGED
package/lib/api/saas.js
CHANGED
|
@@ -2,6 +2,7 @@ import axios from 'axios';
|
|
|
2
2
|
import { ApiClient } from './client.js';
|
|
3
3
|
import { OpenAPIClientAxios } from 'openapi-client-axios';
|
|
4
4
|
import { loadSpec } from './spec-loader.js';
|
|
5
|
+
import { loadTokenCache, saveTokenCache } from '../config.js';
|
|
5
6
|
|
|
6
7
|
export class SaasClient extends ApiClient {
|
|
7
8
|
constructor(config) {
|
|
@@ -16,10 +17,11 @@ export class SaasClient extends ApiClient {
|
|
|
16
17
|
if (this.openApi) return;
|
|
17
18
|
|
|
18
19
|
const definition = loadSpec(this.config.type);
|
|
20
|
+
const basePath = this.baseUrl;
|
|
19
21
|
this.openApi = new OpenAPIClientAxios({
|
|
20
22
|
definition,
|
|
21
23
|
axiosConfigDefaults: {
|
|
22
|
-
baseURL:
|
|
24
|
+
baseURL: basePath, // Base for generated client
|
|
23
25
|
// Note: The definition might have servers block, but we override functionality.
|
|
24
26
|
}
|
|
25
27
|
});
|
|
@@ -40,19 +42,49 @@ export class SaasClient extends ApiClient {
|
|
|
40
42
|
return this.token;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
const tokenUrl =
|
|
45
|
+
const tokenUrl = (this.config.type === 'ac-saas' || this.config.type === 'saas')
|
|
46
|
+
? 'https://ims-na1.adobelogin.com/ims/token/v3'
|
|
47
|
+
: `${this.baseUrl}/oauth/token`;
|
|
44
48
|
|
|
45
49
|
try {
|
|
50
|
+
const cacheKey = `${this.config.type}|${this.config.url}|${this.config.auth.clientId}`;
|
|
51
|
+
const cache = await loadTokenCache();
|
|
52
|
+
const cachedEntry = cache[cacheKey];
|
|
53
|
+
if (cachedEntry?.token && cachedEntry?.expiresAt && Date.now() < cachedEntry.expiresAt) {
|
|
54
|
+
this.token = cachedEntry.token;
|
|
55
|
+
this.tokenExpiresAt = cachedEntry.expiresAt;
|
|
56
|
+
return this.token;
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
// Use raw axios for token fetch to avoid circular dependency or interceptor issues
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
60
|
+
let response;
|
|
61
|
+
if (this.config.type === 'ac-saas' || this.config.type === 'saas') {
|
|
62
|
+
const payload = new URLSearchParams({
|
|
63
|
+
grant_type: 'client_credentials',
|
|
64
|
+
client_id: this.config.auth.clientId,
|
|
65
|
+
client_secret: this.config.auth.clientSecret,
|
|
66
|
+
scope: 'openid,AdobeID,email,profile,additional_info.roles,additional_info.projectedProductContext,commerce.accs'
|
|
67
|
+
});
|
|
68
|
+
response = await axios.post(tokenUrl, payload.toString(), {
|
|
69
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
const payload = {
|
|
73
|
+
grant_type: 'client_credentials',
|
|
74
|
+
client_id: this.config.auth.clientId,
|
|
75
|
+
client_secret: this.config.auth.clientSecret
|
|
76
|
+
};
|
|
77
|
+
response = await axios.post(tokenUrl, payload);
|
|
78
|
+
}
|
|
52
79
|
|
|
53
80
|
this.token = response.data.access_token;
|
|
54
81
|
const expiresIn = response.data.expires_in || 3600;
|
|
55
82
|
this.tokenExpiresAt = Date.now() + (expiresIn * 1000) - 60000;
|
|
83
|
+
cache[cacheKey] = {
|
|
84
|
+
token: this.token,
|
|
85
|
+
expiresAt: this.tokenExpiresAt
|
|
86
|
+
};
|
|
87
|
+
await saveTokenCache(cache);
|
|
56
88
|
|
|
57
89
|
return this.token;
|
|
58
90
|
} catch (error) {
|
|
@@ -66,7 +98,7 @@ export class SaasClient extends ApiClient {
|
|
|
66
98
|
// Use the configured axios instance for generic requests
|
|
67
99
|
// Note: endpoint passed here is like 'store/websites'
|
|
68
100
|
// openapi-client-axios client base is usually set to server root or we adjusted it.
|
|
69
|
-
//
|
|
101
|
+
// baseURL is the instance root; include API version in the endpoint.
|
|
70
102
|
|
|
71
103
|
// Ensure endpoint does not start with / if base has it, or handle cleanly
|
|
72
104
|
const cleanEndpoint = endpoint.replace(/^\//, '');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadConfig, saveConfig, addProfile } from '../config.js';
|
|
1
|
+
import { loadConfig, saveConfig, addProfile, clearTokenCache } from '../config.js';
|
|
2
2
|
import { printTable, handleError } from '../utils.js';
|
|
3
3
|
import { askForProfileSettings } from '../prompts.js';
|
|
4
4
|
import { createClient } from '../api/factory.js';
|
|
@@ -134,7 +134,7 @@ export function registerConnectionCommands(program) {
|
|
|
134
134
|
const client = await createClient(profileConfig);
|
|
135
135
|
|
|
136
136
|
const start = Date.now();
|
|
137
|
-
await client.get('store/storeViews');
|
|
137
|
+
await client.get('V1/store/storeViews');
|
|
138
138
|
const duration = Date.now() - start;
|
|
139
139
|
|
|
140
140
|
results.push([name, chalk.green('SUCCESS'), `${duration}ms`]);
|
|
@@ -155,7 +155,7 @@ export function registerConnectionCommands(program) {
|
|
|
155
155
|
try {
|
|
156
156
|
const client = await createClient(); // Uses active profile
|
|
157
157
|
const start = Date.now();
|
|
158
|
-
await client.get('store/storeViews');
|
|
158
|
+
await client.get('V1/store/storeViews');
|
|
159
159
|
const duration = Date.now() - start;
|
|
160
160
|
|
|
161
161
|
console.log(chalk.green(`\n✔ Connection successful! (${duration}ms)`));
|
|
@@ -211,4 +211,13 @@ export function registerConnectionCommands(program) {
|
|
|
211
211
|
console.log(chalk.green(`Active profile set to "${selected}".`));
|
|
212
212
|
} catch (e) { handleError(e); }
|
|
213
213
|
});
|
|
214
|
+
|
|
215
|
+
connections.command('clear-token-cache')
|
|
216
|
+
.description('Clear cached access tokens')
|
|
217
|
+
.action(async () => {
|
|
218
|
+
try {
|
|
219
|
+
await clearTokenCache();
|
|
220
|
+
console.log(chalk.green('Token cache cleared.'));
|
|
221
|
+
} catch (e) { handleError(e); }
|
|
222
|
+
});
|
|
214
223
|
}
|
|
@@ -16,7 +16,7 @@ export function registerCustomersCommands(program) {
|
|
|
16
16
|
'searchCriteria[currentPage]': options.page,
|
|
17
17
|
'searchCriteria[pageSize]': options.size
|
|
18
18
|
};
|
|
19
|
-
const data = await client.get('customers/search', params);
|
|
19
|
+
const data = await client.get('V1/customers/search', params);
|
|
20
20
|
const rows = (data.items || []).map(c => [c.id, c.email, c.firstname, c.lastname, c.group_id]);
|
|
21
21
|
console.log(chalk.bold(`Total: ${data.total_count}, Page: ${options.page}, Size: ${options.size}`));
|
|
22
22
|
printTable(['ID', 'Email', 'First Name', 'Last Name', 'Group'], rows);
|
|
@@ -33,7 +33,7 @@ export function registerCustomersCommands(program) {
|
|
|
33
33
|
'searchCriteria[filter_groups][0][filters][0][value]': `%${query}%`,
|
|
34
34
|
'searchCriteria[filter_groups][0][filters][0][condition_type]': 'like'
|
|
35
35
|
};
|
|
36
|
-
const data = await client.get('customers/search', params);
|
|
36
|
+
const data = await client.get('V1/customers/search', params);
|
|
37
37
|
const rows = (data.items || []).map(c => [c.id, c.email, c.firstname, c.lastname]);
|
|
38
38
|
printTable(['ID', 'Email', 'Name', 'Lastname'], rows);
|
|
39
39
|
} catch (e) { handleError(e); }
|
|
@@ -43,7 +43,7 @@ export function registerCustomersCommands(program) {
|
|
|
43
43
|
.action(async (id) => {
|
|
44
44
|
try {
|
|
45
45
|
const client = await createClient();
|
|
46
|
-
const data = await client.get(`customers/${id}`);
|
|
46
|
+
const data = await client.get(`V1/customers/${id}`);
|
|
47
47
|
|
|
48
48
|
const answers = await inquirer.prompt([
|
|
49
49
|
{ name: 'firstname', message: 'First Name', default: data.firstname },
|
|
@@ -60,7 +60,7 @@ export function registerCustomersCommands(program) {
|
|
|
60
60
|
}
|
|
61
61
|
};
|
|
62
62
|
|
|
63
|
-
await client.put(`customers/${id}`, payload);
|
|
63
|
+
await client.put(`V1/customers/${id}`, payload);
|
|
64
64
|
console.log(chalk.green(`Customer ${id} updated.`));
|
|
65
65
|
} catch (e) { handleError(e); }
|
|
66
66
|
});
|
|
@@ -75,7 +75,7 @@ export function registerCustomersCommands(program) {
|
|
|
75
75
|
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
76
76
|
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
77
77
|
|
|
78
|
-
const data = await client.get(`customers/${customerId}`, {}, { headers });
|
|
78
|
+
const data = await client.get(`V1/customers/${customerId}`, {}, { headers });
|
|
79
79
|
|
|
80
80
|
if (options.format === 'json') {
|
|
81
81
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -139,7 +139,7 @@ export function registerCustomersCommands(program) {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
const client = await createClient();
|
|
142
|
-
await client.delete(`customers/${customerId}`);
|
|
142
|
+
await client.delete(`V1/customers/${customerId}`);
|
|
143
143
|
console.log(chalk.green(`✅ Customer ${customerId} deleted.`));
|
|
144
144
|
} catch (e) { handleError(e); }
|
|
145
145
|
});
|
|
@@ -154,7 +154,7 @@ export function registerCustomersCommands(program) {
|
|
|
154
154
|
|
|
155
155
|
if (customerId) {
|
|
156
156
|
try {
|
|
157
|
-
const customer = await client.get(`customers/${customerId}`);
|
|
157
|
+
const customer = await client.get(`V1/customers/${customerId}`);
|
|
158
158
|
email = customer.email;
|
|
159
159
|
websiteId = customer.website_id;
|
|
160
160
|
console.log(chalk.gray(`Fetched customer ${customerId}: ${email} (Website: ${websiteId})`));
|
|
@@ -183,7 +183,7 @@ export function registerCustomersCommands(program) {
|
|
|
183
183
|
redirectUrl: options.redirectUrl || undefined
|
|
184
184
|
};
|
|
185
185
|
|
|
186
|
-
await client.post('customers/confirm', payload);
|
|
186
|
+
await client.post('V1/customers/confirm', payload);
|
|
187
187
|
console.log(chalk.green(`✅ Confirmation email sent to ${email}`));
|
|
188
188
|
} catch (e) {
|
|
189
189
|
if (e.response && e.response.status === 400 && e.response.data && e.response.data.message === "Confirmation isn't needed.") {
|
package/lib/commands/eav.js
CHANGED
|
@@ -19,7 +19,7 @@ export function registerEavCommands(program) {
|
|
|
19
19
|
'searchCriteria[currentPage]': options.page,
|
|
20
20
|
'searchCriteria[pageSize]': options.size
|
|
21
21
|
};
|
|
22
|
-
const data = await client.get('eav/attribute-sets/list', params);
|
|
22
|
+
const data = await client.get('V1/eav/attribute-sets/list', params);
|
|
23
23
|
const rows = (data.items || []).map(set => [
|
|
24
24
|
set.attribute_set_id,
|
|
25
25
|
set.attribute_set_name,
|
|
@@ -36,7 +36,7 @@ export function registerEavCommands(program) {
|
|
|
36
36
|
.action(async (id) => {
|
|
37
37
|
try {
|
|
38
38
|
const client = await createClient();
|
|
39
|
-
const data = await client.get(`eav/attribute-sets/${id}`);
|
|
39
|
+
const data = await client.get(`V1/eav/attribute-sets/${id}`);
|
|
40
40
|
|
|
41
41
|
console.log(chalk.bold.blue('\nAttribute Set Details:'));
|
|
42
42
|
console.log(`${chalk.bold('ID:')} ${data.attribute_set_id}`);
|
package/lib/commands/orders.js
CHANGED
|
@@ -16,7 +16,7 @@ export function registerOrdersCommands(program) {
|
|
|
16
16
|
'searchCriteria[currentPage]': options.page,
|
|
17
17
|
'searchCriteria[pageSize]': options.size
|
|
18
18
|
};
|
|
19
|
-
const data = await client.get('orders', params);
|
|
19
|
+
const data = await client.get('V1/orders', params);
|
|
20
20
|
const rows = (data.items || []).map(o => [o.entity_id, o.increment_id, o.status, o.grand_total, o.customer_email]);
|
|
21
21
|
console.log(chalk.bold(`Total: ${data.total_count}, Page: ${options.page}, Size: ${options.size}`));
|
|
22
22
|
printTable(['ID', 'Increment ID', 'Status', 'Total', 'Email'], rows);
|
|
@@ -33,7 +33,7 @@ export function registerOrdersCommands(program) {
|
|
|
33
33
|
'searchCriteria[filter_groups][0][filters][0][value]': `%${query}%`,
|
|
34
34
|
'searchCriteria[filter_groups][0][filters][0][condition_type]': 'like'
|
|
35
35
|
};
|
|
36
|
-
const data = await client.get('orders', params);
|
|
36
|
+
const data = await client.get('V1/orders', params);
|
|
37
37
|
const rows = (data.items || []).map(o => [o.entity_id, o.increment_id, o.status, o.grand_total]);
|
|
38
38
|
printTable(['ID', 'Increment ID', 'Status', 'Total'], rows);
|
|
39
39
|
} catch (e) { handleError(e); }
|
|
@@ -52,7 +52,7 @@ export function registerOrdersCommands(program) {
|
|
|
52
52
|
]);
|
|
53
53
|
|
|
54
54
|
// POST /V1/orders/:id/comments
|
|
55
|
-
await client.post(`orders/${id}/comments`, {
|
|
55
|
+
await client.post(`V1/orders/${id}/comments`, {
|
|
56
56
|
statusHistory: {
|
|
57
57
|
comment: answers.comment,
|
|
58
58
|
status: answers.status,
|
|
@@ -86,7 +86,7 @@ export function registerOrdersCommands(program) {
|
|
|
86
86
|
'searchCriteria[sortOrders][0][field]': 'created_at',
|
|
87
87
|
'searchCriteria[sortOrders][0][direction]': 'DESC'
|
|
88
88
|
};
|
|
89
|
-
const data = await client.get('orders', params);
|
|
89
|
+
const data = await client.get('V1/orders', params);
|
|
90
90
|
const items = data.items || [];
|
|
91
91
|
|
|
92
92
|
if (items.length === 0) {
|
|
@@ -141,7 +141,7 @@ async function showOrder(identifier, format = 'text') {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
try {
|
|
144
|
-
order = await client.get(`orders/${identifier}`, {}, { headers });
|
|
144
|
+
order = await client.get(`V1/orders/${identifier}`, {}, { headers });
|
|
145
145
|
} catch (e) {
|
|
146
146
|
// If 404, maybe it was an increment ID
|
|
147
147
|
// Or if user provided something that is definitely an increment ID
|
|
@@ -150,14 +150,14 @@ async function showOrder(identifier, format = 'text') {
|
|
|
150
150
|
'searchCriteria[filter_groups][0][filters][0][value]': identifier,
|
|
151
151
|
'searchCriteria[filter_groups][0][filters][0][condition_type]': 'eq'
|
|
152
152
|
};
|
|
153
|
-
const searchData = await client.get('orders', params);
|
|
153
|
+
const searchData = await client.get('V1/orders', params);
|
|
154
154
|
if (searchData.items && searchData.items.length > 0) {
|
|
155
155
|
order = searchData.items[0];
|
|
156
156
|
// If format is specific, we might want to re-fetch by ID with headers if search didn't return format?
|
|
157
157
|
// Search API usually returns JSON. If user wants XML, search return helps find ID, then we fetch XML.
|
|
158
158
|
if (format !== 'text') {
|
|
159
159
|
try {
|
|
160
|
-
order = await client.get(`orders/${order.entity_id}`, {}, { headers });
|
|
160
|
+
order = await client.get(`V1/orders/${order.entity_id}`, {}, { headers });
|
|
161
161
|
} catch (subError) {
|
|
162
162
|
// ignore or log? If we fail to get formatted, fallback or throw?
|
|
163
163
|
// If we found it via search, 'order' is the object.
|
package/lib/commands/products.js
CHANGED
|
@@ -10,7 +10,7 @@ export function registerProductsCommands(program) {
|
|
|
10
10
|
.action(async () => {
|
|
11
11
|
try {
|
|
12
12
|
const client = await createClient();
|
|
13
|
-
const data = await client.get('products/types');
|
|
13
|
+
const data = await client.get('V1/products/types');
|
|
14
14
|
const rows = (data || []).map(t => [t.name, t.label]);
|
|
15
15
|
printTable(['Name (ID)', 'Label'], rows);
|
|
16
16
|
} catch (e) { handleError(e); }
|
|
@@ -31,7 +31,7 @@ export function registerProductsCommands(program) {
|
|
|
31
31
|
'searchCriteria[sortOrders][0][field]': 'attribute_code',
|
|
32
32
|
'searchCriteria[sortOrders][0][direction]': 'ASC'
|
|
33
33
|
};
|
|
34
|
-
const data = await client.get('products/attributes', params);
|
|
34
|
+
const data = await client.get('V1/products/attributes', params);
|
|
35
35
|
const rows = (data.items || []).map(a => [
|
|
36
36
|
a.attribute_id,
|
|
37
37
|
a.attribute_code,
|
|
@@ -56,7 +56,7 @@ export function registerProductsCommands(program) {
|
|
|
56
56
|
|
|
57
57
|
let data;
|
|
58
58
|
try {
|
|
59
|
-
data = await client.get(`products/attributes/${attributeCode}`, {}, { headers });
|
|
59
|
+
data = await client.get(`V1/products/attributes/${attributeCode}`, {}, { headers });
|
|
60
60
|
} catch (e) {
|
|
61
61
|
throw new Error(`Attribute '${attributeCode}' not found.`);
|
|
62
62
|
}
|
|
@@ -125,7 +125,7 @@ export function registerProductsCommands(program) {
|
|
|
125
125
|
.action(async () => {
|
|
126
126
|
try {
|
|
127
127
|
const client = await createClient();
|
|
128
|
-
const data = await client.get('products/attributes/types');
|
|
128
|
+
const data = await client.get('V1/products/attributes/types');
|
|
129
129
|
const rows = (data || []).map(t => [t.value, t.label]);
|
|
130
130
|
printTable(['Value', 'Label'], rows);
|
|
131
131
|
} catch (e) { handleError(e); }
|
package/lib/commands/stores.js
CHANGED
|
@@ -10,7 +10,7 @@ export function registerStoresCommands(program) {
|
|
|
10
10
|
.action(async () => {
|
|
11
11
|
try {
|
|
12
12
|
const client = await createClient();
|
|
13
|
-
const data = await client.get('store/storeGroups');
|
|
13
|
+
const data = await client.get('V1/store/storeGroups');
|
|
14
14
|
const rows = data.map(s => [s.id, s.code, s.name, s.website_id, s.root_category_id]);
|
|
15
15
|
printTable(['ID', 'Code', 'Name', 'Web ID', 'Root Cat'], rows);
|
|
16
16
|
} catch (e) { handleError(e); }
|
|
@@ -20,7 +20,7 @@ export function registerStoresCommands(program) {
|
|
|
20
20
|
.action(async (query) => {
|
|
21
21
|
try {
|
|
22
22
|
const client = await createClient();
|
|
23
|
-
const data = await client.get('store/storeGroups');
|
|
23
|
+
const data = await client.get('V1/store/storeGroups');
|
|
24
24
|
const filtered = data.filter(s => s.name.includes(query) || s.code.includes(query));
|
|
25
25
|
const rows = filtered.map(s => [s.id, s.code, s.name]);
|
|
26
26
|
printTable(['ID', 'Code', 'Name'], rows);
|
|
@@ -33,7 +33,7 @@ export function registerStoresCommands(program) {
|
|
|
33
33
|
const client = await createClient();
|
|
34
34
|
const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: `Delete Store Group ${id}?` }]);
|
|
35
35
|
if (!confirm) return;
|
|
36
|
-
await client.delete(`store/storeGroups/${id}`);
|
|
36
|
+
await client.delete(`V1/store/storeGroups/${id}`);
|
|
37
37
|
console.log(chalk.green(`Store Group ${id} deleted.`));
|
|
38
38
|
} catch (e) { handleError(e); }
|
|
39
39
|
});
|
|
@@ -42,7 +42,7 @@ export function registerStoresCommands(program) {
|
|
|
42
42
|
.action(async (id) => {
|
|
43
43
|
try {
|
|
44
44
|
const client = await createClient();
|
|
45
|
-
const all = await client.get('store/storeGroups');
|
|
45
|
+
const all = await client.get('V1/store/storeGroups');
|
|
46
46
|
const current = all.find(s => s.id == id);
|
|
47
47
|
if (!current) throw new Error(`Store ${id} not found`);
|
|
48
48
|
|
|
@@ -52,7 +52,7 @@ export function registerStoresCommands(program) {
|
|
|
52
52
|
{ name: 'website_id', message: 'Website ID', default: current.website_id }
|
|
53
53
|
]);
|
|
54
54
|
|
|
55
|
-
await client.put(`store/storeGroups/${id}`, { group: { id: parseInt(id), ...answers } });
|
|
55
|
+
await client.put(`V1/store/storeGroups/${id}`, { group: { id: parseInt(id), ...answers } });
|
|
56
56
|
console.log(chalk.green(`Store ${id} updated.`));
|
|
57
57
|
} catch (e) { handleError(e); }
|
|
58
58
|
});
|
|
@@ -64,7 +64,7 @@ export function registerStoresCommands(program) {
|
|
|
64
64
|
.action(async () => {
|
|
65
65
|
try {
|
|
66
66
|
const client = await createClient();
|
|
67
|
-
const data = await client.get('store/storeViews');
|
|
67
|
+
const data = await client.get('V1/store/storeViews');
|
|
68
68
|
const rows = data.map(v => [v.id, v.code, v.name, v.store_group_id, v.is_active]);
|
|
69
69
|
printTable(['ID', 'Code', 'Name', 'Group ID', 'Active'], rows);
|
|
70
70
|
} catch (e) { handleError(e); }
|
|
@@ -74,7 +74,7 @@ export function registerStoresCommands(program) {
|
|
|
74
74
|
.action(async (query) => {
|
|
75
75
|
try {
|
|
76
76
|
const client = await createClient();
|
|
77
|
-
const data = await client.get('store/storeViews');
|
|
77
|
+
const data = await client.get('V1/store/storeViews');
|
|
78
78
|
const filtered = data.filter(v => v.name.includes(query) || v.code.includes(query));
|
|
79
79
|
const rows = filtered.map(v => [v.id, v.code, v.name]);
|
|
80
80
|
printTable(['ID', 'Code', 'Name'], rows);
|
|
@@ -87,7 +87,7 @@ export function registerStoresCommands(program) {
|
|
|
87
87
|
const client = await createClient();
|
|
88
88
|
const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: `Delete Store View ${id}?` }]);
|
|
89
89
|
if (!confirm) return;
|
|
90
|
-
await client.delete(`store/storeViews/${id}`);
|
|
90
|
+
await client.delete(`V1/store/storeViews/${id}`);
|
|
91
91
|
console.log(chalk.green(`Store View ${id} deleted.`));
|
|
92
92
|
} catch (e) { handleError(e); }
|
|
93
93
|
});
|
|
@@ -96,7 +96,7 @@ export function registerStoresCommands(program) {
|
|
|
96
96
|
.action(async (id) => {
|
|
97
97
|
try {
|
|
98
98
|
const client = await createClient();
|
|
99
|
-
const all = await client.get('store/storeViews');
|
|
99
|
+
const all = await client.get('V1/store/storeViews');
|
|
100
100
|
const current = all.find(v => v.id == id);
|
|
101
101
|
if (!current) throw new Error(`View ${id} not found`);
|
|
102
102
|
|
|
@@ -106,7 +106,7 @@ export function registerStoresCommands(program) {
|
|
|
106
106
|
{ name: 'is_active', message: 'Is Active (0/1)', default: current.is_active }
|
|
107
107
|
]);
|
|
108
108
|
|
|
109
|
-
await client.put(`store/storeViews/${id}`, { store: { id: parseInt(id), ...answers } });
|
|
109
|
+
await client.put(`V1/store/storeViews/${id}`, { store: { id: parseInt(id), ...answers } });
|
|
110
110
|
console.log(chalk.green(`Store View ${id} updated.`));
|
|
111
111
|
} catch (e) { handleError(e); }
|
|
112
112
|
});
|
package/lib/commands/tax.js
CHANGED
|
@@ -18,7 +18,7 @@ export function registerTaxCommands(program) {
|
|
|
18
18
|
'searchCriteria[currentPage]': options.page,
|
|
19
19
|
'searchCriteria[pageSize]': options.size
|
|
20
20
|
};
|
|
21
|
-
const data = await client.get('taxClasses/search', params);
|
|
21
|
+
const data = await client.get('V1/taxClasses/search', params);
|
|
22
22
|
const rows = (data.items || []).map(tc => [
|
|
23
23
|
tc.class_id,
|
|
24
24
|
tc.class_name,
|
|
@@ -34,7 +34,7 @@ export function registerTaxCommands(program) {
|
|
|
34
34
|
.action(async (id) => {
|
|
35
35
|
try {
|
|
36
36
|
const client = await createClient();
|
|
37
|
-
const data = await client.get(`taxClasses/${id}`);
|
|
37
|
+
const data = await client.get(`V1/taxClasses/${id}`);
|
|
38
38
|
|
|
39
39
|
console.log(chalk.bold.blue('\nTax Class Details:'));
|
|
40
40
|
console.log(`${chalk.bold('ID:')} ${data.class_id}`);
|
package/lib/commands/websites.js
CHANGED
|
@@ -11,7 +11,7 @@ export function registerWebsitesCommands(program) {
|
|
|
11
11
|
.action(async () => {
|
|
12
12
|
try {
|
|
13
13
|
const client = await createClient();
|
|
14
|
-
const data = await client.get('store/websites');
|
|
14
|
+
const data = await client.get('V1/store/websites');
|
|
15
15
|
const rows = data.map(w => [w.id, w.code, w.name, w.default_group_id]);
|
|
16
16
|
printTable(['ID', 'Code', 'Name', 'Def Group ID'], rows);
|
|
17
17
|
} catch (e) { handleError(e); }
|
|
@@ -22,7 +22,7 @@ export function registerWebsitesCommands(program) {
|
|
|
22
22
|
.action(async (query) => {
|
|
23
23
|
try {
|
|
24
24
|
const client = await createClient();
|
|
25
|
-
const data = await client.get('store/websites');
|
|
25
|
+
const data = await client.get('V1/store/websites');
|
|
26
26
|
const filtered = data.filter(w =>
|
|
27
27
|
w.code.includes(query) || w.name.includes(query)
|
|
28
28
|
);
|
|
@@ -44,7 +44,7 @@ export function registerWebsitesCommands(program) {
|
|
|
44
44
|
}]);
|
|
45
45
|
if (!confirm) return;
|
|
46
46
|
|
|
47
|
-
await client.delete(`store/websites/${id}`);
|
|
47
|
+
await client.delete(`V1/store/websites/${id}`);
|
|
48
48
|
console.log(chalk.green(`Website ${id} deleted.`));
|
|
49
49
|
} catch (e) { handleError(e); }
|
|
50
50
|
});
|
|
@@ -57,7 +57,7 @@ export function registerWebsitesCommands(program) {
|
|
|
57
57
|
// Fetch current
|
|
58
58
|
// Actually Magento doesn't have a single GET /store/websites/:id easily publicly documented distinct from the list?
|
|
59
59
|
// But let's assume we fetch list and find it.
|
|
60
|
-
const all = await client.get('store/websites');
|
|
60
|
+
const all = await client.get('V1/store/websites');
|
|
61
61
|
const current = all.find(w => w.id == id);
|
|
62
62
|
if (!current) throw new Error(`Website ${id} not found`);
|
|
63
63
|
|
|
@@ -69,7 +69,7 @@ export function registerWebsitesCommands(program) {
|
|
|
69
69
|
]);
|
|
70
70
|
|
|
71
71
|
// PUT /V1/store/websites/:id
|
|
72
|
-
await client.put(`store/websites/${id}`, { website: { id: parseInt(id), ...answers } });
|
|
72
|
+
await client.put(`V1/store/websites/${id}`, { website: { id: parseInt(id), ...answers } });
|
|
73
73
|
console.log(chalk.green(`Website ${id} updated.`));
|
|
74
74
|
} catch (e) { handleError(e); }
|
|
75
75
|
});
|
package/lib/config.js
CHANGED
|
@@ -6,6 +6,7 @@ import { mkdirp } from 'mkdirp';
|
|
|
6
6
|
const paths = envPaths('mage-remote-run', { suffix: '' });
|
|
7
7
|
const CONFIG_DIR = paths.config;
|
|
8
8
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
const TOKEN_CACHE_FILE = path.join(CONFIG_DIR, 'token-cache.json');
|
|
9
10
|
|
|
10
11
|
export async function loadConfig() {
|
|
11
12
|
try {
|
|
@@ -52,3 +53,37 @@ export async function getActiveProfile() {
|
|
|
52
53
|
export function getConfigPath() {
|
|
53
54
|
return CONFIG_FILE;
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
export async function loadTokenCache() {
|
|
58
|
+
try {
|
|
59
|
+
if (!fs.existsSync(TOKEN_CACHE_FILE)) {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
const data = fs.readFileSync(TOKEN_CACHE_FILE, 'utf-8');
|
|
63
|
+
return JSON.parse(data);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
console.error("Error loading token cache:", e.message);
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function saveTokenCache(cache) {
|
|
71
|
+
try {
|
|
72
|
+
await mkdirp(CONFIG_DIR);
|
|
73
|
+
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2));
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error("Error saving token cache:", e.message);
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function clearTokenCache() {
|
|
81
|
+
try {
|
|
82
|
+
if (fs.existsSync(TOKEN_CACHE_FILE)) {
|
|
83
|
+
fs.unlinkSync(TOKEN_CACHE_FILE);
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error("Error clearing token cache:", e.message);
|
|
87
|
+
throw e;
|
|
88
|
+
}
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mage-remote-run",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "The remote swiss army knife for Magento Open Source, Mage-OS, Adobe Commerce",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "NODE_OPTIONS
|
|
7
|
+
"test": "NODE_OPTIONS=\"--experimental-vm-modules --localstorage-file=./.localstorage\" jest",
|
|
8
8
|
"start": "node bin/mage-remote-run.js",
|
|
9
9
|
"release": "release-it"
|
|
10
10
|
},
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"type": "module",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/muench-dev/mage-remote-run"
|
|
22
|
+
},
|
|
19
23
|
"bin": {
|
|
20
24
|
"mage-remote-run": "./bin/mage-remote-run.js"
|
|
21
25
|
},
|