morphix-env 0.1.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/README.md ADDED
@@ -0,0 +1,272 @@
1
+ # mx-env
2
+
3
+ Environment variable toolkit for multi-project architectures. Combines [Infisical](https://infisical.com) secret management with local override files and client-side runtime injection.
4
+
5
+ ## Why
6
+
7
+ | Problem | Solution |
8
+ |---------|----------|
9
+ | `NEXT_PUBLIC_*` / `VITE_*` baked at build time | `mx-env generate` creates `__env.js` for runtime injection |
10
+ | Scattered env vars across hosting platforms | Single source of truth in Infisical, pulled at startup |
11
+ | No local override when using remote config | `.env.local` always wins — edit one file, restart |
12
+ | Different tools for different needs (dotenv, cross-env, infisical CLI) | One tool, one command |
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pnpm add -D mx-env
18
+ # or
19
+ npm install -D mx-env
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ # Run a command with .env.local overrides
26
+ mx-env run -- next dev
27
+
28
+ # Generate client-side __env.js
29
+ mx-env generate --out public/__env.js
30
+
31
+ # Debug: see what's loaded
32
+ mx-env inspect
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ ### `mx-env run [options] -- <command>`
38
+
39
+ Load environment variables, then execute a command. The child process inherits all injected vars.
40
+
41
+ ```bash
42
+ # Basic: load .env.local, run dev server
43
+ mx-env run -- next dev --turbo -p 3004
44
+
45
+ # Custom env file
46
+ mx-env run -f .env.staging -- npm start
47
+
48
+ # Skip Infisical (use only local files)
49
+ mx-env run --no-infisical -- npm start
50
+
51
+ # Verbose: show which vars were loaded
52
+ mx-env run -v -- node server.js
53
+ ```
54
+
55
+ **Loading priority (high to low):**
56
+
57
+ 1. `.env.local` (or `--env-file`) — always wins
58
+ 2. Infisical secrets — pulled via SDK
59
+ 3. Existing `process.env` — Docker ENV, CI vars, etc.
60
+
61
+ ### `mx-env generate [options]`
62
+
63
+ Extract public environment variables (`NEXT_PUBLIC_*`, `VITE_*`, `EXPO_PUBLIC_*`) and write them to a JS file for browser runtime injection.
64
+
65
+ ```bash
66
+ # Default: public/__env.js
67
+ mx-env generate
68
+
69
+ # Custom output path (Vite projects)
70
+ mx-env generate --out dist/__env.js
71
+
72
+ # Only include specific prefix
73
+ mx-env generate --filter NEXT_PUBLIC_
74
+ ```
75
+
76
+ Output file content:
77
+
78
+ ```js
79
+ window.__ENV={"NEXT_PUBLIC_API_URL":"https://api.example.com","NEXT_PUBLIC_APP_NAME":"MyApp"};
80
+ ```
81
+
82
+ Load it in your HTML before your app bundle:
83
+
84
+ ```html
85
+ <script src="/__env.js"></script>
86
+ ```
87
+
88
+ Read in your app:
89
+
90
+ ```ts
91
+ function getEnv(key: string, fallback = ''): string {
92
+ if (typeof window === 'undefined') return process.env[key] || fallback
93
+ return window.__ENV?.[key] || process.env[key] || fallback
94
+ }
95
+ ```
96
+
97
+ ### `mx-env inspect [options]`
98
+
99
+ Print env var values for debugging. Secrets are masked (first 4 chars shown).
100
+
101
+ ```bash
102
+ mx-env inspect
103
+ mx-env inspect --filter NEXT_PUBLIC_
104
+ mx-env inspect -f .env.production
105
+ ```
106
+
107
+ ## Options
108
+
109
+ | Flag | Short | Description |
110
+ |------|-------|-------------|
111
+ | `--env-file <path>` | `-f` | Env file to load (default: `.env.local`, repeatable) |
112
+ | `--out <path>` | `-o` | Output path for generate (default: `public/__env.js`) |
113
+ | `--filter <prefix>` | | Only include vars with this prefix |
114
+ | `--no-infisical` | | Skip Infisical SDK fetch |
115
+ | `--verbose` | `-v` | Show loaded variable names |
116
+
117
+ ## Config File
118
+
119
+ Create `mx-env.config.json` in your project root to declare project-level settings. This file is committed to git.
120
+
121
+ ```json
122
+ {
123
+ "infisical": {
124
+ "projectId": "your-project-id",
125
+ "paths": ["/ai/shared", "/ai/web"],
126
+ "env": "$DEPLOY_ENV"
127
+ },
128
+ "envFiles": [".env.local"],
129
+ "generate": {
130
+ "out": "public/__env.js",
131
+ "filter": "NEXT_PUBLIC_"
132
+ }
133
+ }
134
+ ```
135
+
136
+ | Field | Description | Committed to git? |
137
+ |-------|-------------|-------------------|
138
+ | `infisical.projectId` | Infisical project ID | Yes — not a secret |
139
+ | `infisical.paths` | Secret paths to pull | Yes — not a secret |
140
+ | `infisical.env` | Environment name, supports `$VAR` references | Yes |
141
+ | `envFiles` | Local override files to load | Yes |
142
+ | `generate` | Client-side __env.js output config | Yes |
143
+
144
+ **Infisical authentication** is always via environment variables — never in the config file:
145
+
146
+ | Env Var | Description |
147
+ |---------|-------------|
148
+ | `INFISICAL_CLIENT_ID` | Machine Identity client ID |
149
+ | `INFISICAL_CLIENT_SECRET` | Machine Identity client secret |
150
+ | `DEPLOY_ENV` | Environment name (`dev` / `staging` / `prod`) |
151
+
152
+ ## Usage Examples
153
+
154
+ ### Next.js
155
+
156
+ ```json
157
+ {
158
+ "scripts": {
159
+ "dev": "mx-env run -- next dev --turbo -p 3004",
160
+ "build": "mx-env run -- next build",
161
+ "start": "mx-env run -- node server.js"
162
+ }
163
+ }
164
+ ```
165
+
166
+ `mx-env.config.json`:
167
+
168
+ ```json
169
+ {
170
+ "infisical": {
171
+ "projectId": "xxx",
172
+ "paths": ["/ai/shared", "/ai/web"],
173
+ "env": "$DEPLOY_ENV"
174
+ },
175
+ "envFiles": [".env.local"],
176
+ "generate": {
177
+ "out": "public/__env.js",
178
+ "filter": "NEXT_PUBLIC_"
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### Vite (React / Vue / Ionic)
184
+
185
+ ```json
186
+ {
187
+ "scripts": {
188
+ "dev": "mx-env run -- vite",
189
+ "build": "mx-env run -- vite build"
190
+ }
191
+ }
192
+ ```
193
+
194
+ ```json
195
+ {
196
+ "infisical": {
197
+ "projectId": "xxx",
198
+ "paths": ["/ai/shared", "/ai/shell"],
199
+ "env": "$DEPLOY_ENV"
200
+ },
201
+ "generate": {
202
+ "out": "dist/__env.js",
203
+ "filter": "VITE_"
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### Express API
209
+
210
+ ```json
211
+ {
212
+ "scripts": {
213
+ "dev": "mx-env run -- tsx watch src/index.ts",
214
+ "start": "mx-env run -- node dist/index.js"
215
+ }
216
+ }
217
+ ```
218
+
219
+ ```json
220
+ {
221
+ "infisical": {
222
+ "projectId": "xxx",
223
+ "paths": ["/ai/shared", "/ai/api"],
224
+ "env": "$DEPLOY_ENV"
225
+ }
226
+ }
227
+ ```
228
+
229
+ No `generate` field — server-side apps don't need `__env.js`.
230
+
231
+ ### Docker
232
+
233
+ ```dockerfile
234
+ FROM node:20-alpine
235
+ WORKDIR /app
236
+ COPY . .
237
+ RUN pnpm install && pnpm build
238
+
239
+ # Only these 3 vars needed at runtime
240
+ ENV INFISICAL_CLIENT_ID=""
241
+ ENV INFISICAL_CLIENT_SECRET=""
242
+ ENV DEPLOY_ENV="prod"
243
+
244
+ CMD ["npx", "mx-env", "run", "--", "node", "server.js"]
245
+ ```
246
+
247
+ No Infisical CLI binary needed in the image.
248
+
249
+ ### Local Development with Infisical CLI
250
+
251
+ If you already use `infisical run` locally, mx-env still adds value as the override layer:
252
+
253
+ ```json
254
+ {
255
+ "dev": "infisical run --path=/ai --env=dev -- mx-env run -- next dev",
256
+ "dev:local": "infisical run --path=/ai --env=dev -- mx-env run -- next dev"
257
+ }
258
+ ```
259
+
260
+ `.env.local` overrides take effect on top of Infisical CLI injection.
261
+
262
+ ## How It Works
263
+
264
+ 1. Read `mx-env.config.json` for project settings
265
+ 2. If `INFISICAL_CLIENT_ID` + `INFISICAL_CLIENT_SECRET` exist → fetch secrets via SDK, inject into `process.env` (does not overwrite existing vars)
266
+ 3. Read `.env.local` → inject into `process.env` (overwrites everything, highest priority)
267
+ 4. If `generate` is configured → write `__env.js` with public vars
268
+ 5. Spawn child command — it inherits the fully assembled `process.env`
269
+
270
+ ## License
271
+
272
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js ADDED
@@ -0,0 +1,376 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/env.ts
27
+ var import_fs = require("fs");
28
+ var import_path = require("path");
29
+ function parseEnvFile(filePath) {
30
+ const absPath = (0, import_path.resolve)(filePath);
31
+ if (!(0, import_fs.existsSync)(absPath)) return {};
32
+ const vars = {};
33
+ const content = (0, import_fs.readFileSync)(absPath, "utf8");
34
+ for (const raw of content.split("\n")) {
35
+ const line = raw.trim();
36
+ if (!line || line.startsWith("#")) continue;
37
+ const eq = line.indexOf("=");
38
+ if (eq === -1) continue;
39
+ const key = line.slice(0, eq).trim();
40
+ let value = line.slice(eq + 1).trim();
41
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
42
+ value = value.slice(1, -1);
43
+ }
44
+ vars[key] = value;
45
+ }
46
+ return vars;
47
+ }
48
+ function loadEnvFiles(files) {
49
+ const overrides = [];
50
+ for (const file of files) {
51
+ const vars = parseEnvFile(file);
52
+ for (const [key, value] of Object.entries(vars)) {
53
+ process.env[key] = value;
54
+ overrides.push({ key, source: file });
55
+ }
56
+ }
57
+ return overrides;
58
+ }
59
+ var PUBLIC_PREFIXES = ["NEXT_PUBLIC_", "VITE_", "EXPO_PUBLIC_"];
60
+ function extractPublicVars() {
61
+ const vars = {};
62
+ for (const [key, value] of Object.entries(process.env)) {
63
+ if (value && PUBLIC_PREFIXES.some((p) => key.startsWith(p))) {
64
+ vars[key] = value;
65
+ }
66
+ }
67
+ return vars;
68
+ }
69
+
70
+ // src/infisical.ts
71
+ var import_sdk = require("@infisical/sdk");
72
+ function getInfisicalConfig() {
73
+ const clientId = process.env.INFISICAL_CLIENT_ID;
74
+ const clientSecret = process.env.INFISICAL_CLIENT_SECRET;
75
+ const projectId = process.env.INFISICAL_PROJECT_ID;
76
+ if (!clientId || !clientSecret || !projectId) return null;
77
+ return {
78
+ clientId,
79
+ clientSecret,
80
+ projectId,
81
+ environment: process.env.DEPLOY_ENV || process.env.INFISICAL_ENV || "dev",
82
+ paths: (process.env.INFISICAL_PATHS || "/").split(",").map((p) => p.trim()),
83
+ siteUrl: process.env.INFISICAL_SITE_URL
84
+ };
85
+ }
86
+ async function fetchInfisicalSecrets(config) {
87
+ const client = new import_sdk.InfisicalSDK({
88
+ ...config.siteUrl ? { siteUrl: config.siteUrl } : {}
89
+ });
90
+ await client.auth().universalAuth.login({
91
+ clientId: config.clientId,
92
+ clientSecret: config.clientSecret
93
+ });
94
+ let count = 0;
95
+ for (const secretPath of config.paths) {
96
+ const result = await client.secrets().listSecrets({
97
+ environment: config.environment,
98
+ projectId: config.projectId,
99
+ secretPath,
100
+ expandSecretReferences: true,
101
+ viewSecretValue: true
102
+ });
103
+ for (const secret of result.secrets) {
104
+ process.env[secret.secretKey] = secret.secretValue;
105
+ count++;
106
+ }
107
+ }
108
+ return count;
109
+ }
110
+
111
+ // src/config.ts
112
+ var import_fs2 = require("fs");
113
+ var import_path2 = require("path");
114
+ var CONFIG_FILE = "mx-env.config.json";
115
+ function resolveEnvRef(value) {
116
+ if (value.startsWith("$")) {
117
+ return process.env[value.slice(1)] || "";
118
+ }
119
+ return value;
120
+ }
121
+ function loadConfig() {
122
+ const configPath = (0, import_path2.resolve)(CONFIG_FILE);
123
+ if (!(0, import_fs2.existsSync)(configPath)) return {};
124
+ try {
125
+ const raw = JSON.parse((0, import_fs2.readFileSync)(configPath, "utf8"));
126
+ if (raw.infisical?.env) {
127
+ raw.infisical.env = resolveEnvRef(raw.infisical.env);
128
+ }
129
+ return raw;
130
+ } catch (e) {
131
+ console.warn(`[mx-env] Failed to parse ${CONFIG_FILE}: ${e.message}`);
132
+ return {};
133
+ }
134
+ }
135
+
136
+ // src/cli.ts
137
+ var import_cross_spawn = __toESM(require("cross-spawn"));
138
+ var import_fs3 = require("fs");
139
+ var import_path3 = require("path");
140
+ var VERSION = "0.1.0";
141
+ var DEFAULT_ENV_FILE = ".env.local";
142
+ function parseArgs(argv) {
143
+ const args = {
144
+ command: "",
145
+ subArgs: [],
146
+ envFiles: [],
147
+ outFile: null,
148
+ verbose: false,
149
+ filter: null,
150
+ noInfisical: false
151
+ };
152
+ let i = 2;
153
+ const command = argv[i];
154
+ if (!command) return args;
155
+ args.command = command;
156
+ i++;
157
+ while (i < argv.length) {
158
+ const arg = argv[i];
159
+ if (arg === "--") {
160
+ args.subArgs = argv.slice(i + 1);
161
+ break;
162
+ }
163
+ if (arg === "--env-file" || arg === "-f") {
164
+ i++;
165
+ if (argv[i]) args.envFiles.push(argv[i]);
166
+ } else if (arg === "--out" || arg === "-o") {
167
+ i++;
168
+ if (argv[i]) args.outFile = argv[i];
169
+ } else if (arg === "--filter") {
170
+ i++;
171
+ if (argv[i]) args.filter = argv[i];
172
+ } else if (arg === "--verbose" || arg === "-v") {
173
+ args.verbose = true;
174
+ } else if (arg === "--no-infisical") {
175
+ args.noInfisical = true;
176
+ } else {
177
+ if (args.command === "run") {
178
+ args.subArgs = argv.slice(i);
179
+ break;
180
+ }
181
+ }
182
+ i++;
183
+ }
184
+ return args;
185
+ }
186
+ function mergeWithConfig(args, config) {
187
+ if (args.envFiles.length === 0) {
188
+ args.envFiles = config.envFiles || [DEFAULT_ENV_FILE];
189
+ }
190
+ if (!args.outFile && config.generate) {
191
+ args.outFile = config.generate.out;
192
+ }
193
+ if (!args.filter && config.generate?.filter) {
194
+ args.filter = config.generate.filter;
195
+ }
196
+ return args;
197
+ }
198
+ async function loadAllEnv(args, config) {
199
+ if (!args.noInfisical) {
200
+ const infisicalConfig = config.infisical ? {
201
+ clientId: process.env.INFISICAL_CLIENT_ID || "",
202
+ clientSecret: process.env.INFISICAL_CLIENT_SECRET || "",
203
+ projectId: config.infisical.projectId,
204
+ environment: config.infisical.env || "dev",
205
+ paths: config.infisical.paths || ["/"],
206
+ siteUrl: config.infisical.siteUrl
207
+ } : getInfisicalConfig();
208
+ if (infisicalConfig && infisicalConfig.clientId && infisicalConfig.clientSecret) {
209
+ try {
210
+ const count = await fetchInfisicalSecrets(infisicalConfig);
211
+ console.log(`[mx-env] Infisical: loaded ${count} secrets (${infisicalConfig.environment}: ${infisicalConfig.paths.join(", ")})`);
212
+ } catch (e) {
213
+ console.warn(`[mx-env] Infisical: failed - ${e.message}`);
214
+ }
215
+ } else if (args.verbose) {
216
+ console.log("[mx-env] Infisical: skipped (no credentials)");
217
+ }
218
+ }
219
+ const overrides = loadEnvFiles(args.envFiles);
220
+ if (overrides.length > 0) {
221
+ console.log(`[mx-env] Loaded ${overrides.length} overrides from ${args.envFiles.join(", ")}`);
222
+ if (args.verbose) {
223
+ for (const o of overrides) {
224
+ console.log(` ${o.key} (from ${o.source})`);
225
+ }
226
+ }
227
+ }
228
+ }
229
+ async function cmdRun(args, config) {
230
+ if (args.subArgs.length === 0) {
231
+ console.error("[mx-env] No command specified. Usage: mx-env run -- <command>");
232
+ process.exit(1);
233
+ }
234
+ await loadAllEnv(args, config);
235
+ if (args.outFile) {
236
+ generateClientEnv(args.outFile, args.filter);
237
+ }
238
+ const [cmd, ...cmdArgs] = args.subArgs;
239
+ const result = import_cross_spawn.default.sync(cmd, cmdArgs, {
240
+ stdio: "inherit",
241
+ env: process.env
242
+ });
243
+ if (result.error) {
244
+ console.error(`[mx-env] Failed to execute: ${cmd}`, result.error.message);
245
+ process.exit(1);
246
+ }
247
+ process.exit(result.status ?? 1);
248
+ }
249
+ function generateClientEnv(outFile, filter) {
250
+ let vars = extractPublicVars();
251
+ if (filter) {
252
+ vars = Object.fromEntries(
253
+ Object.entries(vars).filter(([key]) => key.startsWith(filter))
254
+ );
255
+ }
256
+ const js = `window.__ENV=${JSON.stringify(vars)};`;
257
+ (0, import_fs3.mkdirSync)((0, import_path3.dirname)(outFile), { recursive: true });
258
+ (0, import_fs3.writeFileSync)(outFile, js);
259
+ console.log(`[mx-env] Generated ${outFile} (${Object.keys(vars).length} client vars)`);
260
+ }
261
+ async function cmdGenerate(args, config) {
262
+ const outFile = args.outFile || "public/__env.js";
263
+ await loadAllEnv(args, config);
264
+ generateClientEnv(outFile, args.filter);
265
+ if (args.verbose) {
266
+ const vars = extractPublicVars();
267
+ for (const key of Object.keys(vars)) {
268
+ console.log(` ${key}`);
269
+ }
270
+ }
271
+ }
272
+ function cmdInspect(args) {
273
+ for (const file of args.envFiles.length > 0 ? args.envFiles : [DEFAULT_ENV_FILE]) {
274
+ const vars = parseEnvFile(file);
275
+ const keys = Object.keys(vars);
276
+ if (keys.length === 0) {
277
+ console.log(`${file}: (not found or empty)`);
278
+ continue;
279
+ }
280
+ console.log(`${file}: (${keys.length} vars)`);
281
+ for (const [key, value] of Object.entries(vars)) {
282
+ if (args.filter && !key.startsWith(args.filter)) continue;
283
+ const display = value.length > 8 ? value.slice(0, 4) + "***" : value;
284
+ console.log(` ${key}=${display}`);
285
+ }
286
+ }
287
+ const publicVars = extractPublicVars();
288
+ const filtered = args.filter ? Object.entries(publicVars).filter(([k]) => k.startsWith(args.filter)) : Object.entries(publicVars);
289
+ if (filtered.length > 0) {
290
+ console.log(`
291
+ process.env public vars: (${filtered.length})`);
292
+ for (const [key, value] of filtered) {
293
+ const display = value.length > 8 ? value.slice(0, 4) + "***" : value;
294
+ console.log(` ${key}=${display}`);
295
+ }
296
+ }
297
+ }
298
+ function showHelp() {
299
+ console.log(`
300
+ mx-env v${VERSION} \u2014 MorphixAI environment variable toolkit
301
+
302
+ Usage:
303
+ mx-env run [options] -- <command> Load env + exec command
304
+ mx-env generate [options] Generate __env.js for client-side runtime
305
+ mx-env inspect [options] Print env vars for debugging
306
+
307
+ Options:
308
+ -f, --env-file <path> Env file to load (default: .env.local, repeatable)
309
+ -o, --out <path> Output path for generate (default: public/__env.js)
310
+ --filter <prefix> Only include vars with this prefix
311
+ --no-infisical Skip Infisical SDK fetch
312
+ -v, --verbose Show loaded variable names
313
+ --help, -h Show this help
314
+ --version Show version
315
+
316
+ Config file (mx-env.config.json):
317
+ {
318
+ "infisical": {
319
+ "projectId": "xxx",
320
+ "paths": ["/ai/shared", "/ai/web"],
321
+ "env": "$DEPLOY_ENV"
322
+ },
323
+ "envFiles": [".env.local"],
324
+ "generate": {
325
+ "out": "public/__env.js",
326
+ "filter": "NEXT_PUBLIC_"
327
+ }
328
+ }
329
+
330
+ Env loading priority (high \u2192 low):
331
+ 1. .env.local (or --env-file)
332
+ 2. Infisical secrets
333
+ 3. Existing process.env
334
+
335
+ Examples:
336
+ mx-env run -- next dev --turbo -p 3004
337
+ mx-env run --no-infisical -- npm start
338
+ mx-env run -f .env.staging -- npm start
339
+ mx-env generate --out dist/__env.js
340
+ mx-env inspect --filter VITE_
341
+ `);
342
+ }
343
+ async function main() {
344
+ const config = loadConfig();
345
+ const args = mergeWithConfig(parseArgs(process.argv), config);
346
+ switch (args.command) {
347
+ case "run":
348
+ await cmdRun(args, config);
349
+ break;
350
+ case "generate":
351
+ case "gen":
352
+ await cmdGenerate(args, config);
353
+ break;
354
+ case "inspect":
355
+ cmdInspect(args);
356
+ break;
357
+ case "--help":
358
+ case "-h":
359
+ case "help":
360
+ showHelp();
361
+ break;
362
+ case "--version":
363
+ console.log(VERSION);
364
+ break;
365
+ default:
366
+ if (args.command) {
367
+ console.error(`[mx-env] Unknown command: ${args.command}`);
368
+ }
369
+ showHelp();
370
+ process.exit(args.command ? 1 : 0);
371
+ }
372
+ }
373
+ main().catch((e) => {
374
+ console.error("[mx-env] Fatal:", e.message);
375
+ process.exit(1);
376
+ });
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "morphix-env",
3
+ "version": "0.1.0",
4
+ "description": "Environment variable toolkit — Infisical integration, local overrides, and client-side runtime injection for Next.js / Vite / Express.",
5
+ "author": "MorphixAI <dev@morphixai.com>",
6
+ "homepage": "https://github.com/Morphicai/mx-env#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Morphicai/mx-env.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/Morphicai/mx-env/issues"
13
+ },
14
+ "bin": {
15
+ "morphix-env": "./dist/cli.js"
16
+ },
17
+ "scripts": {
18
+ "build": "tsup src/cli.ts --format cjs --dts --clean",
19
+ "dev": "tsup src/cli.ts --format cjs --watch",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "prepublishOnly": "pnpm build && pnpm test"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "keywords": [
28
+ "env",
29
+ "dotenv",
30
+ "environment-variables",
31
+ "cross-env",
32
+ "infisical",
33
+ "secrets",
34
+ "runtime-env",
35
+ "nextjs",
36
+ "vite",
37
+ "docker",
38
+ "cli"
39
+ ],
40
+ "license": "MIT",
41
+ "engines": {
42
+ "node": ">=18"
43
+ },
44
+ "devDependencies": {
45
+ "@types/cross-spawn": "^6.0.6",
46
+ "tsup": "^8.0.0",
47
+ "typescript": "^5.0.0",
48
+ "vitest": "^4.1.1"
49
+ },
50
+ "dependencies": {
51
+ "@infisical/sdk": "^5.0.0",
52
+ "cross-spawn": "^7.0.6"
53
+ }
54
+ }