@morphixai/mxenv 0.12.0 → 0.12.1
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 +4 -0
- package/dist/SKILL.md +7 -3
- package/dist/cli.js +106 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
L2 run face for [mxenv](https://github.com/Morphicai/morphix-env) — the project-side CLI and SDK runtime. Secrets stay with their owners (Infisical / Doppler / dotenvx / macOS Keychain / local files) and are composed into one value-safe child environment.
|
|
4
4
|
|
|
5
|
+
> Upgrading from the old `morphix-env` package? See "Upgrading an existing project, step by step" in the [repository README](https://github.com/Morphicai/morphix-env#migrating-from-morphix-env--mx-env).
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
10
|
npm i -D @morphixai/mxenv
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
> ⚠️ **Do not install `@morphixai/mxenv` and `@morphixai/mxenv-cli` globally side by side** — both register the `mxenv`/`mx-env`/`morphix-env` bin names and the later install silently takes over the symlinks. Keep this package as a project devDependency and install `mxenv-cli` globally (or vice versa, never both).
|
|
14
|
+
|
|
11
15
|
## Commands
|
|
12
16
|
|
|
13
17
|
`mxenv run|export|inspect|render|inject|generate|fetch|migrate|audit|edit|doc|skill` — see `mxenv --help`. Old bin names `mx-env`/`morphix-env` remain as deprecated aliases in 0.12.x (removed in 0.13).
|
package/dist/SKILL.md
CHANGED
|
@@ -22,11 +22,15 @@ mxenv 是密钥**组装层**,不是新的保险库:值永远留在 provider
|
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
mxenv --version # 检测是否已安装
|
|
25
|
-
npm install -
|
|
25
|
+
npm install -D @morphixai/mxenv # 项目内安装运行面(推荐:run/inspect/edit/migrate 全在)
|
|
26
26
|
npx @morphixai/mxenv@latest inspect # 免安装试用
|
|
27
|
-
npm install -g @morphixai/mxenv-cli #
|
|
27
|
+
npm install -g @morphixai/mxenv-cli # 可选:机器级管理面(shell-init 等)
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
> ⚠️ **不要把 `@morphixai/mxenv` 与 `@morphixai/mxenv-cli` 同时全局安装**:两者都注册
|
|
31
|
+
> `mxenv`/`mx-env`/`morphix-env` bin,后装者会静默接管符号链接。项目用 devDep,
|
|
32
|
+
> 全局只装 cli;若确要全局装运行面,就不要再装 cli。
|
|
33
|
+
|
|
30
34
|
## 配置(mxenv.config.json)
|
|
31
35
|
|
|
32
36
|
配置文件**不含任何值**,只声明 source 接线与组合顺序。项目级放仓库根 `mxenv.config.json`;用户级 `~/.mxenv/config.json` 可选(全局基线,`--no-global` 可排除)。`~/.mxenv/.env` 是唯一自动发现的值文件(个人默认值)。
|
|
@@ -107,7 +111,7 @@ mxenv-cli shell-init [--write|--remove] # 为声明的命令生成 PATH shim(
|
|
|
107
111
|
| `missing-optional, 0 keys` | `"optional": true` 的文件不存在,属预期;要强制存在就删掉 optional |
|
|
108
112
|
| Keychain 权限弹框 / 拒绝 | 首次访问会请求 service 授权;拒绝后重试或换 service 命名空间 |
|
|
109
113
|
| dotenvx 解密失败 | 解密密钥由 dotenvx 引导路径提供,不在本配置内 |
|
|
110
|
-
| `mxenv: command not found` | `npm install -
|
|
114
|
+
| `mxenv: command not found` | 项目内 `npm install -D @morphixai/mxenv`(或全局装它,但勿再装 cli);或用 `npx @morphixai/mxenv@latest` 免安装 |
|
|
111
115
|
| `mxenv-cli: command not found` | shim 能力属全局管理面:`npm install -g @morphixai/mxenv-cli` |
|
|
112
116
|
|
|
113
117
|
---
|
package/dist/cli.js
CHANGED
|
@@ -342,6 +342,37 @@ async function executeUnit(unit, target, options = {}, homeDir = (0, import_os.h
|
|
|
342
342
|
return { unit, status: "failed", backups, warnings: [], detail: message };
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
|
+
function cleanupQuiet(path) {
|
|
346
|
+
try {
|
|
347
|
+
(0, import_fs.rmSync)(path, { recursive: true, force: true });
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
function mergeScaffoldingCopy(source, dest, stamp) {
|
|
352
|
+
const merge = (src, dst) => {
|
|
353
|
+
(0, import_fs.mkdirSync)(dst, { recursive: true });
|
|
354
|
+
for (const entry of (0, import_fs.readdirSync)(src, { withFileTypes: true })) {
|
|
355
|
+
const from = (0, import_path.join)(src, entry.name);
|
|
356
|
+
const to = (0, import_path.join)(dst, entry.name);
|
|
357
|
+
if (entry.isDirectory()) {
|
|
358
|
+
merge(from, to);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (!(0, import_fs.existsSync)(to)) {
|
|
362
|
+
(0, import_fs.copyFileSync)(from, to);
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (from.endsWith(".jsonl") && to.endsWith(".jsonl")) {
|
|
366
|
+
const base = (0, import_fs.readFileSync)(to, "utf8");
|
|
367
|
+
const extra = (0, import_fs.readFileSync)(from, "utf8");
|
|
368
|
+
(0, import_fs.appendFileSync)(to, (base && !base.endsWith("\n") ? "\n" : "") + extra);
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
(0, import_fs.copyFileSync)(from, (0, import_path.join)(dst, `${entry.name}.fold-${stamp}`));
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
merge(source, dest);
|
|
375
|
+
}
|
|
345
376
|
function copyTree(src, dest) {
|
|
346
377
|
(0, import_fs.mkdirSync)(dest, { recursive: true });
|
|
347
378
|
(0, import_fs.chmodSync)(dest, (0, import_fs.statSync)(src).mode & 4095);
|
|
@@ -423,8 +454,22 @@ async function migrateHome(homeDir = (0, import_os.homedir)(), options = {}) {
|
|
|
423
454
|
const targetDir = (0, import_path.resolve)(homeDir, import_mxenv_core.GLOBAL_CONFIG_DIR);
|
|
424
455
|
const markerName = ".mxenv-verify-pending";
|
|
425
456
|
const markerPath = (0, import_path.join)(targetDir, markerName);
|
|
457
|
+
const asideDir = `${targetDir}.scaffold-fold`;
|
|
426
458
|
const allowInsecureGlobal = options.allowInsecureGlobal ?? false;
|
|
427
459
|
const backupsOnDisk = () => (0, import_fs.readdirSync)(homeDir).filter((name) => name.startsWith(`${import_mxenv_core.LEGACY_GLOBAL_CONFIG_DIR}.backup-`)).sort();
|
|
460
|
+
let mergeScaffolding = false;
|
|
461
|
+
if ((0, import_fs.existsSync)(asideDir) && !(0, import_fs.existsSync)(targetDir)) {
|
|
462
|
+
try {
|
|
463
|
+
(0, import_fs.renameSync)(asideDir, targetDir);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
return {
|
|
466
|
+
status: "failed",
|
|
467
|
+
exitCode: 1,
|
|
468
|
+
keyNames: [],
|
|
469
|
+
detail: `cannot restore pre-migration scaffolding from ${asideDir}: ${error instanceof Error ? error.message : "unknown failure"} \u2014 rename it to ${targetDir} by hand, then re-run.`
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
}
|
|
428
473
|
if ((0, import_fs.existsSync)(targetDir)) {
|
|
429
474
|
if ((0, import_fs.existsSync)(markerPath)) {
|
|
430
475
|
let backupDir2 = "";
|
|
@@ -467,13 +512,17 @@ async function migrateHome(homeDir = (0, import_os.homedir)(), options = {}) {
|
|
|
467
512
|
};
|
|
468
513
|
}
|
|
469
514
|
} else if ((0, import_fs.existsSync)(legacyDir)) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
515
|
+
if ((0, import_mxenv_core.hasGlobalDataAt)(targetDir)) {
|
|
516
|
+
return {
|
|
517
|
+
status: "conflict",
|
|
518
|
+
exitCode: 1,
|
|
519
|
+
keyNames: [],
|
|
520
|
+
detail: `both ${targetDir} and ${legacyDir} exist \u2014 refusing to overwrite (no writes made). Resolve by hand: inspect both, keep what you need in ${targetDir}, then rename ${legacyDir} away and re-run.`
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
mergeScaffolding = true;
|
|
476
524
|
} else {
|
|
525
|
+
if ((0, import_fs.existsSync)(asideDir)) cleanupQuiet(asideDir);
|
|
477
526
|
return { status: "noop", exitCode: 0, keyNames: [], detail: `already migrated: ${targetDir} is in use.` };
|
|
478
527
|
}
|
|
479
528
|
}
|
|
@@ -516,25 +565,55 @@ async function migrateHome(homeDir = (0, import_os.homedir)(), options = {}) {
|
|
|
516
565
|
copyTree(legacyDir, partialDir);
|
|
517
566
|
(0, import_fs.writeFileSync)((0, import_path.join)(partialDir, markerName), `${backupDir}
|
|
518
567
|
`);
|
|
568
|
+
if (mergeScaffolding) {
|
|
569
|
+
(0, import_fs.renameSync)(targetDir, asideDir);
|
|
570
|
+
if ((0, import_mxenv_core.hasGlobalDataAt)(asideDir)) {
|
|
571
|
+
(0, import_fs.renameSync)(asideDir, targetDir);
|
|
572
|
+
cleanup(partialDir);
|
|
573
|
+
return {
|
|
574
|
+
status: "conflict",
|
|
575
|
+
exitCode: 1,
|
|
576
|
+
keyNames: [],
|
|
577
|
+
detail: `both ${targetDir} and ${legacyDir} appeared to hold real data mid-migration \u2014 nothing was changed; inspect both, keep what you need in ${targetDir}, then rename ${legacyDir} away and re-run.`
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if ((0, import_fs.existsSync)(asideDir)) mergeScaffoldingCopy(asideDir, partialDir, stamp);
|
|
519
582
|
(0, import_fs.renameSync)(partialDir, targetDir);
|
|
520
583
|
} catch (error) {
|
|
521
584
|
const cleanupIssue = cleanup(partialDir);
|
|
585
|
+
let asideIssue = "";
|
|
586
|
+
if ((0, import_fs.existsSync)(asideDir) && !(0, import_fs.existsSync)(targetDir)) {
|
|
587
|
+
try {
|
|
588
|
+
(0, import_fs.renameSync)(asideDir, targetDir);
|
|
589
|
+
} catch {
|
|
590
|
+
asideIssue = `; restore ${asideDir} to ${targetDir} failed \u2014 rename it back by hand`;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
522
593
|
return {
|
|
523
594
|
status: "failed",
|
|
524
595
|
exitCode: 1,
|
|
525
596
|
keyNames: [],
|
|
526
|
-
detail: `copy failed: ${error instanceof Error ? error.message : "unknown failure"}; ${cleanupIssue ?? `removed partial copy`}, original preserved at ${legacyDir}
|
|
597
|
+
detail: `copy failed: ${error instanceof Error ? error.message : "unknown failure"}; ${cleanupIssue ?? `removed partial copy`}, original preserved at ${legacyDir}.${asideIssue}`
|
|
527
598
|
};
|
|
528
599
|
}
|
|
529
600
|
try {
|
|
530
601
|
(0, import_fs.renameSync)(legacyDir, backupDir);
|
|
531
602
|
} catch (error) {
|
|
532
603
|
const cleanupIssue = [cleanup(partialDir), cleanup(targetDir)].find(Boolean) ?? null;
|
|
604
|
+
let asideIssue = "";
|
|
605
|
+
if ((0, import_fs.existsSync)(asideDir)) {
|
|
606
|
+
try {
|
|
607
|
+
(0, import_fs.renameSync)(asideDir, targetDir);
|
|
608
|
+
} catch {
|
|
609
|
+
asideIssue = `; restore ${asideDir} to ${targetDir} failed \u2014 rename it back by hand`;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
533
612
|
return {
|
|
534
613
|
status: "failed",
|
|
535
614
|
exitCode: 1,
|
|
536
615
|
keyNames: [],
|
|
537
|
-
detail: `backup rename failed: ${error instanceof Error ? error.message : "unknown failure"}; ${cleanupIssue ?? `removed partial copy`}, original preserved at ${legacyDir}
|
|
616
|
+
detail: `backup rename failed: ${error instanceof Error ? error.message : "unknown failure"}; ${cleanupIssue ?? `removed partial copy`}, original preserved at ${legacyDir}.${asideIssue}`
|
|
538
617
|
};
|
|
539
618
|
}
|
|
540
619
|
const restoreFromBackup = (why) => {
|
|
@@ -545,11 +624,19 @@ async function migrateHome(homeDir = (0, import_os.homedir)(), options = {}) {
|
|
|
545
624
|
} catch {
|
|
546
625
|
}
|
|
547
626
|
const cleanupIssue = cleanup(targetDir);
|
|
627
|
+
let asideIssue = "";
|
|
628
|
+
if ((0, import_fs.existsSync)(asideDir)) {
|
|
629
|
+
try {
|
|
630
|
+
(0, import_fs.renameSync)(asideDir, targetDir);
|
|
631
|
+
} catch {
|
|
632
|
+
asideIssue = `; RESTORE FAILED for ${asideDir} \u2014 rename it to ${targetDir} by hand`;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
548
635
|
return {
|
|
549
636
|
status: "failed",
|
|
550
637
|
exitCode: 1,
|
|
551
638
|
keyNames: [],
|
|
552
|
-
detail: `verification failed: ${why}; ${restored ? `restored from backup ${backupDir} (directory backup)` : `RESTORE FAILED \u2014 restore manually by renaming the directory backup ${backupDir} to ${legacyDir}`}; ${cleanupIssue ?? `removed partial ${targetDir}`}.`
|
|
639
|
+
detail: `verification failed: ${why}; ${restored ? `restored from backup ${backupDir} (directory backup)` : `RESTORE FAILED \u2014 restore manually by renaming the directory backup ${backupDir} to ${legacyDir}`}; ${cleanupIssue ?? `removed partial ${targetDir}`}${asideIssue}.`
|
|
553
640
|
};
|
|
554
641
|
};
|
|
555
642
|
let after;
|
|
@@ -568,6 +655,7 @@ async function migrateHome(homeDir = (0, import_os.homedir)(), options = {}) {
|
|
|
568
655
|
(0, import_fs.rmSync)(markerPath);
|
|
569
656
|
} catch {
|
|
570
657
|
}
|
|
658
|
+
if ((0, import_fs.existsSync)(asideDir)) cleanupQuiet(asideDir);
|
|
571
659
|
const rc = rewriteRcBlocksForMigration(homeDir);
|
|
572
660
|
const rcSummary = rc.updated.length ? `; rc PATH block rewired in ${rc.updated.join(", ")}` : "";
|
|
573
661
|
const rcHint = !rc.found || rc.failures.length ? `[mxenv] shim PATH wiring: run \`mxenv-cli shell-init --write\` to point your rc block at ${(0, import_mxenv_core.shimsDir)(homeDir)}${rc.failures.length ? ` (rc rewrite failed: ${rc.failures.join("; ")})` : " (no marked rc block found)"}.` : void 0;
|
|
@@ -1209,7 +1297,7 @@ function warnOutputGitRisk(path) {
|
|
|
1209
1297
|
// package.json
|
|
1210
1298
|
var package_default = {
|
|
1211
1299
|
name: "@morphixai/mxenv",
|
|
1212
|
-
version: "0.12.
|
|
1300
|
+
version: "0.12.1",
|
|
1213
1301
|
description: "L2 run face for mxenv \u2014 run/export/inspect, source management, edit, migrate, skills.",
|
|
1214
1302
|
license: "MIT",
|
|
1215
1303
|
author: "MorphixAI <dev@morphixai.com>",
|
|
@@ -1305,6 +1393,7 @@ function parseArgs(argv) {
|
|
|
1305
1393
|
write: false,
|
|
1306
1394
|
remove: false,
|
|
1307
1395
|
warnPlaintext: false,
|
|
1396
|
+
help: false,
|
|
1308
1397
|
unknownFlags: []
|
|
1309
1398
|
};
|
|
1310
1399
|
let i = 2;
|
|
@@ -1360,6 +1449,8 @@ function parseArgs(argv) {
|
|
|
1360
1449
|
args.serve = true;
|
|
1361
1450
|
} else if (arg === "--warn-plaintext") {
|
|
1362
1451
|
args.warnPlaintext = true;
|
|
1452
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
1453
|
+
args.help = true;
|
|
1363
1454
|
} else if (args.command === "run") {
|
|
1364
1455
|
args.subArgs = argv.slice(i);
|
|
1365
1456
|
break;
|
|
@@ -1952,6 +2043,11 @@ async function main() {
|
|
|
1952
2043
|
(0, import_mxenv_core3.warnDeprecatedBinInvocation)(process.argv[1]);
|
|
1953
2044
|
Object.assign(bootInfo, (0, import_mxenv_core3.sanitizeBootEnv)());
|
|
1954
2045
|
const args = parseArgs(process.argv);
|
|
2046
|
+
if (args.help) {
|
|
2047
|
+
showHelp();
|
|
2048
|
+
return 0;
|
|
2049
|
+
}
|
|
2050
|
+
if (args.noGlobal) (0, import_mxenv_core3.suppressLegacyHomeHint)();
|
|
1955
2051
|
switch (args.command) {
|
|
1956
2052
|
case "run":
|
|
1957
2053
|
return cmdRun(args);
|
package/package.json
CHANGED