跳到主要内容

共享工程标准附录

本附录是案例集所有 7 个案例共同遵循的工程标准的集中参考。每个案例文档只在必要处做最小重复,完整字段表 / capability 列表 / 发布 checklist 等都指向本附录,避免在多处复制粘贴导致失同步。

阅读顺序提示:新读者建议先浏览本附录建立全局认知,再进入具体案例。已经有经验的读者可以按需跳转。

metadata.json / manifest.json Schema

NeoMind 生态有两类可发布工件——扩展(Rust cdylib + 可选 React 前端)与仪表板组件(纯 React,以 bundle.js 形式分发)。两类工件的元数据文件名不同:

  • 扩展 → metadata.json(源仓库 extensions/<id>/metadata.json,构建脚本从 Cargo.toml 自动生成)
  • 组件 → manifest.json(源仓库 components/<id>/manifest.json,手写维护)

两份 schema 字段重合度很高,但扩展独有 builds / frontend / type组件独有 size_constraints / has_* 系列 / default_config。下表合并展示,分组说明,每个字段标注来源。

基础信息

字段类型必填说明示例
idstring工件唯一标识,全生态唯一。扩展用 kebab-case(中划线,如 weather-forecast-v2),组件用 snake_case(下划线,如 ne101_cameraweather-forecast-v2 / ne101_camera
namestring | object展示名。组件支持 { "en": "...", "zh": "..." } 多语言对象"weather forecast" / { "en": "NE101 Camera Panel", "zh": "NE101 感知摄像头面板" }
versionstring (semver)三段语义化版本。扩展从 Cargo.toml 自动读取;组件手写"2.7.6" / "2.14.9"
descriptionstring | object一句话描述,组件支持多语言"Real-time weather forecast..."
authorstring作者或团队名"NeoMind Team" / "CamThink Team"
licensestring扩展必填SPDX 许可证标识"Apache-2.0" / "MIT"
homepagestring (URL)源码或文档地址"https://github.com/camthink-ai/NeoMind-Extensions/tree/main/extensions/weather-forecast-v2"
iconstring否(组件常用)图标标识,对应 NeoMind 图标库"Camera"

类型与分类

字段类型必填说明示例
typestring扩展必填工件类型。当前固定为 "native"(Rust cdylib);未来 WASM 类型会扩展"native"
categoriesstring[]扩展可选市场分类标签数组["weather"]
categorystring组件可选单一分类,组件用"device"

构建产物(扩展独有)

builds 字段是扩展独有的,列出 5 个跨平台构建产物的下载 URL。每个 key 对应一个 Rust target triple,URL 指向 GitHub Release 资产。

{
"builds": {
"darwin-aarch64": { "url": "https://github.com/camthink-ai/NeoMind-Extensions/releases/download/v2.7.6/weather-forecast-v2-2.7.6-darwin_aarch64.nep" },
"darwin-x86_64": { "url": ".../weather-forecast-v2-2.7.6-darwin_x86_64.nep" },
"linux-x86_64": { "url": ".../weather-forecast-v2-2.7.6-linux_amd64.nep" },
"linux-aarch64": { "url": ".../weather-forecast-v2-2.7.6-linux_arm64.nep" },
"windows-x86_64": { "url": ".../weather-forecast-v2-2.7.6-windows_amd64.nep" }
}
}

完整的 5 个 target 说明见 跨平台构建目标矩阵

前端声明(扩展独有)

扩展如带 React 前端,必须声明 frontend 对象:

字段类型必填说明示例
frontend.componentsstring[]组件名数组,纯字符串不是对象["WeatherCard"]
frontend.entrypointstringUMD 入口文件名,必须与 frontend.jsonentrypoint 一致"weather-forecast-v2-components.umd.cjs"

常见错误:把 components 写成对象数组 [{ "name": "WeatherCard", ... }]。市场解析器会拒绝。

组件专用字段(manifest.json 独有)

字段类型必填说明示例
size_constraintsobject网格尺寸约束(单位:网格单元){ "min_w": 2, "min_h": 2, "default_w": 3, "default_h": 3, "max_w": 6, "max_h": 6 }
has_data_sourceboolean是否支持数据源绑定(Data Source tab)false
has_device_bindingboolean是否支持设备绑定,影响 deviceContext prop 注入true
device_type_filterstring[]限定可绑定的设备类型,空表示不限制["ne101_camera"]
has_display_configboolean是否展示「显示配置」tabfalse
has_actionsboolean是否展示「操作」tab(按钮、命令)false
default_configobject默认配置对象,用户未自定义时使用见下方代码块
global_namestringbundle.js 挂载到 window 的全局变量名"NE101CameraPanel"
export_namestringIIFE 默认导出名,与 global_name 通常一致"NE101CameraPanel"
max_data_sourcesinteger可选当 has_data_source 为 true 时生效,限制可绑定数据源数量上限12

default_config 示例(节选自 ne101_camera):

{
"default_config": {
"showMetrics": true,
"showCommands": true,
"location": "",
"displayTitle": "",
"processingEnabled": false,
"processingExtensionId": "",
"processingTemplate": "object_detection"
}
}

Capability 类目

NeoMind 通过显式 capability 申请实现扩展对平台能力的细粒度访问控制。扩展在代码中通过 CapabilityContext::invoke_capability(name, params) 调用平台能力;运行时会校验调用的 capability 是否在扩展声明的白名单内,未声明直接调用会 panic(非降级,强制开发者显式申请)。

完整 Capability 枚举

下表为 SDK ExtensionCapability 全部变体(来源:neomind-extension-sdk):

Capability 标识含义典型使用扩展
device_metrics_read读取设备指标仪表板类扩展
device_metrics_write写入设备指标(含虚拟指标)weather-forecast-v2、所有 bridge 扩展
device_control向设备下发命令homeassistant-bridge、modbus-bridge
storage_query查询时序存储数据分析类扩展
event_publish发布事件自动化触发类扩展
event_subscribe订阅事件联动类扩展
telemetry_history查询设备遥测历史历史趋势类扩展
metrics_aggregate聚合设备指标报表类扩展
extension_call调用其他扩展编排类扩展
agent_trigger触发 AI AgentLLM 联动类扩展
rule_trigger触发自动化规则自动化类扩展
device_template_register注册设备类型模板lorawan-bridge、modbus-bridge、onvif-bridge、bacnet-bridge、opcua-bridge、uink-rms-bridge
device_register注册设备实例所有 bridge 扩展
device_unregister注销设备实例bridge 扩展清理逻辑
Custom(String)自定义 capability项目定制场景

实际代码中的使用模式

通过 grep 仓库源码统计,最常用的 capability 是 device_metrics_write(几乎所有 bridge 扩展都用来上报遥测)和 device_register / device_template_register(bridge 扩展注册外部设备到 NeoMind 设备模型)。典型调用模式:

use neomind_extension_sdk::capabilities::CapabilityContext;
use serde_json::json;

// 写入虚拟指标(最常用)
let _ = ctx.invoke_capability("device_metrics_write", &json!({
"device_id": "virtual-sensor-1",
"metric": "temperature",
"value": 25.5
}));

// 注册设备类型模板(bridge 扩展启动时)
let result = ctx.invoke_capability("device_template_register", &template_json);

// 注册设备实例
let result = ctx.invoke_capability("device_register", &device_json);

同步 vs 异步调用

  • 异步上下文(如 execute_command):使用 ctx.invoke_capability(name, params).await
  • 同步上下文(如 produce_metrics / handle_event):扩展内部封装 invoke_capability_sync() 方法,通过 CapabilityContext::default() 桥接

版本号三段一致性

NeoMind-Extensions 仓库有三个层级的版本号,发布时必须全部一致(除非有明确理由保持不同):

文件版本含义示例
VERSION市场发布版本(仓库级单一版本)2.7.0
extensions/index.jsonversion市场发布版本(与 VERSION 同步)2.7.0
extensions/*/Cargo.tomlversion每个扩展自身版本(影响包文件名)2.7.0
extensions/*/metadata.jsonversion自动从 Cargo.toml 读取,不手写2.7.0

常见错误

只更新了 VERSIONindex.json,但忘了更新各扩展的 Cargo.toml。后果:

  • 包文件名是旧版本:weather-forecast-v2-2.6.0-darwin_aarch64.nep
  • GitHub Release 标题是 v2.7.0,但里面的包都是 2.6.0
  • index.jsonbuilds URL 指向 2.7.0 的资产名,但实际文件名是 2.6.0 → 404
  • 用户体验混乱,市场无法安装

正确流程

使用 ./scripts/update-versions.sh 一键同步:

# 完整更新:同步 Cargo.toml + VERSION + 生成 JSON(推荐)
./scripts/update-versions.sh 2.7.0 --bump-extensions

# 验证版本一致性(必须通过!)
./scripts/update-versions.sh 2.7.0 --check

跨平台构建目标矩阵

NeoMind 扩展支持 5 个 target(不是 6 个,没有 windows-aarch64):

Target KeyRust Target Triple产物后缀用途
darwin-aarch64aarch64-apple-darwin.dylibApple Silicon macOS(M1/M2/M3/M4)
darwin-x86_64x86_64-apple-darwin.dylibIntel macOS
linux-x86_64x86_64-unknown-linux-gnu.so通用 Linux 服务器
linux-aarch64aarch64-unknown-linux-gnu.soARM Linux(树莓派 4/5、ARM 服务器)
windows-x86_64x86_64-pc-windows-msvc.dllWindows 10/11

构建命令

# 一次性构建全部 5 个 target 的 .nep 包
./build.sh --release 2.7.0

# 只构建单个扩展
./build.sh --single weather-forecast-v2 --release 2.7.0

build.sh 内部使用 cross(基于 Docker)或本地工具链交叉编译。开发者本地如已安装对应 target 的 Rust 工具链,可跳过 Docker 直接编译。

.nep 包结构

weather-forecast-v2-2.7.6-darwin_aarch64.nep   (ZIP 格式)
├── manifest.json # 安装清单(从 metadata.json 转换)
├── binaries/
│ └── darwin_aarch64/
│ └── libneomind_extension_weather_forecast_v2.dylib
├── frontend/
│ └── weather-forecast-v2-components.umd.cjs
└── models/ # 可选:ONNX 模型
└── model.onnx

测试覆盖率与质量要求

NeoMind 生态对测试有明确要求,不达标的扩展不予发布

扩展测试(Rust)

测试类型位置要求参考
单元测试src/lib.rs#[cfg(test)] mod tests至少覆盖核心命令的 happy pathweather-forecast-v2/src/lib.rs
集成测试tests/ 目录至少 1 个集成测试文件weather-forecast-v2/tests/

最小化示例:

// src/lib.rs
#[cfg(test)]
mod tests {
use super::*;

#[tokio::test]
async fn test_increment_command() {
let ext = MyExtension::new();
let result = ext.execute_command("increment", &json!({"amount": 5}))
.await
.unwrap();
assert_eq!(result["counter"], 5);
}
}

组件测试(JavaScript)

NeoMind-Dashboard-Components 采用手写 IIFE 作为分发格式,测试通过 mock window 全局验证 IIFE 导出:

测试类型位置要求参考
Bundle 测试<component>/test_bundle.js每个组件必须有ne101_camera/test_bundle.js

test_bundle.js 的典型结构:

// 1. mock window 全局
global.window = {
React: require('react'),
jsxRuntime: { jsx: () => null, jsxs: () => null },
};

// 2. 加载 bundle.js(IIFE 会挂载到 window.<global_name>)
require('./bundle.js');

// 3. 验证导出
const Component = window.NE101CameraPanel;
if (typeof Component !== 'function') {
throw new Error('NE101CameraPanel 未正确导出');
}

console.log('✓ bundle.js 导出测试通过');

CI 要求

  • 扩展仓库:cargo test --workspace 全绿才能发布
  • 组件仓库:每个组件的 test_bundle.js 必须通过 node 执行
  • 两个仓库都有 GitHub Actions 在 PR 时自动运行测试

发布 Checklist

发布新版本前必须逐项确认

  • 所有 Cargo.toml 版本号一致(./scripts/update-versions.sh $VERSION --check 通过)
  • extensions/index.json 版本字段更新
  • VERSION 文件更新
  • cargo test --workspace 全绿
  • ./build.sh --release $VERSION 产出 5 个 target 的 .nep
  • 验证 dist/*.nep 文件名版本号一致(ls dist/*.nep
  • 组件:bundle.js + manifest.json 同步到 NeoMind-Dashboard-Components 仓库
  • GitHub Release 创建,5 个 .nep 上传到 release assets
  • 案例集 0-overview.md版本对齐表 audit 日期更新

完整发布流程

VERSION=2.7.0

# Step 1: 同步版本号 + 生成 JSON
./scripts/update-versions.sh $VERSION --bump-extensions

# Step 2: 验证一致性
./scripts/update-versions.sh $VERSION --check

# Step 3: 提交版本变更
git add . && git commit -m "chore: bump to v$VERSION"

# Step 4: 构建和打包
./build.sh --release $VERSION

# Step 5: 验证包文件名版本一致
ls dist/*.nep

# Step 6: Tag 和发布
git tag v$VERSION
git push origin main --tags
gh release create v$VERSION ./dist/*.nep --title "v$VERSION"

安全要求

NeoMind 对扩展和组件的安全有严格约束,违反任意一条都会被代码审查拒绝。

unsafe Rust

  • 尽量避免 unsafe
  • 若必须使用(如 FFI 绑定、性能关键路径),需在 PR 描述中明确说明:
    • 为什么需要 unsafe
    • 如何保证内存安全
    • 是否有 safe wrapper

Capability 显式申请

扩展在 metadata.json 中声明所需 capability,运行时强制校验:

  • 未声明 device_metrics_write 直接调用 → panic(非降级)
  • 这是有意设计:强制开发者显式申请权限,避免「静默失败」
  • 桥接类扩展启动时需要 device_template_register + device_register + device_metrics_write 三个 capability

进程隔离

所有扩展运行在独立进程中:

┌─────────────────────────────────┐
│ NeoMind 主进程 │
│ ┌───────────────────────────┐ │
│ │ UnifiedExtensionService │ │
│ │ IPC via stdin/stdout │ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘
│ FFI / IPC

┌─────────────────────────────────┐
│ 扩展运行器进程(每个扩展独立) │
│ - Native: .dylib / .so / .dll │
│ - WASM: wasmtime runtime │
│ - 崩溃不影响主进程 │
└─────────────────────────────────┘

收益:

  • 崩溃隔离:扩展 panic 不会拖垮主进程
  • 内存隔离:每个扩展独立地址空间
  • 资源限制:可对单扩展限制 CPU / 内存
  • 独立生命周期:扩展可单独重启

前端组件沙箱

Dashboard 组件的 bundle.js 通过 window 注入运行时依赖,不直接访问

  • 文件系统
  • 网络(fetch / XMLHttpRequest 由 Host 包装)
  • 原生 API
// bundle.js 内部通过 window 注入依赖(不打包)
var React = window.React;
var jsx = window.jsxRuntime.jsx;
var jsxs = window.jsxRuntime.jsxs;

这保证了组件可在任何 Host 环境运行(Tauri 桌面端、Web 浏览器、嵌入式 WebView),由 Host 决定暴露哪些能力。

Panic 配置

扩展的 Cargo.toml 必须设置:

[profile.release]
panic = "unwind" # REQUIRED! "abort" 会在任何 panic 时崩溃宿主进程
opt-level = 3
lto = "thin"

延伸阅读


最后更新: 2026-06-22