# Bundle 生命周期插件

明确插件注册、Bundle 资源授权和 Runtime 执行责任，按新的逻辑引用协议管理外部业务状态。

Bundle 插件为一次完整安装开通、更新或解除外部业务实例。
请求、响应和按需访问解析以[Bundle 生命周期与引用协议](/development/lifecycle-plugin-development/reference-protocol/)为准。

## 工作区可见性与管理权限

插件无需安装。登记后通过稳定的 `pluginId` 直接引用；插件目录与 Bundle 安装状态分别管理。

所有插件归属创建时选择的工作区，没有“内置插件”或全局可见例外。列表和详情只对所属工作区成员开放。
其他工作区即使知道 `pluginId`，也不能读取该插件的登记、执行地址或默认配置。

- 创建、更新、删除及双向凭据管理需要所属工作区的管理权限。创建者记录用于追溯，管理员可协作维护。
- CLI 的 `--workspace-id` 同时选择凭据和插件管理工作区；省略时使用当前工作区。
- 公共 API 使用 `workspaceId` 选择工作区；省略时仅允许从已验证用户凭据的工作区约束取得，不能全局查询。
- 工作区选择不能超出当前账号的授权范围；离开工作区或管理权限被撤销后，不能继续访问相应接口。
- 百积木维护的插件同样属于百积木工作区，遵循相同的成员与管理权限。
- 内网或公网执行由独立的连接与认证配置决定，不由工作区名称、插件名称或创建者决定。环境绑定的内网地址只能通过相应连接配置变更。
- 列表和详情保留历史协议登记，不以当前能否执行过滤。待退役插件的存量引用须核实和迁移，不能直接删除。

工作区可见性管理的是插件登记。Runtime 仍按已有 Bundle 生命周期合同调用插件，不增加插件安装记录。

## 各方职责

| 责任方         | 拥有的事实                    |
| ----------- | ------------------------ |
| 插件目录        | 稳定 pluginId、协议、执行地址和调用身份 |
| Bundle 作者   | 插件绑定、执行顺序、非敏感配置和资源属性授权   |
| Runtime 安装器 | 安装实例、当前版本、操作身份、阶段执行与失败处理 |
| 插件服务        | 外部业务实例及其与平台安装身份的关联       |

Bundle 安装用户不需要再次注册插件。插件只返回授权业务输出，不能直接写安装账。
同一安装的重试应复用外部实例；不同安装的业务状态和凭据必须隔离。

## 注册与绑定

插件目录的 `bundleExecution` 声明 `protocolVersion: "6.0.0"`、HTTPS `executeUrl`，
以及 `supportedStages: ["BEFORE_RESOURCES", "AFTER_RESOURCES"]`。
执行时使用统一的 Bundle 生命周期请求，Module 的业务属性通过资源授权投影。
插件绑定和生命周期只属于 Bundle；Module 不再单独绑定或触发插件。
每个资源独立声明允许读取、写入和解析引用的属性，不要求不同资源使用相同字段。
Module 的 `propertyDefinitions` 定义属性名称与类型，实际值保存在资源属性中；
Bundle 的资源授权仅决定插件能访问其中哪些字段，不复制另一份属性定义或属性值。

Bundle 绑定声明 `pluginId`、`protocolVersion`、`order`、`failurePolicy`、`configuration` 和 `resources`。
作者清单中的资源授权使用 Owner 返回的领域对象 `object`，并指定
`readProperties`、`writeProperties`、`credentialProperties`。Runtime 编译后才生成执行请求中的 ResourceLocator。
后者只允许插件对当前属性执行访问解析，不把平台密钥放入 Bundle。
Endpoint、平台 Token 和业务密钥不得复制到 Bundle 的插件配置中。

先确认 `baijimu lifecycle-plugin update --help` 包含 `--bundle-execution`，
`baijimu bundle manifest --help` 包含 `lifecycle-plugin`；旧客户端应先升级。
以下地址只是结构示例，实际地址从插件提供方的服务登记取得。

将 Bundle 执行端声明保存为 `bundle-execution.json`：

```json
{
  "protocolVersion": "6.0.0",
  "executeUrl": "https://plugin.example/plugin/bundle/lifecycle",
  "supportedStages": ["BEFORE_RESOURCES", "AFTER_RESOURCES"]
}
```

在[插件注册与管理](/development/lifecycle-plugin-development/registration-management/)完成插件记录创建后登记并回读：

```bash
baijimu lifecycle-plugin update "$PLUGIN_ID" --bundle-execution @bundle-execution.json
baijimu lifecycle-plugin get "$PLUGIN_ID"
```

Bundle 作者清单使用 `schemaVersion: "2.0.0"`，插件绑定放在 `definition.lifecyclePlugins`。
`definition` 不带内部安装内容的版本号。先通过[对象选择命令](/development/bundle-development/manifest/)
纳入 Module，再将下面结构保存为 `plugin-binding.json`，使用目录返回的 Module 身份和已声明属性：

```json
{
  "pluginId": "tenant-provider",
  "protocolVersion": "6.0.0",
  "order": 0,
  "failurePolicy": "FAIL_FAST",
  "configuration": {"schemaVersion": "1.0.0", "values": {}},
  "resources": [{
    "object": {"type": "MODULE", "moduleId": "a054ef60-f64c-47cf-8b63-4dbdd05e5ef1"},
    "readProperties": ["region"],
    "writeProperties": ["tenantId"],
    "credentialProperties": ["inventory"]
  }]
}
```

