morphix-env 0.8.0 → 0.10.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/dist/index.d.ts CHANGED
@@ -69,6 +69,14 @@ interface MxEnvConfig {
69
69
  profiles?: Record<string, ProfileInput>;
70
70
  /** Profile selected when a caller does not pass --profile. */
71
71
  defaultProfile?: string;
72
+ /**
73
+ * Commands that get a PATH shim via `mx-env shell-init` (global and project
74
+ * lists are unioned). Wiring only — never values. Deliberately excluded from
75
+ * validateConfig's hard errors: a convenience layer must not break the core
76
+ * injection path, so `shell-init` validates and hard-fails while composition
77
+ * paths only warn (see shims.ts validateShimList).
78
+ */
79
+ shims?: string[];
72
80
  }
73
81
  interface LoadedConfig {
74
82
  config: MxEnvConfig;
package/dist/index.js CHANGED
@@ -140,12 +140,6 @@ function validateConfig(config, label = CONFIG_FILE) {
140
140
  return errors;
141
141
  }
142
142
 
143
- // src/sources.ts
144
- var import_child_process2 = require("child_process");
145
- var import_fs4 = require("fs");
146
- var import_path4 = require("path");
147
- var import_dotenv3 = require("dotenv");
148
-
149
143
  // src/env.ts
150
144
  var import_fs2 = require("fs");
151
145
  var import_path2 = require("path");
@@ -155,6 +149,20 @@ function parseEnvFile(filePath) {
155
149
  if (!(0, import_fs2.existsSync)(absPath)) return {};
156
150
  return (0, import_dotenv.parse)((0, import_fs2.readFileSync)(absPath, "utf8"));
157
151
  }
152
+ function isDotenvxEncrypted(filePath) {
153
+ try {
154
+ const content = (0, import_fs2.readFileSync)((0, import_path2.resolve)(filePath), "utf8");
155
+ return content.includes("DOTENV_PUBLIC_KEY") && /^[A-Za-z_][A-Za-z0-9_]*="?encrypted:/m.test(content);
156
+ } catch {
157
+ return false;
158
+ }
159
+ }
160
+
161
+ // src/sources.ts
162
+ var import_child_process2 = require("child_process");
163
+ var import_fs4 = require("fs");
164
+ var import_path4 = require("path");
165
+ var import_dotenv3 = require("dotenv");
158
166
 
159
167
  // src/infisical.ts
160
168
  var import_sdk = require("@infisical/sdk");
@@ -266,7 +274,7 @@ function assertSafeGlobalFile(file, context, entry) {
266
274
  if ((mode & 63) !== 0) {
267
275
  throw new SourceError(
268
276
  entry.name,
269
- "local",
277
+ entry.source.provider,
270
278
  `[morphix-env] Global env file ${file} is readable by group or other users; set mode 0600 or pass --allow-insecure-global.`
271
279
  );
272
280
  }
@@ -358,6 +366,16 @@ function resolveDotenvx(entry, source, context) {
358
366
  if (existingFiles.length !== files.length) {
359
367
  throw new SourceError(entry.name, source.provider, `[morphix-env] dotenvx source "${entry.name}" is missing a declared env file.`);
360
368
  }
369
+ if (entry.globalImplicit && !context.allowInsecureGlobal && process.platform !== "win32") {
370
+ const keysFile = (0, import_path4.join)((0, import_path4.dirname)(existingFiles[0]), ".env.keys");
371
+ if ((0, import_fs4.existsSync)(keysFile) && ((0, import_fs4.statSync)(keysFile).mode & 63) !== 0) {
372
+ throw new SourceError(
373
+ entry.name,
374
+ source.provider,
375
+ `[morphix-env] Global dotenvx keys file ${keysFile} is readable by group or other users; set mode 0600 or pass --allow-insecure-global.`
376
+ );
377
+ }
378
+ }
361
379
  const command = ensureCommand("dotenvx", entry.name, source.provider);
362
380
  const keys = [...new Set(existingFiles.flatMap((file) => Object.keys(parseEnvFile(file))))].filter((key) => !key.startsWith("DOTENV_PUBLIC_KEY"));
363
381
  if (!keys.length) return { name: entry.name, provider: source.provider, values: {}, availability: "loaded" };
@@ -365,8 +383,10 @@ function resolveDotenvx(entry, source, context) {
365
383
  let stage = "run";
366
384
  try {
367
385
  const output = (0, import_child_process2.execFileSync)(
386
+ // --overload aligns dotenvx with every other provider: composed file
387
+ // values win over same-named variables already in the parent env.
368
388
  command,
369
- ["run", "-f", existingFiles.join(","), "--", process.execPath, "-e", helper, JSON.stringify(keys)],
389
+ ["run", "--overload", "-f", existingFiles.join(","), "--", process.execPath, "-e", helper, JSON.stringify(keys)],
370
390
  {
371
391
  encoding: "utf8",
372
392
  stdio: ["ignore", "pipe", "pipe"],
@@ -389,7 +409,8 @@ function resolveDotenvx(entry, source, context) {
389
409
  return { name: entry.name, provider: source.provider, values, availability: "loaded" };
390
410
  } catch (error) {
391
411
  const category = typeof error?.code === "string" ? error.code : "failed";
392
- throw new SourceError(entry.name, source.provider, `[morphix-env] dotenvx source "${entry.name}" could not decrypt or load (${stage}:${category}).`);
412
+ const sniffHint = entry.globalImplicit ? " The global file looks dotenvx-encrypted; if it is actually plaintext, remove the DOTENV_PUBLIC_KEY / encrypted: markers." : "";
413
+ throw new SourceError(entry.name, source.provider, `[morphix-env] dotenvx source "${entry.name}" could not decrypt or load (${stage}:${category}).${sniffHint}`);
393
414
  }
394
415
  }
395
416
  var keytarModule;
@@ -576,9 +597,10 @@ function resolveEntries(layers, options) {
576
597
  ...includeGlobal ? Object.entries(layers.global.config.profiles || {}) : [],
577
598
  ...Object.entries(layers.project.config.profiles || {})
578
599
  ]);
600
+ const globalEncrypted = isDotenvxEncrypted(layers.globalEnvFile);
579
601
  const globalDotenv = includeGlobal ? [{
580
602
  name: GLOBAL_DOTENV_SOURCE,
581
- source: { provider: "local", files: [layers.globalEnvFile], optional: true },
603
+ source: globalEncrypted ? { provider: "dotenvx", files: [layers.globalEnvFile], optional: true } : { provider: "local", files: [layers.globalEnvFile], optional: true },
582
604
  baseDir: layers.global.baseDir,
583
605
  globalImplicit: true
584
606
  }] : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morphix-env",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Secret composition CLI — Infisical, Doppler, macOS Keychain, dotenvx, and local files stay value owners; composes them into one value-safe child environment and ships a built-in Agent Skill (mx-env skill install).",
5
5
  "author": "MorphixAI <dev@morphixai.com>",
6
6
  "homepage": "https://github.com/Morphicai/morphix-env#readme",