# 配置参考

[返回 README](../README.md) · [通知字段](notifications.md)

## 配置文件

从 Pi 的 `getAgentDir()` 下的 `extensions/pi-notify.json` 读取配置，通常是 `~/.pi/agent/extensions/pi-notify.json`。支持 `PI_CODING_AGENT_DIR`；`PI_NOTIFY_CONFIG=/absolute/path.json` 可进一步覆盖配置文件位置。**不读取项目级配置**。

无配置文件时启用 bell / OSC 777，关闭 webhook。复制 [示例配置](../config.example.json) 时不要覆盖已有文件：

```sh
config_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/extensions"
mkdir -p "$config_dir"
# 在仓库根目录执行；已有文件请手工合并。
if [ ! -e "$config_dir/pi-notify.json" ] && [ ! -L "$config_dir/pi-notify.json" ]; then
  install -m 600 config.example.json "$config_dir/pi-notify.json"
fi
```

优先级：默认值 → 用户 JSON → 白名单环境变量。配置在 `session_start` 读取，包括启动、`/reload` 和会话切换。文件变更后可 `/reload`。

环境变量必须存在于启动 Pi 的进程中；另一个 shell 的 `export` 不会改变运行中的 Pi。改变环境变量后，应从设置好环境的 shell 重启 Pi。

## 字段

所有字段可省略。

| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `notifyOn.settled` | `true` | 自动执行停止后的通知 |
| `notifyOn.prompt` | `true` | 等待扩展 UI 操作时的通知 |
| `tmux.enabled` | `true` | 写入原生 BEL，tmux 外跳过 |
| `terminal.enabled` | `true` | 启用桌面控制序列 |
| `terminal.protocol` | `osc777` | `osc777`、`auto` 或 `off` |
| `webhook.enabled` | `false` | 必须显式开启 |
| `webhook.url` | 空字符串 | 接收 JSON POST 的完整 URL；开启时必须提供 |
| `webhook.headers` | `{}` | 静态请求头，或从环境变量解析的请求头 |
| `webhook.body` | `{"title":"{{title}}","message":"{{body}}"}` | 任意 JSON 模板 |
| `webhook.timeoutMs` | `3000` | 正整数，限幅到 100–10000ms |
| `webhook.allowInsecureHttp` | `false` | HTTP 明文传输开关，启用会警告 |
| `content.cwd` | `basename` | `basename`、`full` 或 `none` |
| `content.promptTitle` | `false` | 追加可用的提示标题，可能泄露命令或路径 |
| `content.details` | `false` | 追加带标签的 tmux ID 和 Pi 会话短 ID |
| `debug` | `false` | 有限、脱敏、限频的本地日志 |

`auto` 只识别 `TERM_PROGRAM=WezTerm`、`TERM_PROGRAM=ghostty` 或 `TERM=xterm-ghostty`。SSH/tmux 常隐藏这些变量，未知环境会安全跳过。显式 `osc777` 不做终端识别，不支持的终端可能没有任何显示。

### 环境变量覆盖

| 变量 | 行为 |
| --- | --- |
| `PI_NOTIFY_CONFIG` | 指定绝对路径的用户配置文件 |
| `PI_NOTIFY_WEBHOOK_URL` | 覆盖 `webhook.url`，不会自动启用 webhook |
| `XDG_CACHE_HOME` | debug 日志缓存根目录 |

`webhook.headers` 还可以逐项引用任意环境变量，见下文。

### 配置错误

- JSON 根结构、共享 policy（`notifyOn` / `content` / `debug`）错误：关闭所有通道并提示。
- 单个通道配置错误：只关闭该通道。
- 拒绝非普通文件、超过 64KiB 的配置、相对路径覆盖，以及无效 JSON。
- 未指定覆盖路径时，默认文件不存在则使用默认值；显式指定的文件不存在则报错。
- webhook 只允许 HTTPS，或显式允许的 HTTP；拒绝 URL 内的用户名、密码和 fragment。查询参数允许存在，因为部分 webhook 把凭据放在 URL 中。
- JSON 模板只识别 `{{title}}` 和 `{{body}}`。未知占位符、循环/过深结构、无效数值或危险对象键会关闭 webhook。
- 请求头最多 32 个，值只允许可打印 ASCII；拒绝重复名称、换行以及 `Content-Type`、`Content-Length`、`Host` 等由 HTTP 客户端管理的头。
- 未知字段不参与配置。不执行 shell、命令替换或模板表达式。

## 通用 webhook

webhook 固定执行 JSON `POST`，设置 `Content-Type: application/json`，不跟随重定向，也不读取或记录响应正文。HTTP 2xx 视为服务端接受；这不代表最终用户已经看到通知。

`webhook.body` 可以是任意 JSON 值。字符串值中的以下占位符会递归替换，JSON 键不会替换：

| 占位符 | 内容 |
| --- | --- |
| `{{title}}` | `Pi 等待输入` 或 `Pi 需要你的操作` |
| `{{body}}` | 项目、主机、可选 tmux 位置及可选详细字段 |

例如 Slack 类接口可以使用 `{"text":"{{title}}\n{{body}}"}`，其他接口可按自身字段结构组织对象或数组。替换发生在 JSON 序列化之前，因此通知文本不能注入新的 JSON 字段。

静态请求头直接写字符串；凭据建议通过环境变量引用：

```json
{
  "headers": {
    "X-Source": "pi-notify",
    "Authorization": {
      "env": "PI_WEBHOOK_TOKEN",
      "prefix": "Bearer ",
      "suffix": ""
    }
  }
}
```

`env` 必须是合法环境变量名。`prefix` / `suffix` 可省略；webhook 开启时，被引用的环境变量必须存在且非空。静态请求头也会随配置文件保存，因此不要把 secret 直接写入字符串值。

### ntfy 示例

[示例配置](../config.example.json) 将 ntfy 当作普通 JSON webhook。受保护 topic 可配置为：

```json
{
  "webhook": {
    "enabled": true,
    "url": "https://ntfy.example.com",
    "headers": {
      "Authorization": {
        "env": "PI_NTFY_TOKEN",
        "prefix": "Bearer "
      }
    },
    "body": {
      "topic": "your-private-topic",
      "title": "{{title}}",
      "message": "{{body}}"
    },
    "timeoutMs": 3000,
    "allowInsecureHttp": false
  }
}
```

公共 topic 可以删除 `Authorization`；公共 topic 名不是访问控制，不要将敏感工作信息发到任何人都能订阅的 topic。私有部署应使用最小发布权限的 token。

优先从密码管理器注入 token；Bash 也可以无回显读取，避免进入 shell history：

```bash
read -r -s -p 'ntfy token: ' PI_NTFY_TOKEN; printf '\n'
export PI_NTFY_TOKEN
pi
```

显式测试：`/pi-notify test webhook`。它仍要求配置有效且 `webhook.enabled: true`，不会越过开关。

## 隐私边界

默认通知文本包含项目目录名和 hostname；在 tmux 中还包含会话名与窗口/分屏索引。不发送完整路径、prompt title、窗口名称、内部 ID、用户回答、对话或工具输出。`content.details`、`content.promptTitle` 和 `content.cwd: full` 会分别扩大范围。

webhook 会发送渲染后的整个 JSON 模板，其中还可能包含用户配置的静态字段。URL 路径、查询参数和请求头也可能是凭据。日志不会保存消息正文、URL、请求头或模板内容；不要提交实际用户配置或环境变量文件。

无论何种配置，都不把通知内容当作 shell 命令或远程授权信息。