示例 Module 必须已在同一清单的 `definition.modules` 中纳入，`inventory` 是需要按需解析的引用属性。
`configuration.schemaVersion` 由插件定义，不随平台生命周期协议强制变化。

```bash
baijimu bundle manifest lifecycle-plugin add @baijimu.bundle.json \
  --binding @plugin-binding.json > baijimu.bundle.next.json
baijimu bundle manifest validate @baijimu.bundle.next.json
baijimu bundle manifest lifecycle-plugin list @baijimu.bundle.next.json
```

增删命令输出完整清单，核对后再用它更新项目源码并走正常 Bundle 版本创建。
CLI 使用 Bundle owner 的类型校验重复插件、缺失资源和引用领取/回写冲突；调整其他资源也会保留插件绑定。
发布端进一步校验资源确实归属该 Bundle。发布后回读不可变版本内容并完成实际安装验收。

移除绑定使用 `baijimu bundle manifest lifecycle-plugin remove @baijimu.bundle.json --plugin-id "$PLUGIN_ID"`，
同样需要创建后继 Bundle 版本再升级安装，不会直接改变已有安装。

## 安装与恢复

Runtime 在 `BEFORE_RESOURCES`、`AFTER_RESOURCES` 阶段按操作类型调用插件。插件必须验证安装、操作、阶段和资源授权，并安全处理同一操作的重复调用。

结果不确定时返回可准确表达事实的结果，由 Runtime 检查后决定后续动作。
不能把重试伪装成新安装，也不能把外部调用超时当成外部操作已撤销。
`DETACH` 按请求和业务所有权处理，不能删除其他安装共享或用户自有的资源。

## 验收

1. 安装、升级、配置变更和卸载均正确关联同一业务实例。
2. 同一次操作重放不会重复创建外部租户，两个安装互不覆盖。
3. 生命周期引用不含地址或密钥，输出只写入授权资源属性。
4. 业务调用前从当前绑定解析 access，重绑与授权撤销立即影响下一次解析。
5. Map 业务键和 List 顺序按当前引用保留，同目标访问材料去重。
6. 请求、响应的身份不匹配以及未授权属性均被拒绝。
7. 插件故障或未知结果不能被标记为安装成功；失败和重试保留可审计结果。

## 外部实例标识声明

作者协议 2.0.0 使用 Owner 目录返回的领域对象选择；下面的 `object.moduleId` 由工具写入，不拼装通用资源引用。

`instanceRefProperty` 要求编译后的 Bundle 内容 `schemaVersion: "12.1.0"`，并要求安装环境已升级至支持该声明的 Runtime。历史 `12.0.0` 内容仍按原版本读取，不能在 `12.0.0` 中加入这一声明。

Rust 插件 SDK `baijimu-bundle-plugin 3.0.0` 沿用 2.1.0 引入的 `BundlePluginBindingV2` 和
`BundlePluginResourceGrantV2` 支持该声明。原有类型与方法继续保持兼容；新类型可从原有声明转换或从 JSON
反序列化，再显式设置 `instance_ref_property`。使用 `validate_bundle_plugin_bindings_v2` 检查写入者唯一性，
使用 V2 绑定上的 `validate_lifecycle_request`、`validate_lifecycle_response` 和 `validate_reference_request`
检查生命周期及凭据请求权限。实例值不可变和安装归属仍分别由 Runtime 与外部组件校验。

有持久外部实例的插件，在对应资源授权中声明 `instanceRefProperty`，其值是该 Module 定义中的属性名。
该声明可省略；平台不会按 `tenantId`、`accountId` 等名称猜测哪个属性是实例标识。
属性名仍须符合插件已发布的业务输入、输出合同；该声明不提供业务字段重命名或映射。

```json
{
  "object": {"type": "MODULE", "moduleId": "owner-returned-module-id"},
  "readProperties": ["tenantName", "tenantId"],
  "writeProperties": ["tenantId"],
  "credentialProperties": [],
  "instanceRefProperty": "tenantId"
}
```

声明的属性必须是无预置实例值的字符串 Data 属性，同时授权当前插件读取和写入，且不能是凭据引用。
同一资源的该属性只能有一个插件写入者。不同插件管理不同外部实例时，可以声明不同属性。

首次创建时属性可以缺失或为 null。插件应根据完整安装身份创建或查找外部实例，并在成功响应中返回
非空实例标识。标识不包含首尾空白；失败结果不应伪造实例标识。后续操作可以省略输出以保留原值，
但不能返回 null、空字符串或另一个实例标识。

普通属性更新不能初始化、更换或删除此属性；普通 Bundle 升级不能移除、改名或转移仍受管理的实例标识声明。
重新绑定不属于普通配置更新；在正式重新绑定合同提供前，应拒绝此类请求。

移除资源前，插件只接收必要的实例标识，不接收整份业务配置或引用凭据。资源移除后的验证阶段可能已没有
属性，插件必须仍能根据完整安装身份检查解绑结果，不能要求 Runtime 另存一份实例标识。

实例标识由外部组件拥有；Runtime 的属性是其稳定引用。平台安装身份仍由安装环境、Runtime、Bundle 安装、
资源定位和插件身份共同限定。插件必须验证实例归属，不能仅凭一个合法格式的实例 ID 操作资源。
平台引用的访问凭据继续通过独立引用解析协议取得，实例标识不授予访问权限。
插件自有的业务 Token 可按[敏感业务属性输出](/development/lifecycle-plugin-development/develop-plugin-service/#插件自有业务凭据)
交给 Runtime，两类凭据的所有权和用途不同。
