morphix-env 0.7.1 → 0.8.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 CHANGED
@@ -1,4 +1,6 @@
1
1
  <p align="center">
2
+
3
+ [本仓文档入口](./docs/README.md) · [本地工程约定](./docs/engineering.md)
2
4
  <img src="docs/logo.png" width="160" alt="morphix-env" />
3
5
  </p>
4
6
 
@@ -77,10 +79,48 @@ Choosing between Infisical and Doppler still depends on each provider's own iden
77
79
 
78
80
  This is leakage minimization, not a cryptographic boundary: a process that receives a secret can still transform or exfiltrate it deliberately. For a high-risk agent capability, use a provider proxy/broker as a separate security design rather than passing that credential through `run`.
79
81
 
82
+ ## On-demand secrets with `run --serve`
83
+
84
+ `mx-env run --serve -- <command>` (modeled on aws-vault's `--ecs-server`) starts a loopback HTTP server on 127.0.0.1 at a random port that shares the child's lifetime:
85
+
86
+ - Composed non-public values never enter the child environment — not the direct child, its descendants, `/proc/<pid>/environ`, or core dumps. The child receives only `MXENV_ENDPOINT` and a random `MXENV_TOKEN`, and the token is redacted from child output like any injected value. Honest caveat on token visibility: an environment-delivered capability token is readable by same-uid processes on Linux via the child's `/proc/<pid>/environ` — the same inherent property as aws-vault's `--ecs-server`; the *values* themselves still appear in no process environment anywhere.
87
+ - The child fetches one value at a time: `mx-env fetch <KEY>` (thin client that reads endpoint/token from its own environment and prints the exact value with no trailing newline), or a direct `GET /key/<KEY>` with `Authorization: Bearer $MXENV_TOKEN`. The endpoint answers 401 without the token and 404 for unknown keys; no request or value is ever logged.
88
+ - Browser-public keys (`NEXT_PUBLIC_`, `VITE_`, `EXPO_PUBLIC_`) stay in the environment so build tools can inline them; they remain servable as well.
89
+ - The server is destroyed the moment the child exits (and dies with the parent process itself), so no listener outlives the run.
90
+
91
+ `mx-env run` without `--serve` is unchanged: values are injected into the environment and redacted from forwarded output.
92
+
93
+ ## Provider plugins
94
+
95
+ Any executable on PATH named `mx-env-provider-<name>` is a provider — new remote stores need no changes to `config.ts` / `sources.ts` / `editor.ts`:
96
+
97
+ ```jsonc
98
+ // mx-env.config.json
99
+ {
100
+ "sources": {
101
+ "vault": { "provider": "plugin", "plugin": "bitwarden", "keys": ["OPENAI_API_KEY"] }
102
+ }
103
+ }
104
+ ```
105
+
106
+ Plugin protocol (summon-style):
107
+
108
+ - Invoked as `mx-env-provider-<plugin> <KEY>`, once per declared key; exit 0 with the value on stdout (one trailing line ending stripped).
109
+ - A non-zero exit or a missing executable fails the source with a value-free error — the plugin's stderr is never surfaced because it may contain sensitive material. `optional: true` downgrades a missing executable to `missing-optional`.
110
+ - Plugins intentionally inherit the already-composed environment at their precedence position (inherited env → global baseline → profile sources loaded so far), so wrapper scripts can authenticate to their own backend without a second credential channel. Least-privilege consequence: a plugin can read every value composed before it — declare plugin sources only after the sources they need and keep each `keys` list minimal. Each key invocation times out after 30s.
111
+ - Login and bootstrap stay inside the plugin; repository config carries only wiring (plugin name + key names), never values.
112
+
113
+ The five built-in providers (`infisical`, `doppler`, `os-keychain`, `dotenvx`, `local`) are unchanged.
114
+
80
115
  ## Commands
81
116
 
82
117
  ```bash
83
118
  mx-env run --profile dev -- <command> # compose and run (the only injection path)
119
+ mx-env run --serve -- <command> # serve values over loopback instead of injecting them into env
120
+ mx-env fetch <KEY> # read one value from a run --serve child environment
121
+ mx-env export [--profile agents] # eval-able export KEY='value' lines (values in stdout only for eval)
122
+ mx-env render <template> <output> # substitute ${VAR} into a config file (mode 0600)
123
+ mx-env inject <template> <output> # resolve mx-env://profile/KEY references into a file (mode 0600)
84
124
  mx-env inspect [--profile dev] # key names + provenance, no values
85
125
  mx-env edit [--profile dev | --source s] # one-shot visual editor
86
126
  mx-env generate --out public/__env.js # public browser runtime config
@@ -89,6 +129,8 @@ mx-env skill install | show # packaged Agent Skill (global / project
89
129
  mx-env audit tail # value-free audit events
90
130
  ```
91
131
 
132
+ `export`, `render`, `inject`, and `run` accept `--dry-run` (key/variable names only, never values). `eval "$(mx-env export --profile agents)"` injects a profile into the current shell without putting values in shell history. `render` and `inject` write mode-0600 outputs, warn about unresolved `${VAR}` / `mx-env://` references before writing, and warn when the output path is not git-ignored. `mx-env://profile/KEY` references (mirroring 1Password's `op://`) let MCP configs and dotenv templates stay in git without values: `mx-env inject` resolves them at the file level, `mx-env run` resolves them inside composed values before spawning.
133
+
92
134
  Full options live in `mx-env --help`; the packaged skill mirrors the commands of the installed version.
93
135
 
94
136
  ## Agent workflow
package/dist/SKILL.md CHANGED
@@ -29,14 +29,15 @@ npx morphix-env@latest inspect # 免安装试用
29
29
  配置文件**不含任何值**,只声明 source 接线与组合顺序。项目级放仓库根 `mx-env.config.json`;用户级 `~/.mx-env/config.json` 可选(全局基线,`--no-global` 可排除)。`~/.mx-env/.env` 是唯一自动发现的值文件(个人默认值)。
30
30
 
31
31
  ```jsonc
32
- // 项目 mx-env.config.json — 五种 provider 全示例
32
+ // 项目 mx-env.config.json — 全部 provider 示例
33
33
  {
34
34
  "sources": {
35
35
  "local": { "provider": "local", "files": [".env.local"], "optional": true, "override": true },
36
36
  "encrypted": { "provider": "dotenvx", "files": [".env.shared"] },
37
37
  "keychain": { "provider": "os-keychain", "platform": "macos", "service": "com.example/myproject" },
38
38
  "infisical": { "provider": "infisical", "paths": ["/team/api"] },
39
- "doppler": { "provider": "doppler", "project": "my-project", "config": "dev" }
39
+ "doppler": { "provider": "doppler", "project": "my-project", "config": "dev" },
40
+ "plugin": { "provider": "plugin", "plugin": "bitwarden", "keys": ["OPENAI_API_KEY"] }
40
41
  },
41
42
  "profiles": {
42
43
  "dev": { "sources": ["keychain", "infisical", "local"] },
@@ -50,6 +51,7 @@ npx morphix-env@latest inspect # 免安装试用
50
51
 
51
52
  - profile 内 `sources` **从低到高**排列;同 key 冲突默认**报错**,靠后 source 显式 `"override": true` 才覆盖(防止两个环境的同名凭据静默错选)。
52
53
  - provider 登录与引导留在 provider 侧(`infisical login`、`doppler login`、dotenvx 解密密钥引导),**不写进项目配置**。
54
+ - plugin source:PATH 上的 `mx-env-provider-<name> <KEY>` 把值写到 stdout(退出码 0);插件自带登录,失败信息不含值。
53
55
  - 固定基线顺序:`inherited process.env → ~/.mx-env/.env → 全局默认 profile → 项目 profile / legacy 配置 → -f 覆盖文件`。legacy 项目(`infisical` + `envFiles` 字段)无需改动即兼容。
54
56
 
55
57
  ## 命令速查
@@ -57,6 +59,11 @@ npx morphix-env@latest inspect # 免安装试用
57
59
  ```bash
58
60
  mx-env run --profile dev -- <command> # 组合环境并执行(子进程输出脱敏)
59
61
  mx-env run --no-global -- <command> # 排除全局基线,可复现的项目-only 运行
62
+ mx-env run --serve -- <command> # 值不进 env:子进程经 MXENV_ENDPOINT/MXENV_TOKEN 按需取值
63
+ mx-env fetch KEY # 在 --serve 子进程内取单个值(精确输出,无换行)
64
+ mx-env export [--profile dev] # 输出可 eval 的 export KEY='value' 行(值只经 stdout 进入 eval)
65
+ mx-env render <tpl> <out> # 把 ${VAR} 渲染进不支持插值的配置文件(0600,缺变量先告警)
66
+ mx-env inject <tpl> <out> # 解析 mx-env://profile/KEY 引用(0600,MCP 配置入仓不落值)
60
67
  mx-env generate --out public/__env.js # 生成浏览器公开变量(NEXT_PUBLIC_/VITE_/EXPO_PUBLIC_ 属公开配置例外)
61
68
  mx-env inspect [--profile dev] # key 名 + source 归属 + 状态(无值)
62
69
  mx-env edit --profile dev | --source X # 一次性可视化编辑器(loopback + 15 分钟 TTL,保存后自动关闭)
@@ -66,6 +73,8 @@ mx-env skill install [--target global|project|trellis] [--force] # 安装本 S
66
73
  mx-env skill show # 查看各目标安装状态
67
74
  ```
68
75
 
76
+ `export` / `render` / `inject` / `run` 均支持 `--dry-run`(只报 key/变量名,不出值)。值永不出现在 shell 历史里:`eval "$(mx-env export --profile dev)"` 才是 export 的消费方式。
77
+
69
78
  ## 进入一个新项目的标准流程
70
79
 
71
80
  1. `mx-env inspect` — 看是否已有配置、哪些 key 已就绪;无配置时走 legacy `.env.local` / Infisical 兼容路径。