这个包是什么What this package ispi-client 是远程 pi 会话的客户端一端,且与传输无关:你给它一个 ByteTransportFactory,它负责握手(并核对物理端点报告的逻辑 serverId)、按 id 关联请求、取消、带外附着和带缓冲的服务订阅。它不构造类型化的服务代理——createClientServiceTransport() 把一个按需解析的路由适配成 Chord 的 RemoteServiceTransport,类型由应用的 Chord 绑定提供。它从不自动重连。pi-client is the client end of remote pi sessions, and transport-neutral: give it a ByteTransportFactory and it handles the handshake (checking the logical serverId the physical endpoint reports), id-correlated requests, cancellation, out-of-band attachments and buffered service subscriptions. It builds no typed service proxies: createClientServiceTransport() adapts a lazily resolved route into a Chord RemoteServiceTransport, and types come from the application's Chord bindings. It never reconnects on its own.
1,135 行 TypeScript,两个导入路径:运行时无关的根入口 ".",以及只在 Node / Bun 上可用的 "./unix"(Unix socket 传输与发现)。依赖 chord 与 pi-protocol。唯一的使用者是 coding-agent 的 experimental/ 运行时。1,135 lines of TypeScript, two import paths: the runtime-neutral root "." and the Node / Bun-only "./unix" (Unix-socket transport and discovery). Depends on chord and pi-protocol. Its only user is coding-agent's experimental/ runtime.
一个 Client、一个 Connection、一个可替换的传输One Client, one Connection, a pluggable transport#
一句话结论TakeawayClient(client.ts)面向调用方:请求、订阅、附着与监听器;它内部的 Connection(connection.ts)管状态机、握手和 ServerMessageDecoder;字节的进出全部经过 ByteTransport 契约(transport.ts)。unix.ts 是一个独立子路径,提供 Unix socket 传输与本机服务器发现。Client (client.ts) faces the caller: requests, subscriptions, attachments and listeners; its internal Connection (connection.ts) owns the state machine, the handshake and the ServerMessageDecoder; every byte goes through the ByteTransport contract (transport.ts). unix.ts is a separate subpath with a Unix-socket transport and local server discovery.
要点Key points
- 根入口不碰 Node API:浏览器或 worker 也可以用,只要提供自己的传输(例如 WebSocket)。The root entry touches no Node APIs: a browser or worker can use it with its own transport (a WebSocket, say).
Client只认@earendil-works/chord的服务控制调用(目录、订阅、退订)和状态解码器,不解释任何应用契约。Clientknows only@earendil-works/chord's service-control calls (catalogue, subscribe, unsubscribe) and state decoder; it interprets no application contract.- coding-agent 里有两种传输:
createUnixTransportFactory()和 radius-relay.ts 的 WebSocket 中继传输。coding-agent uses two transports:createUnixTransportFactory()and the WebSocket relay transport in radius-relay.ts.
工作原理How it works
const client = await Client.connect({ serverId, transportFactory }):失败时它会自己dispose()。const client = await Client.connect({ serverId, transportFactory }): on failure it disposes itself.- 低层调用:
client.request({ serverId: client.serverId }, { serviceId, member, args })。A low-level call:client.request({ serverId: client.serverId }, { serviceId, member, args }). - 类型化调用:把
createClientServiceTransport(client, getTarget)交给 Chord 绑定。Typed calls: handcreateClientServiceTransport(client, getTarget)to a Chord binding.
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/index.tsL1 | index.ts | 根入口the root entry |
| packages/client/src/client.tsL62 | Client | 面向调用方the caller-facing class |
| packages/client/src/connection.tsL41 | Connection | 状态机与握手state machine and handshake |
| packages/client/src/transport.tsL1 | ByteTransport | 传输契约the transport contract |
| packages/client/src/unix.tsL88 | createUnixTransportFactory() | Unix 传输the Unix transport |
公共 APIPublic API#
一句话结论Takeaway2 个导入路径(另有 ./package.json)。根入口从 4 个模块导出 5 个值和 10 个类型:Client、createClientServiceTransport 与三种错误,其余是类型;Unix socket 传输与发现在 ./unix。2 import paths (plus ./package.json). The root exports 5 values and 10 types from 4 modules: Client, createClientServiceTransport and three errors, the rest are types; the Unix-socket transport and discovery live in ./unix.
| 导入路径Import path | 目标Target | 说明Notes |
|---|---|---|
@earendil-works/pi-client | ./dist/index.js | Client、Chord 传输适配器、错误类与传输契约Client, the Chord transport adapter, error classes and the transport contract |
@earendil-works/pi-client/unix | ./dist/unix.js | Node / Bun 专用:Unix socket 传输与本机发现Node / Bun only: the Unix-socket transport and local discovery |
@earendil-works/pi-client/package.json | ./package.json | 包元数据package metadata |
常用导出Key exports
| 模块Module | 常用导出Key exports | 用途Use |
|---|---|---|
| packages/client/src/client.ts | Client | 连接、请求、订阅、附着connect, request, subscribe, attach |
| packages/client/src/client.ts | createClientServiceTransport | 把路由适配成 Chord 传输adapt a route to a Chord transport |
| packages/client/src/errors.ts | ServerError DisconnectedError ClientDisposedError | 三种错误the three errors |
| packages/client/src/transport.ts | ByteTransport ByteTransportFactory ByteTransportHandlers | 自定义传输custom transports |
| packages/client/src/types.ts | ClientOptions ServiceSubscription ConnectionStateChange | 选项与句柄options and handles |
子路径 ./unixSubpath ./unix
| 模块Module | 常用导出Key exports | 用途Use |
|---|---|---|
| packages/client/src/unix.ts | createUnixTransportFactory | 一个 Unix socket 传输工厂a Unix-socket transport factory |
| packages/client/src/unix.ts | discoverUnixServers | 列出可以握手的本机服务器list local servers that complete a handshake |
| packages/client/src/unix.ts | UnixTransportOptions UnixServerRoute DiscoverUnixServersOptions | 选项与结果类型option and result types |
根入口 index.ts 的全部导出(按来源模块)Every root index.ts export, by source module
展开全部 4 个来源模块Show all 4 source modules5 values10 types
| 来源模块Source module | 值导出Value exports | 类型导出Type exports |
|---|---|---|
| packages/client/src/client.ts | Client createClientServiceTransport | — |
| packages/client/src/errors.ts | ClientDisposedError DisconnectedError ServerError | — |
| packages/client/src/transport.ts | — | ByteTransport ByteTransportFactory ByteTransportHandlers |
| packages/client/src/types.ts | — | AttachmentChangeListener ClientOptions ConnectionState ConnectionStateChange ListenerErrorHandler ServiceSubscription Unsubscribe |
由构建脚本解析 export { … } from 语句生成。Generated by parsing the export { … } from statements at build time.
连接的三个状态Three connection states#
一句话结论TakeawayConnection 在 disconnected → connecting → connected 之间切换。每次 connect() 都是一次带新序号的尝试:调用工厂得到一个全新的传输,先发客户端 hello,再等待服务端 hello,并要求其中的 serverId 与期望值完全一致。任何失败、关闭或 disconnect() 都回到 disconnected。Connection moves between disconnected → connecting → connected. Every connect() is an attempt with a new sequence number: the factory builds a fresh transport, the client hello goes first, then the server hello must arrive with exactly the expected serverId. Any failure, close or disconnect() returns to disconnected.
要点Key points
- 旧尝试的回调带着旧 id,被
#isCurrent(id)过滤掉;工厂迟到返回的传输会被直接close()。Callbacks from an old attempt carry its id and are filtered by#isCurrent(id); a transport the factory returns late is simply closed. - 握手前的数据、第一条不是
hello、握手后再来hello/hello_error,都是协议错误。Data before the client hello, a first message other thanhello, or anotherhello/hello_errorafter connecting are all protocol errors. - 进入
disconnected时Client清空 hello 与附着、拒绝全部挂起请求、丢弃全部订阅,再通知onConnectionStateChange。Enteringdisconnected, theClientclears hello and the attachment, rejects every pending request, drops every subscription, then notifiesonConnectionStateChange.
工作原理How it works
client.onConnectionStateChange(({ state, error }) => …)观察状态;client.connectionState读取当前值。Observe withclient.onConnectionStateChange(({ state, error }) => …); readclient.connectionStatefor the current value.maxFrameLength必须是 1 到 0xffffffff 的安全整数,否则构造时抛 TypeError。maxFrameLengthmust be a safe integer from 1 to 0xffffffff, or the constructor throws a TypeError.- 关闭时解码器
end():残留半帧会变成截断错误,作为断开原因上报。On close the decoderend()s: a leftover partial frame becomes a truncation error, reported as the disconnect reason.
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/connection.tsL67 | connect() | 开始一次尝试start an attempt |
| packages/client/src/connection.tsL120 | #openTransport() | 打开传输并发 helloopen the transport, send hello |
| packages/client/src/connection.tsL163 | #handleMessage() | 握手校验handshake checks |
| packages/client/src/connection.tsL234 | #fail() | 回到 disconnectedback to disconnected |
| packages/client/src/client.tsL345 | #handleConnectionStateChange() | 断开时的清理cleanup on disconnect |
一个请求的一生The life of one request#
一句话结论Takeaway#request() 先过三道守卫(已 dispose、未连接、已 abort),通过后才分配 request-N 并登记到 #pendingRequests,再编码、发送。回应按 id 取回条目:ok: true 解析结果,ok: false 变成带 code 的 ServerError。abort 立即在本地拒绝,并在已发送时补发一条 cancel。#request() passes three guards first (disposed, not connected, already aborted), and only then allocates request-N, registers it in #pendingRequests, encodes and sends. A response takes the entry back by id: ok: true resolves the result, ok: false becomes a ServerError with a code. An abort rejects locally at once and, if the request was sent, follows up with a cancel.
要点Key points
- 回应可以乱序到达;没有对应条目的回应是协议错误,会断开连接。Responses may arrive out of order; a response with no matching entry is a protocol error and drops the connection.
- abort 之后条目留在表里,直到服务端的
cancelled回应到来,所以那条回应不算“无主”。After an abort the entry stays until the server'scancelledresponse arrives, so that response is not an orphan. - 调用在发送前用 Chord 的
parseServiceCall()校验;编码失败会取回条目并拒绝,什么也不发送。The call is validated with Chord'sparseServiceCall()before sending; an encoding failure takes back the entry and rejects without sending anything.
工作原理How it works
await client.request(target, call, signal):signal是普通的AbortSignal。await client.request(target, call, signal):signalis an ordinaryAbortSignal.client.serviceCatalogue(target)是同一路径上的一个预置调用,结果用parseServiceCatalogue()校验。client.serviceCatalogue(target)is a preset call on the same path, checked withparseServiceCatalogue().- 错误处理:
catch (e) { if (e instanceof ServerError && e.code === "cancelled") … }。Error handling:catch (e) { if (e instanceof ServerError && e.code === "cancelled") … }.
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/client.tsL238 | #request() | 守卫、登记、发送guard, register, send |
| packages/client/src/client.tsL252 | sendCancel() | 补发 cancelthe follow-up cancel |
| packages/client/src/client.tsL304 | #handleMessage() | 按 id 结算settle by id |
| packages/client/src/client.tsL370 | #rejectPendingRequests() | 断开时全部拒绝reject all on disconnect |
| packages/client/src/client.tsL159 | serviceCatalogue() | 预置调用a preset call |
订阅:先快照,后更新Subscriptions: snapshot first, then updates#
一句话结论TakeawaysubscribeService() 先登记一个 service-N 监听器(hydrated = false, ready = false),再发订阅请求。快照之前到达的更新原样缓存;快照解码后 hydrated = true 并解码积压;调用方装好快照、调用 start() 之后,缓冲的更新才按序交付。交付经 deliveryTail 串行,异步监听器也不会乱序。subscribeService() registers a service-N listener first (hydrated = false, ready = false), then sends the subscribe request. Updates arriving before the snapshot are cached raw; once the snapshot decodes, hydrated = true and the backlog is decoded; only after the caller installs the snapshot and calls start() do buffered updates flow, in order. Delivery is chained on deliveryTail, so async listeners stay ordered too.
要点Key points
- 每个订阅有自己的 Chord 状态解码器(
createServiceStateDecoder()):快照和更新都经过它。Every subscription has its own Chord state decoder (createServiceStateDecoder()); snapshots and updates both go through it. dispose()只在仍连接且路由仍是当前路由时发退订;随后等待deliveryTail并清空缓冲。dispose()sends the unsubscribe only while connected and while the route is current; then it awaitsdeliveryTailand clears the buffers.- 断开时
#serviceListeners整体清空;订阅不会自动恢复。On disconnect#serviceListenersis cleared wholesale; subscriptions are not restored.
工作原理How it works
const sub = await client.subscribeService(target, serviceId, mode, listener);装好sub.snapshot后调用sub.start()。const sub = await client.subscribeService(target, serviceId, mode, listener); installsub.snapshot, then callsub.start().- 监听器抛出的错误只交给
ClientOptions.onListenerError。Listener errors only reachClientOptions.onListenerError. - 通过 Chord 绑定使用时,
activate对应start()、close对应dispose()。Through a Chord binding,activatemaps tostart()andclosetodispose().
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/client.tsL172 | subscribeService() | 登记并订阅register and subscribe |
| packages/client/src/client.tsL316 | hydrated | 快照前缓存cache before the snapshot |
| packages/client/src/client.tsL216 | start() | 开始交付begin delivery |
| packages/client/src/client.tsL417 | #deliverServiceUpdate() | 串行交付ordered delivery |
| packages/client/src/types.tsL16 | ServiceSubscription | 订阅句柄the subscription handle |
附着与 Chord 传输适配器Attachments and the Chord transport adapter#
一句话结论Takeaway附着由服务器带外推送:attachment 信封里的 serverId 必须是本服务器,否则断开;三个字段都相同的更新被忽略,其余通知 onAttachmentChange。createClientServiceTransport(client, getTarget) 在每次调用时才求值 getTarget(),所以 Session 服务可以直接传 () => client.attachment。The server pushes attachments out of band: the serverId in an attachment envelope must be this server's or the connection drops; an update with all three fields equal is ignored, anything else notifies onAttachmentChange. createClientServiceTransport(client, getTarget) evaluates getTarget() on every call, so Session services can pass () => client.attachment directly.
要点Key points
- 附着信封先于 attach 的回应送达:回应 resolve 时
client.attachment已经可用。The attachment envelope arrives before the attach response: when it resolves,client.attachmentis already set. - 路由不存在时,适配器抛 "Remote service target is unavailable",不会发出请求。Without a route the adapter throws "Remote service target is unavailable" and sends nothing.
#targetIsCurrent():服务器级目标与 hello 的serverId比较,Session 目标与当前附着的三个字段比较。#targetIsCurrent(): a server target is compared with the hello'sserverId, a Session target with all three fields of the current attachment.
工作原理How it works
- 服务器级服务:
createClientServiceTransport(client, () => ({ serverId: client.serverId }))。Server-wide services:createClientServiceTransport(client, () => ({ serverId: client.serverId })). - Session 服务:
createClientServiceTransport(client, () => client.attachment)。Session services:createClientServiceTransport(client, () => client.attachment). client.onAttachmentChange((a) => …):undefined表示没有附着。client.onAttachmentChange((a) => …):undefinedmeans no attachment.
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/client.tsL448 | createClientServiceTransport() | Chord 适配器the Chord adapter |
| packages/client/src/client.tsL398 | #setAttachment() | 去重并通知dedupe and notify |
| packages/client/src/client.tsL423 | #targetIsCurrent() | 路由是否仍有效is the route current |
| packages/coding-agent/src/experimental/services/connection.tsL204 | connection.ts | coding-agent 的 Session 服务coding-agent's Session services |
字节传输契约与 Unix 实现The byte-transport contract and the Unix implementation#
一句话结论TakeawayByteTransport 只有 send(chunk): Promise<void> 和 close();工厂收到 onData / onClose / onError 三个回调,每次连接尝试新建一个已认证的传输。UnixByteTransport 用一条 Promise 链保序,按 maxPendingBytes 限制排队字节,写入返回 false 时等待 drain。ByteTransport is just send(chunk): Promise<void> and close(); the factory receives three callbacks, onData / onClose / onError, and builds one authenticated transport per attempt. UnixByteTransport keeps order with a promise chain, caps queued bytes with maxPendingBytes, and waits for drain when a write returns false.
要点Key points
maxPendingBytes默认是帧上限的 4 倍(64 MiB);超过就拒绝这次send,Connection随之断开。maxPendingBytesdefaults to 4 × the frame limit (64 MiB); beyond it thesendrejects and theConnectiondrops.- 每个 chunk 都先
slice()复制,调用方之后修改缓冲也不影响写出的字节。Every chunk is copied withslice()first, so later changes to the caller's buffer cannot affect what is written. - 失败的写不会堵住队列:
#writeTail吞掉错误,只有该次调用的 Promise 被拒绝。A failed write never blocks the queue:#writeTailswallows it, and only that call's promise rejects.
工作原理How it works
createUnixTransportFactory({ path, maxPendingBytes }):path 不能为空,Windows 上不可用。createUnixTransportFactory({ path, maxPendingBytes }): the path must not be empty; unavailable on Windows.- 自定义传输:实现
send/close,并且恰好调用一次onClose或onError。A custom transport: implementsend/closeand call exactly one ofonCloseoronError. - 两端配置相同的
maxFrameLength,并让maxPendingBytes至少容得下一帧。Configure the samemaxFrameLengthon both peers, withmaxPendingByteslarge enough for one frame.
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/transport.tsL18 | ByteTransportFactory | 工厂契约the factory contract |
| packages/client/src/unix.tsL147 | UnixByteTransport | 写队列the write queue |
| packages/client/src/unix.tsL103 | connectUnixSocket() | 连接与终止回调connect and terminal callbacks |
| packages/client/src/unix.tsL93 | validateUnixTransportOptions() | 默认值与检查defaults and checks |
| packages/coding-agent/src/experimental/radius-relay.tsL437 | RadiusClientByteTransport | WebSocket 中继传输the WebSocket relay transport |
发现本机服务器Discovering local servers#
一句话结论TakeawaydiscoverUnixServers({ directory }) 读一个目录,只保留名为 <serverId>.sock 且 serverId 是规范 UUIDv4 的条目,然后用最多 16 个工作者并发探测:确认是 socket、用一个临时 Client 完成握手(默认 1000 ms 超时),最后按 serverId 排序返回 { serverId, path }。discoverUnixServers({ directory }) reads a directory, keeps only entries named <serverId>.sock whose serverId is a canonical UUIDv4, then probes them with up to 16 concurrent workers: check it is a socket, complete a handshake with a temporary Client (1000 ms timeout by default), and return { serverId, path } sorted by serverId.
要点Key points
- 只读:陈旧或无响应的 socket 只会被忽略,从不删除——清理归服务器端的监听器。Read-only: stale or unresponsive sockets are just ignored, never deleted; cleanup belongs to the server's listener.
- 超时、协议错误、版本不符、无 cause 的断开以及 ENOENT / ECONNREFUSED / ECONNRESET / EPIPE / ETIMEDOUT 都算“不是这台服务器”。Timeouts, protocol errors, version mismatches, cause-less disconnects and ENOENT / ECONNREFUSED / ECONNRESET / EPIPE / ETIMEDOUT all count as “not this server”.
- 其它错误让整个发现失败:第一个错误被记下,工作者停止领取新候选。Any other error fails discovery: the first one is remembered and workers stop taking candidates.
工作原理How it works
const routes = await discoverUnixServers({ directory }),directory 用绝对路径,例如展开后的~/.pi/server。const routes = await discoverUnixServers({ directory })with an absolute directory such as the expanded~/.pi/server.timeoutMs覆盖每次探测的超时(1 … 2,147,483,647)。timeoutMsoverrides the per-probe timeout (1 … 2,147,483,647).- 然后
Client.connect({ serverId: route.serverId, transportFactory: createUnixTransportFactory({ path: route.path }) })。ThenClient.connect({ serverId: route.serverId, transportFactory: createUnixTransportFactory({ path: route.path }) }).
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/unix.tsL37 | discoverUnixServers() | 读目录与工作者池directory and worker pool |
| packages/client/src/unix.tsL237 | probeUnixServer() | 一次握手探测one handshake probe |
| packages/client/src/unix.tsL17 | MAX_CONCURRENT_DISCOVERY_PROBES | 并发上限 16concurrency 16 |
| packages/coding-agent/src/experimental/client-runtime.tsL79 | client-runtime.ts | coding-agent 的使用coding-agent's use |
失败语义与恢复Failure semantics and recovery#
一句话结论Takeaway调用方会看到五种拒绝:ServerError(服务器拒绝,带 code)、DisconnectedError(没连上或连接断了,传输错误放在 cause)、ClientDisposedError、ProtocolValidationError(对端违反协议,并且断开)和 AbortError。Client 从不自动重连或重放:断开之后 reconnect()、重新 attach、重新订阅,只重放已知安全的操作——这是应用的策略。Callers see five kinds of rejection: ServerError (the server refused, with a code), DisconnectedError (not connected or the connection dropped; transport errors in cause), ClientDisposedError, ProtocolValidationError (the peer broke the protocol, and the connection drops) and AbortError. The Client never reconnects or replays on its own: after a disconnect, reconnect(), attach again, subscribe again, and replay only operations known to be safe; that is the application's policy.
要点Key points
- 断开时挂起的请求在本地拒绝,但已被服务器接受的工作仍可能在远端完成。On disconnect pending requests reject locally, but work the server accepted may still complete remotely.
- coding-agent 的
RadiusClientReconnect就是这样一种应用策略:指数退避重连并重新附着原来的 Session。coding-agent'sRadiusClientReconnectis one such application policy: reconnect with exponential backoff and reattach the same Session. - 监听器的异常只交给
onListenerError,诊断不能影响协议或传输状态。Listener exceptions only reachonListenerError; diagnostics cannot affect protocol or transport state.
工作原理How it works
e instanceof ServerError→ 看e.code;e instanceof DisconnectedError→ 看e.cause。e instanceof ServerError→ inspecte.code;e instanceof DisconnectedError→ inspecte.cause.await using client = …或await client.dispose():两者等价(Symbol.asyncDispose)。await using client = …orawait client.dispose(): equivalent (Symbol.asyncDispose).client.reconnect()只是再走一次connect():新传输、新握手。client.reconnect()is just anotherconnect(): a new transport and a new handshake.
关键文件Key files · v1.1.0
| 文件File | 符号Symbol | 作用Role |
|---|---|---|
| packages/client/src/errors.tsL3 | ServerError | 带 code 的服务器错误server error with a code |
| packages/client/src/errors.tsL13 | DisconnectedError | 断开,带 causedisconnected, with cause |
| packages/client/src/errors.tsL20 | ClientDisposedError | 已 disposedisposed |
| packages/client/src/client.tsL134 | reconnect() | 显式重连explicit reconnection |
| packages/coding-agent/src/experimental/radius-relay.tsL344 | RadiusClientReconnect | 一种应用策略one application policy |
扩展点Extension points#
ByteTransportFactory接入任何保序字节流:WebSocket、管道、测试替身。Plug in any order-preserving byte stream: a WebSocket, a pipe, a test double.createClientServiceTransport(client, getTarget)把路由交给 Chord 绑定,得到类型化的远程服务。Hand a route to a Chord binding to get typed remote services.ClientOptions.onListenerError收集监听器异常,而不影响连接。Collect listener exceptions without affecting the connection.ClientOptions.maxFrameLength · maxPendingBytes帧上限与 Unix 传输的排队上限;两端配置一致。The frame limit and the Unix transport's queue limit; configure both peers alike.discoverUnixServers({ directory, timeoutMs })列出某个目录里可以握手的本机服务器。List the local servers in a directory that complete a handshake.测试Tests#
一句话结论Takeaway三个测试文件:client.test.ts 用内存传输覆盖握手、乱序回应、取消、订阅缓冲、附着与断开后的拒绝;unix-transport.test.ts 在真实 Unix socket 上跑完整握手与截断帧;unix.test.ts 覆盖发现的排序、过滤、陈旧 socket、超时与 16 路并发上限。Three test files: client.test.ts uses an in-memory transport for the handshake, out-of-order responses, cancellation, subscription buffering, attachments and rejection after disconnect; unix-transport.test.ts runs a full handshake and truncated frames over a real Unix socket; unix.test.ts covers discovery ordering, filtering, stale sockets, timeouts and the 16-probe limit.
| 测试文件Test file | 用例cases |
|---|---|
| packages/client/test/client.test.ts | 15 |
| packages/client/test/unix.test.ts | 8 |
| packages/client/test/unix-transport.test.ts | 4 |
用例数 = 源码中 it( / test( 的出现次数(静态统计,不运行测试)。Cases = occurrences of it( / test( in the source (static count; tests were not run).
交叉链接Cross-links#
线上格式:信封、分帧与 CBORthe wire format: envelopes, framing and CBOR
server已完成Done另一端:握手、路由到 Session、推送附着the other end: handshake, Session routing, attachment pushes
chord已完成Done服务控制调用、状态解码器与 RemoteServiceTransportservice-control calls, the state decoder and RemoteServiceTransport
coding-agent已完成Doneexperimental/ 下的 client 运行时与中继传输the client runtime and relay transport under experimental/