@magentrix-corp/magentrix-cli 1.3.2 → 1.3.3

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
@@ -13,11 +13,11 @@ 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
23
  * iris-dev command - Start Vue dev server with platform assets injected.
@@ -100,55 +100,57 @@ export const irisDev = async (options = {}) => {
100
100
 
101
101
  // Inject assets if enabled
102
102
  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);
103
+ // Check if we have the refresh token for authentication
104
+ if (!vueConfig.refreshToken) {
105
+ console.log(chalk.yellow('Warning: VITE_REFRESH_TOKEN not set in .env.development'));
106
+ console.log(chalk.gray('Asset injection requires authentication. Continuing without assets.'));
107
+ } else {
108
+ console.log(chalk.blue('Fetching platform assets...'));
119
109
 
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;
110
+ try {
111
+ // Get access token using the refresh token from .env.development
112
+ const tokenData = await getAccessToken(vueConfig.refreshToken, siteUrl);
113
+ const assetsResult = await getIrisAssets(siteUrl, tokenData.token);
126
114
 
127
- // Backup before modifying
128
- console.log(chalk.blue(`Backing up ${modifiedFileName}...`));
129
- backupPath = backupFile(modifiedFilePath);
130
- console.log(chalk.green(`\u2713 Backup created`));
115
+ if (assetsResult.success && assetsResult.assets?.length > 0) {
116
+ console.log(chalk.green(`\u2713 Found ${assetsResult.assets.length} platform assets`));
131
117
 
132
- // Inject assets
133
- console.log(chalk.blue('Injecting assets...'));
134
- const injectResult = injectAssets(projectPath, assetsResult.assets);
118
+ // Determine which file will be modified
119
+ const { targetFile, targetName } = getInjectionTarget(projectPath);
135
120
 
136
- if (injectResult.success) {
137
- assetsInjected = true;
138
- console.log(chalk.green(`\u2713 Assets injected into ${injectResult.targetName}`));
121
+ if (!targetFile) {
122
+ console.log(chalk.yellow('Warning: No .env.development file found. Cannot inject assets.'));
123
+ console.log(chalk.gray('Create a .env.development file to enable asset injection.'));
139
124
  } else {
140
- console.log(chalk.yellow('Warning: Could not inject assets. Continuing without injection.'));
125
+ modifiedFilePath = targetFile;
126
+ modifiedFileName = targetName;
127
+
128
+ // Backup before modifying
129
+ console.log(chalk.blue(`Backing up ${modifiedFileName}...`));
130
+ backupPath = backupFile(modifiedFilePath);
131
+ console.log(chalk.green(`\u2713 Backup created`));
132
+
133
+ // Inject assets
134
+ console.log(chalk.blue('Injecting assets...'));
135
+ const injectResult = injectAssets(projectPath, assetsResult.assets);
136
+
137
+ if (injectResult.success) {
138
+ assetsInjected = true;
139
+ console.log(chalk.green(`\u2713 Assets injected into ${injectResult.targetName}`));
140
+ } else {
141
+ console.log(chalk.yellow('Warning: Could not inject assets. Continuing without injection.'));
142
+ }
141
143
  }
144
+ } else if (assetsResult.error) {
145
+ console.log(chalk.yellow(`Warning: Could not fetch assets: ${assetsResult.error}`));
146
+ console.log(chalk.gray('Continuing without asset injection.'));
147
+ } else {
148
+ console.log(chalk.yellow('No platform assets found.'));
142
149
  }
143
- } else if (assetsResult.error) {
144
- console.log(chalk.yellow(`Warning: Could not fetch assets: ${assetsResult.error}`));
150
+ } catch (err) {
151
+ console.log(chalk.yellow(`Warning: Error fetching assets: ${err.message}`));
145
152
  console.log(chalk.gray('Continuing without asset injection.'));
146
- } else {
147
- console.log(chalk.yellow('No platform assets found.'));
148
153
  }
149
- } catch (err) {
150
- console.log(chalk.yellow(`Warning: Error fetching assets: ${err.message}`));
151
- console.log(chalk.gray('Continuing without asset injection.'));
152
154
  }
153
155
  } else if (!inject) {
154
156
  console.log(chalk.gray('Skipping asset injection (--no-inject)'));
@@ -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 iris-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
@@ -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
@@ -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 iris-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.3",
4
4
  "description": "CLI tool for synchronizing local files with Magentrix cloud platform",
5
5
  "main": "index.js",
6
6
  "type": "module",