创建 standalone Extension
本章从 LoopX 官方 scaffold 创建 loopx-text-stats。官方 scaffold 提供完整可运行基线;本章 给出需要收窄的 manifest、request/response 合同、核心函数与验证步骤,不依赖配套练习仓库。
成功标准
完成后,你应该能观察到:
- scaffold 是独立 Python package;
- manifest 使用
loopx_extension_manifest_v0; - request 与 response 都有 versioned JSON Schema;
- provider 只从 stdin 读取一个 JSON object,并向 stdout 写一个 JSON object;
- doctor 无 side effect;
- 不合法 request 在工作开始前 fail closed;
- manifest 与 runtime 都声明零权限。
1. 生成官方 scaffold
从一个准备存放示例的工作目录运行:
loopx extension init loopx-text-stats \
--destination standalone-extension \
--execute \
--format jsonextension init 默认只预览;必须显式加 --execute 才写文件。目标目录必须不存在,即使空目录也会 被拒绝。命令不会自动 build、install、register 或 enable。
生成结构:
standalone-extension/
├── extension.toml
├── pyproject.toml
├── README.md
├── examples/
│ └── request.json
├── schemas/
│ ├── request.schema.json
│ └── response.schema.json
└── src/
└── loopx_text_stats/
├── __init__.py
└── cli.py这是 complete standalone path,不会猜测 [[provides]] 或 [[implements]] 所需的 Capability authority。
2. 读取 manifest
scaffold 生成并经本章收窄后的 manifest:
schema_version = "loopx_extension_manifest_v0"
id = "loopx-text-stats"
version = "0.1.0"
requires_loopx_api = ">=1,<2"
permissions = []
[runtime]
protocol = "loopx_text_stats_extension_v0"
entrypoint = "loopx-text-stats"
doctor_args = ["--doctor"]
required_permissions = []
timeout_seconds = 30关键约束:
id是 lifecycle identity;version参与 revision 与升级;requires_loopx_api明确兼容窗口;protocol是 provider wire contract;entrypoint必须存在于 LoopX 所在 Python environment 的PATH;doctor_args指向只读 readiness probe;permissions与required_permissions都为空;- timeout 由 managed runtime 固定,调用者不能任意覆盖。
3. 定义 request contract
示例 request:
{
"schema_version": "loopx_text_stats_request_v0",
"text": "LoopX keeps project state explicit.\nExtensions keep delivery lifecycle explicit."
}request schema 要求:
- payload 必须是 object;
schema_version必须精确匹配;text必须是包含非空白字符的 string;additionalProperties为 false。
拒绝额外字段很重要。假如 caller 传入:
{
"schema_version": "loopx_text_stats_request_v0",
"text": "hello",
"path": "/tmp/input.txt"
}provider 必须拒绝,而不是擅自把 path 理解为文件读取授权。schema 是 bounded request 的一部分。
4. 实现纯计算
示例的核心函数:
def analyze_text(text: str) -> dict[str, int]:
return {
"characters": len(text),
"non_whitespace_characters": sum(
1 for character in text if not character.isspace()
),
"words": len(re.findall(r"\S+", text)),
"lines": len(text.splitlines()) or 1,
}它具有适合作为首个 standalone Extension 的性质:
- 同一输入得到同一输出;
- 不访问环境变量;
- 不读取文件;
- 不访问网络;
- 不写外部系统;
- 不依赖 LoopX project state。
provider 在计算前完成结构验证,错误也通过 versioned response object 返回:
{
"ok": false,
"schema_version": "loopx_text_stats_response_v0",
"extension_id": "loopx-text-stats",
"error": "extension input has unsupported fields ['path']"
}不要把 traceback、环境变量或本机路径直接输出到 public receipt。
5. 定义 response contract
成功 response 的稳定 domain 部分:
{
"ok": true,
"schema_version": "loopx_text_stats_response_v0",
"extension_id": "loopx-text-stats",
"request_schema_version": "loopx_text_stats_request_v0",
"result": {
"characters": 80,
"non_whitespace_characters": 71,
"words": 10,
"lines": 2
}
}response schema 使用 oneOf 区分成功与失败。测试应断言 domain contract,而不是绑定 LoopX CLI 外层展示的每个字段,否则 minor release 的 receipt 扩展会导致无意义失败。
6. 保持 doctor 无副作用
starter 的 doctor:
if args.doctor:
return 0对于这个纯计算 provider,readiness 只需要证明 entrypoint 可启动和参数可解析。doctor 不应:
- 创建文件;
- 连接网络;
- 写入凭据;
- 修改 extension state;
- 产生业务 effect;
- 输出未经约束的大量日志。
真实 Provider 可以做必要的只读依赖检查,但 readiness probe 仍应有界、可重复、无 effect。
7. 安装 package 并运行 tests
在同一个 Python environment 中:
cd standalone-extension
python3 -m venv .venv
. .venv/bin/activate
python3 -m pip install -e '.[test]'
python3 -m pytest同环境要求不是偶然限制。LoopX lifecycle 根据已安装 console entrypoint 验证 provider;如果 package 装在另一个 venv,正确结果是 entrypoint_missing,而不是静默寻找任意源码路径。
常见错误
手写一个比 scaffold 更小的目录
容易漏掉 schema、doctor、manifest compatibility 或 package entrypoint。先生成完整官方路径,再删改 不需要的领域字段。
让 provider 接受任意 kwargs
这会破坏 bounded request,并可能意外扩大权限。request schema 和 provider validation 应同时 fail closed。
用 doctor 执行业务请求
doctor 证明 readiness,不证明某个 effect 已获授权。业务调用必须通过 managed runtime 或 Capability/domain command。
为演示擅自增加 permission
一旦有 permission,extension run 会拒绝直接调用。先确定真实 Capability 和 authority,再设计 effectful provider,不要为了展示 manifest 字段制造假的权限合同。
