resterm

unkn0wn-rootApache-2.0活跃维护

终端里的 API 客户端,用可版本化的 .http 文件管理 HTTP、GraphQL、gRPC、WebSocket 和 SSE 请求,支持脚本化测试、工作流、Mock、录制和 CI 命令行运行。

Terminal API client for HTTP, GraphQL, gRPC, WebSocket and SSE. Plain .http files you can diff and version, with scripted tests, workflows, mocks, recorder, and a CLI runner.

v1.18.0发布于 2026-11-07 20:01 UTC2,012star +2 / 7 天61fork同步于 2026-11-10 23:46 UTC

怎么上手

由 README 提炼,执行前请确认
brew install resterm
# 或
curl -fsSL https://raw.githubusercontent.com/unkn0wn-root/resterm/main/install.sh | bash
硬件要求按 README 说明

README 未说明硬件要求;提供 macOS、Linux、Windows 预编译二进制,macOS 需 13 Ventura 及以上,Linux 需 glibc 2.32 及以上

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

终端里的 API 即代码工作台,用可 diff 的 .http 文件管理请求、工作流和 mock

终端里的 API 客户端,用可版本化的 .http 文件管理 HTTP、GraphQL、gRPC、WebSocket 和 SSE 请求,支持脚本化测试、工作流、Mock、录制和 CI 命令行运行。

核心能力

  • 用纯文本 .http/.rest 文件定义请求,可读可 diff 可入 git
  • 支持 HTTP、GraphQL、gRPC(含反射与流式)、WebSocket 和 SSE
  • 用 @capture/@assert/@poll/@retry 做响应捕获与断言检查
  • 工作流把请求串成步骤,支持 @if 与 @for-each 条件循环
  • resterm run 在 CI 中跑同一批文件,输出 text/JSON/JUnit 与退出码

容易踩的坑

