Skip to content

[pkg→env] x-cmd/env 多 backend 编排 (env.yml 作为 conda 生态入口) #18

Description

@edwinjhlee

⚠️ 免责声明

本文档由 Claude (MiniMax-M3 模型) 根据与 l (Li Junhao) 的聊天过程生成, 未经 l 全文校对。术语可能与 l 习惯用词不一致, 部分事实可能与 x-cmd/pkg 实际实现有出入。记录的是设计层面的思考, 不是定稿。

🔄 Reframe 说明: 本 issue 范围已 reframe。早期草稿把 conda 当 x-cmd/pkg 的上游, 已废弃。conda / pixi / nix 的引入走 x-cmd/env (env.yml), 不是 x-cmd/pkg。x-cmd/pkg 维持"手作精品店"定位, 自己 build 缺包, 不接 conda。

TL;DR

x-cmd/pkg 是刀, x-cmd/env 是刀架。conda 进刀架 (env.yml 的 pixi: section), 不进刀身 (x-cmd/pkg 的索引)。

x-cmd 已经有一批 backend wrappers: x pixi / x asdf / x nix 等。x-cmd/env 是这些 wrappers 的编排层, 通过 env.yml 把它们缝成一个统一 shell 环境。conda / pixi / nix 的引入走 env.yml, 不动 x-cmd/pkg。

背景

x-cmd 现在的 pkg 体系 (x-cmd/pkg) 是一个手作精品店: ~500 个 CLI 工具, agent-用为主, 静态依赖, RPATH-relative 自包含。这条定位继续保留, 不动。

但实际场景里, 用户经常需要超出这个范围的东西:

  • 重 ABI 栈 (pytorch + cudatoolkit + numpy 矩阵) — 需要 SAT 求解
  • Hermetic 构建 — 需要 Nix 的内容寻址
  • pkgx 生态 — 用户已经用, 想统一入口
  • OS 驱动 / 系统级包 — conda 也不覆盖, 走系统包管理器

这些需求不能塞进 x-cmd/pkg (跟它的"少而精、静态优先"定位冲突), 但也不能让用户开八个 shell / 装八个 pkg 系统。

x-cmd/env 就是这件事的入口: 一个 env.yml, 把多个 pkg backend 编排起来。

x-cmd 的两层架构

x-cmd/
├── pkg/                    # Layer 1: 自有体系
│   定位: 500 个 agent CLI, 静态依赖, RPATH-relative
│  策略: 缺什么 build 什么, 不接 conda
│
└── env/                    # Layer 2: 编排器
   输入: env.yml
   里面: var / pkg / pkgx / pixi / nix
   输出: 组合好的 shell 环境
x-cmd/pkg conda-forge
数量 500 100k+
比例 1× 200×
定位 agent 用的 CLI 通用生态
依赖策略 尽可能静态 多层传递
缺货时 自己 build 直接拉

这不是缺陷, 是定位。x-cmd/pkg 的 supply chain 不是通用工厂, 是手作——每个包靠"动态库减到最少 / 静态优先"获得简洁性。

env.yml schema 草案

var:                                    # 环境变量
  EDITOR: vim
  PATH:   ...

pkg:                                    # x-cmd/pkg — 自有体系, 静态依赖
  - jq
  - ripgrep
  - python: "3.10.0"                    # pin (用户显式锁版本)

pkgx:                                   # pkgx — pkgx 生态擅长的部分
  - deno

pixi:                                   # x pixi — 科学计算 + SAT 求解
  - pytorch
  - numpy: ">=2.0"

nix:                                    # x nix — 内容寻址、hermetic
  - hello

asdf:                                   # x asdf — 多语言 runtime 版本管理
  - nodejs: "20.0.0"

各 backend 定位

backend 已有 wrapper 适用场景 不适用场景
pkg: 内建 日常 CLI / agent 工具 / 静态依赖链 重型 ABI 栈 / hermetic 复现
pkgx: (待补 x pkgx) pkgx 生态特有的工具 pkgx 没有的
pixi: x pixi ✓ pytorch / tensorflow / conda-forge 独有 / 多包 SAT 静态简单包 (走 pkg: 更轻)
nix: x nix ✓ hermetic 构建 / byte-identical 复现 简单工具 (走 pkg: 更快)
asdf: x asdf ✓ 多语言 runtime 版本管理 (nodejs / python / rust) 系统级包

用户按需混用——一个 env.yml 可以同时有 pkg: jq 和 pixi: pytorch, 不互斥。

