mage-remote-run 0.12.0 → 0.14.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.
@@ -0,0 +1,300 @@
1
+ import repl from 'repl';
2
+ import chalk from 'chalk';
3
+ import { Command } from 'commander';
4
+ import { createClient } from '../api/factory.js';
5
+ import { loadConfig, getActiveProfile } from '../config.js';
6
+ import { registerCommands } from '../command-registry.js';
7
+ import { expandCommandAbbreviations } from '../command-helper.js';
8
+
9
+ export function registerConsoleCommand(program) {
10
+ program
11
+ .command('console')
12
+ .alias('repl')
13
+ .description('Start an interactive console')
14
+ .option('-d, --debug', 'Enable debug output')
15
+ .action(async (options) => {
16
+ if (options.debug) {
17
+ process.env.DEBUG = '1';
18
+ console.log(chalk.gray('Debug mode enabled'));
19
+ }
20
+
21
+ console.log(chalk.bold.blue('Mage Remote Run Interactive Console'));
22
+ console.log(chalk.gray('Type your commands directly or write JS code.'));
23
+ console.log(chalk.gray('Global variables available: client (async factory), config, chalk'));
24
+ console.log(chalk.gray('Example JS: await (await client()).get("V1/store/websites")'));
25
+ console.log(chalk.gray('Type "list" to see available commands.'));
26
+ console.log(chalk.gray('Type .exit to quit.\n'));
27
+
28
+ // State for the REPL
29
+ let localProgram;
30
+ let currentProfile;
31
+
32
+ // Function to load/reload commands based on current profile
33
+ const loadLocalCommands = async () => {
34
+ // Create a fresh program instance for the REPL
35
+ localProgram = new Command();
36
+
37
+ // Configure custom output for REPL to avoid duplicate error printing
38
+ localProgram.configureOutput({
39
+ writeOut: (str) => process.stdout.write(str),
40
+ writeErr: (str) => process.stderr.write(str),
41
+ });
42
+
43
+ // Apply exitOverride recursively
44
+ const applyExitOverride = (cmd) => {
45
+ cmd.exitOverride((err) => {
46
+ throw err;
47
+ });
48
+ if (cmd.commands) {
49
+ cmd.commands.forEach(applyExitOverride);
50
+ }
51
+ };
52
+
53
+ // Get current profile and register commands
54
+ const profile = await getActiveProfile();
55
+ registerCommands(localProgram, profile);
56
+ applyExitOverride(localProgram);
57
+
58
+ // Update current profile state (store simple unique string for comparison)
59
+ currentProfile = profile ? `${profile.name}:${profile.type}` : 'null';
60
+
61
+ return { localProgram, profile };
62
+ };
63
+
64
+ // Initial load
65
+ await loadLocalCommands();
66
+
67
+ // Use 'stream' module to create dummy REPL for capturing defaults
68
+ const { PassThrough } = await import('stream');
69
+ const dummy = repl.start({ input: new PassThrough(), output: new PassThrough(), terminal: false });
70
+ const defaultCompleter = dummy.completer;
71
+ const defaultEval = dummy.eval;
72
+ dummy.close();
73
+
74
+ // Custom completer definition
75
+ const myCompleter = (line, callback) => {
76
+ const parts = line.split(/\s+/);
77
+ if (parts[0] === '') parts.shift();
78
+
79
+ let current = '';
80
+ let contextParts = [];
81
+
82
+ if (line.match(/\s$/)) {
83
+ current = '';
84
+ contextParts = parts.filter(p => p.length > 0);
85
+ } else {
86
+ current = parts.pop();
87
+ contextParts = parts;
88
+ }
89
+
90
+ const getCandidates = (cmdObj) => {
91
+ return cmdObj.commands.map(c => c.name());
92
+ };
93
+
94
+ let hits = [];
95
+
96
+ if (contextParts.length === 0) {
97
+ const candidates = getCandidates(localProgram);
98
+ hits = candidates.filter(c => c.startsWith(current));
99
+ } else {
100
+ let cmd = localProgram;
101
+ let validContext = true;
102
+
103
+ for (const part of contextParts) {
104
+ const found = cmd.commands.find(c => c.name() === part || (c.aliases && c.aliases().includes(part)));
105
+ if (found) {
106
+ cmd = found;
107
+ } else {
108
+ validContext = false;
109
+ break;
110
+ }
111
+ }
112
+
113
+ if (validContext) {
114
+ const candidates = getCandidates(cmd);
115
+ hits = candidates.filter(c => c.startsWith(current));
116
+ }
117
+ }
118
+
119
+ if (hits.length > 0) {
120
+ return callback(null, [hits, current]);
121
+ }
122
+
123
+ return defaultCompleter(line, callback);
124
+ };
125
+
126
+ // Custom evaluator definition
127
+ const myEval = async function (cmd, context, filename, callback) {
128
+ // 'this' is the REPLServer instance
129
+ const rInstance = this;
130
+
131
+ cmd = cmd.trim();
132
+
133
+ if (!cmd) {
134
+ callback(null);
135
+ return;
136
+ }
137
+
138
+ if (cmd === 'list') {
139
+ console.log(chalk.bold('\nAvailable Commands:'));
140
+ localProgram.commands.filter(c => !c._hidden).sort((a, b) => a.name().localeCompare(b.name())).forEach(c => {
141
+ console.log(` ${chalk.cyan(c.name().padEnd(25))} ${c.description()}`);
142
+ });
143
+ console.log('');
144
+ callback(null);
145
+ return;
146
+ }
147
+
148
+ if (cmd === 'help') {
149
+ localProgram.outputHelp();
150
+ callback(null);
151
+ return;
152
+ }
153
+
154
+ try {
155
+ const args = (cmd.match(/[^\s"']+|"([^"]*)"|'([^']*)'/g) || []).map(arg => {
156
+ if (arg.startsWith('"') && arg.endsWith('"')) return arg.slice(1, -1);
157
+ if (arg.startsWith("'") && arg.endsWith("'")) return arg.slice(1, -1);
158
+ return arg;
159
+ });
160
+
161
+ if (args.length > 0) {
162
+ if (process.env.DEBUG) {
163
+ console.log(chalk.gray('DEBUG: Parsing args:'), args);
164
+ }
165
+ let expandedArgs;
166
+ try {
167
+ expandedArgs = expandCommandAbbreviations(localProgram, args);
168
+ if (process.env.DEBUG) {
169
+ console.log(chalk.gray('DEBUG: Expanded args:'), expandedArgs);
170
+ }
171
+ } catch (e) {
172
+ if (e.isAmbiguous) {
173
+ console.error(chalk.red(e.message));
174
+ callback(null);
175
+ return;
176
+ }
177
+ throw e;
178
+ }
179
+
180
+ const firstWord = expandedArgs[0];
181
+ const knownCommands = localProgram.commands.map(c => c.name());
182
+
183
+ if (knownCommands.includes(firstWord)) {
184
+ // Valid command found
185
+ let keypressListeners = [];
186
+ try {
187
+ rInstance.pause();
188
+
189
+ // Capture and remove keypress listeners to prevent REPL from intercepting input
190
+ // intended for interactive commands (like inquirer)
191
+ keypressListeners = process.stdin.listeners('keypress');
192
+ process.stdin.removeAllListeners('keypress');
193
+
194
+ if (process.stdin.isTTY) {
195
+ process.stdin.setRawMode(false);
196
+ }
197
+
198
+ await localProgram.parseAsync(['node', 'mage-remote-run', ...expandedArgs]);
199
+
200
+ // Check for profile change after command execution
201
+ const newProfileObj = await getActiveProfile();
202
+ const newProfileKey = newProfileObj ? `${newProfileObj.name}:${newProfileObj.type}` : 'null';
203
+
204
+ if (process.env.DEBUG) {
205
+ console.log(chalk.gray(`DEBUG: Check Profile Switch. Current: ${currentProfile}, New: ${newProfileKey}`));
206
+ }
207
+
208
+ if (newProfileKey !== currentProfile) {
209
+ await loadLocalCommands();
210
+ // Update context variables
211
+ rInstance.context.config = await loadConfig();
212
+
213
+ if (process.env.DEBUG) {
214
+ console.log(chalk.green(`\nConnection switched to ${newProfileObj ? newProfileObj.name : 'none'}. Commands reloaded.`));
215
+ }
216
+ }
217
+
218
+ // Restore REPL state
219
+ setTimeout(() => {
220
+ // Restore keypress listeners
221
+ keypressListeners.forEach(fn => process.stdin.on('keypress', fn));
222
+
223
+ if (process.stdin.isTTY) {
224
+ process.stdin.setRawMode(true);
225
+ }
226
+ process.stdin.resume();
227
+
228
+ // Flush stdin to remove any buffered leftovers
229
+ let chunk;
230
+ while ((chunk = process.stdin.read()) !== null) { }
231
+
232
+ rInstance.resume();
233
+ rInstance.displayPrompt(true);
234
+ }, 100);
235
+
236
+ } catch (e) {
237
+ // Restore listeners in error case too!
238
+ keypressListeners.forEach(fn => process.stdin.on('keypress', fn));
239
+
240
+ if (process.stdin.isTTY) {
241
+ process.stdin.setRawMode(true);
242
+ }
243
+ process.stdin.resume();
244
+ rInstance.resume();
245
+
246
+ // Flush stdin
247
+ let chunk;
248
+ while ((chunk = process.stdin.read()) !== null) { }
249
+
250
+ if (e.code === 'commander.helpDisplayed') {
251
+ // Help was displayed, clean exit for us
252
+ setImmediate(() => rInstance.displayPrompt());
253
+ } else if (e.code === 'commander.unknownOption' || e.code === 'commander.unknownCommand') {
254
+ console.error(chalk.red(e.message));
255
+ setImmediate(() => rInstance.displayPrompt());
256
+ } else {
257
+ if (e.code) {
258
+ // Likely a commander exit error we rethrew
259
+ setImmediate(() => rInstance.displayPrompt());
260
+ } else {
261
+ console.error(chalk.red('Command execution error:'), e);
262
+ setImmediate(() => rInstance.displayPrompt());
263
+ }
264
+ }
265
+ }
266
+ callback(null);
267
+ return;
268
+ }
269
+ }
270
+ } catch (e) {
271
+ // unexpected error
272
+ }
273
+
274
+ // Fallback to default eval which supports top-level await
275
+ return defaultEval.call(rInstance, cmd, context, filename, callback);
276
+ };
277
+
278
+ const r = repl.start({
279
+ prompt: chalk.green('mage> '),
280
+ eval: myEval,
281
+ completer: myCompleter
282
+ });
283
+
284
+ r.context.client = createClient;
285
+ r.context.config = await loadConfig();
286
+ r.context.chalk = chalk;
287
+
288
+ // Helper to reload config if changed
289
+ r.context.reload = async () => {
290
+ r.context.config = await loadConfig();
291
+ // We should also reload commands here in case a manual config edit changed the type
292
+ await loadLocalCommands();
293
+ console.log(chalk.gray('Config and commands reloaded.'));
294
+ };
295
+
296
+ r.on('exit', () => {
297
+ process.exit();
298
+ });
299
+ });
300
+ }
@@ -6,6 +6,10 @@ import inquirer from 'inquirer';
6
6
  export function registerCustomersCommands(program) {
7
7
  const customers = program.command('customer').description('Manage customers');
8
8
 
9
+
10
+ //-------------------------------------------------------
11
+ // "customer list" Command
12
+ //-------------------------------------------------------
9
13
  customers.command('list')
10
14
  .description('List customers')
11
15
  .option('-p, --page <number>', 'Page number', '1')
@@ -45,6 +49,10 @@ Examples:
45
49
  } catch (e) { handleError(e); }
46
50
  });
