npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

switchyard-agent

v0.0.1

Published

Local HTTP/SSE bridge for Codex, Cursor Agent, and Claude Code.

Readme

Switchyard

English README

Switchyard 是一个本地 Agent 桥接服务。它把 Codex、Cursor Agent、Claude Code 三种命令行 Agent 统一暴露成 HTTP API,并用 Server-Sent Events 推送模型输出和运行事件。

适合用来接入自己的本地 Agent 控制台、桌面应用、IDE 插件原型,或者把不同 CLI Agent 归一到同一个服务端协议里。

运行什么

| Provider | 生命周期 | 传输方式 | 说明 | | --- | --- | --- | --- | | codex | 持久会话 | codex --yolo app-server --listen stdio:// | 常驻 app-server 进程,通过 JSON-RPC 发起 turn。 | | cursor | 每任务进程 | cursor agent ... --output-format stream-json | 每次消息启动一个进程,拿到 provider session id 后使用 resume。 | | claude | 每任务进程 | claude -p ... --output-format stream-json | 每次任务通过 stdin 写入一条 stream-json 用户消息。 |

所有 session 都保存在内存里。不实现数据库、鉴权、队列或多用户隔离。

安装

pnpm install

构建全部产物:

pnpm build:all

启动后端:

pnpm serve

默认地址是 http://127.0.0.1:8787

开发前端控制台:

pnpm dev:web

Vite 前端运行在 http://127.0.0.1:5173,并把 /api/health/sessions 代理到后端。

CLI

构建后运行:

node dist/cli.js serve --host 127.0.0.1 --port 8787

从 npm 安装后运行:

npx -p switchyard-agent switchyard serve --host 127.0.0.1 --port 8787

包里主命令是 switchyard,同时保留 tom-agent-mini 作为兼容别名。

API 总览

稳定前缀:/api/v1

