# 使用插件开发工具包

工欲善其事，必先利其器。官方工具包 `flyon-cli` 提供了功能完备的全局命令行工具 `flyon`。

你可以使用 `flyon` 创建项目、构建校验、连接飞块桌面端持续联调、直接调用动作、预览 widget 场景和查看日志，最后生成 `.flyext` 插件安装包。开发插件无需桌面端源码。

---

## 安装与环境准备

在开始使用前，请确保你的开发机器已安装 **Node.js 22.12** 或更高版本，以及配套的 npm。

### 工具包体系构成

官方插件工具包由以下核心模块协同构成：

| 工具包模块 | 核心职责 |
| :--- | :--- |
| `flyon-cli` | 全局 `flyon` CLI 工具、预置脚手架模板与开发联调工作流 |
| `@flyondeck/plugin-contracts` | 公共契约层：协议标准、TypeScript 类型定义与 Schema 校验器 |
| `@flyondeck/plugin-sdk` | 插件运行时 SDK：涵盖后台进程、超级块前端页面与配置面板 API |
| `@flyondeck/plugin-tools` | 工程化底层：自动拉取依赖、重编译原生模块与安装包归档 API |

### 注册全局命令

在 npm 官方镜像源尚未完全公开前，你可以通过获取工具包源码，在 `packages` 目录中完成本地编译与软链：

```sh
cd plugin-toolkit/packages
npm ci
npm run build
npm link --workspace flyon-cli
flyon --help
```

> 💡 **提示**：如果你不想注册全局软链，也可以在 `packages` 目录下通过 `npm exec -- flyon` 直接调用命令。未来工具包在 npm 发布后，直接运行 `npm install -g flyon-cli` 即可一键安装。

全局命令生效后，你便可以在任何个人工作目录中直接运行 `flyon`，无需再进入工具包源码目录。

---

## 快速创建项目（flyon init）

创建一个新插件只需一行交互式命令：

```sh
flyon init my-plugin
cd my-plugin
```

终端会以交互问答的方式引导你选择模板、填写插件 ID 与显示名称。如果你想在脚本或自动化流程中使用，也可以直接传入参数跳过问答：

```sh
flyon init my-plugin --template action --id=example.hello --name="Hello 飞块" --non-interactive
```

> ⚠️ **注意**：`init` 命令遵循安全原则，绝不会静默覆盖已有目录。创建前请确保目标目录尚不存在。同时，`example.hello` 仅供本地测试，准备正式上架时请替换为你在开发者平台申请的专属命名空间（详见[发布插件](../publishing/marketplace.md)）。

### 预置项目模板一览

| 模板名称 | 适用场景与内容 |
| :--- | :--- |
| `action` | 适合业务逻辑型插件。提供 TypeScript 后台、问候动作实现及 Node.js 单元测试样例。 |
| `widget` | 适合自定义界面型插件。包含独立实例计数器、状态广播、主界面 HTML、配置设置页面及 `scenarios/counter.json` 预览场景。 |
| `icon-pack` | 适合静态资源型插件。包含静态图标索引与 SVG 素材，无独立后台进程，轻量易用。 |

以最常见的动作模板为例，初始化生成的标准工程目录结构如下：

```text
my-plugin/
├── manifest.json       # 核心清单：插件唯一身份、权限及所提供功能的权威来源
├── package.json        # 项目依赖与基本信息
├── package-lock.json   # 锁定精确依赖版本
├── flyon.config.json   # CLI 辅助配置：编译规则、静态文件复制及测试文件范围
├── src/                # 源码目录
│   ├── index.ts        # 插件入口
│   └── handlers.ts     # 动作实现
└── tests/              # 单元测试
    └── hello.test.ts
```

---

## 检查环境与构建代码

在项目根目录下，你可以随时运行以下维护命令：

```sh
# 1. 检查开发环境健康度（Node 版本、依赖树与桌面端连接状态）
flyon doctor

# 2. 编译 TypeScript/JavaScript 并同步静态网页资源
flyon build

# 3. 严格校验清单声明、文件引用与运行入口语法
flyon check

# 4. 执行单元测试套件
flyon test
```

