开发本地应用
开发 Connector 本地应用,定义清单、服务、内嵌界面、权限、调试、打包和市场发布流程。
本地应用是安装在用户电脑上的 Connector。它适合连接只能在本机访问的数据和程序,例如桌面软件、本地数据库、文件、浏览器、开发工具或硬件设备。
本地应用不进入 Bundle,也不是平台应用。它由百积木客户端安装和管理,在本机启动独立进程,通过 loopback HTTP 向 Bridge Agent 提供方法和事件;Bridge Agent 完成设备授权、远程调用控制和能力上报。
智能体或工作流
-> 百积木 Relay
-> 用户已授权的 Bridge Agent
-> Connector 注册的方法
-> 127.0.0.1 上的本地服务
-> 本机数据、程序或设备开发流程
- 确认能力必须在用户电脑上运行,并定义最小权限。
- 实现仅监听 loopback 的本地 HTTP 服务。
- 编写
service-registration.json,声明可远程调用的方法和事件。 - 编写
connector.json,声明运行时、权限、配置、管理操作和可选内嵌界面。 - 从本地目录安装,完成启动、健康检查、方法调用和停止测试。
- 生成固定版本的 Git 标签或压缩包,计算 SHA-256。
- 创建本地应用定义和不可变版本,提交市场审核。
- 从市场或固定版本地址重新安装,完成最终验收。
项目结构
推荐使用下面的结构:
example-connector/
├── connector.json
├── service-registration.json
├── README.md
├── ui/
│ ├── index.html
│ ├── app.js
│ └── app.css
├── bin/
│ ├── macos-arm64/
│ │ └── example-connector
│ ├── macos-x86_64/
│ │ └── example-connector
│ └── windows-x86_64/
│ └── example-connector.exe
└── tests/Node.js 项目也可以在 package.json 的 bin 中声明命令;Python 项目可以在 pyproject.toml 的 [project.scripts] 中声明入口。百积木客户端安装 Connector 后会解析这些入口。原生程序优先放在 bin/<平台>-<架构>/,也可以放在包根目录或 bin/。
平台目录使用 macos-arm64、macos-x86_64、windows-x86_64、linux-x86_64 等名称。只在应用定义的 platforms 中声明真正构建和测试过的平台。
编写 Connector 清单
新应用使用 schemaVersion: "1.2"。下面是一份包含本地服务、内嵌界面和管理操作的示例:
{
"schemaVersion": "1.2",
"id": "com.example.connector.notes",
"name": "Example Notes Connector",
"version": "0.1.0",
"description": "Read notes stored on this computer.",
"publisher": {
"name": "Example",
"homepage": "https://example.com"
},
"source": {
"type": "git",
"repo": "example/example-notes-connector",
"revision": "v0.1.0"
},
"runtime": {
"type": "process",
"command": "example-connector",
"args": ["start"],
"startPolicy": "manual",
"healthCheck": {
"type": "http",
"url": "http://127.0.0.1:18120/health",
"timeoutSecs": 2,
"expectStatus": 200
}
},
"permissions": [
{
"id": "filesystem.notes.read",
"title": "读取本机笔记",
"description": "读取用户选择的笔记目录。",
"platforms": ["macos", "windows", "linux"]
}
],
"management": {
"type": "http",
"baseUrl": "http://127.0.0.1:18120",
"auth": {
"type": "connector_token"
},
"operations": {
"runtimeState": {
"method": "GET",
"path": "/management/v1/state"
},
"chooseNotesDirectory": {
"method": "POST",
"path": "/management/v1/choose-directory"
}
}
},
"ui": {
"type": "embedded",
"entry": "ui/index.html",
"title": "笔记设置",
"defaultView": true
},
"configSchema": {
"type": "object",
"properties": {
"port": {
"type": "integer",
"default": 18120,
"minimum": 1024,
"maximum": 65535
}
},
"additionalProperties": false
},
"remoteCapabilities": [
{
"name": "notes.read",
"risk": "medium",
"description": "Read notes from the directory selected by the user."
}
],
"serviceRegistrationFiles": [
"service-registration.json"
]
}必须满足以下约束:
id、name、version不能为空,id只能使用安全的路径字符。第三方应用不要使用保留的com.baijimu.*命名空间。- 至少通过
services或serviceRegistrationFiles声明一个服务。 runtime.startPolicy只能是automatic或manual;权限声明和手动启动策略要求 schema 1.2。management.baseUrl必须是只包含 origin 的 loopback HTTP 地址,不能包含账号、路径、查询参数或片段。- management operation 只支持
GET、POST,路径必须位于/management/下。 - 内嵌 UI 必须放在独立目录中,入口必须是包内
.html文件,不能使用绝对路径或..逃逸目录。
注册方法和事件
service-registration.json 定义远端实际可见的服务。方法应使用明确的输入 JSON Schema,不要接收任意命令、任意文件路径或未约束的 URL。
{
"name": "localNotes",
"description": "Read notes stored on this computer.",
"transport": {
"type": "http",
"baseUrl": "http://127.0.0.1:18120"
},
"healthCheck": {
"type": "http",
"path": "/health",
"httpMethod": "GET",
"timeoutSecs": 2,
"expectStatus": 200
},
"startCommand": {
"type": "shell_command",
"command": ["example-connector", "start"],
"timeoutSecs": 20
},
"stopCommand": {
"type": "shell_command",
"command": ["example-connector", "stop"],
"timeoutSecs": 20
},
"methods": [
{
"name": "listNotes",
"description": "List notes visible to the configured local account.",
"path": "/invoke/listNotes",
"httpMethod": "POST",
"timeoutSecs": 10,
"input_schema": {
"type": "object",
"properties": {
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20
}
},
"additionalProperties": false
}
}
],
"events": [
{
"name": "noteChanged",
"description": "Emitted after a local note changes.",
"enabled": true,
"payload_schema": {
"type": "object",
"required": ["noteId", "occurredAt"],
"properties": {
"noteId": {
"type": "string"
},
"occurredAt": {
"type": "string",
"format": "date-time"
}
},
"additionalProperties": false
}
}
],
"replace": true,
"managed_by": "example-connector"
}Connector 进程必须只监听 127.0.0.1 或 ::1。healthCheck 通过后,Bridge Agent 才会把相应服务作为可用能力上报。启动命令运行时,客户端会提供:
BAIJIMU_CONNECTOR_DATA_DIR:应用私有数据目录,用于配置、状态和本机管理凭证。BAIJIMU_CONNECTOR_START_POLICY:清单中的automatic或manual。
不要把用户数据、密钥或可变状态写回 Connector 安装目录;同步和升级会替换安装副本。
本机管理接口和内嵌 UI
内嵌页面不能直接调用 Tauri 命令、Relay、文件系统或任意本机 HTTP 地址。页面只能调用 connector.json 中已经声明的 management operation:
const state = await window.baijimuLocalApp.invoke("runtimeState");
await window.baijimuLocalApp.invoke("chooseNotesDirectory", {
path: "/Users/me/Documents/Notes"
});Bridge Agent 会读取应用私有目录中的 management-token,并以 Authorization: Bearer ... 调用声明的 loopback management API。Connector 应在首次启动时生成至少 32 个字符的高熵 token,以仅当前用户可读的权限保存;页面代码和 management 响应都不能返回这个 token。
management API 用于本机配置、状态、授权和诊断,不能注册为 Relay 方法。所有来自页面的 payload 仍是不可信输入,Connector 必须进行 schema、路径和权限校验。
本地安装和调试
最终用户优先使用百积木客户端的“应用 > 安装应用 > 自定义安装”。完整安装步骤见安装本地应用。
开发期间可以从项目目录安装。CLI 安装会执行本机程序,确认已经审查源代码后再显式接受非市场来源:
bridge-agent connector install /absolute/path/to/example-connector \
--replace \
--accept-untrusted
bridge-agent connector show com.example.connector.notes
bridge-agent connector start com.example.connector.notes
bridge-agent connector list每次安装都会复制项目内容。修改源码后需要重新安装或在百积木客户端中执行同步,原目录变化不会直接修改已安装副本。
至少完成下面的验收:
connector.json和服务注册文件可以解析。- 安装后命令能从复制后的包目录启动,而不是依赖源码目录。
- 进程只监听 loopback,健康检查从不可用变为可用。
- 每个方法分别覆盖成功、非法参数、无权限、超时和依赖不可用。
- 停止后进程退出,健康检查变为不可用;再次启动不产生重复进程。
- 内嵌 UI 只能调用已声明的 management operation。
- Connector 重启或升级后,私有数据目录中的配置仍然有效。
- Bridge Agent 断线重连后,健康服务、方法和事件可以重新上报。
打包固定版本
正式版本使用语义版本号,例如 0.1.0。connector.json、Git 标签、压缩包文件名和市场版本必须一致。
发布包可以直接以 Connector 目录为根,也可以只包含一个顶层目录。包内必须只有一个可识别的 connector.json。不要打包:
.git、构建缓存、测试数据和日志。- 明文 token、cookie、用户配置或本地数据库。
- 只适用于开发机的绝对路径。
- 没有为目标平台构建的二进制。
生成压缩包后计算 SHA-256,并把包上传到不可变的 HTTPS 地址:
shasum -a 256 example-connector-0.1.0.zip同一个版本号和下载地址不得覆盖。修复后发布新版本,并重新计算校验值。
发布到本地应用市场
先查看当前 CLI 的实际能力:
baijimu --version
baijimu local-app --help
baijimu local-app version create --help
baijimu auth status --verify创建应用定义时,id 和 connectorId 都必须稳定且唯一;第三方 connectorId 不能使用 com.baijimu.*。平台只能是 macos、windows、linux,风险等级只能是 low、medium、high。
baijimu local-app create \
--id example-notes \
--connector-id com.example.connector.notes \
--name "Example Notes Connector" \
--description "Read notes stored on this computer." \
--publisher "Example" \
--risk-level medium \
--platform macos \
--platform windows应用定义与版本是两个资源。创建不可变版本时必须提交语义版本、HTTPS 制品地址、SHA-256 和 manifest;sourceType 只能是 https 或 archive:
baijimu local-app version create example-notes \
--version 0.1.0 \
--source-type archive \
--source https://downloads.example.com/example-connector-0.1.0.zip \
--repo example/example-notes-connector \
--revision v0.1.0 \
--checksum 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef \
--manifest @connector.json \
--capability notes.read示例 checksum 只是格式占位,发布时必须替换为实际压缩包的 SHA-256。创建版本后读取应用,确认服务端保存的版本、来源、校验值和 manifest 与发布物一致,再提交审核:
baijimu local-app get example-notes --json
baijimu local-app submit example-notes 0.1.0 --json
baijimu local-app publications --json提交后状态进入 PENDING_REVIEW。审核通过前不要把版本描述为已发布;只有审核通过并能从市场安装,才算完成交付。
发布后的完整验证
- 在一台没有源码目录和开发依赖的设备上,从市场安装目标版本。
- 核对下载来源、SHA-256、
connectorId和版本。 - 完成系统权限和应用内配置,启动 Connector。
- 确认健康检查、方法、事件和内嵌 UI 都可用。
- 从已授权工作区发起一次真实调用,并在 Bridge Agent 与 Connector 两侧核对日志。
- 安装前一版本,再执行同步或升级,确认配置迁移和回滚说明有效。
- 卸载后确认进程、服务注册和安装包已移除,用户明确保留的数据符合产品说明。
常见问题
提示没有声明服务
connector.json 必须包含非空 services,或通过 serviceRegistrationFiles 引用有效的服务注册文件。
安装后找不到启动命令
确认目标平台二进制位于 bin/<平台>-<架构>/,或已在 package.json 的 bin、pyproject.toml 的 [project.scripts] 中声明。还要确认压缩包里包含构建产物,并且命令具有执行权限。
服务启动了但平台看不到方法
依次检查健康检查、服务名和方法名、HTTP 路径、Bridge Agent 连接状态、设备与工作区授权。健康检查不通过的服务不会作为可用能力上报。
内嵌页面调用失败
确认 operation 名称同时存在于 management.operations 和页面的 window.baijimuLocalApp.invoke() 调用中,management 服务只监听 loopback,并且私有数据目录中存在权限安全的 management-token。
本地可用但安装包不可用
通常是启动命令依赖源码目录、开发机 PATH、未打包的动态库或绝对路径。必须在干净设备上从最终压缩包重新安装验证,不能只测试源码目录。