mage-remote-run 0.21.0 ā 0.23.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/command-registry.js +13 -5
- package/lib/commands/cart.js +238 -0
- package/lib/commands/connections.js +104 -13
- package/lib/commands/modules.js +62 -0
- package/lib/commands/products.js +37 -0
- package/lib/commands/shipments.js +327 -0
- package/lib/config.js +34 -2
- package/package.json +1 -1
package/lib/command-registry.js
CHANGED
|
@@ -6,15 +6,18 @@ import { registerOrdersCommands } from './commands/orders.js';
|
|
|
6
6
|
import { registerEavCommands } from './commands/eav.js';
|
|
7
7
|
import { registerProductsCommands } from './commands/products.js';
|
|
8
8
|
import { registerCompanyCommands } from './commands/company.js';
|
|
9
|
+
import { registerCartCommands } from './commands/cart.js';
|
|
9
10
|
import { registerTaxCommands } from './commands/tax.js';
|
|
10
11
|
import { registerInventoryCommands } from './commands/inventory.js';
|
|
11
12
|
import { registerEventsCommands } from './commands/events.js';
|
|
12
13
|
import { registerWebhooksCommands } from './commands/webhooks.js';
|
|
13
14
|
import { registerPurchaseOrderCartCommands } from './commands/purchase-order-cart.js';
|
|
14
15
|
import { registerImportCommands } from './commands/import.js';
|
|
16
|
+
import { registerModulesCommands } from './commands/modules.js';
|
|
15
17
|
import { registerConsoleCommand } from './commands/console.js';
|
|
18
|
+
import { registerShipmentCommands } from './commands/shipments.js';
|
|
16
19
|
|
|
17
|
-
export { registerConnectionCommands, registerConsoleCommand };
|
|
20
|
+
export { registerConnectionCommands, registerConsoleCommand, registerShipmentCommands };
|
|
18
21
|
|
|
19
22
|
const GROUPS = {
|
|
20
23
|
CORE: [
|
|
@@ -24,8 +27,10 @@ const GROUPS = {
|
|
|
24
27
|
registerOrdersCommands,
|
|
25
28
|
registerEavCommands,
|
|
26
29
|
registerProductsCommands,
|
|
30
|
+
registerCartCommands,
|
|
27
31
|
registerTaxCommands,
|
|
28
32
|
registerInventoryCommands,
|
|
33
|
+
registerShipmentCommands,
|
|
29
34
|
registerConsoleCommand
|
|
30
35
|
],
|
|
31
36
|
COMMERCE: [
|
|
@@ -38,14 +43,17 @@ const GROUPS = {
|
|
|
38
43
|
],
|
|
39
44
|
IMPORT: [
|
|
40
45
|
registerImportCommands
|
|
46
|
+
],
|
|
47
|
+
MODULES: [
|
|
48
|
+
registerModulesCommands
|
|
41
49
|
]
|
|
42
50
|
};
|
|
43
51
|
|
|
44
52
|
const TYPE_MAPPINGS = {
|
|
45
|
-
'magento-os': [...GROUPS.CORE],
|
|
46
|
-
'mage-os': [...GROUPS.CORE],
|
|
47
|
-
'ac-on-prem': [...GROUPS.CORE, ...GROUPS.COMMERCE, ...GROUPS.IMPORT],
|
|
48
|
-
'ac-cloud-paas': [...GROUPS.CORE, ...GROUPS.COMMERCE, ...GROUPS.CLOUD, ...GROUPS.IMPORT],
|
|
53
|
+
'magento-os': [...GROUPS.CORE, ...GROUPS.MODULES],
|
|
54
|
+
'mage-os': [...GROUPS.CORE, ...GROUPS.MODULES],
|
|
55
|
+
'ac-on-prem': [...GROUPS.CORE, ...GROUPS.COMMERCE, ...GROUPS.IMPORT, ...GROUPS.MODULES],
|
|
56
|
+
'ac-cloud-paas': [...GROUPS.CORE, ...GROUPS.COMMERCE, ...GROUPS.CLOUD, ...GROUPS.IMPORT, ...GROUPS.MODULES],
|
|
49
57
|
'ac-saas': [...GROUPS.CORE, ...GROUPS.COMMERCE, ...GROUPS.CLOUD, ...GROUPS.IMPORT] // Assuming SaaS has same feature set as PaaS for CLI
|
|
50
58
|
};
|
|
51
59
|
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createClient } from '../api/factory.js';
|
|
2
|
+
import { printTable, handleError } from '../utils.js';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export function registerCartCommands(program) {
|
|
6
|
+
const carts = program.command('cart').description('Manage carts');
|
|
7
|
+
|
|
8
|
+
//-------------------------------------------------------
|
|
9
|
+
// "cart show" Command
|
|
10
|
+
//-------------------------------------------------------
|
|
11
|
+
carts.command('show <cartId>')
|
|
12
|
+
.description('Show detailed cart information')
|
|
13
|
+
.option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
|
|
14
|
+
.addHelpText('after', `
|
|
15
|
+
Examples:
|
|
16
|
+
$ mage-remote-run cart show 123
|
|
17
|
+
$ mage-remote-run cart show 123 --format json
|
|
18
|
+
`)
|
|
19
|
+
.action(async (cartId, options) => {
|
|
20
|
+
try {
|
|
21
|
+
const client = await createClient();
|
|
22
|
+
const headers = {};
|
|
23
|
+
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
24
|
+
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
25
|
+
|
|
26
|
+
const data = await client.get(`V1/carts/${cartId}`, {}, { headers });
|
|
27
|
+
|
|
28
|
+
if (options.format === 'json') {
|
|
29
|
+
console.log(JSON.stringify(data, null, 2));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (options.format === 'xml') {
|
|
33
|
+
console.log(data);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(chalk.bold.blue('\nš Cart Information'));
|
|
38
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
39
|
+
|
|
40
|
+
// General Info
|
|
41
|
+
console.log(`${chalk.bold('ID:')} ${data.id}`);
|
|
42
|
+
console.log(`${chalk.bold('Status:')} ${data.is_active ? chalk.green('Active') : chalk.gray('Inactive')}`);
|
|
43
|
+
console.log(`${chalk.bold('Created At:')} ${data.created_at || 'N/A'}`);
|
|
44
|
+
console.log(`${chalk.bold('Updated At:')} ${data.updated_at || 'N/A'}`);
|
|
45
|
+
console.log(`${chalk.bold('Items Count:')} ${data.items_count || 0}`);
|
|
46
|
+
console.log(`${chalk.bold('Items Qty:')} ${data.items_qty || 0}`);
|
|
47
|
+
console.log(`${chalk.bold('Virtual:')} ${data.is_virtual ? 'Yes' : 'No'}`);
|
|
48
|
+
|
|
49
|
+
// Customer Info
|
|
50
|
+
if (data.customer && data.customer.email) {
|
|
51
|
+
const customerName = `${data.customer.firstname || ''} ${data.customer.lastname || ''}`.trim();
|
|
52
|
+
console.log(`${chalk.bold('Customer:')} ${customerName} <${data.customer.email}>`);
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`${chalk.bold('Customer:')} Guest`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Totals (if available) - Fetching just for display
|
|
58
|
+
let totals = null;
|
|
59
|
+
try {
|
|
60
|
+
totals = await client.get(`V1/carts/${cartId}/totals`);
|
|
61
|
+
const currency = totals.quote_currency_code || '';
|
|
62
|
+
console.log(`${chalk.bold('Grand Total:')} ${chalk.green(totals.grand_total + ' ' + currency)}`);
|
|
63
|
+
} catch (e) {
|
|
64
|
+
// Ignore missing totals
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
68
|
+
|
|
69
|
+
// Items
|
|
70
|
+
if (data.items && data.items.length > 0) {
|
|
71
|
+
console.log(chalk.bold('\nš¦ Items'));
|
|
72
|
+
const itemRows = data.items.map(item => [
|
|
73
|
+
item.sku,
|
|
74
|
+
item.name,
|
|
75
|
+
item.qty,
|
|
76
|
+
item.price,
|
|
77
|
+
(item.qty * item.price).toFixed(2) // Approximation as row total might not be in item directly? actually cart item usually has price.
|
|
78
|
+
]);
|
|
79
|
+
printTable(['SKU', 'Name', 'Qty', 'Price', 'Row Total'], itemRows);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Totals Breakdown
|
|
83
|
+
if (totals) {
|
|
84
|
+
// We displayed Grand Total above.
|
|
85
|
+
// Order show doesn't list full breakdown usually, just items and grand total and addresses.
|
|
86
|
+
// But user asked for "Add ... totals there" previously.
|
|
87
|
+
// I'll keep the breakdown but make it compact or skip if Order doesn't have it.
|
|
88
|
+
// Order show has "Grand Total" in header.
|
|
89
|
+
// I will stick to the requested structure "similar to order show".
|
|
90
|
+
// Order show DOES NOT show subtotal/tax explicitly in the "Order Information" block usually, just Grand Total.
|
|
91
|
+
// But I will keep the full breakdown nicely formatted if requested.
|
|
92
|
+
// Actually, let's append it after items or before addresses.
|
|
93
|
+
// Or just skip it to strict "match order show".
|
|
94
|
+
// User said "Add the address information... and the totals there". So I MUST include totals.
|
|
95
|
+
console.log(chalk.bold('\nš° Totals Breakdown'));
|
|
96
|
+
console.log(` Subtotal: ${totals.subtotal}`);
|
|
97
|
+
if (totals.tax_amount > 0) console.log(` Tax: ${totals.tax_amount}`);
|
|
98
|
+
if (totals.shipping_amount > 0) console.log(` Shipping: ${totals.shipping_amount}`);
|
|
99
|
+
if (totals.discount_amount < 0) console.log(` Discount: ${totals.discount_amount}`);
|
|
100
|
+
console.log(chalk.bold(` Grand Total: ${totals.grand_total} ${totals.quote_currency_code}`));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Addresses
|
|
104
|
+
if (data.billing_address || (data.extension_attributes && data.extension_attributes.shipping_assignments)) {
|
|
105
|
+
console.log(chalk.bold('\nš Addresses'));
|
|
106
|
+
|
|
107
|
+
if (data.billing_address) {
|
|
108
|
+
console.log(chalk.bold.underline('Billing Address:'));
|
|
109
|
+
printAddress(data.billing_address);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (data.extension_attributes && data.extension_attributes.shipping_assignments) {
|
|
113
|
+
data.extension_attributes.shipping_assignments.forEach((assignment, idx) => {
|
|
114
|
+
if (assignment.shipping && assignment.shipping.address) {
|
|
115
|
+
console.log(chalk.bold.underline(idx === 0 ? '\nShipping Address:' : `\nShipping Address (${idx + 1}):`));
|
|
116
|
+
printAddress(assignment.shipping.address);
|
|
117
|
+
if (assignment.shipping.method) {
|
|
118
|
+
console.log(chalk.gray(`Method: ${assignment.shipping.method}`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Helper for address printing (inline to avoid messing with utils exports for now or duplicate)
|
|
126
|
+
function printAddress(addr) {
|
|
127
|
+
if (!addr) return;
|
|
128
|
+
|
|
129
|
+
const name = `${addr.firstname || ''} ${addr.lastname || ''}`.trim();
|
|
130
|
+
const street = Array.isArray(addr.street) ? addr.street : (addr.street ? [addr.street] : []);
|
|
131
|
+
|
|
132
|
+
// Handle region: could be string, or object with region_code/region
|
|
133
|
+
let region = addr.region || addr.region_code || '';
|
|
134
|
+
if (typeof region === 'object') {
|
|
135
|
+
region = region.region_code || region.region || '';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const cityParts = [
|
|
139
|
+
addr.city,
|
|
140
|
+
region,
|
|
141
|
+
addr.postcode
|
|
142
|
+
].filter(Boolean);
|
|
143
|
+
|
|
144
|
+
const lines = [
|
|
145
|
+
name,
|
|
146
|
+
...street,
|
|
147
|
+
cityParts.length > 0 ? cityParts.join(', ') : null,
|
|
148
|
+
addr.country_id,
|
|
149
|
+
addr.telephone ? `T: ${addr.telephone}` : null
|
|
150
|
+
].filter(Boolean);
|
|
151
|
+
|
|
152
|
+
if (lines.length === 0) console.log(chalk.gray(' (Empty Address)'));
|
|
153
|
+
else lines.forEach(l => console.log(` ${l}`));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
} catch (e) { handleError(e); }
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
//-------------------------------------------------------
|
|
160
|
+
// "cart list" Command (Renamed from search)
|
|
161
|
+
//-------------------------------------------------------
|
|
162
|
+
carts.command('list')
|
|
163
|
+
.description('List carts')
|
|
164
|
+
.option('-p, --page <number>', 'Page number', '1')
|
|
165
|
+
.option('-s, --size <number>', 'Page size', '20')
|
|
166
|
+
.option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
|
|
167
|
+
.option('--filter <filter...>', 'Filter options (e.g. "field:value:condition_type" or "field:value")', [])
|
|
168
|
+
.option('--sort <sort...>', 'Sort options (e.g. "field:direction")', [])
|
|
169
|
+
.addHelpText('after', `
|
|
170
|
+
Examples:
|
|
171
|
+
$ mage-remote-run cart list
|
|
172
|
+
$ mage-remote-run cart list --page 2 --size 50
|
|
173
|
+
$ mage-remote-run cart list --filter "is_active:1"
|
|
174
|
+
$ mage-remote-run cart list --sort "created_at:DESC"
|
|
175
|
+
`)
|
|
176
|
+
.action(async (options) => {
|
|
177
|
+
try {
|
|
178
|
+
const client = await createClient();
|
|
179
|
+
const headers = {};
|
|
180
|
+
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
181
|
+
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
182
|
+
|
|
183
|
+
let params = {
|
|
184
|
+
'searchCriteria[currentPage]': options.page,
|
|
185
|
+
'searchCriteria[pageSize]': options.size
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Process filters
|
|
189
|
+
if (options.filter && options.filter.length > 0) {
|
|
190
|
+
options.filter.forEach((f, idx) => {
|
|
191
|
+
const parts = f.split(':');
|
|
192
|
+
const field = parts[0];
|
|
193
|
+
const value = parts[1];
|
|
194
|
+
const condition = parts[2] || 'eq';
|
|
195
|
+
|
|
196
|
+
params[`searchCriteria[filter_groups][${idx}][filters][0][field]`] = field;
|
|
197
|
+
params[`searchCriteria[filter_groups][${idx}][filters][0][value]`] = value;
|
|
198
|
+
params[`searchCriteria[filter_groups][${idx}][filters][0][condition_type]`] = condition;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Process sorting
|
|
203
|
+
if (options.sort && options.sort.length > 0) {
|
|
204
|
+
options.sort.forEach((s, idx) => {
|
|
205
|
+
const parts = s.split(':');
|
|
206
|
+
const field = parts[0];
|
|
207
|
+
const direction = parts[1] || 'ASC';
|
|
208
|
+
|
|
209
|
+
params[`searchCriteria[sortOrders][${idx}][field]`] = field;
|
|
210
|
+
params[`searchCriteria[sortOrders][${idx}][direction]`] = direction;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const data = await client.get('V1/carts/search', params, { headers });
|
|
215
|
+
|
|
216
|
+
if (options.format === 'json') {
|
|
217
|
+
console.log(JSON.stringify(data, null, 2));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (options.format === 'xml') {
|
|
221
|
+
console.log(data);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const rows = (data.items || []).map(c => [
|
|
226
|
+
c.id,
|
|
227
|
+
c.customer ? c.customer.email : 'Guest',
|
|
228
|
+
c.is_active ? 'Yes' : 'No',
|
|
229
|
+
c.items_qty || 0,
|
|
230
|
+
c.created_at
|
|
231
|
+
]);
|
|
232
|
+
|
|
233
|
+
console.log(chalk.bold(`Total: ${data.total_count}, Page: ${options.page}, Size: ${options.size}`));
|
|
234
|
+
printTable(['ID', 'Customer', 'Active', 'Qty', 'Created'], rows);
|
|
235
|
+
|
|
236
|
+
} catch (e) { handleError(e); }
|
|
237
|
+
});
|
|
238
|
+
}
|
|
@@ -57,6 +57,107 @@ async function configureAndTestConnection(name, initialSettings = {}) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
// Helper to print connection status
|
|
61
|
+
async function printConnectionStatus(config) {
|
|
62
|
+
if (!config.activeProfile) {
|
|
63
|
+
console.log(chalk.yellow('No active profile configured. Run "connection add" or "connection select".'));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const profile = config.profiles[config.activeProfile];
|
|
68
|
+
|
|
69
|
+
if (profile) {
|
|
70
|
+
// ASCII Logos
|
|
71
|
+
const logos = {
|
|
72
|
+
adobe: chalk.red(`
|
|
73
|
+
@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@
|
|
74
|
+
@@@@@@@@@@@@@@ @@@@@@@@@@@@@@
|
|
75
|
+
@@@@@@@@@@@@@ @@@@@@@@@@@@@
|
|
76
|
+
@@@@@@@@@@@@@ @@@@@@@@@@@@
|
|
77
|
+
@@@@@@@@@@@@ @@@@@@@@@@@@
|
|
78
|
+
@@@@@@@@@@@ @@@@@@@@@@@
|
|
79
|
+
@@@@@@@@@@ @@@@@@@@@@
|
|
80
|
+
@@@@@@@@@ @@@ @@@@@@@@@
|
|
81
|
+
@@@@@@@@@ @@@ @@@@@@@@@
|
|
82
|
+
@@@@@@@@ @@@@@ @@@@@@@@
|
|
83
|
+
@@@@@@@ @@@@@@@ @@@@@@@
|
|
84
|
+
@@@@@@ @@@@@@@@@ @@@@@@
|
|
85
|
+
@@@@@@ @@@@@@@@@@@ @@@@@@
|
|
86
|
+
@@@@@ @@@@@@@@@@@@ @@@@@
|
|
87
|
+
@@@@ @@@@@@@@@@@@@ @@@@
|
|
88
|
+
@@@ @@@@@@@ @@@
|
|
89
|
+
@@ @@@@@@@ @@
|
|
90
|
+
@@ @@@@@@ @@
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
`),
|
|
94
|
+
magento: chalk.hex('#FFA500')(`
|
|
95
|
+
@@
|
|
96
|
+
@@@@@@@@
|
|
97
|
+
@@@@@@@@@@@@@@
|
|
98
|
+
@@@@@@@@@@@@@@@@@@@@
|
|
99
|
+
@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
100
|
+
@@@@@@@@@@@@@@ @@@@@@@@@@@@@@
|
|
101
|
+
@@@@@@@@@@@@@@ @@@@@@@@@@@@@@
|
|
102
|
+
@@@@@@@@@@@@@ @@@@@@@@@@@@@
|
|
103
|
+
@@@@@@@@@@ @@@@@@@@@@
|
|
104
|
+
@@@@@@@ @@@@ @@@@ @@@@@@@
|
|
105
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
106
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
107
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
108
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
109
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
110
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
111
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
112
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
113
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
114
|
+
@@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
|
|
115
|
+
@@@@@ @@@@@@@ @@@@@@@ @@@@@
|
|
116
|
+
@@@ @@@@@@@ @@@@@@@ @@@
|
|
117
|
+
@@@@@@@@@@@@@@@@@@
|
|
118
|
+
@@@@@@@@@@@@@@@@@@
|
|
119
|
+
@@@@@@@@@@@@@@
|
|
120
|
+
@@@@@@@@
|
|
121
|
+
@@ `),
|
|
122
|
+
mageos: chalk.hex('#FFA500')(`
|
|
123
|
+
====== ======
|
|
124
|
+
============ ============
|
|
125
|
+
====================================
|
|
126
|
+
==========================================
|
|
127
|
+
================================================
|
|
128
|
+
================================-===============--
|
|
129
|
+
=============----============----============-----
|
|
130
|
+
=========--------=========-------=========--------
|
|
131
|
+
========---------========--------========---------
|
|
132
|
+
========---------========--------========---------
|
|
133
|
+
========---------========--------========---------
|
|
134
|
+
=====------ ======------ =====------
|
|
135
|
+
==--- ===--- ==---
|
|
136
|
+
`)
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
let logo = '';
|
|
140
|
+
if (profile.type && profile.type.startsWith('ac-')) {
|
|
141
|
+
logo = logos.adobe;
|
|
142
|
+
} else if (profile.type === 'mage-os') {
|
|
143
|
+
logo = logos.mageos;
|
|
144
|
+
} else if (profile.type === 'magento-os') {
|
|
145
|
+
logo = logos.magento;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (logo) {
|
|
149
|
+
console.log(logo);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log(chalk.bold('Active Profile:'), chalk.green(config.activeProfile));
|
|
153
|
+
console.log(`Type: ${profile.type}`);
|
|
154
|
+
console.log(`URL: ${profile.url}`);
|
|
155
|
+
} else {
|
|
156
|
+
console.log(chalk.red('Profile not found in configuration!'));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
60
161
|
export function registerConnectionCommands(program) {
|
|
61
162
|
const connections = program.command('connection').description('Manage mage-remote-run connection profiles');
|
|
62
163
|
|
|
@@ -307,18 +408,7 @@ Examples:
|
|
|
307
408
|
.action(async () => {
|
|
308
409
|
try {
|
|
309
410
|
const config = await loadConfig();
|
|
310
|
-
|
|
311
|
-
console.log(chalk.yellow('No active profile configured. Run "connection add" or "connection select".'));
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
console.log(chalk.bold('Active Profile:'), chalk.green(config.activeProfile));
|
|
315
|
-
const profile = config.profiles[config.activeProfile];
|
|
316
|
-
if (profile) {
|
|
317
|
-
console.log(`Type: ${profile.type}`);
|
|
318
|
-
console.log(`URL: ${profile.url}`);
|
|
319
|
-
} else {
|
|
320
|
-
console.log(chalk.red('Profile not found in configuration!'));
|
|
321
|
-
}
|
|
411
|
+
await printConnectionStatus(config);
|
|
322
412
|
} catch (e) { handleError(e); }
|
|
323
413
|
});
|
|
324
414
|
|
|
@@ -355,7 +445,8 @@ Examples:
|
|
|
355
445
|
|
|
356
446
|
config.activeProfile = selected;
|
|
357
447
|
await saveConfig(config);
|
|
358
|
-
|
|
448
|
+
|
|
449
|
+
await printConnectionStatus(config);
|
|
359
450
|
} catch (e) { handleError(e); }
|
|
360
451
|
});
|
|
361
452
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
|
|
2
|
+
import { createClient } from '../api/factory.js';
|
|
3
|
+
import { printTable, handleError } from '../utils.js';
|
|
4
|
+
|
|
5
|
+
export function registerModulesCommands(program) {
|
|
6
|
+
const modules = program.command('module').description('Manage modules');
|
|
7
|
+
|
|
8
|
+
//-------------------------------------------------------
|
|
9
|
+
// "module list" Command
|
|
10
|
+
//-------------------------------------------------------
|
|
11
|
+
modules.command('list')
|
|
12
|
+
.description('List all modules')
|
|
13
|
+
.option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
|
|
14
|
+
.addHelpText('after', `
|
|
15
|
+
Examples:
|
|
16
|
+
$ mage-remote-run module list
|
|
17
|
+
$ mage-remote-run module list --format json
|
|
18
|
+
`)
|
|
19
|
+
.action(async (options) => {
|
|
20
|
+
try {
|
|
21
|
+
const client = await createClient();
|
|
22
|
+
const headers = {};
|
|
23
|
+
// The API endpoint seems to be just a list of strings usually for /V1/modules?
|
|
24
|
+
// Actually standard Magento API GET /V1/modules returns string[].
|
|
25
|
+
|
|
26
|
+
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
27
|
+
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
28
|
+
|
|
29
|
+
let data;
|
|
30
|
+
try {
|
|
31
|
+
data = await client.get('V1/modules', {}, { headers });
|
|
32
|
+
} catch (apiError) {
|
|
33
|
+
// Sometimes client.get throws if non-200, which is good.
|
|
34
|
+
throw apiError;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (options.format === 'json') {
|
|
38
|
+
console.log(JSON.stringify(data, null, 2));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (options.format === 'xml') {
|
|
42
|
+
// If the response was already XML string because of Accept header, data might be the string.
|
|
43
|
+
// But client.get usually parses JSON by default. If Accept is XML, axios might return string.
|
|
44
|
+
// Let's assume data is what we got.
|
|
45
|
+
console.log(data);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Default text format
|
|
50
|
+
// data is expected to be an array of module names strings ["Magento_Store", ...]
|
|
51
|
+
// Map to rows for table
|
|
52
|
+
if (Array.isArray(data)) {
|
|
53
|
+
const rows = data.map(m => [m]);
|
|
54
|
+
printTable(['Module'], rows);
|
|
55
|
+
} else {
|
|
56
|
+
// Fallback if structure is different
|
|
57
|
+
console.log(data);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
} catch (e) { handleError(e); }
|
|
61
|
+
});
|
|
62
|
+
}
|
package/lib/commands/products.js
CHANGED
|
@@ -357,4 +357,41 @@ Examples:
|
|
|
357
357
|
printTable(['Value', 'Label'], rows);
|
|
358
358
|
} catch (e) { handleError(e); }
|
|
359
359
|
});
|
|
360
|
+
|
|
361
|
+
const linkTypes = products.command('link-type').description('Manage product link types');
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
//-------------------------------------------------------
|
|
365
|
+
// "product link-type list" Command
|
|
366
|
+
//-------------------------------------------------------
|
|
367
|
+
linkTypes.command('list')
|
|
368
|
+
.description('List available product link types')
|
|
369
|
+
.option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
|
|
370
|
+
.addHelpText('after', `
|
|
371
|
+
Examples:
|
|
372
|
+
$ mage-remote-run product link-type list
|
|
373
|
+
$ mage-remote-run product link-type list --format json
|
|
374
|
+
`)
|
|
375
|
+
.action(async (options) => {
|
|
376
|
+
try {
|
|
377
|
+
const client = await createClient();
|
|
378
|
+
const headers = {};
|
|
379
|
+
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
380
|
+
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
381
|
+
|
|
382
|
+
const data = await client.get('V1/products/links/types', {}, { headers });
|
|
383
|
+
|
|
384
|
+
if (options.format === 'json') {
|
|
385
|
+
console.log(JSON.stringify(data, null, 2));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (options.format === 'xml') {
|
|
389
|
+
console.log(data);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const rows = (data || []).map(t => [t.code, t.name]);
|
|
394
|
+
printTable(['Code', 'Name'], rows);
|
|
395
|
+
} catch (e) { handleError(e); }
|
|
396
|
+
});
|
|
360
397
|
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { createClient } from '../api/factory.js';
|
|
2
|
+
import { printTable, handleError } from '../utils.js';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
|
|
6
|
+
export function registerShipmentCommands(program) {
|
|
7
|
+
const shipments = program.command('shipment').description('Manage shipments');
|
|
8
|
+
|
|
9
|
+
//-------------------------------------------------------
|
|
10
|
+
// "shipment list" Command
|
|
11
|
+
//-------------------------------------------------------
|
|
12
|
+
shipments.command('list')
|
|
13
|
+
.description('List shipments')
|
|
14
|
+
.option('-p, --page <number>', 'Page number', '1')
|
|
15
|
+
.option('-s, --size <number>', 'Page size', '20')
|
|
16
|
+
.option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
|
|
17
|
+
.option('--order-id <id>', 'Filter by Order ID')
|
|
18
|
+
.addHelpText('after', `
|
|
19
|
+
Examples:
|
|
20
|
+
$ mage-remote-run shipment list
|
|
21
|
+
$ mage-remote-run shipment list --page 1 --size 10
|
|
22
|
+
$ mage-remote-run shipment list --format json
|
|
23
|
+
$ mage-remote-run shipment list --order-id 123
|
|
24
|
+
`)
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
try {
|
|
27
|
+
const client = await createClient();
|
|
28
|
+
const headers = {};
|
|
29
|
+
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
30
|
+
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
31
|
+
|
|
32
|
+
const params = {
|
|
33
|
+
'searchCriteria[currentPage]': options.page,
|
|
34
|
+
'searchCriteria[pageSize]': options.size
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (options.orderId) {
|
|
38
|
+
params['searchCriteria[filter_groups][0][filters][0][field]'] = 'order_id';
|
|
39
|
+
params['searchCriteria[filter_groups][0][filters][0][value]'] = options.orderId;
|
|
40
|
+
params['searchCriteria[filter_groups][0][filters][0][condition_type]'] = 'eq';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const data = await client.get('V1/shipments', params, { headers });
|
|
44
|
+
|
|
45
|
+
if (options.format === 'json') {
|
|
46
|
+
console.log(JSON.stringify(data, null, 2));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (options.format === 'xml') {
|
|
50
|
+
console.log(data);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const rows = (data.items || []).map(s => [
|
|
55
|
+
s.entity_id,
|
|
56
|
+
s.increment_id,
|
|
57
|
+
s.order_id,
|
|
58
|
+
s.total_qty,
|
|
59
|
+
s.created_at
|
|
60
|
+
]);
|
|
61
|
+
console.log(chalk.bold(`Total: ${data.total_count}, Page: ${options.page}, Size: ${options.size}`));
|
|
62
|
+
printTable(['ID', 'Increment ID', 'Order ID', 'Total Qty', 'Created At'], rows);
|
|
63
|
+
} catch (e) { handleError(e); }
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
//-------------------------------------------------------
|
|
67
|
+
// "shipment show" Command
|
|
68
|
+
//-------------------------------------------------------
|
|
69
|
+
shipments.command('show <id>')
|
|
70
|
+
.description('Show shipment details')
|
|
71
|
+
.option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
|
|
72
|
+
.addHelpText('after', `
|
|
73
|
+
Examples:
|
|
74
|
+
$ mage-remote-run shipment show 123
|
|
75
|
+
`)
|
|
76
|
+
.action(async (id, options) => {
|
|
77
|
+
try {
|
|
78
|
+
const client = await createClient();
|
|
79
|
+
const headers = {};
|
|
80
|
+
if (options.format === 'json') headers['Accept'] = 'application/json';
|
|
81
|
+
else if (options.format === 'xml') headers['Accept'] = 'application/xml';
|
|
82
|
+
|
|
83
|
+
let shipment;
|
|
84
|
+
try {
|
|
85
|
+
shipment = await client.get(`V1/shipment/${id}`, {}, { headers });
|
|
86
|
+
} catch (e) {
|
|
87
|
+
// Try to search by increment_id
|
|
88
|
+
const params = {
|
|
89
|
+
'searchCriteria[filter_groups][0][filters][0][field]': 'increment_id',
|
|
90
|
+
'searchCriteria[filter_groups][0][filters][0][value]': id,
|
|
91
|
+
'searchCriteria[filter_groups][0][filters][0][condition_type]': 'eq'
|
|
92
|
+
};
|
|
93
|
+
const searchData = await client.get('V1/shipments', params);
|
|
94
|
+
|
|
95
|
+
if (searchData.items && searchData.items.length > 0) {
|
|
96
|
+
shipment = searchData.items[0];
|
|
97
|
+
// If format is XML, we need to fetch by ID to get XML response
|
|
98
|
+
if (options.format === 'xml') {
|
|
99
|
+
shipment = await client.get(`V1/shipment/${shipment.entity_id}`, {}, { headers });
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
// If original error was 404, report as not found
|
|
103
|
+
if (e.response && e.response.status === 404) {
|
|
104
|
+
throw new Error(`Shipment ${id} not found.`);
|
|
105
|
+
}
|
|
106
|
+
// Otherwise rethrow original error (e.g. 401, 500)
|
|
107
|
+
throw e;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (options.format === 'json') {
|
|
112
|
+
console.log(JSON.stringify(shipment, null, 2));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (options.format === 'xml') {
|
|
116
|
+
console.log(shipment);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log(chalk.bold.blue('\nš¦ Shipment Information'));
|
|
121
|
+
console.log(chalk.gray('ā'.repeat(50)));
|
|
122
|
+
console.log(`${chalk.bold('ID:')} ${shipment.entity_id}`);
|
|
123
|
+
console.log(`${chalk.bold('Increment ID:')} ${shipment.increment_id}`);
|
|
124
|
+
console.log(`${chalk.bold('Order ID:')} ${shipment.order_id}`);
|
|
125
|
+
console.log(`${chalk.bold('Total Qty:')} ${shipment.total_qty}`);
|
|
126
|
+
console.log(`${chalk.bold('Created At:')} ${shipment.created_at}`);
|
|
127
|
+
|
|
128
|
+
if (shipment.items && shipment.items.length > 0) {
|
|
129
|
+
console.log(chalk.bold('\nš Items'));
|
|
130
|
+
const itemRows = shipment.items.map(item => [
|
|
131
|
+
item.sku,
|
|
132
|
+
item.name,
|
|
133
|
+
Math.floor(item.qty),
|
|
134
|
+
item.price
|
|
135
|
+
]);
|
|
136
|
+
printTable(['SKU', 'Name', 'Qty', 'Price'], itemRows);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (shipment.tracks && shipment.tracks.length > 0) {
|
|
140
|
+
console.log(chalk.bold('\nš Tracking'));
|
|
141
|
+
const trackRows = shipment.tracks.map(track => [
|
|
142
|
+
track.title,
|
|
143
|
+
track.carrier_code,
|
|
144
|
+
track.track_number
|
|
145
|
+
]);
|
|
146
|
+
printTable(['Title', 'Carrier', 'Number'], trackRows);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (shipment.comments && shipment.comments.length > 0) {
|
|
150
|
+
console.log(chalk.bold('\nš¬ Comments'));
|
|
151
|
+
const commentRows = shipment.comments.map(c => [
|
|
152
|
+
c.created_at,
|
|
153
|
+
c.comment
|
|
154
|
+
]);
|
|
155
|
+
printTable(['Date', 'Comment'], commentRows);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
} catch (e) { handleError(e); }
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
//-------------------------------------------------------
|
|
162
|
+
// "shipment create" Command
|
|
163
|
+
//-------------------------------------------------------
|
|
164
|
+
shipments.command('create <orderId>')
|
|
165
|
+
.description('Create shipment for an order')
|
|
166
|
+
.option('--notify', 'Notify customer via email')
|
|
167
|
+
.option('--append-comment', 'Append comment')
|
|
168
|
+
.option('--comment <text>', 'Comment text')
|
|
169
|
+
.option('--visible', 'Comment visible on frontend')
|
|
170
|
+
.option('--tracks <json>', 'Tracks array JSON string')
|
|
171
|
+
.option('--items <json>', 'Items array JSON string (if partial shipment)')
|
|
172
|
+
.addHelpText('after', `
|
|
173
|
+
Examples:
|
|
174
|
+
$ mage-remote-run shipment create 123 --notify
|
|
175
|
+
$ mage-remote-run shipment create 123 --tracks '[{"carrier_code":"fedex","title":"FedEx","track_number":"123456"}]'
|
|
176
|
+
`)
|
|
177
|
+
.action(async (orderId, options) => {
|
|
178
|
+
try {
|
|
179
|
+
const client = await createClient();
|
|
180
|
+
const payload = {
|
|
181
|
+
notify: !!options.notify,
|
|
182
|
+
appendComment: !!options.appendComment,
|
|
183
|
+
comment: {
|
|
184
|
+
extension_attributes: {},
|
|
185
|
+
comment: options.comment || '',
|
|
186
|
+
is_visible_on_front: options.visible ? 1 : 0
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
if (options.tracks) {
|
|
191
|
+
payload.tracks = JSON.parse(options.tracks);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (options.items) {
|
|
195
|
+
payload.items = JSON.parse(options.items);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const result = await client.post(`V1/order/${orderId}/ship`, payload);
|
|
199
|
+
console.log(chalk.green(`Shipment created. ID: ${result}`));
|
|
200
|
+
} catch (e) { handleError(e); }
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
//-------------------------------------------------------
|
|
204
|
+
// "shipment label" Command
|
|
205
|
+
//-------------------------------------------------------
|
|
206
|
+
shipments.command('label <id>')
|
|
207
|
+
.description('Retrieve shipping label')
|
|
208
|
+
.addHelpText('after', `
|
|
209
|
+
Examples:
|
|
210
|
+
$ mage-remote-run shipment label 123
|
|
211
|
+
`)
|
|
212
|
+
.action(async (id) => {
|
|
213
|
+
try {
|
|
214
|
+
const client = await createClient();
|
|
215
|
+
const label = await client.get(`V1/shipment/${id}/label`);
|
|
216
|
+
if (label) {
|
|
217
|
+
console.log(label);
|
|
218
|
+
} else {
|
|
219
|
+
console.log(chalk.yellow('No label found.'));
|
|
220
|
+
}
|
|
221
|
+
} catch (e) { handleError(e); }
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
//-------------------------------------------------------
|
|
225
|
+
// "shipment track" Command
|
|
226
|
+
//-------------------------------------------------------
|
|
227
|
+
shipments.command('track <id>')
|
|
228
|
+
.description('Add tracking number to shipment')
|
|
229
|
+
.requiredOption('--carrier <code>', 'Carrier code')
|
|
230
|
+
.requiredOption('--title <title>', 'Carrier title')
|
|
231
|
+
.requiredOption('--number <number>', 'Tracking number')
|
|
232
|
+
.addHelpText('after', `
|
|
233
|
+
Examples:
|
|
234
|
+
$ mage-remote-run shipment track 123 --carrier fedex --title FedEx --number 987654321
|
|
235
|
+
`)
|
|
236
|
+
.action(async (id, options) => {
|
|
237
|
+
try {
|
|
238
|
+
const client = await createClient();
|
|
239
|
+
const payload = {
|
|
240
|
+
entity: {
|
|
241
|
+
order_id: null, // Will be filled by backend logic usually, or we might need order_id?
|
|
242
|
+
// Actually V1/shipment/:id/track requires 'entity' which is SalesDataShipmentTrackInterface
|
|
243
|
+
// Parent ID is the shipment ID.
|
|
244
|
+
parent_id: id,
|
|
245
|
+
carrier_code: options.carrier,
|
|
246
|
+
title: options.title,
|
|
247
|
+
track_number: options.number
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Note: The endpoint is POST /V1/shipment/track (Wait, is it /V1/shipments/{id}/...? No, documentation says POST /V1/shipment/track usually, checking...)
|
|
252
|
+
// Standard Magento API: POST /V1/shipment/track
|
|
253
|
+
// Body: { "entity": { "parent_id": ... } }
|
|
254
|
+
|
|
255
|
+
const result = await client.post(`V1/shipment/track`, payload);
|
|
256
|
+
console.log(chalk.green(`Tracking added. ID: ${result}`));
|
|
257
|
+
} catch (e) { handleError(e); }
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
//-------------------------------------------------------
|
|
261
|
+
// "shipment email" Command
|
|
262
|
+
//-------------------------------------------------------
|
|
263
|
+
shipments.command('email <id>')
|
|
264
|
+
.description('Send shipment email')
|
|
265
|
+
.addHelpText('after', `
|
|
266
|
+
Examples:
|
|
267
|
+
$ mage-remote-run shipment email 123
|
|
268
|
+
`)
|
|
269
|
+
.action(async (id) => {
|
|
270
|
+
try {
|
|
271
|
+
const client = await createClient();
|
|
272
|
+
// POST /V1/shipment/{id}/emails
|
|
273
|
+
const result = await client.post(`V1/shipment/${id}/emails`);
|
|
274
|
+
if (result) {
|
|
275
|
+
console.log(chalk.green('Email sent.'));
|
|
276
|
+
} else {
|
|
277
|
+
// Sometimes it returns boolean true/false
|
|
278
|
+
console.log(chalk.green('Email sent signal processed.'));
|
|
279
|
+
}
|
|
280
|
+
} catch (e) { handleError(e); }
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
//-------------------------------------------------------
|
|
284
|
+
// "shipment comments" Command
|
|
285
|
+
//-------------------------------------------------------
|
|
286
|
+
shipments.command('comments <id>')
|
|
287
|
+
.description('Add comment to shipment')
|
|
288
|
+
.requiredOption('--comment <text>', 'Comment text')
|
|
289
|
+
.option('--visible', 'Visible on frontend')
|
|
290
|
+
.option('--notify', 'Notify customer')
|
|
291
|
+
.addHelpText('after', `
|
|
292
|
+
Examples:
|
|
293
|
+
$ mage-remote-run shipment comments 123 --comment "Package is on the way" --notify
|
|
294
|
+
`)
|
|
295
|
+
.action(async (id, options) => {
|
|
296
|
+
try {
|
|
297
|
+
const client = await createClient();
|
|
298
|
+
const payload = {
|
|
299
|
+
entity: {
|
|
300
|
+
parent_id: id,
|
|
301
|
+
comment: options.comment,
|
|
302
|
+
is_visible_on_front: options.visible ? 1 : 0,
|
|
303
|
+
is_customer_notified: options.notify ? 1 : 0
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
// Endpoint: POST /V1/shipment/comments
|
|
307
|
+
// Wait, standard magento is POST /V1/shipment/{id}/comments mostly?
|
|
308
|
+
// Checking standard swagger... POST /V1/shipment/{id}/comments maps to ShipmentCommentRepositoryInterface.save
|
|
309
|
+
// Actually, there is POST /V1/shipment/comments as well?
|
|
310
|
+
// Let's use POST /V1/shipment/{id}/comments
|
|
311
|
+
|
|
312
|
+
// Update: Actually standard Magento might be POST /V1/shipment/comments to create a comment with parent_id in body?
|
|
313
|
+
// Let's check a reliable source or trust the pattern. Order comments are /V1/orders/{id}/comments.
|
|
314
|
+
// Shipment comments... Magento 2.4 REST API docs:
|
|
315
|
+
// POST /V1/shipment/{id}/comments -> salesShipmentCommentRepositoryV1
|
|
316
|
+
// Body: { entity: { ... } }
|
|
317
|
+
|
|
318
|
+
// Also common is POST /V1/shipment/comments (no ID in URL) -> same repo?
|
|
319
|
+
// Let's use the ID in URL one if possible, but let's stick to /V1/shipment/{id}/comments if we can confirm it exists.
|
|
320
|
+
// If not sure, standard practice.
|
|
321
|
+
|
|
322
|
+
// Let's try /V1/shipment/:id/comments
|
|
323
|
+
const result = await client.post(`V1/shipment/${id}/comments`, payload);
|
|
324
|
+
console.log(chalk.green(`Comment added. ID: ${result.entity_id || result}`));
|
|
325
|
+
} catch (e) { handleError(e); }
|
|
326
|
+
});
|
|
327
|
+
}
|
package/lib/config.js
CHANGED
|
@@ -3,14 +3,44 @@ import path from 'path';
|
|
|
3
3
|
import envPaths from 'env-paths';
|
|
4
4
|
import { mkdirp } from 'mkdirp';
|
|
5
5
|
|
|
6
|
+
import os from 'os';
|
|
7
|
+
|
|
6
8
|
const paths = envPaths('mage-remote-run', { suffix: '' });
|
|
7
|
-
const CONFIG_DIR =
|
|
9
|
+
const CONFIG_DIR = process.platform === 'darwin'
|
|
10
|
+
? path.join(os.homedir(), '.config', 'mage-remote-run')
|
|
11
|
+
: paths.config;
|
|
8
12
|
const CACHE_DIR = paths.cache;
|
|
9
13
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
10
14
|
const TOKEN_CACHE_FILE = path.join(CACHE_DIR, 'token-cache.json');
|
|
11
15
|
|
|
16
|
+
|
|
17
|
+
const OLD_CONFIG_DIR = paths.config;
|
|
18
|
+
const OLD_CONFIG_FILE = path.join(OLD_CONFIG_DIR, 'config.json');
|
|
19
|
+
|
|
20
|
+
async function migrateOldConfig() {
|
|
21
|
+
// print platform
|
|
22
|
+
if (process.platform !== 'darwin') {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (fs.existsSync(OLD_CONFIG_FILE)) {
|
|
31
|
+
try {
|
|
32
|
+
console.log(`Migrating config from ${OLD_CONFIG_FILE} to ${CONFIG_FILE}`);
|
|
33
|
+
await mkdirp(CONFIG_DIR);
|
|
34
|
+
fs.copyFileSync(OLD_CONFIG_FILE, CONFIG_FILE);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.error("Error migrating config:", e.message);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
12
41
|
export async function loadConfig() {
|
|
13
42
|
try {
|
|
43
|
+
await migrateOldConfig();
|
|
14
44
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
15
45
|
return { profiles: {}, activeProfile: null };
|
|
16
46
|
}
|
|
@@ -26,7 +56,9 @@ export async function saveConfig(config) {
|
|
|
26
56
|
try {
|
|
27
57
|
await mkdirp(CONFIG_DIR);
|
|
28
58
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
29
|
-
|
|
59
|
+
if (process.env.DEBUG) {
|
|
60
|
+
console.log(`Configuration saved to ${CONFIG_FILE}`);
|
|
61
|
+
}
|
|
30
62
|
} catch (e) {
|
|
31
63
|
console.error("Error saving config:", e.message);
|
|
32
64
|
throw e;
|