@magentrix-corp/magentrix-cli 1.3.2 → 1.3.4

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/README.md CHANGED
@@ -480,7 +480,7 @@ magentrix iris-link
480
480
  - `--cleanup` - Remove invalid (non-existent) linked projects
481
481
 
482
482
  **Menu options:**
483
- - Link a new Vue project
483
+ - Link a Vue project
484
484
  - View all linked projects
485
485
  - Unlink a project
486
486
  - Cleanup invalid projects
@@ -503,17 +503,19 @@ magentrix vue-build-stage
503
503
 
504
504
  #### Vue Development Server
505
505
  ```bash
506
- magentrix iris-dev
506
+ magentrix run-dev
507
507
  ```
508
508
  **What it does**: Starts the Vue development server with platform assets (CSS, fonts) automatically injected from your Magentrix instance.
509
509
 
510
+ **Authentication**: Uses `VITE_REFRESH_TOKEN` from `.env.development` (no CLI authentication required). This command can be run inside the Vue project directory.
511
+
510
512
  **Options:**
511
513
  - `--path <dir>` - Specify Vue project path
512
514
  - `--no-inject` - Skip asset injection, just run dev server
513
515
  - `--restore` - Restore `.env.development` from backup without running
514
516
 
515
517
  **Process:**
516
- 1. Fetch platform assets from Magentrix
518
+ 1. Fetch platform assets from Magentrix using `.env.development` credentials
517
519
  2. Backup `.env.development` and inject assets
518
520
  3. Run `npm run dev`
519
521
  4. Restore `.env.development` on exit (Ctrl+C)