**命令解析与实践建议：**
- **`flyon doctor`**：遇到启动异常时的首选排查工具。它会检查锁文件、运行依赖及桌面端开发通道。即使处于离线网络环境，也不会影响你的本地构建。
- **`flyon build`**：负责将源码编译输出到构建目录，**该命令不会生成最终的 `.flyext` 安装包**。
- **`flyon check`**：依据官方 JSON Schema 严格校验文件合法性。请注意它不会执行代码，也不能完全代替真实的联调验证。
- **关于 SDK 加载机制**：如果你的插件有第三方 npm 依赖，请在本地安装并务必提交 `package-lock.json`。插件运行时所用的 `@flyondeck/plugin-sdk` 会在启动时由飞块宿主通过环境变量 `FLYONDECK_SDK_PATH` 动态提供，**切勿将 SDK 强行打入生产安装包**，也不要尝试在外部用独立 `node` 直接执行入口文件。

---

## 持续联调与热重载（flyon dev）

飞块支持极速高效的热开发体验。修改代码无需手动反复打包安装！

### 开启联调会话

1. 在飞块桌面端打开“**设置 → 通用**”，确认已勾选“**开发者模式**”。
2. 在终端运行：
   ```sh
   # 查看当前局域网或本机可连接的飞块桌面端
   flyon host status

   # 启动热重载开发模式
   flyon dev
   ```

**后台发生了什么？**
- `flyon dev` 会在内部暂存区（`.flyon/staging`）快速构建，并将生产依赖安全挂载至开发环境。
- 当你保存修改任何 `src/` 或页面源码时，CLI 会自动触发增量编译并平滑重启插件。
- 桌面端预览区域、超级块目录卡片、配置窗口乃至移动端 Web Client 界面均会自动刷新呈现最新状态！
- 如果代码中出现编译语法错误或初始化异常，CLI 会保留上一次正常运行的版本并清晰打印错误堆栈，不会导致桌面端闪退。

### 直接调用动作与查看实例

保持 `flyon dev` 运行，在另一个终端进入同一项目：

```sh
flyon catalog
# action 模板示例
flyon invoke hello --args '{"name":"开发者"}'
# widget 模板示例：将实例引用替换为 catalog 返回的三个 ID
flyon invoke increment --widget counter --instance '{"deviceId":"device","screenId":"screen","blockId":"block"}'
```

`catalog` 列出当前开发插件的动作声明、widget 实例动作和真实实例。普通动作无需配置按钮即可执行；实例动作使用已放置实例的三个 ID，仍由宿主校验归属。`--args` 默认为 `{}`，两类调用均沿用正式执行路径的声明、参数、权限与执行规则。成功返回结果，失败以非零退出码报错；支持 `--json`。

| 命令或参数 | 用途 |
| --- | --- |
| `flyon catalog` | 查看动作参数声明、widget 实例动作和已放置实例。 |
| `flyon invoke <动作ID> --args '<JSON对象>'` | 执行普通动作并查看返回结果。 |
| `--widget <widgetId> --instance '<JSON实例引用>'` | 一起提供以执行实例动作；引用只包含 deviceId、screenId、blockId。 |
| `--cwd <项目目录>` | 在其他目录操作指定项目的开发会话。 |
| `--host <hostId>` | 存在多个飞块桌面实例时指定连接目标。 |

### 使用临时场景预览 widget

widget 模板包含 `scenarios/counter.json`，也可以自行创建以下 JSON 文件：

```json
{
  "widgetId": "counter",
  "width": 240,
  "height": 160,
  "settings": {"label": "长文本场景"},
  "state": {"count": 42}
}
```

```sh
flyon preview scenarios/counter.json
flyon logs --follow
```

预览在已安装飞块的独立窗口中使用真实 HTML、页面 SDK 和正式资源路由，无需先放置该 widget。width/height 是窗口初始内容尺寸（CSS 像素），支持 64–4096 的整数，默认 320×200；settings 默认 `{}`，state 默认 `null`。只接受这五个字段，场景文件和数据最多 64 KiB。路径相对于当前调用目录，或 `--cwd` 指定目录。

