# @dp-bohrium/widget-sdk 中文文档

规范地址：https://npmcn.com/package/%40dp-bohrium/widget-sdk/

版本：0\.10\.0
npm 元数据更新时间：2026-11-08T06:43:47.009Z
中文译文更新时间：2026-11-12T23:09:53.276Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40dp-bohrium/widget-sdk
原文对照：https://npmcn.com/package/%40dp-bohrium/widget-sdk/?lang=original
项目主页：https://dev.dp.tech/bohrium-widget-sdk/

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.3 MB，198 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：0 个
- 首次发布：2026-09-18T17:52:26.806Z
- 最新版本发布：2026-11-08T06:43:46.858Z
- 近 30 天下载量：3342（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：18（近 12 个月 18 个）

## 中文简介

Bohrium Widget QQ SDK: 12. Widget Bundle QX主机 出道桥,XX。

## 安装

```sh
npm install @dp-bohrium/widget-sdk
```

也可以使用：`pnpm add @dp-bohrium/widget-sdk`、`yarn add @dp-bohrium/widget-sdk`、`bun add @dp-bohrium/widget-sdk`

## README

# @dp-bohrium/widget-sdk

Widget 开发者 SDK，也是创建、构建、校验、开发和打包 Widget 的统一安装入口。普通作者只实现 `activate(context)` 和可选的 `deactivate()`；SDK 自带的 `bohr-widget` 会生成 Widget Host 入口， 负责向 Host 宣告 Bundle、接收转移过来的 `MessagePort`，并暴露共享的 生命周期与能力协议。作者无需直接处理 `window.postMessage`，也无需手动启动插件。

