# 内置插件安装与开发

> 将构建后的扩展安装到 Provision，检查 Service Worker，添加工具栏弹出窗口，并更新或移除插件。

Source: https://console.getchrovia.com/zh/docs/sdk-plugins

SDK 内置插件是从 `<Provision 根目录>/Extensions/<插件名>` 加载的 Chrome 扩展。内置插件会自动安装；使用 `--show-component-extension-options` 可在 `chrome://extensions` 中显示它们。安装插件不会授予它需要的 License 能力。

## 安装已有插件

1. 完全退出该浏览器安装目录对应的运行实例。
2. 获取插件的**构建后扩展文件**，包括 Chrome `manifest.json`、JavaScript 和引用资源。先解压，不要直接把 `.zip` 或 `.crx` 当作插件目录复制。
3. 将目录复制到实际 [Provision 根目录](https://console.getchrovia.com/zh/docs/sdk-provision)的 `Extensions` 下。
4. 确认 `Extensions/my-plugin/manifest.json` 直接存在，避免误套一层变成 `my-plugin/dist/manifest.json`。
5. 保持提供的目录名，安装所需的 [License 能力](https://console.getchrovia.com/zh/docs/sdk-capabilities)和 EP 配置，再重新启动。

```text
Extensions/
  chrovia-infra/
    manifest.json
    background.js
  my-plugin/
    manifest.json
    background.js
    popup.html
    popup.js
```

保留 `chrovia-infra` 以完成 License 在线续租。其他附带插件以发行包为准，使用前检查 `Extensions` 目录及对应插件文档。

不要用**加载已解压的扩展程序**安装 License。以普通方式加载的开发扩展也不会自动获得 Provision Service Worker 身份。本教程使用 Provision 目录安装。

## 带弹出窗口的最小插件

此例需要 `extendedPrefs`。弹出窗口只向自己的 Service Worker 请求一个明确操作，不直接读取特权 API。

`manifest.json`：

```json
{
  "manifest_version": 3,
  "name": "My SDK Plugin",
  "version": "1.0.0",
  "background": { "service_worker": "background.js" },
  "action": { "default_popup": "popup.html" },
  "toolbar_pin": "default_pinned"
}
```

`toolbar_pin: "default_pinned"` 会在每次加载 Provision 插件时固定工具栏图标；手动取消固定后，重启时也会再次固定。仅为实际使用的 Chrome API 和外部域名添加标准权限或 `host_permissions`。

`background.js`：

```js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (sender.id !== chrome.runtime.id || message?.type !== 'read-label') {
    return;
  }
  const prefs = globalThis.chrovia?.prefs;
  if (!prefs) {
    sendResponse({ error: 'extendedPrefs unavailable' });
    return;
  }
  prefs.get('internal.instance_appearance.label').then(
    label => sendResponse({ label: typeof label === 'string' ? label : '' }),
    () => sendResponse({ error: 'Unable to read label' }),
  );
  return true;
});
```

`popup.html`：

```html
<!doctype html>
<html>
  <body>
    <p id="label">Loading...</p>
    <script src="popup.js"></script>
  </body>
</html>
```

`popup.js`：

```js
chrome.runtime.sendMessage({ type: 'read-label' }).then(
  result => {
    document.getElementById('label').textContent = result.error || result.label || 'No label';
  },
  () => {
    document.getElementById('label').textContent = 'Service Worker unavailable';
  },
);
```

按[实例外观](https://console.getchrovia.com/zh/docs/sdk-instance-appearance)配置标签。读取字段需要 `extendedPrefs`，显示浏览器徽标还需要 `instanceAppearance`。渲染文本使用 `textContent`，不要向任意消息开放通用特权方法分发器。

## 验证安装

使用 `--show-component-extension-options` 启动，打开 `chrome://extensions`，启用开发者模式，检查插件的 **Service Worker**。在其中运行 `globalThis.chrovia`。根对象缺失通常是上下文或安装位置错误；某个 API 域缺失则应检查其能力。

弹出窗口和扩展标签页默认没有 `chrovia`。不要只是为了修复 popup 就开启 `unrestrictedApi`，应使用上面的消息模式。调试实例不要启用 `disable_devtools`。

## 更新与移除

更新时保持目录名稳定，在浏览器关闭后替换构建文件，为发布追踪递增扩展清单版本，然后重新启动。只改源码或 Provision 根清单不会更新已安装脚本。核对实际启动的可执行文件，特别是桌面客户端可能解压了自己的浏览器副本。

移除时先退出浏览器，删除 `Extensions` 下对应的直接子目录，再重新启动。重启后确认插件不再出现在列表中。移除不保证清除全部扩展设置或产品数据。需要租约的发行包不要在缺少有效授权续租集成的情况下移除 Infra。

Service Worker 可能重启，每次脚本执行都应注册处理器，不要只在 `onInstalled` 中注册。避免多个插件争抢唯一的[网络拦截器](https://console.getchrovia.com/zh/docs/sdk-network-intercept)或同一个[启动回复通道](https://console.getchrovia.com/zh/docs/sdk-messaging)。