47
51
 
52
+
53
+ //-------------------------------------------------------
54
+ // "customer search" Command
55
+ //-------------------------------------------------------
48
56
  customers.command('search <query>')
49
57
  .description('Search customers by email')
50
58
  .addHelpText('after', `
@@ -65,6 +73,10 @@ Examples:
65
73
  } catch (e) { handleError(e); }
66
74
  });
67
75
 
76
+
77
+ //-------------------------------------------------------
78
+ // "customer edit" Command
79
+ //-------------------------------------------------------
68
80
  customers.command('edit <id>')
69
81
  .description('Edit a customer')
70
82
  .addHelpText('after', `
@@ -96,6 +108,10 @@ Examples:
96
108
  } catch (e) { handleError(e); }
97
109
  });
98
110
 
111
+
112
+ //-------------------------------------------------------
113
+ // "customer show" Command
114
+ //-------------------------------------------------------
99
115
  customers.command('show <customerId>')
100
116
  .description('Show detailed customer information')
101
117
  .option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
@@ -156,6 +172,10 @@ Examples:
156
172
  } catch (e) { handleError(e); }
157
173
  });
158
174
 
175
+
176
+ //-------------------------------------------------------
177
+ // "customer delete" Command
178
+ //-------------------------------------------------------
159
179
  customers.command('delete <customerId>')