兼容别名:

  • GET /health
  • /sessions/*

路由:

| Method | Path | 用途 | | --- | --- | --- | | GET | /api/v1/health | 健康检查。 | | GET | /api/v1/providers | 查看 provider 能力摘要。 | | GET | /api/v1/openapi.json | 简单 OpenAPI 3.1 文档。 | | GET | /api/v1/fs/directories?path=... | 浏览本地目录,供 UI 选择工作目录。 | | POST | /api/v1/fs/directories | 创建子目录。 | | POST | /api/v1/sessions | 创建 Agent 会话。 | | GET | /api/v1/sessions | 列出会话。 | | GET | /api/v1/sessions/:id | 获取单个会话快照。 | | GET | /api/v1/sessions/:id/events | 订阅 SSE 事件流。 | | POST | /api/v1/sessions/:id/messages | 发送用户消息。 | | POST | /api/v1/sessions/:id/interrupt | 中断当前任务。 | | DELETE | /api/v1/sessions/:id | 销毁并删除会话。 |

Session Snapshot

会话接口返回统一结构:

type AgentSessionSnapshot = {
  id: string;
  provider: "codex" | "cursor" | "claude";
  cwd: string;
  executable: string;
  state: "created" | "starting" | "idle" | "busy" | "closing" | "closed" | "failed";
  providerSessionId: string | null;
  activeTurnId: string | null;
  createdAt: string;
  updatedAt: string;
  eventsUrl: string;
};

前端或其他客户端使用 eventsUrl 建立 EventSource

创建会话

请求:

POST /api/v1/sessions
Content-Type: application/json
{
  "provider": "codex",
  "cwd": "/tmp",
  "prompt": "请用一句话介绍当前目录。",
  "model": "可选模型名",
  "systemPrompt": "可选系统提示",
  "executable": "可选 CLI 名称或路径"
}

字段说明:

  • provider 默认是 codex
  • cwd 默认是服务端进程当前目录;如果传入,必须存在且是目录。
  • prompt 会在会话启动后自动发送。
  • model 会透传给对应 provider。
  • systemPrompt 会作为 Codex base instructions,Claude 则使用 --append-system-prompt
  • executable 默认分别是 codexcursorclaude

curl 示例:

curl -s http://127.0.0.1:8787/api/v1/sessions \
  -H 'Content-Type: application/json' \
  -d '{"provider":"codex","cwd":"/tmp","prompt":"请用一句话介绍当前目录。"}'

订阅事件

SSE 地址:

curl -N http://127.0.0.1:8787/api/v1/sessions/<id>/events

浏览器示例:

const source = new EventSource("/api/v1/sessions/<id>/events");

source.addEventListener("text_delta", (event) => {
  const data = JSON.parse(event.data);
  console.log(data.text);
});

source.addEventListener("error", (event) => {
  console.error(JSON.parse(event.data));
});

每个 SSE frame 格式:

event: text_delta
data: {"id":"...","at":"2026-06-07T00:00:00.000Z","type":"text_delta","text":"hello"}

常见事件:

| Event | 含义 | | --- | --- | | ready | SSE 已连接。 | | created | 会话已创建。 | | state | 会话状态变化。 | | session | 获取到 provider session/thread id。 | | text_delta | 模型流式文本,前端应该增量渲染它。 | | text | 完整模型文本。 | | raw | provider 原始 stream-json payload。 | | stderr | provider 进程 stderr。 | | process_start | per-task provider 进程启动。 | | process_exit | per-task provider 进程退出。 | | error | 归一化错误事件。 |

服务端每个会话保留最近 500 条事件,新 SSE 客户端连接后会自动 replay。

发送消息

请求:

POST /api/v1/sessions/:id/messages
Content-Type: application/json
{ "text": "继续,用更短的表达。" }

也可以使用 message 作为 text 的别名。

curl 示例:

curl -s http://127.0.0.1:8787/api/v1/sessions/<id>/messages \
  -H 'Content-Type: application/json' \
  -d '{"text":"继续,用更短的表达。"}'

响应:

{ "accepted": true }

Codex 在 busy 时会走 turn/steer。Cursor 和 Claude 是 per-task 进程,busy 时会拒绝新消息。

图片附件

消息可以携带图片 data URL:

{
  "text": "描述这张截图。",
  "attachments": [
    {
      "name": "screen.png",
      "mimeType": "image/png",
      "dataUrl": "data:image/png;base64,iVBORw0KGgo..."
    }
  ]
}

限制:

  • 每次最多 8 张图片。
  • 单张最多 6 MB。
  • 支持 image/pngimage/jpegimage/webpimage/gif
  • 服务端会检查文件头,确认内容和 MIME 类型匹配。

服务端会把图片写入临时目录,然后把本地文件路径追加到消息文本里交给 Agent。

中断和删除

中断当前任务:

curl -s http://127.0.0.1:8787/api/v1/sessions/<id>/interrupt \
  -X POST \
  -H 'Content-Type: application/json' \
  -d '{}'

删除会话:

curl -s http://127.0.0.1:8787/api/v1/sessions/<id> -X DELETE

删除 Codex 会话时,会尝试调用 thread/dispose,终止进程,并移除内存 session。

目录选择 API

浏览目录:

curl -s 'http://127.0.0.1:8787/api/v1/fs/directories?path=/tmp'

创建子目录:

curl -s http://127.0.0.1:8787/api/v1/fs/directories \
  -H 'Content-Type: application/json' \
  -d '{"path":"/tmp","name":"switchyard-demo"}'

这个 API 主要给本地 UI 使用。它能读取目录名和创建文件夹,因此请保持服务只在本机可访问。

最小 Node 客户端

const baseUrl = "http://127.0.0.1:8787";

const createRes = await fetch(`${baseUrl}/api/v1/sessions`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    provider: "codex",
    cwd: process.cwd(),
    prompt: "列出这个项目里最重要的文件。"
  })
});

const session = await createRes.json();
console.log(session.id, session.eventsUrl);

await fetch(`${baseUrl}/api/v1/sessions/${session.id}/messages`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ text: "现在建议一个小改进。" })
});

Node 里订阅 SSE 可以使用 eventsource 这类 EventSource 兼容包。

如何复刻

  1. 用 pnpm workspace 创建 TypeScript Hono 后端,并把 Vite React 前端放在 web/
  2. 后端主 API 使用 /api/v1 前缀,兼容路由只在必要时保留。
  3. 把每个 provider 建模为共享 BaseSessionEventBus 的 session。
  4. Codex 使用持久 app-server,通过 stdio JSON-RPC 驱动。
  5. Cursor 和 Claude 使用 per-task process,并把 stream-json 归一成统一事件。
  6. session 默认只放内存,除非明确要做持久化。
  7. 用户可见输出走 text_delta;诊断信息走 rawstderrerror
  8. 服务默认绑定 127.0.0.1

安全模型

Switchyard 面向可信本地使用。

  • 默认 host 是 127.0.0.1
  • 带不可信浏览器 Origin 的变更请求会被拒绝。
  • 非空 JSON body 必须使用 application/json
  • 图片上传有大小限制,并会校验文件签名。
  • 服务可以启动本地 CLI 进程,不要当作公开多用户服务。

如果绑定到 0.0.0.0,CLI 会打印警告。只应在可信网络里这样做。

脚本

pnpm typecheck
pnpm build
pnpm build:web
pnpm build:all
pnpm smoke
pnpm serve
pnpm serve:bun

发布

pnpm build:all
pnpm smoke
npm pack --dry-run
npm publish --registry https://registry.npmjs.org/

npm 包名是 [email protected]