# Pi Codex Search

为 [Pi](https://pi.dev) 提供 Codex 风格的远程网络搜索。插件可以使用自身保存的
OpenAI-compatible/Sub2API 连接信息直接调用 `/v1/alpha/search`，也可以复用 Pi 的
官方 `openai-codex` 登录。

本项目从 `@fadouse/pi-web@0.5.0` 精简而来。可复现的上游来源和校验值见
[ORIGIN.md](./ORIGIN.md)。

## 功能范围

插件只注册一个工具 `web_search`，保留 Codex 远程搜索的完整命令面：

- `search_query`、`image_query`
- `open`、`click`、`find`、`screenshot`
- `finance`、`weather`、`sports`、`time`

它不包含 `web_fetch`、本地 HTTP 抓取、浏览器自动化、HTML 清洗、PDF/Office/EPUB
解析、Exa 或本地二次总结。其他本地能力继续使用 Pi 自带工具或独立插件。

## 安装

发布到 npm 后：

```bash
pi install npm:pi-codex-search-remote
```

从当前源码目录测试：

```bash
pi install /absolute/path/to/pi-codex-search
```

需要 Node.js 22.19 或更高版本。

> npm 上的无 scope 包名 `pi-codex-search` 已被其他作者占用，因此本项目当前使用
> `pi-codex-search-remote`。如果你有 npm scope，可以在发布前改为
> `@your-scope/pi-codex-search`。

## Sub2API 配置

运行以下命令，选择 `openai-compatible`，并依次填写中转站 `baseUrl`、API key 和
远程模型 ID。这里的模型 ID 直接发送给中转站，不要求出现在 Pi 的 `/model` 列表中。

```text
/codex-search-config
```

全局配置保存在 `~/.pi/agent/pi-codex-search.json`：

```json
{
  "provider": "openai-compatible",
  "baseUrl": "https://your-sub2api.example/v1",
  "apiKey": "sk-...",
  "model": "gpt-5.6-luna",
  "mode": "live",
  "search_context_size": "medium",
  "max_output_tokens": 10000,
  "timeout_ms": 300000
}
```

Pi 的扩展输入框不是密码框，因此填写 API key 时字符可见。配置文件权限会设置为
`0600`。`/codex-search-config show` 会把 key 显示为
`[configured]`，但文件本身仍含明文凭据。建议把含 key 的配置保存为全局配置；若使用
可信项目的 `.pi/pi-codex-search.json`，不要提交到版本库。配置优先级为：

```text
环境变量 > 可信项目配置 > 全局配置
```

`openai-compatible` 不读取 Pi 的 `models.json`、provider 登录状态或
`OPENAI_API_KEY`。对于上述配置，实际搜索地址为：

```text
https://your-sub2api.example/v1/alpha/search
```

因此能否成功搜索取决于中转站是否真正支持该接口、模型和请求体，而不只是普通的
Responses API。

检查当前配置和路径：

```text
/codex-search-config show
/codex-search-config paths
```

手动修改配置后运行 `/reload`。

## OpenAI Codex 登录

也可以不使用 Sub2API，改用 Pi 的 `openai-codex` provider：

1. 在 Pi 中运行 `/login` 并选择 OpenAI Codex。
2. 在 `/codex-search-config` 中选择 `openai-codex` 和已安装模型。

插件会从 Pi 解析出的 OAuth 认证中取得访问令牌和 ChatGPT account ID，并根据模型
`baseUrl` 选择 `/codex/alpha/search` 路径。`openai-codex` 不接受插件配置中的
`baseUrl` 或 `apiKey`，也不需要改名。

## 从 0.1.0 迁移

旧版 `provider: "openai"` 会明确报迁移错误，因为它容易与 Pi 的内置 OpenAI
provider 混淆。升级后重新运行 `/codex-search-config`，选择
`openai-compatible` 并填写中转站连接信息。旧的 `openai-codex` 配置无需修改。

## 配置字段

| 字段 | 含义 | 默认值 |
|---|---|---|
| `provider` | `openai-compatible` 或 `openai-codex` | 必填 |
| `baseUrl` | 中转站的 API 根地址；仅 `openai-compatible` | 该模式必填 |
| `apiKey` | 中转站 API key；仅 `openai-compatible` | 该模式必填 |
| `model` | 中转模型 ID，或 Pi 中已安装的 Codex 模型 ID | 必填 |
| `mode` | `live`、`cached`、`indexed` 或 `disabled` | `live` |
| `search_context_size` | `low`、`medium` 或 `high` | provider 默认 |
| `allowed_domains` | 最多 100 个域名 | 不限制 |
| `user_location` | country/region/city/timezone | 未设置 |
| `max_output_tokens` | 1 到 50000 | `10000` |
| `timeout_ms` | 1000 到 600000 | `300000` |

对应环境变量使用 `PI_CODEX_SEARCH_` 前缀，例如：

```bash
export PI_CODEX_SEARCH_PROVIDER=openai-compatible
export PI_CODEX_SEARCH_BASE_URL=https://your-sub2api.example/v1
export PI_CODEX_SEARCH_API_KEY=sk-...
export PI_CODEX_SEARCH_MODEL=gpt-5.6-luna
export PI_CODEX_SEARCH_MODE=live
export PI_CODEX_SEARCH_ALLOWED_DOMAINS=github.com,openai.com
```

## 搜索 artifact

每次成功搜索会在以下目录保存独立快照：

```text
~/.pi/agent/cache/pi-codex-search/<session>/ws_<id>/
├── report.md
├── raw-search.txt
└── metadata.json
```

- `report.md`：带永久“不可信网页内容”声明的完整报告。
- `raw-search.txt`：中转站返回的原始文本。
- `metadata.json`：query、provider/model、引用 ID、字节数和 SHA-256。

工具结果会返回 `reportPath`、`rawSearchPath` 和 `metadataPath`。文件权限为 `0600`，
目录权限为 `0700`。中转站 HTTP 响应最多读取 32 MiB，单次搜索 artifact 上限为
64 MiB；默认保留七天，所有会话合计最多占用 256 MiB，清理时优先删除最旧
artifact。

较早用户轮次中的搜索正文会从活跃模型上下文中替换为包含 `report_path` 的短回执，
磁盘快照和 Pi 会话记录保持不变。需要时让 Pi 用内置 `read` 读取该路径即可。

## 安全边界

- 每次工具调用只向所选模型解析出的 `/alpha/search` 地址发送一次 POST；插件不自动
  重试，以免中转站重复计费或重复执行有状态操作。
- 插件不连接搜索结果 URL，不执行网页 JavaScript，不启动浏览器或外部程序。
- 搜索返回内容会带有“untrusted web search results”标记后进入模型上下文；网页内容
  仍可能包含 prompt injection。该标记和工具提示用于要求模型把它当证据而非指令，
  但不构成内容净化或绝对隔离。
- 为保持连续搜索和页面引用能力，请求会包含当前用户消息、前一个用户消息，以及两者
  之间最多约 1000 token 的 assistant 文本。系统提示、工具结果和其他会话记录不会被
  加入。使用 Sub2API 时，这些有限近期对话会发送给中转站；不要在相关消息中放入不愿
  交给中转站的代码、凭据或内部信息。
- 插件不主动读取工作区、SSH 目录或其他用户文件。除上述 Pi 提供的有限近期消息外，
  它只读取自身 prompt/config、模型信息和本插件创建的搜索 artifact。
- 与所有 Pi 扩展一样，安装后的代码在 Pi 进程权限下运行。发布前应审阅源码及
  `npm pack --dry-run` 的文件列表。

## 开发和发布

```bash
npm install
npm run check
npm pack --dry-run
```

发布前确认 npm 包名和账户权限，然后：

```bash
npm publish --access public
```

当前版本没有生产依赖；Pi 相关包均为 peer dependencies。完整目标范围见
[SPEC.md](./SPEC.md)。

## License

MIT。保留的上游版权声明见 [LICENSE](./LICENSE)。