160
180
  .description('Delete a customer')
161
181
  .option('--force', 'Force delete without confirmation')
@@ -185,6 +205,10 @@ Examples:
185
205
  } catch (e) { handleError(e); }
186
206
  });
187
207
 
208
+
209
+ //-------------------------------------------------------
210
+ // "customer confirm" Command
211
+ //-------------------------------------------------------
188
212
  customers.command('confirm [customerId]')
189
213
  .description('Resend customer confirmation email')
190
214
  .option('--redirect-url <url>', 'Redirect URL after confirmation')
@@ -242,6 +266,10 @@ Examples:
242
266
  });
243
267
  const groups = customers.command('group').description('Manage customer groups');
244
268
 
269
+
270
+ //-------------------------------------------------------
271
+ // "customer group list" Command
272
+ //-------------------------------------------------------
245
273
  groups.command('list')
246
274
  .description('List customer groups')
247
275
  .option('-p, --page <number>', 'Page number', '1')
@@ -8,6 +8,10 @@ export function registerEavCommands(program) {
8
8
  // Attribute Sets
9
9
  const attributeSets = eav.command('attribute-set').description('Manage attribute sets');
10
10
 
11
+
12
+ //-------------------------------------------------------
13
+ // "eav attribute-set list" Command
14
+ //-------------------------------------------------------
11
15
  attributeSets.command('list')
12
16
  .description('List all attribute sets')
13
17
  .option('-p, --page <number>', 'Page number', '1')
@@ -51,6 +55,10 @@ Examples:
51
55
  } catch (e) { handleError(e); }
