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

@jiangsier/local-spy

v0.2.0

Published

Local HTTP traffic inspector for macOS — passive capture with an opt-in MITM HTTPS proxy and Chrome-style Copy-as-cURL

Downloads

30

Readme

local-spy

English | 简体中文

local-spy 是一款 macOS 上的本地 HTTP 流量检查器:从你 Mac 上正在运行的进程中任选一个,即可在浏览器中实时查看它的 HTTP 请求与响应——请求头、报文主体、耗时、状态码——一切都在线路上发生的那一刻同步呈现。它就像 Chrome DevTools 的 Network 面板,但面向的是原生进程:CLI 工具、后台守护进程、构建工具、Electron 辅助进程,任何使用纯 HTTP/1.x 通信的程序。

无需配置代理。无需注入代码。后台也不会运行任何 root 守护进程。

安装

仅支持 macOS · 需要 Node.js ≥ 22.18 — 见环境要求

从 npm 全局安装,然后启动:

npm install -g @jiangsier/local-spy
local-spy              # 在 http://127.0.0.1:7634 提供 UI

打开 http://127.0.0.1:7634,选定进程并按下 Capture —— 之后的操作与快速开始完全一致。用 Ctrl-C 停止 CLI;若 7634 被占用可使用 PORT=8080 local-spy,需要启用可选的 MITM 代理时使用 LOCAL_SPY_PROXY=1 local-spy(CLI 会继承你的环境变量,与 run.sh 采用 env -i 净化启动的方式不同)。

如需从源码运行(克隆、npm install./scripts/run.sh),参见快速开始

特性

  • 进程选择器 — 实时列出运行中的进程及其 CPU/内存占用与 TCP 端口数;选定一个 pid,点击 Capture。
  • 实时流量表 — 每一次 HTTP 交换(方法、URL、状态、大小、耗时、TLS 徽标)均经 WebSocket 流式推送,pending → complete 状态转换实时呈现。
  • 完整的请求/响应详情 — 请求头、解码后的报文主体(透明处理 gzip/chunked)、二进制主体以 base64 呈现、MIME 类型。
  • 右键任意请求 — 每行都提供与 Chrome DevTools 一致的右键菜单:打开详情、复制 URL,或按 Chrome 的命令约定复制为 cURL(URL 在前、Cookie 走 -b、不带请求体的 GET 与带请求体的 POST 不输出 -X)。
  • 真正的数据包捕获 — 基于 tcpdump 通过 macOS BPF 设备实现:没有任何伪造内容;在 lo0 + en0 上执行 classic-pcap 捕获(pktap pid 归属已实现,但在 Apple 的 tcpdump 上处于休眠状态——见“限制”)。
  • 按目标进程限定范围 — 通过 lsof 端口集进行准入控制:只有属于所选进程的流量才会被重组并展示。
  • 健壮的 TCP 重组 — 序号回绕、重传、乱序分节、中途接入、FIN/RST 拆除;未获准的流会被缓存,获准后随即回放。
  • 设计上即安全 — 服务器绝不以 root 运行;仅有一个约 290 行、可完整审计的辅助脚本会通过 Apple 自带的授权对话框获得权限提升。
  • 一条命令管理生命周期./scripts/run.sh / ./scripts/stop.sh,附带 pidfile 管理、过期构建检测以及遗留 root 进程检查。

环境要求

| 要求 | 说明 | |---|---| | macOS 12+ | 使用 BPF 设备、tcpdumplsofosascript(均随 macOS 附带) | | Node.js ≥ 22.18 | 在 package.jsonengines 中声明;版本不匹配时 npm 只会发出警告(EBADENGINE),除非设置了 engine-strict——可用 node --version 核实 | | 你的管理员密码 | 由 macOS(而非 local-spy)在每个捕获会话中询问一次,用于授权抓包 | | GUI 会话 | 授权对话框需要窗口服务器——请从 Terminal.app 运行,而不是无图形界面的 SSH |

快速开始

npm install          # once
./scripts/run.sh     # builds if needed, starts local-spy on http://127.0.0.1:7634

注:react、MUI 与 i18next 位于 devDependencies,因为 SPA 已预先构建——npm install --omit=dev 加上 npm start 就足以运行服务器并托管已构建好的 UI;完整安装 + npm run build 仅在进行 Web 开发时才需要。

