跳到主内容
创建时间:最后更新:作者:白川

打包、测试并复用扩展

通过独立资源包、真实加载器和分层验证,把本地扩展变成可检查的交付物。

打包、测试并复用扩展

扩展在源码目录里能运行,打包后却加载失败,通常与模型无关:入口文件没进包、相对依赖丢失,或宿主库被重复打进产物。分发验证应从实际包文件开始。

本文把工具样例做成独立资源包,同时保留其他阶段的练习文件。资源清单只激活 tools.ts,防止同名工具被重复注册。

本文示例固定使用 @earendil-works/pi-coding-agent@1.5.0,源码位于仓库 columns/coding-agent/examples/extension-development/。示例是独立包,先在该目录安装依赖;不需要修改专栏站点依赖。下面的命令均从这个示例目录运行。

包清单声明的是资源入口

示例的关键清单如下,完整版本见示例目录 package.json:

json
{
  "name": "agent-extension-lessons",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "files": ["extensions", "src", "fixtures", "test"],
  "pi": { "extensions": ["./extensions/tools.ts"] },
  "peerDependencies": {
    "@earendil-works/pi-coding-agent": "1.5.0",
    "@earendil-works/pi-schema": "1.5.0"
  }
}

files 决定 npm 包包含什么,pi.extensions 决定宿主把什么当作扩展入口,两者不能混用。工具入口引用 src/register-inspection.ts 和 src/inspect.ts,这些辅助文件也要进入产物。fixtures 用来让教学包可复现,真实扩展应明确它读取调用者项目还是包内资源,不能默认二者路径相同。

宿主 API 采用 peer 依赖,开发目录另用固定 dev 依赖做类型检查。不要把宿主运行时整个 bundle 进扩展,再期待其中的对象身份、模块单例和资源管理自动与真实宿主一致。本包设为 private,教程不需要 npm 发布。

直接调用真实加载器

src/audit.ts 在临时工作目录和 agent 配置目录中加载指定路径,避免读入个人扩展配置。扩展本身仍以进程权限执行;临时目录是配置隔离,不是代码沙箱。

ts
import { mkdtemp, rm } from "node:fs/promises"
import { tmpdir } from "node:os"
import { resolve } from "node:path"
import { discoverAndLoadExtensions } from "@earendil-works/pi-coding-agent"

const scratch = await mkdtemp(resolve(tmpdir(), "extension-audit-"))
try {
  const file = resolve(process.argv[2] || "extensions/tools.ts")
  const result = await discoverAndLoadExtensions([file], scratch, resolve(scratch, "agent"))
  console.log(
    JSON.stringify(
      {
        errors: result.errors,
        extensions: result.extensions.map((extension) => ({
          tools: [...extension.tools.keys()],
          commands: [...extension.commands.keys()],
          events: [...extension.handlers.keys()],
        })),
      },
      null,
      2,
    ),
  )
  if (result.errors.length > 0 || result.extensions.length !== 1) {
    process.exitCode = 1
  }
} finally {
  await rm(scratch, { recursive: true, force: true })
}
bash
npm run typecheck
npm run audit -- extensions/tools.ts
npm pack --dry-run --json --ignore-scripts

应看到注册工具 lesson_inspect、命令 lesson-check,errors 为空;pack 的文件清单应包含入口及其相对导入。开发时使用 jiti 加载 .ts 及其本地 .js 导入映射,仍要通过真实 loader 验证,不能只看 tsc。

分层测试各自证明什么

检查层本系列的检查对象未覆盖内容
业务函数
白名单、大小、取消、报告内容
模型选择
handler
拒绝分支、UI 降级、状态刷新
宿主完整事件顺序
SDK loader
文件加载、注册、错误诊断
命令是否被真实 UI 派发
包文件
入口和辅助文件齐全
干净机器的完整安装
真实宿主
输入命令、切换、重载、退出
跨环境全部兼容
模型联调
实际选择、参数、错误后的行为
全任务质量保证

测试工具参数时至少包括合法路径、无效路径、缺失文件、超大文件和取消;测试事件时同时验证合法调用没有被误拦。不要只判断函数返回某个对象,还要检查拒绝路径有没有触碰文件或留下资源。

从本地路径安装到复用

在隔离练习项目里可以用 pi install /绝对路径/extension-development --local 记录本地资源包,再重新启动检查工具清单。不要一开始就安装进用户级配置,否则会污染其他项目,也让“为什么加载了两个同名工具”更难解释。

更新后先检查包内容,再加载,再走业务与宿主交互。多资源包可以包含 extensions、skills、prompts 和 themes,但它们各有发现与使用机制。Skill 不会因为同包就自动执行脚本,主题也不会自动赋予扩展新的权限。

本系列的机器检查记录在仓库 research/extension-development-verification.md。未做的模型、终端和跨平台验证应保留为明确的验收项,不能由加载清单替代。

依据与版本

代码以已发布的 1.5.0 包导出、类型声明和加载器为准。在线主分支会继续变化,遇到接口差异时先核对版本。