52
56
  });
53
57
 
58
+
59
+ //-------------------------------------------------------
60
+ // "eav attribute-set show" Command
61
+ //-------------------------------------------------------
54
62
  attributeSets.command('show <id>')
55
63
  .description('Show attribute set details')
56
64
  .addHelpText('after', `
@@ -8,6 +8,10 @@ export function registerInventoryCommands(program) {
8
8
 
9
9
  const stock = inventory.command('stock').description('Manage inventory stocks');
10
10
 
11
+
12
+ //-------------------------------------------------------
13
+ // "inventory stock list" Command
14
+ //-------------------------------------------------------
11
15
  stock.command('list')
12
16
  .description('List inventory stocks')
13
17
  .addHelpText('after', `
@@ -28,6 +32,10 @@ Examples:
28
32
  } catch (e) { handleError(e); }
29
33
  });
30
34
 
35
+
36
+ //-------------------------------------------------------
37
+ // "inventory stock show" Command
38
+ //-------------------------------------------------------
31
39
  stock.command('show <stockId>')
32
40
  .description('Show stock details')
33
41
  .addHelpText('after', `
@@ -52,6 +60,10 @@ Examples:
52
60
  } catch (e) { handleError(e); }
53
61
  });
54
62
 
63
+
64
+ //-------------------------------------------------------
65
+ // "inventory resolve-stock" Command
66
+ //-------------------------------------------------------
55
67
  inventory.command('resolve-stock <type> <code>')
56
68
  .description('Resolve stock for a sales channel')
57
69
  .addHelpText('after', `
@@ -68,6 +80,10 @@ Examples:
68
80
 
69
81
  const source = inventory.command('source').description('Manage inventory sources');
70
82
 
83
+
84
+ //-------------------------------------------------------
85
+ // "inventory source list" Command
86
+ //-------------------------------------------------------
71
87
  source.command('list')
72
88
  .description('List inventory sources')
73
89
  .option('-p, --page <number>', 'Page number', '1')
@@ -108,6 +124,10 @@ Examples:
108
124
 
109
125
  const ssa = source.command('selection-algorithm').description('Manage source selection algorithms');
110
126
 
127
+
128
+ //-------------------------------------------------------
129
+ // "inventory source selection-algorithm list" Command
130
+ //-------------------------------------------------------
111
131
  ssa.command('list')
112
132
  .description('List available source selection algorithms')
113
133
  .addHelpText('after', `
@@ -6,6 +6,10 @@ import inquirer from 'inquirer';
6
6
  export function registerOrdersCommands(program) {
7
7
  const orders = program.command('order').description('Manage orders');
8
8
 
9
+
10
+ //-------------------------------------------------------
11
+ // "order list" Command
12
+ //-------------------------------------------------------
9
13
  orders.command('list')
10
14
  .option('-p, --page <number>', 'Page number', '1')
11
15
  .option('-s, --size <number>', 'Page size', '20')
@@ -44,6 +48,10 @@ Examples:
44
48
  } catch (e) { handleError(e); }
45
49
  });
46
50
 
51
+
52
+ //-------------------------------------------------------
53
+ // "order search" Command
54
+ //-------------------------------------------------------
47
55
  orders.command('search <query>')
48
56
  .description('Search orders by Increment ID')
49
57
  .addHelpText('after', `
@@ -64,6 +72,10 @@ Examples:
64
72
  } catch (e) { handleError(e); }
65
73
  });
66
74
 
75
+
76
+ //-------------------------------------------------------
77
+ // "order edit" Command
78
+ //-------------------------------------------------------
67
79
  orders.command('edit <id>')
68
80
  .description('Update order status (via Comment)')