x-cmd/pkg 不动什么

  • ✅ RPATH-relative + 静态优先 — x-cmd/pkg 的核心设计, 保留
  • ✅ 局部版本共存 + 锁版本 — x-cmd/pkg 的算法, 保留
  • ✅ 三个范式 (选 / 锁-by-hash / 锁-by-string) — 概念清晰化, 保留
  • ✅ "build 时消灭依赖 hell" — 仍然是 x-cmd/pkg 的算法原则
  • ✅ SONAME 池 + per-sphere — 仍然适用 (per-sphere 的 l/ 池跨 pkg sphere, 但 不跨 env backend)

x-cmd/env 要做什么

  1. env.yml 解析: 读取用户 yaml, 识别各 backend section
  2. dispatch 到具体 backend:
    • pkg: → x pkg add (走 x-cmd/pkg 自维护索引)
    • pkgx: → 调用 pkgx CLI
    • pixi: → 调用 pixi add / 生成临时 pixi.toml 调 pixi install
    • nix: → 调用 nix profile install 或临时生成 nix 表达式
  3. PATH 合成: 把各 backend 安装的 bin 都拉进一个统一 PATH, 优先级用户配
  4. 变量注入: 把 var: 块的 env vars 注入到 shell
  5. shell 激活: x env use / x env try 子命令, 启动一个 shell session 让 vars/PATH 生效

关键设计决策 (跟早期 issue 的演化对比)

旧 issue 假设 新框架结论
"x-cmd/pkg 接 conda-forge 当上游" ❌ 不接 — conda 走 env, pkg 自己 build
"RPATH-relative 重写 conda 包 populate" ❌ 不在 pkg 做 — conda 包在 pixi/conda-forge 里跑自己的 RPATH, x-cmd 不介入
"SAT 求解器接入 pkg" ❌ SAT 在 pixi backend 里, 不进 x-cmd/pkg
"Nix / pkgx 路径分析" ✅ 保留 — 但放 env 层讨论, 不在 pkg 层
"x-cmd/pkg 的三个范式 (选/锁-by-hash/锁-by-string)" ✅ 保留 — 这是 x-cmd/pkg 自己的算法, 跟 env backend 无关
"RPATH-relative + 静态优先" ✅ 保留 — x-cmd/pkg 的核心, 跟 env backend 无关

不在 env.yml 里要做的事

  • 不替用户做决策 — 用户显式选哪个 backend, env 不"自动推荐"
  • 不解决跨 backend 的 ABI 兼容 — pkg: numpy 和 pixi: numpy 在不同 sphere, 各自 RPATH 锁定自己版本, 不会冲突 (跟局部版本共存同源)
  • 不替用户写 env.yml — 用户自己配, AI 可以辅助生成

TODO

  • env.yml schema 完整定义 (var / pkg / pkgx / pixi / nix 各自格式)
  • x env use <env.yml> 子命令: 读 yaml, dispatch 各 backend, 合成 PATH
  • x env try <env.yml> 子命令: session-only, 退出失效
  • x env info 子命令: 当前活跃 env, 各 backend 已装内容
  • pkgx backend dispatch: pkgx install 包装
  • pixi backend dispatch: 临时生成 pixi.toml 调 pixi install / pixi add
  • nix backend dispatch: nix profile install 或临时 nix expr 包装
  • PATH 合成策略: 各 backend 的 bin 目录优先级、冲突检测
  • 变量注入策略: var: 块直接注入, 还是写到 ~/.pam_environment / ~/.config/environment.d/
  • env.yml 验证: schema 完整 + 互相引用 (比如 pkg: 包被 pixi: 引用)
  • env.yml 文档 / advise

关键不变量

env layer 的所有设计应该满足:

  1. 不修改各 backend 的内部模型 — pkg / pkgx / pixi / nix 各自怎么装怎么管是它们的事
  2. 只编排, 不翻译 — env 不替任何 backend 重新实现
  3. 用户显式选 backend — 不"自动猜"
  4. 跨 backend 不假设共享 — pkg: 装的不跟 pixi: 装的"等价"或"互通", 各自独立

与现有 issue 的关系

关联阅读

  • 背景叙事 (story): [pkg] pixi/conda 上游包接入 — 背景叙事 (story) #19
  • pixi 主仓库 (本机镜像 /Users/l/.x-repo/github.com/x-cmd-sourcecode/pixi):
    • 入口: crates/pixi_command_dispatcher/src/solve_conda/mod.rs:318 (Solver.solve(task)? 是 SAT 唯一入口)
    • 并行模型: crates/pixi_command_dispatcher/src/solve_binary.rs:60-73 (conda_solve_semaphore)
    • 通道优先级: docs/advanced/channel_logic.md
  • x-cmd/pkg 相关 issue (x-cmd/pkg 自己不动 conda, 这些只供设计参考):
    • 0119.npm-pip-source.yml — 源镜像 env var 模式
    • 0125.版本号问题.yml — 版本号规范化
  • 同类设计先例: asdf / rtx / mise (多个 runtime 编排, 单一 manifest), devbox (类似 Nix 但更轻)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions