跳转至

开发指南

本文面向想在 chatxxx 项目中接入 ChatEnv 的开发者。ChatEnv 只负责 typed env/profile 的通用运行时,具体变量、连通性测试和业务含义都留在各项目内部。

开发边界

ChatEnv 提供:

  • EnvField:描述一个环境变量的 key、默认值、说明和敏感性;
  • BaseEnvConfig:typed schema 基类和自动 registry;
  • EnvStore:active .env 与 named profile 的读写;
  • TokenStore:按 tokens/<Service>/<profile>.json 原子保存动态 token/session,status/list 不输出 raw values;
  • chatenv CLI:init/new/paste/use/list/cat/get/set/save/delete/testtoken status/refresh/import/list/clear
  • chatenv.configs entry point discovery:加载外部项目注册的 schema provider;
  • chatenv.token_refreshers entry point discovery:让服务包注册“从 stable env/profile 自动刷新 runtime token”的 hook。

业务项目负责:

  • 定义自己的 BaseEnvConfig 子类;
  • 给 schema 设置 _aliases_storage_dir
  • pyproject.toml 注册 chatenv.configs entry point;
  • 如需 runtime token/session,提供登录或 refresh 函数,并可注册 chatenv.token_refreshers entry point;
  • 如需验证服务可用性,在 schema 类里实现 test()
  • 不在 ChatEnv 中加入具体业务变量或业务 token 语义。

目录约定

ChatEnv 默认只读一个根变量:

export CHATARCH_HOME=~/.chatarch

env/profile 数据固定放在:

$CHATARCH_HOME/envs/      # stable typed env/profile files
$CHATARCH_HOME/tokens/    # generated runtime token/session files

例如 ExampleConfig._storage_dir = "Example" 时:

~/.chatarch/envs/
  Example/
    .env
    work.env

不再提供 CHATTOOL_ENV_FILECHATARCH_ENV_FILE、platformdirs fallback 或自动迁移逻辑。需要迁移旧目录时,由上层项目提供显式脚本。

在 chatxxx 项目中接入

第一步,依赖 ChatEnv:

[project]
dependencies = [
    "chatenv>=0.2.0,<0.3.0",
]

第二步,定义 schema。建议放在业务项目自己的 config 包内,例如 chatfoo/config.py

from chatenv import BaseEnvConfig, EnvField


class FooConfig(BaseEnvConfig):
    _title = "Foo Configuration"
    _aliases = ["foo", "chatfoo"]
    _storage_dir = "Foo"

    FOO_API_BASE = EnvField("FOO_API_BASE", desc="Foo API base URL")
    FOO_API_KEY = EnvField("FOO_API_KEY", desc="Foo API key", is_sensitive=True)

    @classmethod
    def test(cls):
        print(f"Testing {cls._title}...")
        # 这里写业务项目自己的连通性验证。
        print("✅ Success!")

第三步,注册 provider entry point:

[project.entry-points."chatenv.configs"]
chatfoo = "chatfoo.config"

安装 chatfoo 后,运行 chatenv 时会加载 chatfoo.config。模块 import 后,FooConfig(BaseEnvConfig) 会通过 BaseEnvConfig.__init_subclass__ 自动进入 registry。

Runtime token refresh provider

如果业务项目需要 access token、web session、cookie/CSRF 等运行态,stable env/profile 应保存可再认证/可刷新所需配置;runtime token 应由登录或 refresh 函数生成,不应让用户手工维护 token JSON。

业务项目可注册 refresh hook:

[project.entry-points."chatenv.token_refreshers"]
Foo = "chatfoo.tokens:refresh_chatenv_token"

函数接收 keyword-only 上下文并返回 TokenRefreshResult 或同形 mapping:

from chatenv import TokenRefreshResult


def refresh_chatenv_token(*, service, profile, home, env_store, token_store):
    # 1. 从 env_store / 业务 config 读取 envs/Foo/<profile>.env 的稳定配置
    # 2. 调用业务登录/refresh API 获取新的运行态 token/session
    # 3. 只返回 opaque values + safe summary;不要 print raw token/cookie
    return TokenRefreshResult(
        values={"access_token": "[REDACTED]"},
        token_type="oauth_access_token",
        summary={"account": "operator", "base_url": "https://foo.example"},
        expires_at="2026-08-11T23:00:00Z",
    )

用户执行 chatenv token refresh Foo work 时,ChatEnv 调用该 hook 并写入 tokens/Foo/work.jsonchatenv token import 只用于迁移或外部刷新器交接,是显式 import,不是日常 refresh 入口。

CLI 如何找到 schema

chatenv 启动时会执行 provider discovery:

from importlib.metadata import entry_points

for ep in entry_points(group="chatenv.configs"):
    ep.load()

它的含义是:从已安装包的 metadata 中找到所有声明在 chatenv.configs 组下的入口,然后 import 对应模块。

完整过程如下:

chatenv cat -t foo
  -> load_config_providers()
  -> entry_points(group="chatenv.configs")
  -> ep.load() imports chatfoo.config
  -> FooConfig 自动注册到 BaseEnvConfig._registry
  -> -t foo 解析到 FooConfig
  -> EnvStore 读取 ~/.chatarch/envs/Foo/.env

因此,ChatEnv CLI 拥有固定命令;业务项目只提供 schema provider。不要在每个项目里重复实现一套 env CLI。

内置共享 schema

ChatEnv 内置少量 ChatArch 生态中会被多个工具交叉引用的共享 schema:

  • OpenAIConfigOpenAI / oai / openai
  • FeishuConfigFeishu / feishu / lark

这些 schema 可直接从 chatenv.configschatenv 导入。业务项目不要重复注册同一 logical config;若旧 provider 仍注册相同 _storage_dir,ChatEnv 会保留先注册的 canonical schema 并跳过重复注册。

工具私有 schema 仍应留在各自包中,通过 chatenv.configs entry point 注册。

本地联调

在两个本地仓库联调时,可以用 editable install:

python -m pip install -e /path/to/ChatEnv
python -m pip install -e /path/to/chatfoo

查看当前 Python 环境里有哪些 provider:

python - <<'PY'
from importlib.metadata import entry_points

for ep in entry_points(group="chatenv.configs"):
    print(ep.name, "=>", ep.value)
PY

验证 CLI:

chatenv status --detail
chatenv list
chatenv init -t foo -i
chatenv cat -t foo
chatenv test -t foo

chatenv status --detail 会按 platform/schema 展示变量清单,并在 platform 行和每个变量行标出 provider=<entry-point-package>;内置共享 schema 的 provider 显示为 chatenv

如果 provider 加载失败,可打开 debug 输出:

CHATENV_DEBUG=1 chatenv list

ChatEnv 仓库开发

python -m pip install -e .[dev,docs]
python -m pytest -q
python -m mkdocs build --strict

构建发布包:

python -m build
python -m twine check dist/*

发布前检查:

  • src/chatenv/__init__.py 中的 __version__ 已更新;
  • README.mddocs/cli.mddocs/design.md、本文档已同步;
  • 没有把业务项目的具体变量写入 ChatEnv;
  • 没有新增分散路径环境变量;
  • 测试和 MkDocs strict build 通过。