module contract v2

开发一个模块

模块是独立 Python 进程,通过 stdio JSON-RPC 与核心通信。核心不 import 模块代码; 数据、权限与 UI 都经受限接口传递。本页给出可以照着做的最小路径。

事实源:schemas/module.schema.json、docs/MODULE_CONTRACT_V2.md。 文档与实现冲突时以 schema 为准。

1 · 契约概览

契约分为传输层与贡献层。传输层保证模块能启动、被调用、被审计;贡献层告诉核心模块提供哪些能力。

传输:JSON-RPC over stdio

每行一条 JSON。请求 {"jsonrpc":"2.0","id":1,"method":"...","params":{}};单条消息上限 4 MB,大结果写临时文件并返回路径与 sha256。

生命周期

discover → verify → install → start → initialize → operate。核心注入 ECLIPSE_MODULE_ID、ECLIPSE_RPC_TOKEN、ECLIPSE_PERMISSIONS。

贡献是声明式的

contributes 在安装时校验。UI 只渲染受控组件;禁止任意 React/JSX、内联脚本与 iframe。

模块类型决定必须声明哪些贡献:device-adapter 需要 importFormats; signal-engine 需要 pipelines;analysis-model 需要 models; dashboard-widget 需要 ui.widgets;exporter 需要 exporters; ai-tool 需要 tools。

2 · .ehmod 包结构

.ehmod 是 ZIP 包,安装时执行路径穿越、符号链接、文件数与解压体积检查。

目录结构
my-module/
├── module.json          # 必需:manifest
├── src/
│   └── main.py          # 必需:entry.python 指向的文件
├── models/              # 可选:ONNX / safetensors 权重
├── ui/                  # 可选:声明式 UI 资源
├── LICENSE              # 必需:完整许可证文本
├── NOTICE               # Apache-2.0 等许可证要求时必需
├── README.md            # 建议:用法、权限、数据来源
└── checksums.json       # 打包时生成:逐文件 sha256
  • 安装路径为 <dataDir>/modules/<id>/<version>/;目录视为只读。
  • 单文件 ≤ 64 MB,解压后 ≤ 256 MB;禁止符号链接、绝对路径、.. 与 Windows 保留设备名。
  • 发布包必须包含 checksums.json;安装时逐文件校验,缺失只会降级为警告。
  • 不要把 venv/、__pycache__/、.git/ 或任何密钥打进包。

3 · module.json 示例

下面这份 manifest 通过 schemas/module.schema.json 校验,可直接改 ID 与入口后使用。

module.json · schema 1.0
{
  "schemaVersion": "1.0",
  "id": "io.github.example.heart-rate",
  "name": "Heart Rate Summary",
  "description": "Summarize heart rate from user-selected CSV files. Not a medical device.",
  "version": "0.1.0",
  "apiVersion": "1.0",
  "engines": { "eclipse": ">=1.0 <2.0" },
  "type": "ai-tool",
  "author": { "name": "Example Author", "url": "https://github.com/example" },
  "license": "MIT",
  "homepage": "https://github.com/example/heart-rate",
  "repository": "https://github.com/example/heart-rate",
  "permissions": ["files:user-selected", "records:write"],
  "dependencies": ["numpy>=2.0,<3"],
  "entry": { "python": "src/main.py", "factory": "create_module" },
  "contributes": {
    "tools": [
      {
        "id": "hr.summary",
        "name": "Heart rate summary",
        "description": "Return mean and max heart rate from a CSV file.",
        "inputSchema": {
          "type": "object",
          "properties": { "path": { "type": "string" } },
          "required": ["path"]
        }
      }
    ],
    "ui": {
      "widgets": [
        {
          "id": "hr-latest",
          "title": "最近心率",
          "size": "sm",
          "dataSource": { "type": "module-rpc", "method": "summary" },
          "view": { "type": "stat", "valuePath": "mean", "unit": "bpm" }
        }
      ]
    }
  },
  "resources": { "memoryMb": 256, "cpu": 0.5, "startupTimeoutMs": 8000 },
  "updateChannel": "stable"
}

社区模块 ID 必须是 io.github.<GitHub 用户名或组织>.<模块名>;官方前缀 org.eclipsehealth.* 保留给项目维护者。

4 · 权限

只申请实际需要的最小集合。升级新增权限必须由用户重新确认,不会自动继承。

Scope风险含义
records:read低读取记录、测量与分析结果
records:write中创建或更新记录
records:delete高删除记录;默认不授予
files:user-selected低读取用户主动选择的文件
files:export中写入用户确认的目录
network高访问 allowedHosts 中列出的主机
llm中调用用户配置的 LLM
models:download中下载模型权重
ui:custom高沙箱中的自定义 UI
system:notify低发送系统通知
system:background中最小化后继续运行