@@ -577,7 +579,7 @@ export const config = {
577
579
  ```bash
578
580
  VITE_SITE_URL = https://yourinstance.magentrix.com
579
581
  VITE_REFRESH_TOKEN = your-api-key
580
- VITE_ASSETS = '[]' # Injected automatically by iris-dev
582
+ VITE_ASSETS = '[]' # Injected automatically by run-dev
581
583
  ```
582
584
 
583
585
  **Accepted field names in config.ts:**
@@ -586,6 +588,19 @@ VITE_ASSETS = '[]' # Injected automatically by iris-dev
586
588
  - Description: `appDescription` or `app_description`
587
589
  - Icon: `appIconId` or `app_icon_id`
588
590
 
591
+ ### Command Availability
592
+
593
+ **In Vue project directories** (detected by presence of `config.ts`):
594
+ - ✓ `magentrix iris-link` - Link project to CLI
595
+ - ✓ `magentrix run-dev` - Start dev server (uses `.env.development` credentials)
596
+ - ✓ `magentrix vue-build-stage` - Build and stage
597
+
598
+ **In Magentrix workspace directories** (has `.magentrix/` folder):
599
+ - ✓ All standard commands (`setup`, `pull`, `publish`, etc.)
600
+ - ✓ All Iris commands (`iris-delete`, `iris-recover`, etc.)
601
+
602
+ **Note**: Commands like `pull`, `publish`, `autopublish` require a Magentrix workspace and will show an error if run inside a Vue project directory.
603
+
589
604
  ### Typical Development Workflow
590
605
 
591
606
  ```bash
@@ -593,7 +608,7 @@ VITE_ASSETS = '[]' # Injected automatically by iris-dev
593
608
  magentrix iris-link # Link your Vue project
594
609
 
595
610
  # Development
596
- magentrix iris-dev # Start dev server with platform assets
611
+ magentrix run-dev # Start dev server with platform assets
597
612
  # Make changes, test locally
598
613
  # Press Ctrl+C to stop
599
614
 
@@ -1058,7 +1073,7 @@ magentrix status # Shows sync status
1058
1073
  - `magentrix update` - Update to latest version
1059
1074
  - `magentrix iris-link` - Link Vue.js projects for deployment
1060
1075
  - `magentrix vue-build-stage` - Build and stage Vue.js apps
1061
- - `magentrix iris-dev` - Start Vue dev server with platform assets
1076
+ - `magentrix run-dev` - Start Vue dev server with platform assets
1062
1077
  - `magentrix iris-delete` - Delete an Iris app with recovery backup
1063
1078
  - `magentrix iris-recover` - Recover a deleted Iris app
1064
1079
 
@@ -1072,6 +1087,6 @@ Once you're comfortable with basic usage:
1072
1087
  2. **Learn the autopublish workflow** for faster development
1073
1088
  3. **Explore conflict resolution** if you work in a team
1074
1089
  4. **Check out templates** created by the `create` command to understand best practices
1075
- 5. **Deploy Vue.js apps** using `iris-link`, `vue-build-stage`, and `iris-dev` commands
1090
+ 5. **Deploy Vue.js apps** using `iris-link`, `vue-build-stage`, and `run-dev` commands
1076
1091
 
1077
1092
  Happy coding with MagentrixCLI! 🚀
@@ -13,22 +13,23 @@ import {
13
13
  getInjectionTarget
14
14
  } from '../../utils/iris/config-reader.js';
15
15
  import { getIrisAssets } from '../../utils/magentrix/api/iris.js';
16
+ import { getAccessToken } from '../../utils/magentrix/api/auth.js';
16
17
  import {
17
18
  getLinkedProjectsWithStatus,
18
19
  buildProjectChoices
19
20
  } from '../../utils/iris/linker.js';
20
- import { ensureValidCredentials } from '../../utils/cli/helpers/ensureCredentials.js';
21
21
 
22
22
  /**
23
- * iris-dev command - Start Vue dev server with platform assets injected.
23
+ * run-dev command - Start Vue dev server with platform assets injected.
24
24
  *
25
- * Assets are injected into .env.development (if exists) or config.ts.
26
- * The modified file is backed up and restored when the dev server exits.
25
+ * Uses credentials from .env.development (VITE_REFRESH_TOKEN, VITE_SITE_URL).
26
+ * Assets are injected into .env.development only.
27
+ * The .env.development file is backed up and restored when the dev server exits.
27
28
  *
28
29
  * Options:
29
30
  * --path <dir> Specify Vue project path
30
31
  * --no-inject Skip asset injection, just run dev server
31
- * --restore Restore .env.development or config.ts from backup
32
+ * --restore Restore .env.development from backup
32
33
  */
33
34
  export const irisDev = async (options = {}) => {
34
35
  process.stdout.write('\x1Bc'); // Clear console
@@ -100,55 +101,57 @@ export const irisDev = async (options = {}) => {
100
101
 
101
102
  // Inject assets if enabled
102
103
  if (inject && siteUrl) {
103
- console.log(chalk.blue('Fetching platform assets...'));
104
-
105
- try {
106
- // Get credentials for API call
107
- const { instanceUrl, token } = await ensureValidCredentials();
108
-
109
- // Use siteUrl from config if different from instanceUrl
110
- const targetUrl = siteUrl || instanceUrl;
111
-
112
- const assetsResult = await getIrisAssets(targetUrl, token.value);
113
-
114
- if (assetsResult.success && assetsResult.assets?.length > 0) {
115
- console.log(chalk.green(`\u2713 Found ${assetsResult.assets.length} platform assets`));
116
-
117
- // Determine which file will be modified
118
- const { targetFile, targetName } = getInjectionTarget(projectPath);
104
+ // Check if we have the refresh token for authentication
105
+ if (!vueConfig.refreshToken) {
106
+ console.log(chalk.yellow('Warning: VITE_REFRESH_TOKEN not set in .env.development'));
107
+ console.log(chalk.gray('Asset injection requires authentication. Continuing without assets.'));
108
+ } else {
109
+ console.log(chalk.blue('Fetching platform assets...'));
119
110
 
120
- if (!targetFile) {
121
- console.log(chalk.yellow('Warning: No .env.development file found. Cannot inject assets.'));
122
- console.log(chalk.gray('Create a .env.development file to enable asset injection.'));
123
- } else {
124
- modifiedFilePath = targetFile;
125
- modifiedFileName = targetName;
111
+ try {
112
+ // Get access token using the refresh token from .env.development
113
+ const tokenData = await getAccessToken(vueConfig.refreshToken, siteUrl);
114
+ const assetsResult = await getIrisAssets(siteUrl, tokenData.token);
126
115
 
127
- // Backup before modifying
128
- console.log(chalk.blue(`Backing up ${modifiedFileName}...`));
129
- backupPath = backupFile(modifiedFilePath);
130
- console.log(chalk.green(`\u2713 Backup created`));
116
+ if (assetsResult.success && assetsResult.assets?.length > 0) {
117
+ console.log(chalk.green(`\u2713 Found ${assetsResult.assets.length} platform assets`));
131
118
 
132
- // Inject assets
133
- console.log(chalk.blue('Injecting assets...'));
134
- const injectResult = injectAssets(projectPath, assetsResult.assets);
119
+ // Determine which file will be modified
120
+ const { targetFile, targetName } = getInjectionTarget(projectPath);
135
121
 
136
- if (injectResult.success) {
137
- assetsInjected = true;
138
- console.log(chalk.green(`\u2713 Assets injected into ${injectResult.targetName}`));
122
+ if (!targetFile) {
123
+ console.log(chalk.yellow('Warning: No .env.development file found. Cannot inject assets.'));
124
+ console.log(chalk.gray('Create a .env.development file to enable asset injection.'));
139
125
  } else {
140
- console.log(chalk.yellow('Warning: Could not inject assets. Continuing without injection.'));
126
+ modifiedFilePath = targetFile;
127
+ modifiedFileName = targetName;
128
+
129
+ // Backup before modifying
130
+ console.log(chalk.blue(`Backing up ${modifiedFileName}...`));
131
+ backupPath = backupFile(modifiedFilePath);
132
+ console.log(chalk.green(`\u2713 Backup created`));
133
+
134
+ // Inject assets
135
+ console.log(chalk.blue('Injecting assets...'));
136
+ const injectResult = injectAssets(projectPath, assetsResult.assets);
137
+
138
+ if (injectResult.success) {
139
+ assetsInjected = true;
140
+ console.log(chalk.green(`\u2713 Assets injected into ${injectResult.targetName}`));
141
+ } else {
142
+ console.log(chalk.yellow('Warning: Could not inject assets. Continuing without injection.'));
143
+ }
141
144
  }
145
+ } else if (assetsResult.error) {
146
+ console.log(chalk.yellow(`Warning: Could not fetch assets: ${assetsResult.error}`));
147
+ console.log(chalk.gray('Continuing without asset injection.'));
148
+ } else {
149
+ console.log(chalk.yellow('No platform assets found.'));
142
150
  }
143
- } else if (assetsResult.error) {
144
- console.log(chalk.yellow(`Warning: Could not fetch assets: ${assetsResult.error}`));
151
+ } catch (err) {
152
+ console.log(chalk.yellow(`Warning: Error fetching assets: ${err.message}`));
145
153
  console.log(chalk.gray('Continuing without asset injection.'));
146
- } else {
147
- console.log(chalk.yellow('No platform assets found.'));
148
154
  }
149
- } catch (err) {
150
- console.log(chalk.yellow(`Warning: Error fetching assets: ${err.message}`));
151
- console.log(chalk.gray('Continuing without asset injection.'));
152
155
  }
153
156
  } else if (!inject) {
154
157
  console.log(chalk.gray('Skipping asset injection (--no-inject)'));
@@ -171,12 +174,13 @@ export const irisDev = async (options = {}) => {
171
174
  */
172
175
  async function runDevServer(projectPath, modifiedFilePath, modifiedFileName, backupPath, assetsInjected) {
173
176
  return new Promise((resolvePromise) => {
174
- const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
177
+ const isWindows = process.platform === 'win32';
178
+ const npmCmd = isWindows ? 'npm.cmd' : 'npm';
175
179
 
176
180
  const child = spawn(npmCmd, ['run', 'dev'], {
177
181
  cwd: projectPath,
178
182
  stdio: 'inherit',
179
- shell: false
183
+ shell: isWindows // Windows requires shell: true for .cmd files
180
184
  });
181
185
 
182
186
  // Handle cleanup on exit
@@ -314,7 +318,7 @@ async function selectProject() {
314
318
  console.log();
315
319
  console.log(chalk.gray('To get started:'));
316
320
  console.log(chalk.white(` 1. Link a Vue project: ${chalk.cyan('magentrix iris-link')}`));
317
- console.log(chalk.white(` 2. Or specify path: ${chalk.cyan('magentrix iris-dev --path /path/to/vue-project')}`));
321
+ console.log(chalk.white(` 2. Or specify path: ${chalk.cyan('magentrix run-dev --path /path/to/vue-project')}`));
318
322
  console.log();
319
323
  }
320
324
 
@@ -77,9 +77,9 @@ async function showMainMenu() {
77
77
 
78
78
  const choices = [
79
79
  {
80
- name: 'Link a new Vue project',
80
+ name: 'Link a Vue project',
81
81
  value: 'link',
82
- description: 'Add a Vue project to the CLI'
82
+ description: chalk.dim('Add a Vue project to the CLI')
83
83
  }
84
84
  ];
85
85
 
@@ -87,12 +87,12 @@ async function showMainMenu() {
87
87
  choices.push({
88
88
  name: 'View linked projects',
89
89
  value: 'list',
90
- description: 'Show all linked Vue projects with status'
90
+ description: chalk.dim('Show all linked Vue projects with status')
91
91
  });
92
92
  choices.push({
93
93
  name: 'Unlink a project',
94
94
  value: 'unlink',
95
- description: 'Remove a Vue project from the CLI'
95
+ description: chalk.dim('Remove a Vue project from the CLI')
96
96
  });
97
97
  }
98
98
 
@@ -100,7 +100,7 @@ async function showMainMenu() {
100
100
  choices.push({
101
101
  name: `Clean up invalid projects (${invalidCount})`,
102
102
  value: 'cleanup',
103
- description: 'Remove projects with missing paths'
103
+ description: chalk.dim('Remove projects with missing paths')
104
104
  });
105
105
  }
106
106
 
@@ -152,33 +152,31 @@ async function handleLink(pathOption) {
152
152
  if (cwdConfig.found && cwdConfig.errors.length === 0) {
153
153
  // Valid Vue project
154
154
  choices.push({
155
- name: `Current directory - ${cwdConfig.appName} (${cwdConfig.slug})`,
155
+ name: `Current directory (${cwdPath})`,
156
156
  value: cwdPath,
157
- description: cwdPath
157
+ description: chalk.dim(`→ App: "${cwdConfig.appName}" (${cwdConfig.slug})`)
158
158
  });
159
159
  } else if (cwdConfig.found && cwdConfig.errors.length > 0) {
160
160
  // Has config.ts but with errors
161
161
  const errorMsg = cwdConfig.errors[0] || 'Invalid config';
162
162
  choices.push({
163
- name: `Current directory`,
163
+ name: `Current directory (${cwdPath})`,
164
164
  value: '__disabled__',
165
- disabled: `Config error: ${errorMsg}`,
166
- description: cwdPath
165
+ disabled: `Config error: ${errorMsg}`
167
166
  });
168
167
  } else {
169
168
  // No config.ts found
170
169
  choices.push({
171
- name: `Current directory`,
170
+ name: `Current directory (${cwdPath})`,
172
171
  value: '__disabled__',
173
- disabled: 'No config.ts found',
174
- description: cwdPath
172
+ disabled: 'No config.ts found'
175
173
  });
176
174
  }
177
175
 
178
176
  choices.push({
179
177
  name: 'Enter path manually',
180
178
  value: '__manual__',
181
- description: 'Specify the full path to a Vue project'
179
+ description: chalk.dim('Specify the full path to a Vue project')
182
180
  });
183
181
 
184
182
  choices.push({
@@ -303,7 +301,7 @@ async function handleUnlink(pathOption) {
303
301
  return {
304
302
  name: `${prefix}${displayName} (${displaySlug})`,
305
303
  value: p.slug,
306
- description: p.path
304
+ description: chalk.dim(`→ Path: ${p.path}`)
307
305
  };
308
306
  });
309
307
 
package/bin/magentrix.js CHANGED
@@ -3,6 +3,8 @@
3
3
  // Imports
4
4
  import { Command } from 'commander';
5
5
  import chalk from 'chalk';
6
+ import { existsSync } from 'node:fs';
7
+ import { join } from 'node:path';
6
8
  import { VERSION } from '../vars/config.js';
7
9
  import { setup } from '../actions/setup.js';
8
10
  import { main } from '../actions/main.js';
@@ -17,6 +19,40 @@ import { update } from '../actions/update.js';
17
19
  import { configWizard } from '../actions/config.js';
18
20
  import { irisLink, irisDev, irisDelete, irisRecover, vueBuildStage } from '../actions/iris/index.js';
19
21
 
22
+ // ── Vue Project Detection ────────────────────────────────
23
+ /**
24
+ * Check if current directory is a Vue project (has config.ts)
25
+ */
26
+ function isInVueProject() {
27
+ const configLocations = [
28
+ 'src/config.ts',
29
+ 'config.ts',
30
+ 'src/iris-config.ts',
31
+ 'iris-config.ts'
32
+ ];
33
+ return configLocations.some(loc => existsSync(join(process.cwd(), loc)));
34
+ }
35
+
36
+ /**
37
+ * Block non-iris commands when in a Vue project
38
+ */
39
+ function requireMagentrixWorkspace(fn) {
40
+ return async (...args) => {
41
+ if (isInVueProject()) {
42
+ console.error(`\n${chalk.bgRed.white.bold(' ERROR ')} ${chalk.redBright('This command must be run in a Magentrix workspace')}\n`);
43
+ console.error(chalk.yellow('It looks like you\'re in a Vue project directory.'));
44
+ console.error(chalk.gray('This command requires a Magentrix workspace with global API key and instance URL.\n'));
45
+ console.error(chalk.cyan('Available commands in Vue projects:'));
46
+ console.error(chalk.gray(' • magentrix iris-link'));
47
+ console.error(chalk.gray(' • magentrix run-dev'));
48
+ console.error(chalk.gray(' • magentrix vue-build-stage\n'));
49
+ process.exit(1);
50
+ }
51
+ // Execute the command
52
+ await fn(...args);
53
+ };
54
+ }
55
+
20
56
  // ── Middleware ────────────────────────────────
21
57
  async function preMiddleware() {
22
58
  await recacheFileIdIndex(EXPORT_ROOT);
@@ -32,6 +68,11 @@ const withMiddleware = ({ pre, post }) => (fn) => async (...args) => {
32
68
  if (post) await post(...args);
33
69
  };
34
70
 
71
+ const withDefault = withMiddleware({ pre: preMiddleware, post: postMiddleware });
72
+
73
+ // Combined wrapper: check for Vue project + run middleware
74
+ const withWorkspaceCheck = (fn) => requireMagentrixWorkspace(withDefault(fn));
75
+
35
76
  // ── CLI Setup ────────────────────────────────
36
77
  const program = new Command();
37
78
  program
@@ -64,7 +105,7 @@ program
64
105
  { name: 'update', desc: 'Update MagentrixCLI to the latest version', icon: '⬆️ ' },
65
106
  { name: 'iris-link', desc: 'Link a Vue project to the CLI', icon: '🔗 ' },
66
107
  { name: 'vue-build-stage', desc: 'Build Vue project and stage for publish', icon: '🏗️ ' },
67
- { name: 'iris-dev', desc: 'Start Vue dev server with platform assets', icon: '🌐 ' },
108
+ { name: 'run-dev', desc: 'Start Vue dev server with platform assets', icon: '🌐 ' },
68
109
  { name: 'iris-delete', desc: 'Delete an Iris app with backup', icon: '🗑️ ' },
69
110
  { name: 'iris-recover', desc: 'Recover a deleted Iris app from backup', icon: '♻️ ' }
70
111
  ];
@@ -93,8 +134,6 @@ program
93
134
  }
94
135
  });
95
136
 
96
- const withDefault = withMiddleware({ pre: preMiddleware, post: postMiddleware });
97
-
98
137
  // ── Error Handlers ───────────────────────────
99
138
  program.showHelpAfterError(false);
100
139
  program.configureOutput({
@@ -128,8 +167,8 @@ program
128
167
  .description('Configure your Magentrix API key')
129
168
  .option('--api-key <apiKey>', 'Magentrix API key')
130
169
  .option('--instance-url <instanceUrl>', 'Magentrix instance URL (e.g., https://example.magentrixcloud.com)')
131
- .action(withDefault(setup));
132
- program.command('pull').description('Pull files from the remote server').action(withDefault(pull));
170
+ .action(withWorkspaceCheck(setup));
171
+ program.command('pull').description('Pull files from the remote server').action(withWorkspaceCheck(pull));
133
172
  const createCommand = program
134
173
  .command('create')
135
174
  .description('Create files locally')
@@ -138,7 +177,7 @@ const createCommand = program
138
177
  .option('--name <name>', 'Name of the file to create')
139
178
  .option('--description <description>', 'Optional description')
140
179
  .option('--entity-id <entityId>', 'Entity ID (required for triggers)')
141
- .action(withDefault(create));
180
+ .action(withWorkspaceCheck(create));
142
181
 
143
182
  // Override help for create command to show options
144
183
  createCommand.configureHelp({
@@ -185,17 +224,17 @@ createCommand.configureHelp({
185
224
  return help;
186
225
  }
187
226
  });
188
- program.command('status').description('Show file conflicts').action(withDefault(status));
189
- program.command('autopublish').description('Watch & sync changes in real time').action(withDefault(autoPublish));
227
+ program.command('status').description('Show file conflicts').action(withWorkspaceCheck(status));
228
+ program.command('autopublish').description('Watch & sync changes in real time').action(withWorkspaceCheck(autoPublish));
190
229
  // Publish does its own comprehensive file scanning, so skip the pre-cache middleware
191
- program.command('publish').description('Publish pending changes to the remote server').action(withDefault(publish));
192
- program.command('update').description('Update MagentrixCLI to the latest version').action(update);
230
+ program.command('publish').description('Publish pending changes to the remote server').action(withWorkspaceCheck(publish));
231
+ program.command('update').description('Update MagentrixCLI to the latest version').action(requireMagentrixWorkspace(update));
193
232
 
194
233
  // Config command - interactive wizard
195
234
  program
196
235
  .command('config')
197
236
  .description('Configure CLI settings')
198
- .action(configWizard);
237
+ .action(requireMagentrixWorkspace(configWizard));
199
238
 
200
239
  // Iris commands for Vue.js app management
201
240
  program
@@ -215,7 +254,7 @@ program
215
254
  .action(vueBuildStage);
216
255
 
217
256
  program
218
- .command('iris-dev')
257
+ .command('run-dev')
219
258
  .description('Start Vue dev server with platform assets injected')
220
259
  .option('--path <path>', 'Path to the Vue project')
221
260
  .option('--no-inject', 'Skip asset injection')
@@ -236,6 +275,18 @@ program
236
275
  // ── Unknown Command Handler ──────────────────
237
276
  program.argument('[command]', 'command to run').action((cmd) => {
238
277
  const runMain = async () => {
278
+ // Check if in Vue project
279
+ if (isInVueProject()) {
280
+ console.error(`\n${chalk.bgRed.white.bold(' ERROR ')} ${chalk.redBright('This command must be run in a Magentrix workspace')}\n`);
281
+ console.error(chalk.yellow('It looks like you\'re in a Vue project directory.'));
282
+ console.error(chalk.gray('This command requires a Magentrix workspace with global API key and instance URL.\n'));
283
+ console.error(chalk.cyan('Available commands in Vue projects:'));
284
+ console.error(chalk.gray(' • magentrix iris-link'));
285
+ console.error(chalk.gray(' • magentrix run-dev'));
286
+ console.error(chalk.gray(' • magentrix vue-build-stage\n'));
287
+ process.exit(1);
288
+ }
289
+
239
290
  await preMiddleware();
240
291
  await main();
241
292
  await postMiddleware();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magentrix-corp/magentrix-cli",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "CLI tool for synchronizing local files with Magentrix cloud platform",
5
5
  "main": "index.js",
6
6
  "type": "module",