morphix-env 0.6.0 → 0.7.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 +32 -1
- package/dist/SKILL.md +89 -0
- package/dist/cli.js +111 -21
- package/docs/logo.png +0 -0
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/logo.png" width="160" alt="morphix-env" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# morphix-env
|
|
2
6
|
|
|
3
7
|
`morphix-env` is a secret composition CLI for local development, CI, and agent-assisted workflows. It does not become another secret vault: Infisical, Doppler, macOS Keychain, dotenvx, and local files continue to own their values, authentication, access controls, rotation, and provider UI.
|
|
@@ -6,6 +10,8 @@
|
|
|
6
10
|
|
|
7
11
|
The concise command is `mx-env`; the published `morphix-env` command remains fully supported.
|
|
8
12
|
|
|
13
|
+
The package also ships a maintained, value-free Agent Skill (`mx-env skill install`) so agents can adopt the whole workflow — safety rules, configuration schema, commands — without a second distribution; see [Agent workflow](#agent-workflow).
|
|
14
|
+
|
|
9
15
|
## Quick start: global local defaults
|
|
10
16
|
|
|
11
17
|
Create this file once:
|
|
@@ -202,6 +208,12 @@ mx-env edit --profile api-dev
|
|
|
202
208
|
# Generate this project's value-free Agent workflow Skill
|
|
203
209
|
mx-env doc --profile api-dev
|
|
204
210
|
|
|
211
|
+
# Install the packaged Agent Skill (global | project | trellis)
|
|
212
|
+
mx-env skill install --target project
|
|
213
|
+
|
|
214
|
+
# Show per-target install state and template drift
|
|
215
|
+
mx-env skill show
|
|
216
|
+
|
|
205
217
|
# Value-free audit events
|
|
206
218
|
mx-env audit tail
|
|
207
219
|
```
|
|
@@ -217,12 +229,31 @@ Options:
|
|
|
217
229
|
| `--no-global` | Ignore `~/.mx-env/.env` and `~/.mx-env/config.json`. |
|
|
218
230
|
| `--allow-insecure-global` | Explicitly permit a global dotenv file readable by group/other users. Avoid this outside controlled troubleshooting. |
|
|
219
231
|
| `--print-editor-url` | Explicitly print the one-time local editor URL; normally the browser opens without emitting it to the terminal. |
|
|
232
|
+
| `--target <scope>` | `skill install` destination: `global` (`~/.claude/skills/`), `project` (`.claude/skills/` inside the repo — commit it), or `trellis` (`~/.trellis/skills/` canonical store). |
|
|
233
|
+
| `--force` | Replace an installed skill that differs from this version's template; a modified file is never overwritten without it. |
|
|
220
234
|
| `-o, --out <path>` / `--filter <prefix>` | Configure public browser-env generation. |
|
|
221
235
|
| `-v, --verbose` | Show source state and key counts, not values. |
|
|
222
236
|
|
|
223
237
|
## Agent workflow
|
|
224
238
|
|
|
225
|
-
|
|
239
|
+
### Packaged Agent Skill
|
|
240
|
+
|
|
241
|
+
The npm package ships a maintained, value-free Agent Skill at `dist/SKILL.md`. It teaches an agent the full workflow: safety red lines (inspect for validation, `edit` for changes, `run` as the only injection path), the complete `mx-env.config.json` schema for all five providers, a command cheatsheet, project onboarding steps, and troubleshooting. Because it travels with the CLI, it always documents commands that exist in the installed version.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Agent bootstrap straight from npm — no global install required
|
|
245
|
+
npx morphix-env@latest skill install --target project
|
|
246
|
+
|
|
247
|
+
mx-env skill install --target project # <repo>/.claude/skills/morphix-env/SKILL.md — commit it with the repository
|
|
248
|
+
mx-env skill install --target global # ~/.claude/skills/morphix-env/SKILL.md — every project on this machine
|
|
249
|
+
mx-env skill install --target trellis # ~/.trellis/skills/morphix-env/SKILL.md — then `trellis sync skills`
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Installs are idempotent: a re-run against an unchanged template is a no-op, and a skill file the user has customized is never overwritten without `--force`. `mx-env skill show` reports each target's install state and whether it still matches the shipped template.
|
|
253
|
+
|
|
254
|
+
### Project workflow Skill
|
|
255
|
+
|
|
256
|
+
`mx-env doc --profile <profile>` writes a value-free workflow Skill to `.agents/skills/morphix-env/SKILL.md` by default, listing this project's resolved key names and their winning sources. Generated or maintained Skills contain this rule:
|
|
226
257
|
|
|
227
258
|
```md
|
|
228
259
|
When the user asks to add, modify, or rotate a credential:
|
package/dist/SKILL.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: morphix-env
|
|
3
|
+
description: 通过 morphix-env(mx-env)为项目组装、检查、可视化编辑环境变量与密钥——多 provider(本地 .env / dotenvx 加密 / macOS 钥匙串 / Infisical / Doppler)、按 profile 组合、Agent 上下文零密钥。当用户提到环境变量、密钥、API key、credentials、.env、Infisical、Doppler、Keychain、"配置环境"、担心 secret 泄露,或需要 Agent/进程带着密钥运行而值不出现在对话里时使用。
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "[install|show]"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# morphix-env(mx-env)Agent 密钥工作流
|
|
9
|
+
|
|
10
|
+
morphix-env 是密钥**组装层**,不是新的保险库:值永远留在 provider(本地文件、macOS 钥匙串、Infisical、Doppler、dotenvx)里。CLI 只把 profile 选定的 sources 组合成一次性子进程环境,并保证 inspect / audit / run 子进程输出全部脱敏。
|
|
11
|
+
|
|
12
|
+
## 安全红线(任何情况下都不违反)
|
|
13
|
+
|
|
14
|
+
1. 绝不打印、记录、索取密钥值:不运行 `env` / `printenv` / `cat .env*` / `doppler secrets get --plain`,也不让用户把值粘贴进对话。
|
|
15
|
+
2. 只用 key 名验证状态:`mx-env inspect --profile <name>` 输出 key → source 归属与状态,不含值。
|
|
16
|
+
3. 增删改密钥一律走可视化编辑器:`mx-env edit --profile <name>`(或 `--source <name>`),用户在本地浏览器完成,值不经过 CLI 与对话。
|
|
17
|
+
4. 进程获得密钥的唯一方式是 `mx-env run`;子进程 stdout/stderr 中的注入值会被替换为 `[REDACTED]`。
|
|
18
|
+
|
|
19
|
+
## 安装与检测
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
mx-env --version # 检测是否已安装
|
|
23
|
+
npm install -g morphix-env # 安装(mx-env 与 morphix-env 两个 bin 等价)
|
|
24
|
+
npx morphix-env@latest inspect # 免安装试用
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 配置(mx-env.config.json)
|
|
28
|
+
|
|
29
|
+
配置文件**不含任何值**,只声明 source 接线与组合顺序。项目级放仓库根 `mx-env.config.json`;用户级 `~/.mx-env/config.json` 可选(全局基线,`--no-global` 可排除)。`~/.mx-env/.env` 是唯一自动发现的值文件(个人默认值)。
|
|
30
|
+
|
|
31
|
+
```jsonc
|
|
32
|
+
// 项目 mx-env.config.json — 五种 provider 全示例
|
|
33
|
+
{
|
|
34
|
+
"sources": {
|
|
35
|
+
"local": { "provider": "local", "files": [".env.local"], "optional": true, "override": true },
|
|
36
|
+
"encrypted": { "provider": "dotenvx", "files": [".env.shared"] },
|
|
37
|
+
"keychain": { "provider": "os-keychain", "platform": "macos", "service": "com.example/myproject" },
|
|
38
|
+
"infisical": { "provider": "infisical", "paths": ["/team/api"] },
|
|
39
|
+
"doppler": { "provider": "doppler", "project": "my-project", "config": "dev" }
|
|
40
|
+
},
|
|
41
|
+
"profiles": {
|
|
42
|
+
"dev": { "sources": ["keychain", "infisical", "local"] },
|
|
43
|
+
"ci": { "sources": ["doppler"] }
|
|
44
|
+
},
|
|
45
|
+
"defaultProfile": "dev"
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
要点:
|
|
50
|
+
|
|
51
|
+
- profile 内 `sources` **从低到高**排列;同 key 冲突默认**报错**,靠后 source 显式 `"override": true` 才覆盖(防止两个环境的同名凭据静默错选)。
|
|
52
|
+
- provider 登录与引导留在 provider 侧(`infisical login`、`doppler login`、dotenvx 解密密钥引导),**不写进项目配置**。
|
|
53
|
+
- 固定基线顺序:`inherited process.env → ~/.mx-env/.env → 全局默认 profile → 项目 profile / legacy 配置 → -f 覆盖文件`。legacy 项目(`infisical` + `envFiles` 字段)无需改动即兼容。
|
|
54
|
+
|
|
55
|
+
## 命令速查
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
mx-env run --profile dev -- <command> # 组合环境并执行(子进程输出脱敏)
|
|
59
|
+
mx-env run --no-global -- <command> # 排除全局基线,可复现的项目-only 运行
|
|
60
|
+
mx-env generate --out public/__env.js # 生成浏览器公开变量(NEXT_PUBLIC_/VITE_/EXPO_PUBLIC_ 属公开配置例外)
|
|
61
|
+
mx-env inspect [--profile dev] # key 名 + source 归属 + 状态(无值)
|
|
62
|
+
mx-env edit --profile dev | --source X # 一次性可视化编辑器(loopback + 15 分钟 TTL,保存后自动关闭)
|
|
63
|
+
mx-env doc # 生成本项目的 key→source 工作流 Skill(.agents/skills/morphix-env/)
|
|
64
|
+
mx-env audit [tail] # 值无关的本地审计事件
|
|
65
|
+
mx-env skill install [--target global|project|trellis] [--force] # 安装本 Skill
|
|
66
|
+
mx-env skill show # 查看各目标安装状态
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 进入一个新项目的标准流程
|
|
70
|
+
|
|
71
|
+
1. `mx-env inspect` — 看是否已有配置、哪些 key 已就绪;无配置时走 legacy `.env.local` / Infisical 兼容路径。
|
|
72
|
+
2. 项目密钥来源多而杂且无 `mx-env.config.json` → 引导用户创建:给出上面模板改 source 名与 provider 选择,**不填值**。
|
|
73
|
+
3. 缺值时 `mx-env edit --profile <name>` 打开本地编辑器让用户填写;macOS 浏览器自动打开,编辑器 URL 默认不打印(`--print-editor-url` 显式索取)。
|
|
74
|
+
4. `mx-env doc` — 生成**本项目**的 key→source 表(`.agents/skills/morphix-env/SKILL.md`,0600),后续以它为准。
|
|
75
|
+
5. 运行:`mx-env run --profile <name> -- <command>`。
|
|
76
|
+
|
|
77
|
+
## 故障排查
|
|
78
|
+
|
|
79
|
+
| 症状 | 处理 |
|
|
80
|
+
|---|---|
|
|
81
|
+
| 冲突报错:同 key 来自两个 source | 确认哪个 source 应赢,给靠后的加 `"override": true` |
|
|
82
|
+
| `missing-optional, 0 keys` | `"optional": true` 的文件不存在,属预期;要强制存在就删掉 optional |
|
|
83
|
+
| Keychain 权限弹框 / 拒绝 | 首次访问会请求 service 授权;拒绝后重试或换 service 命名空间 |
|
|
84
|
+
| dotenvx 解密失败 | 解密密钥由 dotenvx 引导路径提供,不在本配置内 |
|
|
85
|
+
| `mx-env: command not found` | `npm install -g morphix-env`,或用 `npx morphix-env@latest` 免安装 |
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
本 Skill 与安装它的 CLI 同版本分发:`mx-env skill install` 覆盖安装即升级。模板内容 value-free;项目专属的 key 清单请以 `mx-env doc` 的输出为准。
|
package/dist/cli.js
CHANGED
|
@@ -29482,9 +29482,9 @@ var require_jsx_runtime = __commonJS({
|
|
|
29482
29482
|
|
|
29483
29483
|
// src/cli.ts
|
|
29484
29484
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
29485
|
-
var
|
|
29486
|
-
var
|
|
29487
|
-
var
|
|
29485
|
+
var import_fs8 = require("fs");
|
|
29486
|
+
var import_os5 = require("os");
|
|
29487
|
+
var import_path8 = require("path");
|
|
29488
29488
|
|
|
29489
29489
|
// src/audit.ts
|
|
29490
29490
|
var import_fs = require("fs");
|
|
@@ -37182,11 +37182,81 @@ var StreamingRedactor = class {
|
|
|
37182
37182
|
}
|
|
37183
37183
|
};
|
|
37184
37184
|
|
|
37185
|
+
// src/skill.ts
|
|
37186
|
+
var import_fs7 = require("fs");
|
|
37187
|
+
var import_os4 = require("os");
|
|
37188
|
+
var import_path7 = require("path");
|
|
37189
|
+
var SKILL_TARGETS = ["global", "project", "trellis"];
|
|
37190
|
+
function resolveSkillPath(target) {
|
|
37191
|
+
if (target === "project") return (0, import_path7.join)(process.cwd(), ".claude", "skills", "morphix-env", "SKILL.md");
|
|
37192
|
+
if (target === "trellis") return (0, import_path7.join)((0, import_os4.homedir)(), ".trellis", "skills", "morphix-env", "SKILL.md");
|
|
37193
|
+
return (0, import_path7.join)((0, import_os4.homedir)(), ".claude", "skills", "morphix-env", "SKILL.md");
|
|
37194
|
+
}
|
|
37195
|
+
function targetHint(target) {
|
|
37196
|
+
if (target === "project") {
|
|
37197
|
+
return "Project skill installed; commit it so every Agent working in this repository picks it up.";
|
|
37198
|
+
}
|
|
37199
|
+
if (target === "trellis") {
|
|
37200
|
+
return "Installed into the Trellis canonical store; run `trellis sync skills` to distribute it to managed agents.";
|
|
37201
|
+
}
|
|
37202
|
+
return "Installed as a Claude Code global skill; available in every project.";
|
|
37203
|
+
}
|
|
37204
|
+
function printNextSteps() {
|
|
37205
|
+
console.log(`[morphix-env] Next steps:
|
|
37206
|
+
1. mx-env inspect check this project's config and resolved key names
|
|
37207
|
+
2. create mx-env.config.json if absent see the skill's configuration section (source wiring only, no values)
|
|
37208
|
+
3. mx-env edit --profile <name> let the user fill values in the local visual editor
|
|
37209
|
+
4. mx-env doc generate this project's key-to-source workflow Skill`);
|
|
37210
|
+
}
|
|
37211
|
+
function cmdSkill(subcommand, options) {
|
|
37212
|
+
const templatePath = (0, import_path7.join)(__dirname, "SKILL.md");
|
|
37213
|
+
const template = (0, import_fs7.readFileSync)(templatePath, "utf8");
|
|
37214
|
+
if (subcommand === "show") {
|
|
37215
|
+
console.log(`[morphix-env] Skill template: ${templatePath}`);
|
|
37216
|
+
for (const target2 of SKILL_TARGETS) {
|
|
37217
|
+
const path = resolveSkillPath(target2);
|
|
37218
|
+
if (!(0, import_fs7.existsSync)(path)) {
|
|
37219
|
+
console.log(` ${target2}: not installed (${path})`);
|
|
37220
|
+
continue;
|
|
37221
|
+
}
|
|
37222
|
+
const state = (0, import_fs7.readFileSync)(path, "utf8") === template ? "installed (matches this version)" : "installed (differs from this version)";
|
|
37223
|
+
console.log(` ${target2}: ${state} (${path})`);
|
|
37224
|
+
}
|
|
37225
|
+
return 0;
|
|
37226
|
+
}
|
|
37227
|
+
if (subcommand !== "install") {
|
|
37228
|
+
console.error(`[morphix-env] Unknown skill subcommand "${subcommand}". Use install or show.`);
|
|
37229
|
+
return 1;
|
|
37230
|
+
}
|
|
37231
|
+
const target = options.target ?? "global";
|
|
37232
|
+
if (!SKILL_TARGETS.includes(target)) {
|
|
37233
|
+
console.error(`[morphix-env] Unknown skill target "${options.target}". Use global, project, or trellis.`);
|
|
37234
|
+
return 1;
|
|
37235
|
+
}
|
|
37236
|
+
const dest = resolveSkillPath(target);
|
|
37237
|
+
if ((0, import_fs7.existsSync)(dest)) {
|
|
37238
|
+
if ((0, import_fs7.readFileSync)(dest, "utf8") === template) {
|
|
37239
|
+
console.log(`[morphix-env] Skill already installed and up to date: ${dest}`);
|
|
37240
|
+
return 0;
|
|
37241
|
+
}
|
|
37242
|
+
if (!options.force) {
|
|
37243
|
+
console.error(`[morphix-env] ${dest} exists with different content; refusing to overwrite. Re-run with --force to replace it.`);
|
|
37244
|
+
return 1;
|
|
37245
|
+
}
|
|
37246
|
+
}
|
|
37247
|
+
(0, import_fs7.mkdirSync)((0, import_path7.dirname)(dest), { recursive: true });
|
|
37248
|
+
(0, import_fs7.writeFileSync)(dest, template, { mode: 420 });
|
|
37249
|
+
console.log(`[morphix-env] Installed skill: ${dest}`);
|
|
37250
|
+
console.log(`[morphix-env] ${targetHint(target)}`);
|
|
37251
|
+
printNextSteps();
|
|
37252
|
+
return 0;
|
|
37253
|
+
}
|
|
37254
|
+
|
|
37185
37255
|
// package.json
|
|
37186
37256
|
var package_default = {
|
|
37187
37257
|
name: "morphix-env",
|
|
37188
|
-
version: "0.
|
|
37189
|
-
description: "
|
|
37258
|
+
version: "0.7.0",
|
|
37259
|
+
description: "Secret composition CLI \u2014 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).",
|
|
37190
37260
|
author: "MorphixAI <dev@morphixai.com>",
|
|
37191
37261
|
homepage: "https://github.com/Morphicai/morphix-env#readme",
|
|
37192
37262
|
repository: {
|
|
@@ -37201,7 +37271,7 @@ var package_default = {
|
|
|
37201
37271
|
"mx-env": "./dist/cli.js"
|
|
37202
37272
|
},
|
|
37203
37273
|
scripts: {
|
|
37204
|
-
build: "node scripts/build-editor-ui.mjs && tsup src/cli.ts --format cjs --dts --clean --external keytar",
|
|
37274
|
+
build: "node scripts/build-editor-ui.mjs && tsup src/cli.ts --format cjs --dts --clean --external keytar && node scripts/copy-skill.mjs",
|
|
37205
37275
|
dev: "node scripts/build-editor-ui.mjs && tsup src/cli.ts --format cjs --watch",
|
|
37206
37276
|
pretest: "pnpm build",
|
|
37207
37277
|
test: "vitest run",
|
|
@@ -37209,7 +37279,8 @@ var package_default = {
|
|
|
37209
37279
|
prepublishOnly: "pnpm build && pnpm test"
|
|
37210
37280
|
},
|
|
37211
37281
|
files: [
|
|
37212
|
-
"dist"
|
|
37282
|
+
"dist",
|
|
37283
|
+
"docs/logo.png"
|
|
37213
37284
|
],
|
|
37214
37285
|
keywords: [
|
|
37215
37286
|
"env",
|
|
@@ -37217,7 +37288,12 @@ var package_default = {
|
|
|
37217
37288
|
"environment-variables",
|
|
37218
37289
|
"cross-env",
|
|
37219
37290
|
"infisical",
|
|
37291
|
+
"doppler",
|
|
37292
|
+
"keychain",
|
|
37220
37293
|
"secrets",
|
|
37294
|
+
"secret-management",
|
|
37295
|
+
"agent",
|
|
37296
|
+
"agent-skills",
|
|
37221
37297
|
"runtime-env",
|
|
37222
37298
|
"nextjs",
|
|
37223
37299
|
"vite",
|
|
@@ -37268,7 +37344,9 @@ function parseArgs(argv) {
|
|
|
37268
37344
|
noGlobal: false,
|
|
37269
37345
|
allowInsecureGlobal: false,
|
|
37270
37346
|
printEditorUrl: false,
|
|
37271
|
-
source: null
|
|
37347
|
+
source: null,
|
|
37348
|
+
target: null,
|
|
37349
|
+
force: false
|
|
37272
37350
|
};
|
|
37273
37351
|
let i = 2;
|
|
37274
37352
|
const command = argv[i];
|
|
@@ -37303,6 +37381,10 @@ function parseArgs(argv) {
|
|
|
37303
37381
|
args.allowInsecureGlobal = true;
|
|
37304
37382
|
} else if (arg === "--print-editor-url") {
|
|
37305
37383
|
args.printEditorUrl = true;
|
|
37384
|
+
} else if (arg === "--target") {
|
|
37385
|
+
if (argv[++i]) args.target = argv[i];
|
|
37386
|
+
} else if (arg === "--force") {
|
|
37387
|
+
args.force = true;
|
|
37306
37388
|
} else if (args.command === "run") {
|
|
37307
37389
|
args.subArgs = argv.slice(i);
|
|
37308
37390
|
break;
|
|
@@ -37312,7 +37394,7 @@ function parseArgs(argv) {
|
|
|
37312
37394
|
return args;
|
|
37313
37395
|
}
|
|
37314
37396
|
function resolveComposition(args) {
|
|
37315
|
-
const layers = loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
37397
|
+
const layers = loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os5.homedir)() });
|
|
37316
37398
|
return composeEnvironment(layers, {
|
|
37317
37399
|
profile: args.profile,
|
|
37318
37400
|
noGlobal: args.noGlobal,
|
|
@@ -37320,11 +37402,11 @@ function resolveComposition(args) {
|
|
|
37320
37402
|
envName: args.env,
|
|
37321
37403
|
allowInsecureGlobal: args.allowInsecureGlobal,
|
|
37322
37404
|
envFiles: args.envFiles,
|
|
37323
|
-
homeDir: (0,
|
|
37405
|
+
homeDir: (0, import_os5.homedir)()
|
|
37324
37406
|
});
|
|
37325
37407
|
}
|
|
37326
37408
|
function generatedOptions(args) {
|
|
37327
|
-
const config = loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
37409
|
+
const config = loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os5.homedir)() }).project.config;
|
|
37328
37410
|
return {
|
|
37329
37411
|
outFile: args.outFile || config.generate?.out || "public/__env.js",
|
|
37330
37412
|
filter: args.filter || config.generate?.filter || null
|
|
@@ -37333,8 +37415,8 @@ function generatedOptions(args) {
|
|
|
37333
37415
|
function generateClientEnv(outFile, filter, environment) {
|
|
37334
37416
|
let vars = extractPublicVars(environment);
|
|
37335
37417
|
if (filter) vars = Object.fromEntries(Object.entries(vars).filter(([key]) => key.startsWith(filter)));
|
|
37336
|
-
(0,
|
|
37337
|
-
(0,
|
|
37418
|
+
(0, import_fs8.mkdirSync)((0, import_path8.dirname)(outFile), { recursive: true });
|
|
37419
|
+
(0, import_fs8.writeFileSync)(outFile, `window.__ENV=${JSON.stringify(vars)};`);
|
|
37338
37420
|
console.log(`[morphix-env] Generated ${outFile} (${Object.keys(vars).length} client vars)`);
|
|
37339
37421
|
}
|
|
37340
37422
|
async function runChild(command, commandArgs, composition) {
|
|
@@ -37382,7 +37464,7 @@ async function cmdRun(args) {
|
|
|
37382
37464
|
}
|
|
37383
37465
|
if (args.outFile || generatedOptions(args).outFile) {
|
|
37384
37466
|
const { outFile, filter } = generatedOptions(args);
|
|
37385
|
-
const configured = Boolean(args.outFile || loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
37467
|
+
const configured = Boolean(args.outFile || loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os5.homedir)() }).project.config.generate);
|
|
37386
37468
|
if (configured) generateClientEnv(outFile, filter, composition.environment);
|
|
37387
37469
|
}
|
|
37388
37470
|
const [command, ...commandArgs] = args.subArgs;
|
|
@@ -37420,7 +37502,7 @@ async function cmdEdit(args) {
|
|
|
37420
37502
|
}
|
|
37421
37503
|
const composition = await resolveComposition(args);
|
|
37422
37504
|
if (args.source && !composition.sourceEntries.some((entry) => entry.name === args.source)) {
|
|
37423
|
-
const layers = loadConfigLayers({ cwd: process.cwd(), homeDir: (0,
|
|
37505
|
+
const layers = loadConfigLayers({ cwd: process.cwd(), homeDir: (0, import_os5.homedir)() });
|
|
37424
37506
|
const declared = layers.project.config.sources?.[args.source] ? { source: layers.project.config.sources[args.source], baseDir: layers.project.baseDir } : !args.noGlobal && layers.global.config.sources?.[args.source] ? { source: layers.global.config.sources[args.source], baseDir: layers.global.baseDir } : null;
|
|
37425
37507
|
if (!declared) {
|
|
37426
37508
|
console.error(`[morphix-env] Unknown source "${args.source}".`);
|
|
@@ -37438,7 +37520,7 @@ async function cmdEdit(args) {
|
|
|
37438
37520
|
}
|
|
37439
37521
|
async function cmdDoc(args) {
|
|
37440
37522
|
const composition = await resolveComposition(args);
|
|
37441
|
-
const outFile = args.outFile || (0,
|
|
37523
|
+
const outFile = args.outFile || (0, import_path8.join)(process.cwd(), ".agents", "skills", "morphix-env", "SKILL.md");
|
|
37442
37524
|
const keyRows = Object.entries(composition.provenance).sort(([a], [b]) => a.localeCompare(b)).map(([key, provenance]) => `| \`${key}\` | ${provenance.source} (${provenance.provider}) |`).join("\n") || "| _No resolved keys_ | \u2014 |";
|
|
37443
37525
|
const skill = `---
|
|
37444
37526
|
name: morphix-env
|
|
@@ -37472,18 +37554,18 @@ ${keyRows}
|
|
|
37472
37554
|
|
|
37473
37555
|
Secret values and value prefixes do not belong in this Skill, command output, audit records, or conversation context.
|
|
37474
37556
|
`;
|
|
37475
|
-
(0,
|
|
37476
|
-
(0,
|
|
37557
|
+
(0, import_fs8.mkdirSync)((0, import_path8.dirname)(outFile), { recursive: true });
|
|
37558
|
+
(0, import_fs8.writeFileSync)(outFile, skill, { mode: 384 });
|
|
37477
37559
|
console.log(`[morphix-env] Generated value-free workflow Skill: ${outFile}`);
|
|
37478
37560
|
return 0;
|
|
37479
37561
|
}
|
|
37480
37562
|
function cmdAudit(args) {
|
|
37481
|
-
const file = (0,
|
|
37482
|
-
if (!(0,
|
|
37563
|
+
const file = (0, import_path8.join)((0, import_os5.homedir)(), ".mx-env", "audit", `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`);
|
|
37564
|
+
if (!(0, import_fs8.existsSync)(file)) {
|
|
37483
37565
|
console.log("[morphix-env] No audit events for today.");
|
|
37484
37566
|
return 0;
|
|
37485
37567
|
}
|
|
37486
|
-
const lines = (0,
|
|
37568
|
+
const lines = (0, import_fs8.readFileSync)(file, "utf8").trim().split("\n").filter(Boolean);
|
|
37487
37569
|
const selected = args.subArgs[0] === "tail" ? lines.slice(-20) : lines;
|
|
37488
37570
|
for (const line of selected) console.log(line);
|
|
37489
37571
|
return 0;
|
|
@@ -37499,6 +37581,8 @@ Usage:
|
|
|
37499
37581
|
mx-env doc [options] Generate a value-free project Skill
|
|
37500
37582
|
mx-env audit [tail] Read value-free local audit events
|
|
37501
37583
|
mx-env edit --profile <name> Open the one-shot visual editor
|
|
37584
|
+
mx-env skill install [--target <t>] Install the Agent Skill (global|project|trellis)
|
|
37585
|
+
mx-env skill show Show skill install status per target
|
|
37502
37586
|
|
|
37503
37587
|
Both mx-env and morphix-env are supported.
|
|
37504
37588
|
|
|
@@ -37512,6 +37596,8 @@ Options:
|
|
|
37512
37596
|
--no-global Ignore ~/.mx-env/.env and ~/.mx-env/config.json
|
|
37513
37597
|
--allow-insecure-global Allow a global .env with permissive file mode
|
|
37514
37598
|
--print-editor-url Explicitly print the one-time local editor URL
|
|
37599
|
+
--target <scope> Skill install target: global, project, or trellis
|
|
37600
|
+
--force Overwrite an installed skill that differs from this version
|
|
37515
37601
|
-v, --verbose Print source status and key counts only
|
|
37516
37602
|
--help, -h Show this help
|
|
37517
37603
|
--version Show version
|
|
@@ -37533,6 +37619,10 @@ async function main() {
|
|
|
37533
37619
|
return cmdEdit(args);
|
|
37534
37620
|
case "doc":
|
|
37535
37621
|
return cmdDoc(args);
|
|
37622
|
+
case "skill": {
|
|
37623
|
+
const positional = process.argv.slice(3).find((arg) => !arg.startsWith("-")) ?? "install";
|
|
37624
|
+
return cmdSkill(positional, { target: args.target, force: args.force });
|
|
37625
|
+
}
|
|
37536
37626
|
case "--help":
|
|
37537
37627
|
case "-h":
|
|
37538
37628
|
case "help":
|
package/docs/logo.png
ADDED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "morphix-env",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.7.0",
|
|
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",
|
|
7
7
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"mx-env": "./dist/cli.js"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "node scripts/build-editor-ui.mjs && tsup src/cli.ts --format cjs --dts --clean --external keytar",
|
|
19
|
+
"build": "node scripts/build-editor-ui.mjs && tsup src/cli.ts --format cjs --dts --clean --external keytar && node scripts/copy-skill.mjs",
|
|
20
20
|
"dev": "node scripts/build-editor-ui.mjs && tsup src/cli.ts --format cjs --watch",
|
|
21
21
|
"pretest": "pnpm build",
|
|
22
22
|
"test": "vitest run",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"prepublishOnly": "pnpm build && pnpm test"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
|
-
"dist"
|
|
27
|
+
"dist",
|
|
28
|
+
"docs/logo.png"
|
|
28
29
|
],
|
|
29
30
|
"keywords": [
|
|
30
31
|
"env",
|
|
@@ -32,7 +33,12 @@
|
|
|
32
33
|
"environment-variables",
|
|
33
34
|
"cross-env",
|
|
34
35
|
"infisical",
|
|
36
|
+
"doppler",
|
|
37
|
+
"keychain",
|
|
35
38
|
"secrets",
|
|
39
|
+
"secret-management",
|
|
40
|
+
"agent",
|
|
41
|
+
"agent-skills",
|
|
36
42
|
"runtime-env",
|
|
37
43
|
"nextjs",
|
|
38
44
|
"vite",
|