临时 settings/state 仅发送给预览页面，不写入实际实例或插件配置，不发布正式状态。页面实例动作只写入开发日志，不执行；数据提供器请求立即返回不支持错误。预览不模拟业务服务，后台插件仍由真实开发会话运行。修改插件代码后自动刷新并重新注入已打开的场景；修改场景数据后重新执行 `preview`。每个开发会话只保留一个预览窗口，替换、手动关闭、结束会话或关闭开发者模式时清理相应资源。

> 💡 **安全与退出机制**：
> - 随时按 `Ctrl + C` 即可优雅结束联调，临时挂载的开发关联会自动注销，绝不残留垃圾文件，且完整保留你的插件业务数据与源码。
> - CLI 与宿主之间通过本机的 Unix Domain Socket（macOS/Linux）或 Named Pipe（Windows）高效通讯，依然严格遵循飞块的安全权限授权机制。

---

## 日志查看与即时控制

在 `flyon dev` 运行期间，主终端会实时输出构建状态和关键运行信息。如果你希望在另外的终端窗口单独追踪特定日志流，可以使用日志命令：

```sh
# 查看最近保留的运行日志
flyon logs

# 实时持续追踪（类似 tail -f）
flyon logs --follow

# 仅监听标准错误输出流（排查异常报错神器）
flyon logs --stream stderr

# 手动让已关联的开发插件重新初始化
flyon restart
```

每个开发会话会在内存中缓冲最近 300 条结构化日志（单条约 2 KiB），方便你快速回溯刚才的操作轨迹，无需担心日志文件撑爆磁盘空间。

---

## 一键构建生产包（flyon pack）

当功能开发完毕并通过验证后，使用 `pack` 命令即可生成用于分发的标准安装包：

```sh
flyon pack
```

`flyon pack` 会在独立的沙箱临时环境中模拟真实的全新安装流程：锁定生产依赖、编译源码、剔除开发辅助依赖（devDependencies），并严格按照白名单规范打出紧凑的 `.flyext` 文件。**该过程绝对不会污染或修改你的工作区源码与现有的 `node_modules`。**

默认输出路径为：
```text
releases/example.hello-1.0.0-universal.flyext
```

你也可以自定义输出位置或深度检查包体内容：
```sh
# 指定打包输出目录
flyon pack --out-dir ./releases

# 检查归档包的文件清单、权限及 Schema 合规性
flyon inspect releases/example.hello-1.0.0-universal.flyext
```

> 🎯 **规范提示**：生成的 `.flyext` 本质是标准 ZIP 归档，解压后根目录下即为 `manifest.json`，绝不会多套一层文件夹。详细标准请阅读[打包与安装](../publishing/package.md)。

---

## 自定义工程配置（flyon.config.json）

对于使用 TypeScript 或包含前端页面的复杂插件，可通过项目根目录下的 `flyon.config.json` 进行精确配置：

```json
{
  "schemaVersion": 1,
  "entry": "src/index.ts",
  "resources": [
    { "from": "pages", "to": "dist/pages" }
  ],
  "files": [
    "manifest.json",
    "package.json",
    "dist",
    "assets",
    "locales"
  ],
  "test": [
    "tests/hello.test.ts"
  ]
}
```

**核心字段说明：**
- **`entry`**：编译入口文件路径，产物会自动对应到 `manifest.json` 中的 `main` 字段。
- **`resources`**：静态资源复制映射表，将页面 HTML、图标或样式无损复制到输出目录。
- **`files`**：**生产发布白名单**。精确指定打包时需要包含的文件或目录。请务必确认包含编译后的入口产物及其运行时依赖；源码与单元测试文件不应包含在其中。
- **`test`**：单元测试入口清单。

---

## 高级进阶：原生模块支持

对于使用 C/C++ 或 Rust 编写原生 Node Addon（`.node`）的高性能插件，构建过程需指定与飞块内置环境匹配的 Electron 可执行文件：

```sh
flyon pack --target macos-arm64 --electron "/path/to/Electron.app/Contents/MacOS/Electron"
```

你也可以直接设置环境变量 `FLYONDECK_ELECTRON_PATH`。详细跨平台编译矩阵与 ABI 兼容性规范，请参阅[平台与运行时声明](../publishing/package.md#2-跨平台与原生运行时声明)。