用 Go 安装需 Go 1.25 或更新版本;macOS 二进制要求 macOS 13 Ventura 以上、Linux 要求 glibc 2.32 以上;Homebrew 安装用 brew upgrade resterm 更新,脚本或二进制安装用 resterm --update

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.18.0
  1. v1.18.0最新正式版2026-11-07 · 2026-11-07 20:01 UTC

    v1.18.0

    This release fixes a few security issues, makes collection --force safer, and cleans up some smaller bugs and quirks.

    The biggest change is how Resterm handles secrets. resterm run now masks them throughout its output, including values that weren't referenced through templates. History also masks secrets captured later in a session.

    There's also a new --response flag for including responses in JSON reports, a skill for coding agents, and a change to OAuth handling so headless runs can no longer open a browser.

    I added the skill because I realized that most folks now use local LLMs or frontier models like Claude or Codex, so it should help your LLM work with Resterm.

    Secret masking in resterm run

    In previous versions, Resterm masked secrets referenced through templates like {{api.key}}. But if you put the same value directly in a URL, it could still appear in the report:

    # @file-secret api.key s3cr3t-token-value-123
    
    ### Search
    # @name search
    GET http://localhost:3000/search?key=s3cr3t-token-value-123
    

    The output looked like this:

    PASS GET search [200 OK in 550.625µs]
      Source Target: http://localhost:3000/search?key=s3cr3t-token-value-123
      Effective Target: http://localhost:3000/search?key=•••
    

    In 1.18.0:

    PASS GET search [200 OK in 447.458µs]
    

    The target lines are only printed when they differ. Once both values are masked, there's no difference to show.

    • Secrets are masked with ••• in text, JSON, and JUnit reports, stream transcripts, trace files, and errors. This also covers JSON target fields, JUnit test names, and the request list printed when a file contains multiple requests.
    • Masking uses all secrets known by the end of the run. If a later request captures a token as a secret, that value is also masked in earlier results.
    • Secrets declared with fewer than 4 characters produce a warning, since masking a short value might also replace unrelated text:
    Warnings:
      secret "pin" is shorter than 4 characters, so reports may also mask the same word in unrelated text
    

    JSON reports include this under warnings. The warning doesn't change the exit code.

    See run and security.

    Include responses in JSON reports

    You can now use --response to include response data in the JSON report. It adds a response object to request results, including individual workflow, for-each, and compare steps.

    resterm run --all --format json --response api.http
    

    Example output:

    "response": {
      "headers": {
        "Content-Type": ["application/json"],
        "Set-Cookie": ["•••"]
      },
      "body": "{\"token\":\"•••\",\"user\":\"ada\"}",
      "bodySize": 47,
      "captures": [
        {"scope": "file", "name": "auth.token", "value": "•••", "secret": true}
      ]
    }
    

    Responses are masked before being written to the report.

    • Secret values, -secret captures, Set-Cookie, and sensitive headers such as Authorization and X-API-Key are replaced with •••.
    • Gzip bodies are decoded before masking. If Resterm can't decode a body, such as with br or invalid gzip data, the body field is omitted. Empty bodies are kept as "body": "", along with their headers.
    • Bodies that aren't valid UTF-8 are base64-encoded and include "bodyEncoding": "base64".
    • Masked bodies larger than 1 MiB are truncated to the first 1 MiB, with "bodyTruncated": true.
    • Skipped requests, profile runs, and requests without a response don't include a response field.

    The flag only works with JSON output. Using it with another format exits with code 2:

    error[unknown]: run: --response can only be combined with --format json
    

    If you're using Resterm as a Go package, the equivalent option is Response.Enabled.

    See run and headless.

  2. v1.17.22026-10-30 · 2026-10-30 22:55 UTC
    查看更新说明

    v1.17.2

    Clipboard over SSH (OSC 52)

    • With no clipboard tool on the host (usual over SSH), yank failed:
    Clipboard unavailable; saved in editor register
    
    • Now the copied text goes to your terminal with OSC 52:
    Copied selection. Sent via OSC 52
    
    • Editor yank, delete, change and response pane copy.
    • Only when the clipboard tool fails.
    • Works by default in Kitty, Ghostty, WezTerm, Alacritty, foot, Windows Terminal.
    • iTerm2: Settings > General > Selection > "Applications in terminal may access clipboard".
    • Not supported: macOS Terminal, GNOME Terminal.
    • tmux: set -g set-clipboard on
    • Paste unchanged. p uses the editor register, terminal paste in insert mode uses your clipboard.

    Troubleshooting

    osc52_limit

    • Default 1 MiB, about 768 KB of text. Bigger copies stay in the editor register:
    Too large for OSC 52 (limit 1 MiB). Saved in editor register
    
    [clipboard]
    osc52_limit = "8MiB"
    
    • JSON: {"clipboard":{"osc52_limit":"8MiB"}}
    • Raise it for Kitty. 16KiB for mosh.
    • KB, KiB, MB, MiB, GB, GiB

    Configuration

    Broken settings file no longer overwritten

    • A file that failed to load, for example diagnostics = 'yes', was replaced with defaults on the next theme change or layout save (g L).
    • With settings.json, a new settings.toml was written and the JSON file was ignored after that.
    • Now the save is refused and the status line shows why:
    layout save error: settings not saved: parse settings "/home/you/.config/resterm/settings.toml": toml: cannot decode TOML string into struct field config.EditorSettings.Diagnostics of type *bool
    
    • Fix the file and restart.

    Mock server, recorder and OAuth

    • :mock restart, :mock stop, the toggle key and quitting hung 3s:
    Mock server stop failed: context deadline exceeded
    
    • Stopping the recorder: same 3s, Recorder stopped: context deadline exceeded.
    • OAuth auth code login: about 5s extra after "Authentication complete" in the browser.
    • Cause: a connection that never sent a request (Go 1.27 clients, browsers). Resterm waited about 5s for it.
    • Requests in progress still finish.
  3. v1.17.12026-10-30 · 2026-10-30 11:47 UTC
    查看更新说明

    v1.17.1

    Failed tests in the text report

    The text report only gave a count for a failing request or step. 1.17.1 lists each failed test with its message, and workflow step details start under the step text instead of one or two columns to its left. A step with a failed JavaScript test in 1.17.0:

    FAIL WORKFLOW check-user [0 passed, 1 failed, 0 skipped in 1.739666ms]
      1. FAIL Get [1 test(s) failed]
        Source Target: {{base.url}}/users/1
        Effective Target: http://localhost:3000/users/1
    

    And in 1.17.1:

    FAIL WORKFLOW check-user [0 passed, 1 failed, 0 skipped in 1.435875ms]
      1. FAIL Get [1 test(s) failed]
         Source Target: {{base.url}}/users/1
         Effective Target: http://localhost:3000/users/1
         Failed Tests:
           user has an email
    
    • Passing tests are left out. A message over several lines stays lined up under its first line.
    • With 10 or more steps, the step numbers are right-aligned so the details of every step start in the same column.
    • This applies to -f text, to the default auto format whenever it prints the text report, and to the headless Text format.

    JavaScript asserts show their text once

    tests.assert and tests.fail used their text as both the test name and its message, so you saw it twice:

    Tests:
      [PASS] status is 200
      [FAIL] user has an email - user has an email
    

    Now it's [FAIL] user has an email. In JSON, failure.message is user has an email instead of user has an email: user has an email, and the failed test has no message field. client.test hasn't changed and still shows what failed it, as in user has an email - Error: no email for user 1.

    --timeout stops JavaScript tests

    @assert and RTS test blocks already stopped at the request timeout. JavaScript tests didn't, so a test stuck in a loop ran for 30 seconds whatever the timeout was. This one never moves to the next page:

    ### Orders
    # @name Orders
    # @script test
    > {%
    let page = response.json();
    let count = 0;
    while (page.next) {
      count += page.items.length;
    }
    tests.assert(count > 0, "has orders");
    %}
    GET {{base.url}}/orders
    

    With --timeout 5s, 1.17.0 still waited 30 seconds and exited with 24:

    Errors:
      Script error:
        error[timeout]: script exceeded the 30s time limit
        test script 1
    

    1.17.1 stops it after 5 seconds and exits with 20:

    Errors:
      Script error:
        error[timeout]: response checks timed out after 5s
        test script 1
        ╰─> context deadline exceeded
    
    • All checks share one timeout that starts after captures. They run in order: @assert, RTS test blocks, then JavaScript tests. A check that hasn't started when the time runs out is skipped.
    • @timeout on a request works the same way.
    • A timeout in the checks exits with 20, like any other timeout. 1.17.0 exited with 24, the code for script errors, even when the error said timeout. A script that throws still exits with 24.
    • A script block still stops after 30 seconds at most. Pre-request scripts don't use --timeout.

    See exit codes.

    Loop variables in step conditions

    A step's @when or @skip-if is checked once, before the @for-each of the request it runs, so it can't read that loop's variable. For most names this fails with a hint. If the variable had the name of a built-in, such as list, json or len, the condition read the built-in instead and carried on without an error.

    # @workflow sync-lists
    # @skip-if list.archived
    # @step Sync using=SyncList
    
    ### Sync list
    # @name SyncList
    # @for-each json.file("lists.json") as list
    POST {{base.url}}/lists/{{= list.id }}/sync
    

    old-promos is archived, and 1.17.0 synced it anyway:

    PASS WORKFLOW sync-lists [3 passed, 0 failed, 0 skipped in 2.967291ms]
      1. PASS Sync (1/3) [200 OK in 1.218709ms]
        Source Target: {{base.url}}/lists/{
    
  4. v1.17.02026-10-27 · 2026-10-27 11:27 UTC
    查看更新说明

    v1.17.0

    gRPC requests can load their schema straight from .proto files, without server reflection or a compiled descriptor set. GraphQL subscriptions work over SSE and WebSocket.

    gRPC with .proto files

    Point @grpc-proto at your .proto files and Resterm compiles them when it sends the request, so there's no protoc step.

    ### Say Hello
    # @grpc acme.v1.Greeter/SayHello
    # @grpc-proto ./proto/acme/v1/greeter.proto
    # @grpc-import-path ./proto
    GRPC {{grpc.host}}
    
    {"name": "Ada"}
    

    Here greeter.proto imports acme/v1/types.proto from proto/. If a file doesn't compile, the error tells you where:

    error[protocol]: compile proto files
    ╰─> acme/v1/types.proto:6:3
        ╰─> field acme.v1.Greeting.text: unknown type strng
    
    • Without @grpc-import-path, the folder of each listed file is its import path. Add import paths when imports start from a root folder, as with protoc -I. Both directives can repeat, and paths can use {{variables}}.
    • The standard google/protobuf/*.proto files are built in. Others, such as google/api/annotations.proto, have to be on an import path.
    • Resterm reads the files on every send, so an edit to a .proto file applies to the next request without a restart.
    • proto2, proto3 and edition 2023 files work. Edition 2024 isn't supported yet.
    • A request can use @grpc-proto or @grpc-descriptor, but not both.
    • The navigator lists the .proto files your requests use, and completion after @grpc-proto suggests only .proto files. Explain shows each proto file and import path.
    • resterm collection export bundles the proto files together with every file they import.

    See the proto files docs.

    GraphQL subscriptions

    @graphql works with @sse and @websocket. @operation, @query and @variables behave the same as over HTTP.

    Over SSE

    ### Order events
    # @graphql
    # @operation OrderEvents
    # @sse duration=30s idle=10s
    # @assert stream.summary().reason == "complete"
    POST {{graphql.endpoint}}
    
    subscription OrderEvents($id: ID!) {
      orderEvents(id: $id) { status }
    }
    
    # @variables
    { "id": "{{order.id}}" }
    

    The query is sent like any other GraphQL request, in the body for POST or as query parameters for GET. The stream ends at the first complete event and the summary reason is complete. If the server closes the stream before that, the reason is eof. This is the distinct connections mode of GraphQL over SSE.

    Over WebSocket

    ### Order events over WebSocket
    # @graphql
    # @operation OrderEvents
    # @query < ./queries/order-events.graphql
    # @variables < ./queries/order-events.json
    # @websocket
    GET {{graphql.wsEndpoint}}
    Authorization: Bearer {{token}}
    

    Resterm uses the graphql-transport-ws subprotocol. It sends connection_init, waits for connection_ack, subscribes and answers each ping. When the operation ends with complete or error, Resterm closes the socket with code 1000. @ws steps run after the subscription starts, so # @ws wait 20s keeps listening for up to 20 seconds.

    A GraphQL error doesn't fail the request, the same as over HTTP. Check for it with @assert. A message that breaks the protocol does fail it, with exit code 26. Here is the stream summary after a server sent a next message whose payload wasn't an object:

    closeCode: 4400,
    closeReason: "server sent next without an object payload",
    closedBy: "error",
    errorClass: "protocol",
    
    • The request also fails if the server closes the socket before connection_ack (exit code 26), or if idle runs out before it arrives (exit code 20). After the ack, a close with any code, such as 4403, is a normal ending.
    • Only graphql-transport-ws is supported, not the older subscriptions-transport-ws. On a @graphql request, any other subprotocols value is a parse error.
    • connection_init has no payload, so send crede
  5. v1.16.12026-10-22 · 2026-10-22 08:36 UTC
    查看更新说明

    v1.16.1

    This release moves resterm to Go 1.27.3.

    Important: Please update. This release blocks certificates from a compromised certificate authority (eMudhra).

    Compromised eMudhra certificates

    The private keys of eight intermediate certificates from the certificate authority eMudhra were so weak that anyone could recover them from the public certificates. With those keys, anyone can issue a valid HTTPS certificate for any website. eMudhra has revoked the certificates, and browsers and operating systems are blocking them. (CVE-2026-111943, Mozilla's write-up)

    resterm 1.16.1 rejects any certificate chain through these intermediates on every platform. resterm 1.16.0 can still accept them on systems whose certificate store doesn't block them yet.

    Before you update

    macOS 12 Monterey is no longer supported. The macOS binaries need macOS 13 Ventura or newer. On macOS 12, do not run resterm --update, because it installs a binary your system does not support. Instead, build 1.16.1 with Go 1.26.10 or a newer 1.26 release, which has the same certificate fix:

    go install github.com/unkn0wn-root/resterm/cmd/[email protected]
    

    Some TLS-inspecting proxies may drop HTTPS connections. resterm now offers post-quantum (ML-DSA) signature algorithms when it opens a TLS connection. Some proxies and load balancers reset these connections. Reports so far name Palo Alto Prisma Access and F5 BIG-IP (golang/go#81199). If HTTPS requests fail with EOF or connection reset by peer after you update, build with Go 1.26.10.

    Fixes

    • An SSE stream that stops at its size or event limit now closes its connection right away instead of reading on in the background.
    • Go security fixes for net/http, crypto/tls, net/textproto and html/template: CVE-2026-97031, CVE-2026-94440, CVE-2026-97032, CVE-2026-94439, CVE-2026-78663, CVE-2026-78669, CVE-2026-78660, CVE-2026-78667, CVE-2026-56866, CVE-2026-78659, CVE-2026-97030, CVE-2026-94448.

    Other changes

    • GODEBUG=tlsrsakex=1 and GODEBUG=tls3des=1 no longer turn RSA key exchange or 3DES back on for old servers.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Resterm is a code-first API client built around plain .http/.rest files. A request is a few lines of text you can read, diff and keep in git, and the same goes for workflows and local mocks. You send them from the terminal UI while you work, and resterm run runs the same files in CI. If your API sits behind an SSH bastion or inside a Kubernetes cluster, Resterm opens the tunnel or port-forward for you.

    Features

    • HTTP, GraphQL, gRPC (with reflection and streaming), WebSocket and SSE
    • environment files, grouped environments, dotenv files and OS variables
    • bearer tokens, OAuth 2.0 (client credentials, password, and authorization code with PKCE), and tokens from CLIs you already use, like gh auth token
    • TLS client certificates, custom root CAs, proxies, HTTP/2 and a cookie jar per environment
    • captures and checks with @capture and @assert, plus @poll and @retry
    • workflows that chain requests into steps, with @if and @for-each
    • RestermScript for logic and @rts test blocks
    • editor with Vim motions, syntax highlighting, completion for directives, headers, file paths, and inline diagnostics
    • @compare to send one request to several environments and diff the results
    • timeline tracing with per-phase budgets, and export to OpenTelemetry
    • @profile with latency stats, a histogram, P95 and P99
    • SSH tunnels and Kubernetes port-forwards that Resterm opens for the request
    • local mock servers from your .http files, and a recording proxy that turns real traffic into requests or mocks
    • resterm run for CI, with text, JSON or JUnit output and exit codes that tell a failed check from a TLS or tunnel error
    • headless Go API to run the same files from your own Go code
    • import from curl and OpenAPI, and share collections as bundles or zip files
    • custom key bindings and themes
    • open a file in $EDITOR, or a response body in your default app

    No account and no telemetry.

    [!NOTE] Resterm has no MCP server or built-in AI features. To help your LLM use Resterm, install the skill with resterm skill --agent claude or resterm skill --agent codex. See the skill docs for other agents. You can also point your agent at docs/ or resterm.app/docs.

    Screenshots

    Install

    macOS and Linux:

    brew install resterm
    # or
    curl -fsSL https://raw.githubusercontent.com/unkn0wn-root/resterm/main/install.sh | bash
    

    Windows:

    iwr -useb https://raw.githubusercontent.com/unkn0wn-root/resterm/main/install.ps1 | iex
    

    With Go 1.25 or newer:

    go install github.com/unkn0wn-root/resterm/cmd/resterm@latest
    

    Prebuilt binaries for macOS, Linux and Windows are on the releases page. The macOS binaries need macOS 13 Ventura or newer, and the Linux binaries need glibc 2.32 or newer.

    To update a Homebrew installation, run brew upgrade resterm. For release binaries or installations made with the scripts, run resterm --update. If you installed with Go, rerun the go install command above. More in the install guide.

    Quick start

    mkdir my-api && cd my-api
    resterm init
    resterm
    

    resterm init creates a requests.http file with a few sample requests and local mocks to answer them, so you don't need a real API to try it. Press g Shift+M to start the mock server, move the cursor to a request and press Ctrl+Enter to send it. Press ? for help at any time.

    You don't need init either. Run resterm, type a URL or paste a curl command, and press Ctrl+Enter.

    The same file also runs without the UI. Start the mocks in one terminal and run a request in another:

    resterm mock requests.http                       # terminal 1
    resterm run --request CreateUser requests.http   # terminal 2
    

    Request files

    Resterm reads the standard .http format and adds # @ directives on top for settings and automation:

    # @setting base-url https://api.example.com/v1/
    
    ### Create users
    // Send this request once for each name in the list.
    # @for-each ["david", "tom"] as name
    # @when env.meta.name == "dev"
    # @assert response.statusCode == 201
    # @rts test "user has an id"
    > if response.json().id == null {
    >   t.error("no id for", name)
    > }
    POST users
    Content-Type: application/json
    
    {"name":"{{= name }}"}
    

    A @setting before the first request applies to the whole file. ### starts a new request. Directives above a request can repeat it, skip it or check the response, either with @assert or with an @rts test block of RestermScript code.

    There are more examples in _examples/, and every directive is listed in the request file reference.

    Documentation

    The full docs are at resterm.app/docs. Good places to start are workflows, mock servers, recording, the CLI and RestermScript.

    Inside the TUI, :help <topic> opens the built-in manual and :docs <topic> opens the same page on the website.

    To run request files from your own Go code, see the headless package.

    Contributing

    To suggest a feature, start a discussion in the Ideas category. To report a bug, open an issue. To contribute code, open a pull request.

    Reporting bugs and suggesting features helps just as much as code.

    Sponsor

    Resterm is and always will be free and open source. If you like my work, you can sponsor it on GitHub.

    License

    Apache License 2.0.