@evalexp/pi-http

A minimal HTTP/1.1 request tool for Pi with raw request and response snapshots

Packages

Package details

extension

Install @evalexp/pi-http from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@evalexp/pi-http
Package
@evalexp/pi-http
Version
1.0.2
Published
Nov 5, 2026
Downloads
642/mo · 619/wk
Author
evalexp
License
MIT
Types
extension
Size
36.4 KB
Dependencies
0 dependencies · 2 peers
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-http

一个简洁的 Pi HTTP/1.1 请求工具。每次调用发送一次请求,直接返回 HTTP 响应。工具提示默认将直接 HTTP 请求、接口调用和原生请求/响应检查交给 http,尤其适用于需要控制方法、查询参数、请求头、正文、TLS 和 snapshot 的操作。

使用 Node 标准库 node:http / node:https;运行时仅依赖 Pi 提供的 typebox,不需要 curl、OpenSSL 或其他外部命令。支持 Node.js 22.19 或更新版本,也已在 Bun 1.4.4 下验证。

安装

pi install npm:@evalexp/pi-http

本地开发时可以从项目目录加载:

npm ci
pi -e ./src/index.ts

请求参数

工具名为 http。

参数 类型 必填 含义
method string 是 HTTP 方法,例如 GET、POST。发送时转为大写。
url string 是 完整的 http:// 或 https:// URL,包含路径和查询参数。
headers object 否 头字段到字符串的映射;字符串数组表示同名头字段的多个值。
body string / object 否 原始 UTF-8 字符串、{ "file": "路径" } 原始文件正文,或 { "multipart": [...] } 表单。GET 和 HEAD 禁止设置。
verifyTls boolean 否 是否严格校验证书及主机名,默认 true。
snapshot string 否 请求与响应的文件路径前缀 / ID。绝对路径直接使用;相对路径以 Pi 当前会话工作目录为基准,可指向工作目录之外。
{
  "method": "POST",
  "url": "https://localhost:8443/api/items?draft=true",
  "headers": {
    "Content-Type": "application/json",
    "Authorization": "Bearer example-token"
  },
  "body": "{\"name\":\"example\"}",
  "verifyTls": false,
  "snapshot": ".auto-audit/V0001/verify-1"
}

JSON、表单等字符串正文由调用方自行序列化并设置 Content-Type。字符串原样按 UTF-8 发送,也可以配合调用方指定的 Content-Type: multipart/form-data; boundary=... 发送手写的 multipart 字符串。

multipart 也可以由工具组装,支持普通字符串参数、文件、空字符串和重复字段名,并保留数组顺序:

{
  "method": "POST",
  "url": "https://example.com/upload",
  "body": {
    "multipart": [
      { "name": "title", "value": "普通字符串参数" },
      { "name": "tag", "value": "one" },
      { "name": "tag", "value": "two" },
      {
        "name": "attachment",
        "file": "./sample.bin",
        "filename": "upload.bin",
        "contentType": "application/octet-stream"
      }
    ]
  },
  "snapshot": ".auto-audit/V0001/upload"
}

工具读取文件原始字节,使用 Node/Bun 原生 FormData 编码并生成 boundary,自动设置带 boundary 的 Content-Type。此时省略请求头 Content-Type,或只写不带 boundary 的 multipart/form-data;其它值会被拒绝,避免头部与实际编码不一致。文件的 filename 默认取本地文件名,contentType 默认 application/octet-stream。原生编码器可能为文本文件类型补充 UTF-8 charset。

如果你们自行组装了含不可见字符的 multipart 或其它二进制正文,可直接传入完整正文文件:

{
  "method": "POST",
  "url": "https://example.com/upload",
  "headers": {
    "Content-Type": "multipart/form-data; boundary=my-boundary"
  },
  "body": { "file": "./request-body.bin" }
}

body.file 和 multipart 文件路径都相对 Pi 当前会话工作目录解析。这种完整文件正文不做任何编码或拼接,按原始字节发送;字符串正文也不会被 multipart 逻辑重新处理。

工具补充必要的 Host、Connection: close 和 Content-Length,正文长度按最终发送的字节计算。手动提供的 Content-Length 必须匹配正文,不接受手动指定 Transfer-Encoding。URL 中的凭据应改用 Authorization 请求头,URL 不接受 fragment。

返回响应

可显示的响应以 HTTP 报文文本返回,正文保留原有格式:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 14
X-Pi-Http-Started-At: 1700000000000
X-Pi-Http-Timespan: 123.45 ms

{ "ok": true }

