[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-82238":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":17,"compositeScore":18,"rankGlobal":10,"rankLanguage":10,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":22,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":15,"starSnapshotCount":15,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},82238,"proxyline","openclaw\u002Fproxyline","openclaw","Process-global proxy routing for Node.js","https:\u002F\u002Fproxyline.dev",null,"TypeScript",25,5,21,0,3,9,50.13,"MIT License",false,"main",true,[],"2026-06-12 04:01:37","# 🌐 Proxyline\n\n![Proxyline banner](docs\u002Fassets\u002Freadme-banner.jpg)\n\n[![npm](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002F%40openclaw%2Fproxyline.svg)](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@openclaw\u002Fproxyline)\n[![node](https:\u002F\u002Fimg.shields.io\u002Fnode\u002Fv\u002F%40openclaw%2Fproxyline.svg)](https:\u002F\u002Fnodejs.org\u002F)\n[![license](https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fl\u002F%40openclaw%2Fproxyline.svg)](.\u002FLICENSE)\n\nProcess-global proxy routing for Node.js. One install replaces `node:http`, `node:https`, the undici\u002Ffetch global dispatcher, and provides WebSocket and explicit HTTP CONNECT helpers for the same policy.\n\nProxyline exists to make proxy behavior **explicit, observable, and hard to bypass accidentally** — so that \"all egress goes through this gateway\" is something you encode in code rather than hope for from environment variables.\n\nProxyline's runtime assurances assume it is installed before application and plugin networking code is loaded. Code that captured networking functions before installation, uses raw sockets, or owns a private\u002Fnative transport stack is outside the normal Proxyline model.\n\nWebsite: [proxyline.dev](https:\u002F\u002Fproxyline.dev)\n\n## Highlights\n\n- **Two modes.** `managed` forces traffic through a configured proxy and fails closed on bad config. `ambient` reads `HTTP_PROXY` \u002F `HTTPS_PROXY` \u002F `ALL_PROXY` \u002F `NO_PROXY` for tooling that needs environment compatibility.\n- **Covers the surfaces that matter.** `http.request`, `http.get`, `https.request`, `https.get`, both global agents, the undici global dispatcher, and helpers for WebSocket agents and HTTP CONNECT sockets.\n- **Replaces caller agents.** In managed mode and active ambient mode, a per-request `http.Agent` passed by a library does not bypass the proxy. TLS options on the caller agent (`ca`, `cert`, `key`, `rejectUnauthorized`, …) are preserved so destination TLS still validates.\n- **Intentional bypasses only.** Managed mode can accept a `bypassPolicy` callback, process-wide `registerBypass()`, or async-scoped `withBypass()` calls for trusted loopback or control-plane traffic that must stay direct; every bypass is visible through `explain()`.\n- **Embeddable runtime controls.** `ifActive` handles process singleton reuse\u002Freplacement, `undici` options tune dispatcher defaults, and `isProxylineDispatcher()` identifies Proxyline-owned dispatchers without constructor-name checks.\n- **Scoped proxy CA trust.** `proxyTls.ca` \u002F `proxyTls.caFile` trust a private CA for the proxy endpoint only — no `NODE_EXTRA_CA_CERTS` and no `NODE_TLS_REJECT_UNAUTHORIZED=0`.\n- **Observable.** `proxy.explain(url)` returns a structured decision (`proxied` \u002F `direct` with a `reason`), and an `onEvent` callback receives `runtime.installed`, `runtime.stopped`, and per-decision events. Proxy URLs are credential-redacted.\n- **Restoreable.** `proxy.stop()` restores the captured Node HTTP(S) methods, global agents, undici dispatcher, and fetch globals. The runtime is a process-wide singleton; by default a second active install throws `RUNTIME_ALREADY_ACTIVE`, while `ifActive` can reuse or replace intentionally.\n\n## Install\n\n```bash\npnpm add @openclaw\u002Fproxyline\n# or\nnpm install @openclaw\u002Fproxyline\n```\n\nRequires Node 22.19.0+ and a host `undici` dependency compatible with `>=8.3.0 \u003C9`.\n\n## Quick start\n\n### Managed mode\n\n```ts\nimport { installGlobalProxy } from \"@openclaw\u002Fproxyline\";\n\nconst proxy = installGlobalProxy({\n  mode: \"managed\",\n  proxyUrl: \"https:\u002F\u002Fproxy.corp.example:8443\",\n  proxyTls: { caFile: \"\u002Fetc\u002Fproxy-ca.pem\" },\n  onEvent: (event) => console.debug(\"[proxyline]\", event),\n});\n\nconsole.log(proxy.explain(\"https:\u002F\u002Fapi.example.com\u002F\"));\n```\n\n### Ambient mode\n\n```ts\nimport { installGlobalProxy } from \"@openclaw\u002Fproxyline\";\n\nconst proxy = installGlobalProxy({ mode: \"ambient\" });\nif (!proxy.active) {\n  console.warn(\"no HTTP_PROXY\u002FHTTPS_PROXY\u002FALL_PROXY set — egress will be direct\");\n}\n```\n\n### WebSocket\n\n```ts\nimport WebSocket from \"ws\";\n\nconst socket = new WebSocket(\"wss:\u002F\u002Fevents.example.com\u002F\", {\n  agent: proxy.createWebSocketAgent(),\n});\n```\n\n### Explicit HTTP CONNECT\n\n```ts\nimport { openProxyConnectTunnel } from \"@openclaw\u002Fproxyline\";\n\nconst socket = await openProxyConnectTunnel({\n  proxyUrl: \"https:\u002F\u002Fproxy.corp.example:8443\",\n  proxyTls: { caFile: \"\u002Fetc\u002Fproxy-ca.pem\" },\n  targetHost: \"api.example.com\",\n  targetPort: 443,\n  timeoutMs: 2_000,\n});\n```\n\n### Conditional Node agent\n\n```ts\nimport { createAmbientNodeProxyAgent } from \"@openclaw\u002Fproxyline\";\n\nconst agent = createAmbientNodeProxyAgent({\n  protocol: \"https\",\n  proxyTls: { caFile: \"\u002Fetc\u002Fproxy-ca.pem\" },\n});\n```\n\nThe helper returns `undefined` when ambient proxy env is not configured, so callers can pass an agent only when needed.\nIt uses Proxyline's built-in HTTP\u002FHTTPS Node agent, and `proxyTls` applies only to HTTPS proxy endpoints. SOCKS and PAC proxy schemes remain unsupported.\n\n## Product coverage\n\n- `http.request` \u002F `http.get`: covered by global method patching and global agent replacement.\n- `https.request` \u002F `https.get`: covered by global method patching and global agent replacement.\n- `globalThis.fetch`: covered by the fetch patch, including explicit dispatcher options and later Undici global dispatcher replacement in managed mode.\n- Undici global dispatcher: installed for Undici APIs that read the current process dispatcher.\n- WebSocket clients accepting a Node `agent`: covered with `proxy.createWebSocketAgent()`.\n- Caller-built `http.Agent` \u002F `https.Agent`: overridden in managed and active ambient mode, with TLS options preserved.\n- Explicit HTTP CONNECT sockets: covered with `openProxyConnectTunnel()`.\n- Raw `net.connect` \u002F `tls.connect`: out of scope; see [Security](.\u002Fdocs\u002Fsecurity.md).\n- Native or private transport stacks: out of scope; see [Security](.\u002Fdocs\u002Fsecurity.md).\n\n## Why not just env vars?\n\nEnvironment-based proxies are best-effort. A missing variable, a stale shell, a `NO_PROXY` typo, or a library that built its own `Dispatcher` quietly turns \"always through the proxy\" into \"sometimes direct.\" Proxyline encodes the policy in code, replaces caller-built agents, and exposes a structured decision so logs can prove every request went the right way.\n\nFor tooling that *should* honor whatever the operator configured, ambient mode keeps the conventional behavior — with the same observability and the same credential redaction.\n\n## Documentation\n\nFull docs live in [`docs\u002F`](.\u002Fdocs\u002FREADME.md):\n\n- [Getting Started](.\u002Fdocs\u002Fgetting-started.md)\n- [Modes](.\u002Fdocs\u002Fmodes.md) — managed vs ambient\n- [Surfaces](.\u002Fdocs\u002Fsurfaces.md) — per-API behavior\n- [API Reference](.\u002Fdocs\u002Fapi-reference.md)\n- [Environment Variables](.\u002Fdocs\u002Fenvironment-variables.md)\n- [Proxy TLS](.\u002Fdocs\u002Fproxy-tls.md)\n- [Observability](.\u002Fdocs\u002Fobservability.md)\n- [Security](.\u002Fdocs\u002Fsecurity.md)\n- [Troubleshooting](.\u002Fdocs\u002Ftroubleshooting.md)\n- [Testing](.\u002Fdocs\u002Ftesting.md)\n\n## Limits\n\nProxyline is a Node-process runtime, not an operating-system sandbox. Code can still bypass it by using raw `net`, raw `tls`, custom native networking, or a library that owns a private transport stack. Anything that captured `http.request` or `https.request` before Proxyline installed also bypasses it — install before loading third-party integrations when proxy routing is a security policy. See [`docs\u002Fsecurity.md`](.\u002Fdocs\u002Fsecurity.md) for the full threat model.\n\n## License\n\n[MIT](.\u002FLICENSE)\n","Proxyline 是一个用于 Node.js 的全局代理路由工具，旨在使所有出站流量通过指定的代理服务器。其核心功能包括两种模式：`managed` 模式强制所有流量通过配置的代理并在配置错误时关闭；`ambient` 模式则读取环境变量中的代理设置以兼容现有工具。该项目覆盖了 `http.request`, `https.request` 等关键网络接口，并且能够替换调用者提供的代理以确保策略的一致性。此外，它还支持自定义绕过策略、嵌入式运行时控制及代理 CA 信任等功能，同时提供详细的决策日志和恢复机制。适用于需要严格控制出站流量的企业级应用或开发测试环境。",2,"2026-06-11 04:08:09","CREATED_QUERY"]