# 开发、架构与发布

[返回 README](../README.md)

## 工具链

```sh
pnpm install --frozen-lockfile
pnpm check
```

`pnpm-workspace.yaml` 只包含单包配置与依赖构建策略，并不表示这是多包仓库。对当前开发依赖中不需要的安装脚本明确设为 false；不使用“允许全部构建”。新增未审批的脚本会使安装报错，应先审阅再更新策略。

只维护 `pnpm-lock.yaml`，不要混入 npm/yarn 锁文件。使用 pnpm 开发不影响包发布到 npm registry，也不要求使用者为本插件安装 pnpm。

## 源码结构

```text
extensions/
└── pi-notify.ts       唯一公开的 Pi 扩展入口（默认导出工厂）
src/
├── bridge.ts          Pi 事件、生命周期和用户命令
├── attention.ts       等待状态、去重和票据失效
├── config.ts          配置解析、安全默认值
├── format.ts          人类可读的标题与正文
├── dispatcher.ts      独立的有界通道队列
├── diagnostics.ts     脱敏日志
├── types.ts           通道间共享契约
└── notifiers/
    ├── tmux.ts        来源位置查询、BEL、透传权限检查
    ├── terminal.ts    OSC 编码、tmux 包装、TTY 写入
    └── webhook.ts     JSON 模板渲染与单向 HTTP 发送
```

`package.json` 显式声明 `extensions/pi-notify.ts` 为唯一入口；`src/` 中的文件仅作为实现模块被入口导入，不会被 Pi 分别加载。

主数据流：

```text
Pi 事件 → bridge → attention（通知是否仍有效）
                   │
                   ▼
               dispatcher（独立队列）
                   ├─ bell → 裸 BEL
                   └─ 查询位置 → format → terminal / webhook
```

`format.ts` 只决定内容，不调用外部进程、HTTP 或终端；notifier 负责编码与发送，不决定业务文案。`types.ts` 没有运行时依赖，避免 HTTP/诊断模块从 tmux 实现导入公共类型。

入口工厂只注册事件和命令，`session_start` 才建立运行时，`session_shutdown` 清理。每次会话替换均重新建立状态，不保留跨会话有效的通知票据。

直接加载 TypeScript，无编译产物或根目录转发入口。源码修改后，在使用该本地包的 Pi 中 `/reload`。

## 测试命令

```sh
pnpm typecheck         # strict TypeScript，无输出产物
pnpm test              # 单元、模拟传输、Pi 加载和 tmux/PTY 集成
pnpm test:tmux         # 单独运行真实 tmux/PTY 集成
pnpm check:package     # 真实打包、解压、检查清单及 Pi/jiti 加载
pnpm check             # 上述检查的标准入口
```

Node 的 `--experimental-transform-types` 提示是测试执行方式的提示，不是插件运行失败。

### 自动覆盖

- 配置校验、临时文件加载、隐私开关、安全默认值。
- 状态机去重、prompt 失效、session 关闭与替换、非 TUI/TTY 门禁。
- 派发过程中弹窗打开/关闭、元数据/权限查询延迟及新 run/关闭竞态；补发仅恢复未尝试通道，不重复成功或失败的 HTTP 尝试。
- 中文通知精确文案、可读位置、可选 ID、UTF-8 截断、控制字符清洗。
- 模拟 tmux 查询、BEL、OSC 字节、webhook JSON 模板/请求头/超时/取消、队列容量与通道隔离。
- TTY 背压、超时、同步/异步错误、取消及关闭后的延迟回调和监听器清理。
- debug 日志限频、行数/大小上限、禁用/关闭、符号链接拒绝与文件系统错误恢复。
- 真实 Pi 包解析器和 jiti loader：本地源码包与解压后的发布包均只加载一个入口。
- 真实 tmux/PTY：独立临时 socket、空配置、受控 PTY 客户端，验证后台 bell flag/清除及 OSC off/on/all 透传。

最后一项需要 Linux、tmux 3.5a+ 和 Python 3（标准库 PTY）。缺少必要工具或版本时明确 skip；工具存在但测试失败则报错。所有客户端输出均被捕获，不回显控制序列到当前终端，不连接用户的 tmux server，不需要模型凭据或真实 webhook 服务。

真实 tmux 测试验证的是终端字节和窗口标记，**不是桌面弹窗**。完整 Pi 会话中的 retry/compaction/第三方 UI 组合、真实 SSH、桌面权限和 webhook 下游展示仍需 [手工验收](troubleshooting.md)。

## 持续集成

GitHub Actions 在 Debian trixie 的 Node 24 容器中安装固定的 pnpm 12.5.1，以及 tmux/Python，然后执行冻结锁文件安装、`pnpm check` 和官方 registry 依赖审计。

CI 设置 `PI_NOTIFY_REQUIRE_TMUX=1`：缺少 Linux、tmux 版本或 Python 时直接失败，不能把跳过 PTY 测试当成验证成功。本地默认仍允许明确 skip。可用以下命令执行相同的强制检查和审计：

```sh
PI_NOTIFY_REQUIRE_TMUX=1 pnpm check
pnpm audit --registry=https://registry.npmjs.org
```

审计包含开发依赖；`tar` 的漏洞同样需要处理，但不应把开发工具的公告等级等同于插件运行时的可利用风险。第三方镜像可能没有审计接口，因此审计命令明确使用官方 registry。

## 打包与分发

```sh
pnpm check
pnpm pack              # 在当前目录生成 .tgz，已被 Git 忽略
```

`prepack` 先执行类型检查与测试。`check:package` 则用 pnpm 创建临时真实归档，不依赖 npm 的 `--dry-run`；由于 check 已执行测试，此处关闭生命周期脚本以避免重复执行。它检查：

1. 发布清单仅含 `extensions/`、`src/`、公开文档、示例配置和 package.json。
2. 运行时相对导入全部包含在归档内。
3. Markdown 相对链接在发布包中仍然有效。
4. 解压到没有开发依赖的临时目录后，Pi 仍能解析并加载唯一入口。

临时目录在 finally 中清理。`tar` 仅用于开发侧归档检查，不是插件运行时依赖。SDK 不打包进插件；`.pi/`、用户 JSON、环境文件、测试与开发脚本也不发布。

发布前自行确定许可证、仓库地址与 npm 名称可用性。当前清单并不代表包已经公开发布。发布后使用 Pi 支持的源格式安装：

```text
pi install git:<host>/<owner>/<repo>@<tag>
pi install npm:<package-name>@<version>
```

上述为格式占位符，请替换成真实源。