声明 network 时必须同时提供 allowedHosts,且每个主机必须是具体域名,禁止通配符 *。

5 · 依赖与网络

  • 每个模块使用独立 venv;dependencies 是 PEP 508 列表,安装到该模块自己的环境。
  • 只接受包名、extra、版本范围与环境标记;拒绝 URL(@)、本地路径、file: 与 -e 形式。
  • 锁版本或给出合理上界,避免上游大版本破坏;首次安装会联网,用户需知情。
  • models 仅允许 onnx / safetensors / remote;禁止 pickle。远程权重必须有 sha256。
  • API Key / token 必须存 OS 密钥库,不得写入日志、manifest 或模块包。
允许与拒绝的依赖写法
# 允许
"numpy>=2.0,<3"
"heartpy==1.2.7"
"pandas[performance]>=2.2 ; python_version >= '3.12'"

# 拒绝(CI 与安装器都会拦截)
"https://example.com/pkg.whl"
"git+https://github.com/owner/repo"
"-e ../local-package"
"../../somewhere"

6 · 测试清单

提交 PR 前至少完成以下检查;CI 会重新执行结构与打包校验。

  • module.json 通过 schemas/module.schema.json 校验。
  • 空 stdin、未知方法、非法参数都返回结构化 JSON-RPC 错误,不崩溃、不挂起。
  • 启动后发送 initialize,确认返回 moduleId 与 apiVersion。
  • 用临时目录跑主路径;不要读取真实用户健康数据,不要写日志记录波形或文件名。
  • 断网、超时与空数据场景有明确错误码;大结果使用临时文件。
  • 打包后在干净目录解包并运行一次,确认不依赖仓库外的相对路径。
本地校验(示例路径)
python -m json.tool module.json
python -m pip install jsonschema
python .github/scripts/validate_module.py marketplace/submissions/my-module

7 · 提交 PR

  1. Fork 仓库,在 marketplace/submissions/<module-id>/ 下放入 module.json、src/、LICENSE(Apache-2.0 另需 NOTICE)与 README.md。
  2. 本地运行校验脚本并打包,确认 .ehmod 可解包、校验和一致。
  3. 填写 PR 模板(.github/PULL_REQUEST_TEMPLATE/module.md):作者、许可证、上游、权限、测试、截图与非诊断声明。
  4. CI validate-module 检查 schema、路径安全、许可证白名单、NOTICE 与包完整性;失败会阻止合并。
  5. 合并后由维护者在 GitHub Releases 上传 .ehmod,再生成并离线签名索引。

打开 GitHub Pull Requests

仓库地址为占位;发布后替换为真实组织名。提交清单见 marketplace/CONTRIBUTING.md。

8 · 许可证与署名

官方索引只接受 OSI 开源许可证;社区索引允许专有/付费模块,但必须显著标注。CI 白名单会拒绝 AGPL 与无许可证提交。

许可证官方索引要求
MIT / ISC / BSD-2-Clause / BSD-3-Clause允许保留版权声明与许可证全文
Apache-2.0允许保留 LICENSE 与 NOTICE,标注修改
MPL-2.0允许修改文件需公开源码
GPL-2.0 / GPL-3.0 / LGPL允许仅经 stdio JSON-RPC 隔离,不静态链接进核心
AGPL / SSPL / BUSL / 无许可证拒绝与分发模型冲突或无法确认再分发权
  • 保留上游 LICENSE/NOTICE,不得删除版权头;在 README.md 与 description 中写清 upstream 仓库、原版权人与修改内容。
  • 作者字段写适配层维护者,不得暗示上游为你的模块背书。
  • 优先 wrapper(子进程或库调用),确需 patch 时保留原许可证并公开修改源码。
  • 模型权重与代码许可证分开记录:来源 URL、SPDX、sha256、许可范围。

9 · 安全与非诊断声明

必须写进模块描述

“本模块不是医疗器械,输出不用于诊断或治疗。”任何声称诊断、治愈或替代医生的描述都会被扫描标记为高风险并可能被拒绝。

  • 禁止把 API Key、私钥、令牌打进包;CI 会扫描常见密钥模式。
  • 禁止下载并执行任意代码、混淆、pickle 反序列化与可疑网络请求。
  • 健康数据默认不出本机;联网必须声明 network 并在安装页逐项解释。
  • 日志不得包含波形内容、用户备注、原始文件名;只允许 ID 与哈希。
  • 社区模块未经人工审核:安装页会显示“未审核,风险自担”。