打开 http://127.0.0.1:7634,从列表中选定一个进程,然后按下 Capture

  1. macOS 弹出标准的管理员密码对话框(这是 Apple 的授权 UI——local-spy 永远看不到你的密码)。请点击批准。
  2. 该进程的流量会在一两秒内开始流入表格。
  3. 检查各条目;完成后按下 Stop

结束时:

./scripts/stop.sh    # stops capture + server, cleans runtime files (keeps logs)

run.sh 是幂等的——运行两次只会提示 "already running"。如果 7634 端口已被占用,可使用 PORT=8080 ./scripts/run.sh

工作原理

┌────────────┐  REST + WS   ┌─────────────────────────── server (never root) ───────────────────────────┐
│  web UI    │◄────────────►│  express + ws                                                              │
│ (browser)  │              │   │ capture manager                                                        │
└────────────┘              │   │  1. elevation: osascript "with administrator privileges"                │
                            │   │     → runs scripts/capture-helper.sh (the ONLY root code)              │
                            │   │  2. helper starts tcpdump pktap/legacy  →  .run/pcap/*.pcap            │
                            │   │  3. pcap tailer follows the files as they grow                          │
                            │   │  4. pcap → IP → TCP reassembly                                          │
                            │   │     · admission gating by lsof port sets of the target pid             │
                            │   │     · unadmitted flows buffered, replayed from stream start             │
                            │   │  5. HTTP/1.x reconstruction (http-parser-js) + gzip/chunked decoding   │
                            │   │  6. ring store (2000 entries) → WS broadcast to the UI                 │
                            └─────────────────────────────────────────────────────────────────────────────┘

分步说明:

  1. UI → REST:浏览器选定一个 pid 并发出 POST /api/capture/start {"pid":1234}
  2. 权限提升:服务器请求 macOS(通过 osascript)以管理员权限运行 scripts/capture-helper.sh。Apple 的对话框弹出;你批准之后,以 root 运行的是这个辅助脚本——而非服务器。
  3. 捕获:辅助脚本按接口(lo0 + en0;Apple 的 tcpdump 会为 pktap 设备强制使用 PCAP-NG,因此 auto 模式直接选择该方式)启动 tcpdump,BPF 过滤器排除 local-spy 自身的端口,并将 classic pcap 文件写入 .run/pcap/。辅助脚本的每一步都会记录到 .run/logs/capture-helper.log
  4. 跟踪与重组:服务器持续 tail 这些 pcap 文件,解析链路层/IP/TCP,重组字节流,并按目标进程的 lsof 端口集对流进行准入控制(每秒刷新一次,并配合回放缓存,因此新连接等待准入期间不会丢失任何数据)。
  5. HTTP 重建:字节流按 HTTP/1.x 解析;gzip 与 chunked 主体在服务器端解码;每条消息的主体上限为 512 KiB。
  6. 存储与推送:各次交换落入一个 2000 条目的环形缓冲区,并经 WebSocket 推送到浏览器。

授权与安全模型

  • local-spy 服务器绝不以 root 运行。 它以你的普通用户身份运行,仅绑定 127.0.0.1,并由 ./scripts/run.sh 在净化后的环境(env -i)中启动。

  • 只有 scripts/capture-helper.sh 以 root 运行 —— 一个约 290 行、可以一口气读完的 POSIX shell 脚本。它启动 tcpdump、写入 pidfile、轮询 stopfile、然后退出。它经由 osascript … with administrator privileges 调用,也就是 Apple 的标准授权对话框——你的密码交给 macOS,永远不会交给 local-spy。

  • 管理员授权会被 macOS 短暂缓存(约 5 分钟),因此该时间窗内的连续捕获通常不会再次弹出提示。

  • 停止操作从不需要 sudo。 ./scripts/stop.sh(以及 UI 的 Stop 按钮)只需 touch .run/stop-capture.flag;root 辅助脚本每 0.5 秒轮询一次该文件,并终止自身及其 tcpdump。

  • 限定范围的清理。 stop.sh 只会匹配命令行参数中引用了本项目 .run/pcap 的 tcpdump 进程——你自己无关的 tcpdump 会话绝不会收到信号。若发现遗留进程,它会打印出确切的清理命令:

    pgrep -fl 'tcpdump.*\.run/pcap'        # check first
    sudo pkill -f 'tcpdump.*\.run/pcap'    # kill local-spy's tcpdumps only
  • 手动回退。 如果你宁愿自行授权辅助脚本,而不是点击对话框(或对话框根本无法弹出),请在项目根目录运行:

    sudo sh scripts/capture-helper.sh --pcap-dir .run/pcap --filter 'tcp and not port 7634 and not port 5173' --iface lo0 --stopfile .run/stop-capture.flag --seq 0

    然后在 UI 中按下 Capture——服务器会接管已在运行的捕获:它检测到存活的 tcpdump pidfile 以及 .run/pcap/ 下辅助脚本的 mode 文件后,会完全跳过自身的权限提升提示(不会出现第二个授权对话框),绝不截断或重建 pcap 文件,并从手动捕获迄今为止写入的内容继续 tail。Stop 的行为与平时完全一致——共享的 stopfile 同样会关闭手动启动的辅助脚本。这条一模一样的命令也内置于 Web UI 的帮助页面中。

隔离模型

local-spy 有意使用 Docker/虚拟机:容器或虚拟机无法捕获宿主机的环回与物理接口流量——你检查到的将是错误的网络命名空间。隔离改由以下设计提供:

  • 干净的环境:服务器通过 env -i 启动,仅保留 HOME、最小化的系统 PATHPORT,外加仅在调用方显式设置时透传的可选代理变量(LOCAL_SPY_PROXYLOCAL_SPY_PROXY_PORTNO_PROXY/no_proxy)——不继承其余任何 shell/开发工具链变量。
  • 项目内的状态:所有运行时状态都存放在项目内的 .run/ 下(pid/logs/pcap/、stopfile)。不会在其他位置写入任何内容;.run/ 已被 gitignore。
  • 专用端口:API/UI 监听 7634(可用 PORT=… 覆盖);BPF 过滤器排除 local-spy 自身的端口,因此检查器无法捕获到自己。
  • 仅环回地址:服务器只绑定 127.0.0.1——网络上的其他主机无法访问它。
  • Pidfile 生命周期run.sh 拒绝重复启动(存活 pid 检查),stop.sh 在发送信号前会核实该 pid 确实属于 local-spy。

HTTPS MITM 代理(可选启用)

被动捕获只能把 TLS 流量显示为不透明的元数据(见限制)。当你需要看清 HTTPS 内部时,local-spy 内置了一个可选启用的本地 HTTPS 解密代理:被你显式指向它的客户端,其 TLS 流量会在本地被解密,解密后的完整交换——完整的请求/响应头与主体,外加 Copy-as-cURL——会与被动条目一同出现在同一个 UI 中。被动抓包行为保持不变,且该功能默认关闭

启用代理

启动服务器时设置 LOCAL_SPY_PROXY=1——该标志(以及 LOCAL_SPY_PROXY_PORTNO_PROXY)可以穿透 run.sh 的净化环境:

LOCAL_SPY_PROXY=1 ./scripts/run.sh   # primary — managed launch (pidfile, log, ./scripts/stop.sh)
LOCAL_SPY_PROXY=1 npm start          # alternative — dist must exist: npm run build (or one prior ./scripts/run.sh boot)

代理在 127.0.0.1:7635 上监听 CONNECT(可用 LOCAL_SPY_PROXY_PORT 覆盖);启动日志中的 proxy: 一行会确认其状态。run.sh 启动的实例用 ./scripts/stop.sh 停止;直接启动的服务器(无 pidfile)用 Ctrl+C。

信任 CA(一次性)

代理通过出示由本地 CA 签发的逐主机证书来解密 TLS,该 CA 由 local-spy 在首次使用时生成。只有当该 CA 受到信任后,客户端才会接受这些证书——一次性设置,两种方式任选:

  • 帮助页面(最简单):按下 Trust——CA 会被记入当前用户登录钥匙串的信任设置,无需管理员密码(若 macOS 弹出证书信任确认框,点击确认即可)。Untrust 可再次移除信任。API 在每步之后都会用 security verify-cert 校验信任是否真正生效,失败会如实上报。

  • 手动,在项目根目录执行:

    sh scripts/trust-ca.sh      # 信任 CA(无需 sudo)
    sh scripts/untrust-ca.sh    # 移除信任

CA 存放在 .run/ca/(已 gitignore;私钥权限 0600),并在重启后保留——请妥善保管。纯 Node 客户端不读取系统钥匙串,因此需要显式指向该 CA:

NODE_EXTRA_CA_CERTS="$(pwd)/.run/ca/ca.pem" node your-client.js

将客户端指向代理

先做一个快速健全性检查(在项目根目录执行——直接信任该 CA,无需写入钥匙串):

curl --cacert .run/ca/ca.pem -x http://127.0.0.1:7635 https://example.com/ -o /dev/null

不同应用支持的代理机制各不相同——按以下顺序逐档尝试,并实证验证你的应用响应哪一档:

  1. Node ≥ 24 —— 携带代理环境变量重新启动客户端:

    NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://127.0.0.1:7635 HTTP_PROXY=http://127.0.0.1:7635 NO_PROXY=localhost,127.0.0.1 node your-client.js
  2. Electron / Chromium —— 向应用可执行文件传入启动开关:

    your-app --proxy-server=http://127.0.0.1:7635
  3. macOS 系统代理(最后手段——影响机器上的所有应用):

    networksetup -setsecurewebproxy "Wi-Fi" 127.0.0.1 7635
    networksetup -setsecurewebproxystate "Wi-Fi" off   # revert when done

已在本机(macOS 26)实证验证:curl 携带 --cacert .run/ca/ca.pem 可以工作,且在通过 Trust 按钮信任 CA(用户域钥匙串信任)之后,即使不带该参数也能工作;Node ≥ 24 的 fetch 恰好需要 NODE_USE_ENV_PROXY=1 + HTTPS_PROXY/HTTP_PROXY(外加 NODE_EXTRA_CA_CERTS)——已在 Node 25.2.1 上验证——缺少该标志时 fetch 会静默绕过代理。

安全须知

  • 解密后的机密会变得可见。 解密流量中的一切——包括 AuthorizationCookie 请求头——都会显示在本应用中,并被原样嵌入导出的 cURL 命令,不做任何脱敏(与浏览器 DevTools 的 "Copy as cURL" 语义相同)。请将条目与导出内容视为敏感信息。
  • 妥善保管 .run/ca/ 任何持有 CA 私钥的人,都可以对信任该 CA 的客户端冒充任意主机。
  • 仅限单机使用。 切勿共享该 CA,也不要在共享机器上启用此功能。
  • 证书固定(certificate pinning)。 执行证书固定的应用会拒绝代理签发的叶子证书;此时 local-spy 会回退为不透明隧道(条目仅保留元数据),而不会破坏应用。
  • 默认关闭,正是出于以上全部原因。

工作原理

代理拦截 HTTP CONNECT 隧道,用本地 CA 按需签发的逐主机叶子证书(RSA-2048、30 天有效期)终结 TLS,两侧均使用 HTTP/1.1,并向真实上游重新加密。主体语义与被动管线完全一致——每条消息保留 512 KiB 前缀、byteSize 为真实线上总字节数、截断会被标记——解密后的交换会落入与被动捕获相同的 2000 条目环形缓冲区。

代理集成测试作为 npm test 的一部分运行(见测试)。

卸载 / 回滚

回滚代码不会清除机器上的状态。无论在回滚之前还是之后执行,都请移除 CA 信任并删除 CA 与私钥:

sh scripts/untrust-ca.sh                          # if the script is already gone:
security remove-trusted-cert -d .run/ca/ca.pem    # ...remove the trust record directly
rm -rf .run/ca                                    # deletes the CA + private key

遗留的受信任 CA 会让任何持有其私钥的程序在你的机器上冒充任意 HTTPS 站点。

测试

三个层级:

npm test                    # offline unit/integration suite (synthetic pcaps, no privileges)
./scripts/e2e.sh --synthetic  # same suite via the e2e driver (also no privileges)
./scripts/e2e.sh            # REAL end-to-end (see below)

合成管线测试(npm test,共 32 项:25 项管线 + 6 项来源/主机白名单 + 1 项工具链冒烟)

确定性、手工构造的 pcap/IP/TCP 夹具驱动完整的重组 + HTTP 重建管线:

  • 简单 GET + 200 响应 → 一次完整交换
  • 携带 JSON 主体的 POST 与 JSON echo 响应
  • chunked 传输 + gzip 内容编码解码还原为原始 JSON
  • 跨约 12 个分节的 64 KiB 响应按字节精确重组
  • TCP 序号回绕(ISN 接近 2³²)
  • 重传时每个字节恰好交付一次
  • 乱序分节(1、3、2、4)按顺序组装
  • 中途接入 → 部分交换,解析器重新同步到下一个请求
  • FIN 拆除关闭双向;RST 中止并按部分交换终结
  • DLT_NULL(环回)解码,兼容小端与大端 AF 头
  • 未获准的流先缓存,获准后从流起点回放
  • 带 VLAN 标签的以太网帧可解析;二进制主体以 base64 呈现
  • pcapng 魔数与不支持的 DLT(含错误的 pktap 链路类型)产生清晰的错误
  • lsof 输出解析(IPv4/IPv6、LISTEN 通配、已建立连接的对端)
  • pktap pid 标记解码(DLT_PKTAP 149):被包裹的 DLT_NULL 帧可解析,分节携带 pid + 接口
  • pktap pid 准入——目标 pid 匹配则立即放行该流;不匹配则永不放行
  • pktap 头部填充(156 字节的 xnu 结构)被跳过以抵达内层帧
  • 被截断/畸形的 pktap 记录会被跳过,完好的记录仍可解析
  • 半关闭:客户端在响应到达前发出 FIN,仍会得到一次完整交换
  • close 定界的响应(无 Content-Length/chunked)在流结束(EOF)时完成
  • 错误后的重同步恢复从干净的解析器状态开始(不会并入损坏的消息)
  • 两侧同时重同步保持相互隔离(无跨方向残留污染)

真实端到端(./scripts/e2e.sh

启动完整技术栈,拉起一个临时 HTTP 目标(test/e2e/server.js,端口 18888)和一个临时客户端进程(test/e2e/client.js),后者发出三个已知请求(一次 gzip JSON GET、一次 JSON echo POST、一次 64 KiB GET),并携带唯一的 X-Test-Marker 请求头。驱动程序对该客户端 pid 启动真实捕获——这会触发 macOS 管理员密码提示——等待后通过 API 断言三次交换均被正确重建(gzip 已解码、主体按字节精确、大小准确)。使用 --keep-service 可让服务在结束后继续运行。需要 GUI 会话(请在 Terminal.app 中运行)。

限制

  • 仅支持明文 HTTP/1.x。 TLS 流量会显示锁形徽标及连接元数据(地址、端口、耗时、大小),但加密负载按设计保持不透明。HTTP/2 不会被重组。
  • 接口:环回(lo0)+ 默认活动接口(en0);其他接口可通过手动辅助命令捕获。pktap 限制(macOS):Apple 的 tcpdump(tcpdump-158)会为任何 pktap* 设备强制写入 PCAP-NG(open_capture: DLT_PKTAP ⇒ ndo_Pflag++pcap_ng_dump_open),而 local-spy 仅解析 classic pcap——因此 --mode auto 会直接选择 legacy,而不是去尝试一场永远无法就绪的捕获。完整的 pktap 机制(spawn、classic 魔数探测、管线中的 pid 准入、--mode pktap 诊断路径)均已实现并保留:对于未来任何会写 classic pcap 的 tcpdump,它都会自动就绪(DLT_PKTAP 的链路类型号是 149——Apple 将其定义为 DLT_USER2;258 是上游的文件别名;278 是 OPENVIZSLA,并非 pktap)。
  • 新连接的端口拾取 ≤ 约 1 秒lsof 端口集每秒刷新一次;回放缓存意味着新开连接上的流量一旦获准就会被回放,而不是丢失。
  • 主体上限 512 KiB(每条消息,超出即被截断并标记);环形存储 2000 条目(最旧的会被逐出)。

故障排查

| 症状 | 原因 → 解决办法 | |---|---| | BPF permission denied / 捕获启动失败,或管理员提示被取消 | 权限提升被拒绝。重试并批准 Apple 的对话框,或使用帮助页面上的手动回退命令(见授权与安全模型)。 | | 捕获正在运行但表格始终为空 | 目标进程可能没有 TCP 流量,或者它是纯 TLS(设计上仅显示元数据)。请查看帮助页面;另请为新连接的准入留出约 1 秒。 | | ELEVATION_UNAVAILABLE / 完全没有弹出提示 | 你处于无图形界面的会话(SSH)。请在 Mac 上真实的 Terminal.app 窗口中运行,或使用手动 sudo 回退命令。 | | 7634 端口已被占用 | PORT=8080 ./scripts/run.sh(捕获过滤器会自动排除你选择的任何端口)。 | | 崩溃后遗留 root tcpdump | 先用 pgrep -fl 'tcpdump.*\.run/pcap' 确认,再执行 sudo pkill -f 'tcpdump.*\.run/pcap'——该模式只会匹配 local-spy 的捕获。 | | 按下 Capture 时出现 CAPTURE_LEFTOVER | 上一次捕获的 root 辅助脚本或 tcpdump 仍存活(崩溃遗留)——服务器拒绝在其之上启动新的捕获。运行 ./scripts/stop.sh(或按上述方法清除遗留进程),然后再次按下 Capture。 | | node: command not found / engine 警告 / 启动时语法错误 | 需要 Node ≥ 22.18:用 node --version 检查,并从 nodejs.org 或 Homebrew 安装。 | | run.sh 提示 pidfile pid is alive but health did not answer | pid 被复用,或服务器已卡死。删除 .run/pid/server.pid(前提是该 pid 不属于 local-spy),或先运行 ./scripts/stop.sh。 |

开发

npm 脚本

| 脚本 | 作用 | |---|---| | npm run build | tsc(服务器 → server/dist)+ Web 类型检查 + vite build(Web → web/dist) | | npm run build:server / npm run build:web | 分别执行上述两半 | | npm run typecheck:web | 仅对 Web 应用做类型检查(tsc -p web/tsconfig.json,不产出文件) | | npm run dev:server | 服务器的 tsx watch 模式 | | npm run dev:web | 面向 UI 的 Vite 开发服务器(热重载) | | npm test | node --test test/*.test.ts(类型剥离,无需编译步骤) | | npm start | 运行已构建的服务器(推荐使用 ./scripts/run.sh) |

Shell 脚本

| 脚本 | 作用 | |---|---| | ./scripts/run.sh | 预检 → 过期即构建 → 隔离启动 → 等待健康检查通过 | | ./scripts/stop.sh | stopfile → 终止服务器 → 清理产物(仅在确认服务器 pid 已终止后)→ 遗留 tcpdump/capture-helper 报告 | | ./scripts/e2e.sh | E2E 驱动(--synthetic、真实模式、--keep-service--help) | | scripts/capture-helper.sh | root 辅助脚本(由服务器运行,或经 sudo 手动运行——绝不会被上述脚本修改) |

端口

| 端口 | 使用者 | |---|---| | 7634 | local-spy 服务器(API + 已构建的 UI);可用 PORT 覆盖 | | 7635 | HTTPS MITM 代理,仅在 LOCAL_SPY_PROXY=1 时启用(默认关闭) | | 5173 | 开发期间的 Vite 开发服务器 | | 18888 | 临时 E2E 目标服务器(test/e2e/server.js) |

项目结构

local-spy/
├── server/src/          # express + ws server
│   ├── index.ts         # entry: http server, static UI, WS upgrade
│   ├── routes.ts        # REST API
│   ├── store.ts         # 2000-entry exchange ring
│   ├── processes.ts     # ps/lsof process listing
│   ├── capture/         # manager, elevation, pcap tailer, TCP/HTTP pipeline
│   ├── proxy/           # opt-in MITM proxy: CA minting, CONNECT interception, recording
│   └── config.ts        # ports, budgets, .run/ paths
├── web/                 # React + MUI inspector UI (vite)
├── shared/protocol.ts   # frozen wire contract shared by server + web
├── scripts/             # run.sh, stop.sh, e2e.sh, capture-helper.sh, trust-ca.sh, untrust-ca.sh
├── test/                # synthetic fixtures + pipeline tests
│   └── e2e/             # server.js, client.js, assert.js (real E2E)
└── .run/                # runtime state (pid/, logs/, pcap/, ca/) — gitignored

许可证

MIT