使用 Agent 开发时，先读 [`llms.txt`](https://npmcn.com/package/%40dp-bohrium/widget-sdk/llms.txt) 获取文档索引，再读 [`AGENTS.md`](https://npmcn.com/package/%40dp-bohrium/widget-sdk/AGENTS.md) 了解安装、运行时调用和错误恢复规则。

激活事件目录、`context.events` 与低层 `topic` 的边界以本包的 `AGENTS.md`、本 README 和本包公开的类型与 Schema 为准；协议实现保持同一份事实源。

## 环境要求

| 项 | 要求 |
| --- | --- |
| Node.js | ≥ 20（执行 `bohr-widget` 与构建时；Widget 产物运行在浏览器 iframe 内） |
| 源码格式 | 纯 ESM，必须经打包器处理后交付 |
| 运行环境 | 必须由 Bohrium Host 加载，脱离 Host 无法完成握手 |
| 产物格式 | Host iframe 为 `sandbox="allow-scripts"` 且无 `allow-same-origin` 时，打成 classic script / IIFE（隔离源下 `type="module"` 需要 CORS） |

## 安装

安装 SDK 即可获得 Widget API、协议类型、JSON Schema、测试工具和 `bohr-widget` 命令。

```bash
npm install @dp-bohrium/widget-sdk
pnpm add @dp-bohrium/widget-sdk
yarn add @dp-bohrium/widget-sdk
```

新建工程只需调用 SDK 自带的命令，生成结果也只直接声明精确 SDK 版本：

```bash
pnpm dlx @dp-bohrium/widget-sdk create my-widget --template panel
cd my-widget
pnpm install
pnpm exec bohr-widget build .
pnpm exec bohr-widget check .
```

常用作者类型从本包根入口导入；完整协议从 `@dp-bohrium/widget-sdk/contracts` 导入， 编辑器 Schema 使用 `@dp-bohrium/widget-sdk/schemas/*.json`，协议测试语料使用 `@dp-bohrium/widget-sdk/conformance/*`。升级已有工程时，参照 [版本兼容](https://dev.dp.tech/bohrium-widget-sdk/reference/versioning)。

在自己的 `package.json` 里钉死精确 SemVer，不要用 `^` 或裸 `latest`。SDK 运行时会从已安装 包自身读取版本，插件源码和 Manifest 不需要再次填写 SDK 版本。

## 校验工程

使用 SDK 自带的命令检查 Widget 工程：

```bash
pnpm exec bohr-widget build .
pnpm exec bohr-widget check .
pnpm exec bohr-widget pack . --out widget.zip
```

命令复用同一份协议 Schema 校验 Manifest 与样例，并检查入口、资源和打包要求。 校验实现供内部工具使用，不提供独立公开子入口，普通 SDK 导入不加载校验器或 AJV。 校验通过只表示本地工程符合检查要求，Host 仍负责实际权限、网络策略与运行隔离。

## 快速开始

```ts
import type { WidgetContext } from "@dp-bohrium/widget-sdk";

export function activate(context: WidgetContext) {
  context.render((request, renderContext) => {
    renderContribution(request.contribution, request.content, renderContext.phase);
  });
  context.events.on("tool-call-result", () => {
    void context.ui.present("floating");
  });
}
```

CLI 生成的 `src/widget-host.ts` 会调用平台入口 `runWidgetHost(widget)`。 这借鉴 VS Code 的激活入口。最终模型按 Binding 建立一个 Extension Runtime，`activate(context)` 在 Runtime 内只执行一次；工具、文件或面板目标是挂载到 Runtime 的 Surface Instance，拥有 独立的快照和释放句柄，不会再次触发 `activate`。开发者实现 `activate(context)`，可选实现 `deactivate()`；Widget Host/SDK 负责加载、等待初始化、报告启动失败和执行清理。普通插件 代码不需要调用 `ready()`，也不应该写 `plugin.ready().catch(() => {})`。`whenReady` 只保留 给测试和高级宿主编排；`whenDisposed` 可用于等待已完成激活插件的异步 `deactivate()`。 停用不会等待一个永不结束的 `activate()`；若激活在停用后迟到完成，SDK 会在已 abort 的生命周期上补偿释放并调用一次 `deactivate()`，但这不再延长 `whenDisposed`。

这是 SDK 的目标生命周期契约；当前 Widget Host 的动态加载仍由 Surface-bound Adapter 接线， Binding 级 Runtime Loader 尚未开放。插件可以按 Runtime/Surface 分层设计，但不能把当前版本 当成“无界面必然已启动”的生产保证。

`context.signal` 会在 Host 停用插件或 Bridge 断开时 `abort`。`context.render` 和 `context.events` 会自动登记到当前插件生命周期；`subscriptions` 只用于 第三方计时器、观察器或其它需要在停用时释放的资源。

三条时序约定：

-   `ready()` 只在 `activate(context)` 返回（含其 Promise settle）之后才发给 Host， `handshakeTimeoutMs` 同时约束这一步；
-   初始化失败时，SDK 先报结构化错误，再调用已登记的插件资源清理；成功激活后的释放会 最多调用一次 `deactivate()`，已完成激活的异步清理可通过 `whenDisposed` 等待；
-   `context.render` 首帧和更新都会收到完整快照，通过 `renderContext.phase` 区分。

### 渲染失败与本地诊断

`runWidgetHost` 会隔离 `context.render` 处理器的同步异常和异步拒绝，并在 Guest console 记录原始异常。已有 `onError` 和 `events.onError` 也会收到本地结构化诊断：首帧使用 `RENDER_FAILED/render`，更新使用 `UPDATE_FAILED/update`，`correlationId` 对应失败快照的 `requestId`，`retryable` 为 `false`。结构化消息不包含渲染负载或原始异常。

这类通知本身不会让 SDK 终止 Bridge、销毁界面或自动重试；其他处理器和后续快照仍可继续， 插件应保留草稿并提供可恢复的提示。观察者显式调用 `dispose()` 时，SDK 仍尊重停用意图。 SDK 不回滚处理器失败前已执行的 DOM 修改；界面和草稿的恢复仍由插件拥有。 观察者自身的同步异常、异步拒绝和 console 失败不会阻断渲染，也不会递归触发 `onError`； 停用后迟到的渲染拒绝会被消费，但不再产生本地诊断。

这些是 Guest 本地出口，SDK 不通过 Bridge 把它们发送给 Host。 旧 Host 会把 Widget 的协议 `error` 当作致命失败， 不能用它承载可恢复的渲染诊断；Host 的 `ready` 或 `render-sent` 也不能证明 Guest DOM 渲染成功。

## 从 Manifest 到插件运行

Manifest 是插件的静态声明，SDK 代码是隔离 iframe 内的运行时实现，Host 负责二者之间的生命周期编排。一个插件可以这样声明：

```json
{
  "$schema": "@dp-bohrium/widget-sdk/schemas/widget-plugin-manifest.schema.json",
  "name": "research",
  "version": "1.0.0",
  "entry": "src/widget.ts",
  "displayName": "研究面板",
  "description": "展示研究工具的结构化结果",
  "activationEvents": ["session.start", "tool-call-result:ScholarSearch", "ui-tool-opened"],
  "contributes": {
    "widget": { "type": "panel", "id": "research", "surfaces": ["workspace", "floating"] }
  },
  "capabilities": {
    "files": { "read": true },
    "events": { "topics": ["tool-call-result", "workspace-file-opened"] }
  }
}
```

需要接管完整的会话右侧区域时，在同一个 Panel contribution 上声明结构性放置意图，并同时申请匹配的 `ui.region` capability：

```json
{
  "contributes": {
    "widget": {
      "type": "panel",
      "id": "better-sidebar",
      "placement": { "kind": "region", "region": "session.rightRail", "mode": "replace" }
    }
  },
  "capabilities": {
    "region": { "regions": ["session.rightRail"], "mode": "replace" }
  }
}
```

`placement` 只表达贡献点的结构性意图；区域租约、确认、几何、焦点、原生面板恢复和失败回退仍由 Host 负责。没有匹配 capability 时，SDK 编译阶段直接拒绝清单。

`activationEvents` 只决定 Host 何时创建插件运行时；它不授予事件读取权限，也不负责打开思考过程或某个面板。插件激活后通过 `context.events` 和 `context.render` 注册监听与渲染器。Host 会对每个事件和能力请求做白名单投影与授权，插件收到的事件可能是有限回放，也可能是实时事件。

实现建议：事件处理器保持幂等，按 `toolCallId` 或 `requestId` 去重；不要从 iframe URL、DOM 或宿主绝对路径推断内部数据；当能力未授予时显示结构化空态并继续保留可渲染的主界面。声明但没有实际使用的能力会被 Host 记录为诊断信息，未声明能力的调用会被拒绝并结束当前 Bridge 会话。

### 统一插件入口

所有公开插件类型都使用同一个 `activate(context)` 约定。面板和文件预览由 Manifest 的 `contributes.widget` 决定；运行时代码只认识统一的 `context.events`、 `context.render`、`context.capabilities` 和 `context.ui`。首帧和后续快照通过 `renderContext.phase` 区分，不需要维护两套回调。

Manifest 的编辑器提示来自本包 `/schemas` 的 JSON Schema 和公开 TypeScript 类型， 不依赖某个 `defineManifest` 工厂。构建工具调用 `compileWidgetPluginManifest`，自动补齐 `schemaVersion`、`hostApiVersion`、SDK 版本和 宿主加载字段；它会直接读取 Manifest 内的 `samples`，只有构建器从别处生成样例时 才需要传入第二个参数。`createWidgetClient` 是协议级基础实现，不属于 Widget 作者 API；当前根入口只提供统一 Widget API，协议级代码应从 `./runtime` 显式引入。

受信任的内部 Host/协议测试仍可直接构造低层 Tool Result RenderRequest，但该能力不属于外部作者清单或公共示例。

SDK 发布包会生成 `dist/index.d.ts` 与各子入口声明，并通过 `package.json` 的 `types`/`exports` 暴露。插件安装 `@dp-bohrium/widget-sdk` 后，编辑器会直接获得 `WidgetContext`、事件名、能力请求、渲染请求和生命周期返回值的类型提示；Manifest 编辑器提示由本包的 `/schemas` 和声明文件提供，Schema 在构建时从协议唯一源生成。

## 包入口

| 入口 | 内容 | 用途 |
| --- | --- | --- |
| `.` | `runWidgetHost`、`compileWidgetPluginManifest` 和常用作者类型 | 生成的生产 Bundle |
| `./contracts` | 完整协议类型、事件目录与判定函数 | 高级协议集成；同名协议类型显式从此处获取 |
| `./conformance/*` | 同源协议校验语料 | Host/控制面验证 |
| `./schemas/*.json` | 作者清单、宿主清单、运行配置与消息 Schema | 编辑器提示和工具校验 |
| `./testing` | `MemoryTransport`、`MockWidgetHost` | 单元测试 |
| `./runtime` | 低层 `WidgetClient`、transport 与 bootstrap 常量 | 协议级集成 |
| `./package.json` | 已安装包的元数据 | SDK 和构建工具自动读取版本，作者无需复制版本字符串 |
| `bohr-widget` | Node.js 命令入口 | 创建、构建、校验、开发和打包工程；不进入浏览器 Bundle |

根入口只导出插件运行时和 Manifest 编译所需的少量值，其余均为类型。低层 `MessagePortTransport` 与 bootstrap 消息不属于普通 Widget API。

`runWidgetHost` 是生成的 `widget-host.ts` 使用的 Widget Host 启动边界； 插件作者只维护 `widget.ts` 中的 `activate/deactivate`。

## 能力

公开能力要在 Manifest 的 `capabilities` 里声明，并由 Host 实际 grant； `service.invoke` 需要由 Host binding 显式授权。缺任一侧，调用在 Widget 本地即被拒绝。 所有能力调用都必须在 `ready()` 之后。

下面列出的能力是当前 SDK 支持的能力。grant 名与 Manifest 申请键不一定同名 —— `ui.presentation` 对应的作者侧申请键是 `capabilities.presentation`（编译后的 宿主格式字段才叫 `requestedCapabilities.presentation`）。

| grant 名 | Manifest 申请键 | SDK 调用 |
| --- | --- | --- |
| `files.list` / `files.metadata` | `files` | `context.capabilities.request({ operation, input })` |
| `files.read` | `files.read` | `context.files.read({ file, offset, length, encoding, expectedRevision? })` |
| `files.write` | `files.write` | `context.files.write({ file, content, encoding, expectedRevision, mutationId })` |
| `files.download` | `files.download` | `context.files.download({ file, suggestedName })` |
| `events.read` | `events`（需列出 `topics`） | 低层分页订阅；`context.events.on(…)` 是 Host 主动推送，不需要该 grant |
| `ui.state` | `state`（`read` / `write`） | `context.ui.state.read(key)` / `write(key, value)` |
| `actions.invoke` | `actions` | `context.capabilities.request({ operation: "actions.invoke", input })` |
| `network.fetch` | `network` | `context.network.fetch(…)` |
| `service.invoke` | 由 Host binding 显式授权 | `context.services.invoke({ service, action, payload })` |
| `ui.presentation` | `presentation` | `context.ui.present(…)` / `changeSurface` / `close` |
| `ui.region` | `region`（固定申请 `session.rightRail`） | `context.ui.region.acquire()` / `release()` |

除 Host 主动推送事件外，其余能力共用同一套调用范式，掌握下面这一个就会调其余能力：

```ts
const controller = new AbortController();
const result = await context.network.fetch(
  { url: "https://api.example.org/papers", method: "GET", auth: "optional" },
  { signal: controller.signal, timeoutMs: 15_000 },
);

if (result.ok) {
  consume(result.data.status, result.data.headers, result.data.body);
} else {
  // 策略拒绝、用户拒绝、未授权都走这里，不是 throw。
  showNetworkFailure(result.reason, result.retryable);
}
```

跨能力约定：

-   Host 拒绝以 `{ ok: false, reason, message, retryable }` 正常兑现；本地状态、输入、客户端超时、 中止、dispose 和畸形响应可 reject。`files.write` 发出后的结果不确定时兑现为 `write-outcome-unknown`，处理方式见下文。
-   中止与超时统一用 `{ signal, timeoutMs }`，复用既有 MessageChannel 能力生命周期。
-   Widget 永远拿不到 Host 凭据，也不应直接调用浏览器 `fetch`：是否弹窗、记住 Origin 授权、跟随重定向还是拒绝，全部由 Host 决定。
-   `context.services` 仅在 Host binding 授予 `service.invoke` 后可用。可调用的服务、操作、请求/响应 大小和有效期由 Host 的授权策略决定；未获授权时调用会被拒绝。认证状态由宿主侧管理，SDK 只收发 业务 payload，不会把 Cookie、Bearer Token 或其他凭据放入 iframe。
-   `context.files.download` 只提交文件引用和建议文件名；Host 在用户确认后经宿主 Download Adapter 惰性打开内容源。Widget 不会收到源 URL、保存路径或凭据。
-   `context.ui.state` 只保存按 Widget + Session 隔离的 JSON UI 状态；Host 按 key、单值大小、总字节数和调用 频率限流。它不能保存任务执行、凭据、文件内容或 Workspace 权限状态。
-   外部插件的工具结果场景通过 `tool-call-result` 事件作为可选运行时信号；内部 Host/协议测试仍可使用低层 Tool Result RenderRequest 或展示槽位。插件应把结果转换成自己的 Panel 数据，再通过 `context.render` 更新面板。
-   呈现只是**请求**。Host 可能降级到别的表面，成功结果里的 `surface` 才是真实表面； Widget 不能指定目标 Panel、身份、运行时 generation、坐标或层级。
-   高层插件 API 使用 `context.events.on(handler)` 接收 Host 从当前 Runtime **主动推送**的 聚合事件，只按 `type` 分发，不依赖 Manifest Topic 或 `events.read` grant。需要直接使用有界 分页读取时，从 `@dp-bohrium/widget-sdk/runtime` 使用 `subscribeEvents` / `onTool`。
-   低层 `subscribeEvents` / `onTool` 的所有订阅共享同一实例的 events 读预算，并各自遵守 topic 最小轮询间隔 —— 订阅越多，单个 topic 刷新越慢。游标只在 `onBatch` 成功后 提交，重新订阅需显式传回 `events.cursor`。工具 Hook 观察的是**已经发生的**服务端 事实，不是执行前拦截器。

### 读取和保存当前文件

作者 Manifest 申请 `"capabilities": { "files": { "read": true, "write": true } }`，Host 仍需独立 授权两个能力。`files.write` 对 Host 签发引用指向的已有文件作整文件条件替换：在 `file-preview` 中引用来自 `request.content.file`；在获得写 grant 的 Panel 中引用来自 `data.root` 或 `files.list` 的结果。不能传路径、URL、自造的文件 id 或指定另存位置。Host 校验引用、作用域、 写权限和实际字节预算，成功回执表示执行端已确认持久化。SDK 不负责 ASE、晶格或文件格式转换， 插件应使用自己的序列化器生成最终字节。

`FileReadInput` / `FileReadResult`、`FileWriteInput` / `FileWriteResult`、`FileRevision`、 `FileMutationId` 与两个 `*CapabilityResult` 类型均可从 SDK 根入口导入。`revision` 是文件内容的 不透明版本，与渲染快照的 `RenderRequest.revision` 无关。下面的 `file` 来自当前文件预览：

```ts
const loaded = await context.files.read({
  file, offset: 0, length: 64_000, encoding: "utf8",
  ...(file.revision ? { expectedRevision: file.revision } : {}),
});
if (!loaded.ok) return showReadFailure(loaded.reason);
if (!loaded.data.revision) return showReadOnlyFile(loaded.data.content);
if (loaded.data.eof === false || loaded.data.truncated === true) {
  return showFileNeedsChunkedReading();
}

const expectedRevision = loaded.data.revision;
const content = serializeEditedModel(loaded.data.content);
// 一次逻辑保存只生成一次；核对或重试同一内容时复用。
const mutationId = crypto.randomUUID();
const saved = await context.files.write({
  file, content, encoding: "utf8", expectedRevision, mutationId,
}, { signal: saveController.signal, timeoutMs: 15_000 });
if (saved.ok) {
  markSaved(saved.data.file, saved.data.revision);
} else {
  keepUnsavedChanges(saved.reason, mutationId);
}
```

`read` 兼容旧 Host 的 `{ content, encoding }` 结果；`offset`、`bytesRead`、`nextOffset`、`eof`、 `truncated` 和 `revision` 均为可选扩展。SDK 原样返回结果，只验证已提供字段，不补造游标或版本。 没有 `revision` 时内容可展示，但不能据此保存；带 `expectedRevision` 的读取必须返回有效且匹配的 版本，不能降级为无版本读取。Host 必须从同次读取的内容快照签发版本，不能另用 metadata/stat 补取。

有游标的 `read` 按原始字节计数：使用 `nextOffset` 继续；若首块带版本，后续块携带对应 `expectedRevision`，防止混合版本。`utf8` 字节区间可能切断多字节字符，不能用重新编码后的文本长度推断 `bytesRead`。 二进制文件或需完整解码的分块文本使用 `base64`，先将每块解码为字节并拼接，再解码文本。 `base64` 使用标准、带正确 padding 的编码；空文件和空字符串替换均合法。 上面的整文件保存示例以 Host 返回完整内容为前提；缺少完整性标记时，插件需依据 Host 的读取约定 确认内容完整，不能把一个未完成的分块直接替换为整个文件。

`write` 必填读取时的 `expectedRevision` 和一次逻辑保存的 `mutationId`（1–128 个字符，只含 字母、数字、`_ . : -` 且以字母或数字开头）。`revision-conflict` 表示当前文件已变化，应重新读取 并由用户合并；相同 mutationId 携带不同内容会返回 `mutation-conflict`。保存成功后更新本地文件引用 和基础版本；未修改内容的保存可以返回相同版本。 游标元信息不是保存的前提；只有读取内容所绑定的有效版本才能用作 `expectedRevision`。

SDK 不自动重试保存。调用前已中止、客户端未就绪或输入非法会 reject，且不发送请求；缺少 grant 返回 `permission-denied`。发出后超时、中止、断线或畸形回执都不能证明未写入，返回 `{ ok: false, reason: "write-outcome-unknown", retryable: false }`。此时保留未保存内容、原基础版本 和 mutationId，通过 Host 核对文件版本或用**同一 mutationId、同一内容**确认结果；不要直接生成新 id 重试，也不要提前标记已保存。新的编辑内容属于新的逻辑保存，应使用新的 mutationId。

## 测试

```ts
import { MockWidgetHost } from "@dp-bohrium/widget-sdk/testing";
```

`MockWidgetHost` 负责 init/ready 时序、首帧 render 与 update 选择、resize 事件、 能力 adapter 响应与终态 dispose；`capabilityResult` 给出确定性的默认允许/拒绝， 同时提供 `handleCapability` 时后者优先。`host.capabilityRequests` 返回有序的防御性 拷贝，断言不会改动 Mock 状态。

它使用与生产 Host 相同的公开 contracts，但**不等同于**生产 iframe、CSP、授权或资源 隔离保证。

## 限制

-   SDK 不做授权决策，能力是否可用最终由 Host 判定。
-   `invokeAction` 不自动重试，回调完成也不代表工具执行完成。
-   `subscribeEvents` 的 `events.read` 是有界分页读取，不是 websocket；实时推送走 `onEvent`。当前版本没有单请求取消的独立协议消息：`unsubscribe` 会停止后续轮询并取消 本地等待，已发出的服务端读取可能完成，但结果被忽略。
-   Host 发来畸形 Bridge 信封或载荷时，transport 报结构化 `MESSAGE_INVALID` 并关闭 连接，不会让 Widget 悬等。

## 许可证

MIT。

## 常见问题

### @dp-bohrium/widget-sdk 是什么？

Bohrium Widget QQ SDK: 12\. Widget Bundle QX主机 出道桥,XX。

### 如何安装 @dp-bohrium/widget-sdk？

在项目目录运行 npm install @dp-bohrium/widget-sdk，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 bohr-widget，可以用 npx bohr-widget 直接运行。

### @dp-bohrium/widget-sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @dp-bohrium/widget-sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @dp-bohrium/widget-sdk 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @dp-bohrium/widget-sdk 可以商用吗？

@dp-bohrium/widget-sdk 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @dp-bohrium/widget-sdk 的下载量有多少？

根据 npm 官方下载统计，@dp-bohrium/widget-sdk 在 2026/10/13 – 2026/11/11 这 30 天内被下载 3,342 次，日均约 111 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @dp-bohrium/widget-sdk 还在维护吗？

最新版本 0\.10\.0 发布于 2026/11/08（5 天前）；至今共发布 18 个版本，其中过去 12 个月发布了 18 个；首次发布于 2026/09/19。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