Node IncomingMessage 会自动移除 chunked 分块编码,但不会解压 Content-Encoding。工具继续解码标准 gzip、deflate 和 br(Brotli)内容编码,支持叠加编码;随后移除已解码的编码头,按解码后的正文重算 Content-Length。不解析 JSON、不重新排版、不转换字符集,也不转换 Base64。未知编码保留原样;解码失败会明确返回错误并保留未能解码的正文。

响应中间状态(例如 100 Continue、103 Early Hints)会保留;其它头字段保留大小写、顺序和多个值。收到的 trailers 以附加头字段的方式放入最终响应的头部,原有 Trailer 声明随 chunked 编码移除。HEAD、204、304 响应没有正文,不进行内容解压。

X-Pi-Http-Started-At 是工具本地记录的请求开始时间,以 Unix epoch 毫秒整数表示;结构化详情中的 details.startedAtMs 与该头的数值一致。它与 durationMs 在 HTTP 请求开始时一起采样,不包含请求正文文件读取和 snapshot 文件预留时间。

X-Pi-Http-Timespan 是工具本地添加的响应头,单位为毫秒,保留两位小数。它测量从本次 HTTP 请求开始到响应接收完成的时间,包括 DNS、连接、TLS、请求发送和响应接收,不包括正文文件读取、解压和 snapshot 写盘。返回文本和响应 snapshot 中都会包含这两个时间头;结构化详情同时提供 startedAtMs 和 durationMs,通过 startedAtMs + durationMs 可计算响应接收完成的 Unix 毫秒时刻。这两个头名由工具保留,服务端同名字段会被本次测量值替换。

二进制、非 UTF-8 或含不可见控制字符的响应返回文件读取路径。超过 48 KiB 或 1500 行的文本只显示开头,同时提供完整响应的文件路径:

  • 指定 snapshot:使用指定的响应文件,不额外创建临时副本。
  • 未指定 snapshot:在系统临时目录创建 pi-http-*/response.http,按字节保存完整响应。

临时文件保存失败会单独记录为 details.outputError,保留已经收到的 HTTP 状态、完整性和耗时;写入失败的临时文件会清理,失败路径不会被当作可读取的响应文件返回。

HTTP 3xx、4xx、5xx 都是正常响应。工具不跟随重定向、不重试、不持久化 Cookie。每次请求独立,HTTP 收发与响应解压共用 30 秒 deadline,两个阶段都支持 Pi 取消操作;不提供 HTTP/2、CONNECT 隧道或协议升级。

Snapshot

snapshot 是文件前缀,不是目录。绝对路径直接使用,相对路径从当前会话工作目录解析;../ 也按正常路径规则解析,不限制保存在工作目录内。上面的示例产生两个文件:

.auto-audit/V0001/verify-1.request.http
.auto-audit/V0001/verify-1.response.http

文件内容是 HTTP 起始行、头字段、空行和正文,使用 CRLF 分隔,正文按字节保存。请求文件保存本次请求报文;响应文件与返回响应采用相同的 HTTP 编码解码结果,包含本地耗时头,不做正文重排或额外编码。父目录自动创建,已有同名文件不会被覆盖;工具会在发送请求之前预留两个文件。

发生连接失败、超时或取消时,仍保存请求和已经收到的响应。如果尚未收到响应,响应文件为空。工具结果中的 details.complete、details.requestWritten 和 details.error 说明响应完整性、请求是否写入完成及失败原因;写入完成不代表服务器已经接收。Snapshot 写入失败也会明确返回错误,目标路径不会被标记为已保存的完整响应文件。

开发

src/
  index.ts        Pi 工具入口
  schema.ts       六个请求参数
  body.ts         字符串、原始文件正文与原生 multipart 编码
  request.ts      HTTP/HTTPS 请求及报文封装
  response.ts     标准响应解压与协议头同步
  snapshot.ts     两个 snapshot 文件的保存
  output.ts       原生文本输出及文件路径处理
tests/
  *.test.ts       HTTP、HTTPS、解压、取消、文件输出和工具集成验证
  fixtures/      仅用于本地测试的自签名证书与测试私钥
npm ci
npm run typecheck
npm test
npm run test:bun
npm pack --dry-run

开发和 CI 使用提交的 package-lock.json 与 npm ci 安装同一棵依赖树。需要调整依赖时再使用 npm install,并将更新后的 lockfile 一同提交,避免每次测试重新解析浮动依赖和可选 peer 依赖。

实现与测试使用跨平台 API。测试证书仅供本地验证使用,不用于生产。GitHub Actions 配置了 Windows、Linux 和 macOS 上的 Node 22.19 / 24 与 Bun 1.4.4 测试,并显式检查测试所用的 runtime。