69
81
  .addHelpText('after', `
@@ -92,6 +104,10 @@ Examples:
92
104
  console.log(chalk.green(`Order ${id} updated.`));
93
105
  } catch (e) { handleError(e); }
94
106
  });
107
+
108
+ //-------------------------------------------------------
109
+ // "order show" Command
110
+ //-------------------------------------------------------
95
111
  orders.command('show <identifier>')
96
112
  .description('Show detailed order information by ID or Increment ID')
97
113
  .option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
@@ -106,6 +122,10 @@ Examples:
106
122
  } catch (e) { handleError(e); }
107
123
  });
108
124
 
125
+
126
+ //-------------------------------------------------------
127
+ // "order latest" Command
128
+ //-------------------------------------------------------
109
129
  orders.command('latest')
110
130
  .description('List latest orders sorted by created_at DESC with selection')
111
131
  .option('-p, --page <number>', 'Page number', '1')
@@ -199,7 +219,7 @@ async function showOrder(identifier, format = 'text') {
199
219
  order = await client.get(`V1/orders/${order.entity_id}`, {}, { headers });
200
220
  } catch (subError) {
201
221
  // ignore or log? If we fail to get formatted, fallback or throw?
202
- // If we found it via search, 'order' is the object.
222
+ // If we found it via search, 'order' is the object.
203
223
  // If format is json, we are good.
204
224
  // If format is xml, we NEED to re-fetch because 'order' is currently a JS object from JSON response.
205
225
  if (format === 'xml') throw subError;
@@ -267,8 +287,8 @@ function statusColor(status) {
267
287
  // Basic color coding
268
288
  if (['pending'].includes(status)) return status;
269
289
  if (['processing'].includes(status)) return status;
270
- if (['complete'].includes(status)) return status; // green?
271
- // Since we don't have direct chalk access in this helper easily without passing it,
290
+ if (['complete'].includes(status)) return status; // green?
291
+ // Since we don't have direct chalk access in this helper easily without passing it,
272
292
  // and I don't want to import it again if I can avoid it or I can just return string.
273
293
  // Actually, I can import properly in module scope if I change top imports but I am overwriting the file so I can add imports.
274
294
  return status;
@@ -5,6 +5,10 @@ import chalk from 'chalk';
5
5
  export function registerProductsCommands(program) {
6
6
  const products = program.command('product').description('Manage products');
7
7
 
8
+
9
+ //-------------------------------------------------------
10
+ // "product list" Command
11
+ //-------------------------------------------------------
8
12
  products.command('list')
9
13
  .description('List products')
10
14
  .option('-p, --page <number>', 'Page number', '1')
@@ -48,6 +52,10 @@ Examples:
48
52
  } catch (e) { handleError(e); }
49
53
  });
50
54
 
55
+
56
+ //-------------------------------------------------------
57
+ // "product show" Command
58
+ //-------------------------------------------------------
51
59
  products.command('show <sku>')
52
60
  .description('Show product details')
53
61
  .option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
@@ -165,6 +173,10 @@ Examples:
165
173
 
166
174
  const types = products.command('type').description('Manage product types');
167
175
 
176
+
177
+ //-------------------------------------------------------
178
+ // "product type list" Command
179
+ //-------------------------------------------------------
168
180
  types.command('list')
169
181
  .description('List available product types')
170
182
  .addHelpText('after', `
@@ -182,6 +194,10 @@ Examples:
182
194
 
183
195
  const attributes = products.command('attribute').description('Manage product attributes');
184
196
 
197
+
198
+ //-------------------------------------------------------
199
+ // "product attribute list" Command
200
+ //-------------------------------------------------------
185
201
  attributes.command('list')
186
202
  .description('List product attributes')
187
203
  .option('-p, --page <number>', 'Page number', '1')
@@ -213,6 +229,10 @@ Examples:
213
229
  } catch (e) { handleError(e); }
214
230
  });
215
231
 
232
+
233
+ //-------------------------------------------------------
234
+ // "product attribute show" Command
235
+ //-------------------------------------------------------
216
236
  attributes.command('show <attributeCode>')
217
237
  .description('Show product attribute details')
218
238
  .option('-f, --format <type>', 'Output format (text, json, xml)', 'text')
@@ -293,6 +313,10 @@ Examples:
293
313
  });
294
314
 
295
315
  const attributeTypes = attributes.command('type').description('Manage attribute types');
316
+
317
+ //-------------------------------------------------------
318
+ // "product attribute type list" Command
319
+ //-------------------------------------------------------
296
320
  attributeTypes.command('list')
297
321
  .description('List product attribute types')
298
322
  .addHelpText('after', `