@labacacia/nps-sdk 1.0.0-alpha.1 → 1.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/.npmrc.publish +1 -0
  2. package/CHANGELOG.cn.md +39 -0
  3. package/CHANGELOG.md +39 -0
  4. package/CONTRIBUTING.cn.md +35 -0
  5. package/CONTRIBUTING.md +2 -0
  6. package/README.cn.md +155 -0
  7. package/README.md +5 -3
  8. package/dist/core/frames.d.ts +1 -0
  9. package/dist/core/frames.d.ts.map +1 -1
  10. package/dist/core/frames.js +1 -0
  11. package/dist/core/frames.js.map +1 -1
  12. package/dist/core/index.d.ts +6 -4
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/index.js +17 -5
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/ncp/frames.d.ts +18 -0
  21. package/dist/ncp/frames.d.ts.map +1 -1
  22. package/dist/ncp/frames.js +45 -0
  23. package/dist/ncp/frames.js.map +1 -1
  24. package/dist/ncp/registry.d.ts.map +1 -1
  25. package/dist/ncp/registry.js +2 -1
  26. package/dist/ncp/registry.js.map +1 -1
  27. package/doc/nps-sdk.core.cn.md +321 -0
  28. package/doc/nps-sdk.core.md +326 -0
  29. package/doc/nps-sdk.ncp.cn.md +270 -0
  30. package/doc/nps-sdk.ncp.md +276 -0
  31. package/doc/nps-sdk.ndp.cn.md +267 -0
  32. package/doc/nps-sdk.ndp.md +273 -0
  33. package/doc/nps-sdk.nip.cn.md +235 -0
  34. package/doc/nps-sdk.nip.md +242 -0
  35. package/doc/nps-sdk.nop.cn.md +329 -0
  36. package/doc/nps-sdk.nop.md +332 -0
  37. package/doc/nps-sdk.nwp.cn.md +217 -0
  38. package/doc/nps-sdk.nwp.md +224 -0
  39. package/doc/overview.cn.md +149 -0
  40. package/doc/overview.md +153 -0
  41. package/package.json +21 -4
  42. package/src/core/frames.ts +1 -0
  43. package/src/core/index.ts +37 -5
  44. package/src/index.ts +1 -1
  45. package/src/ncp/frames.ts +52 -0
  46. package/src/ncp/registry.ts +2 -1
@@ -0,0 +1,217 @@
1
+ [English Version](./nps-sdk.nwp.md) | 中文版
2
+
3
+ # `@labacacia/nps-sdk/nwp` — 类与方法参考
4
+
5
+ > 规范:[NPS-2 NWP v0.4](https://github.com/labacacia/nps/blob/main/spec/NPS-2-NWP.md)
6
+
7
+ NWP 是 AI 的 HTTP。本模块提供两个 NWP 帧
8
+ (`QueryFrame`、`ActionFrame`)、异步 `NwpClient`,以及用于查询排序、
9
+ 向量检索和异步动作响应的 dataclass 风格接口。
10
+
11
+ ---
12
+
13
+ ## 目录
14
+
15
+ - [`QueryOrderClause`](#queryorderclause)
16
+ - [`VectorSearchOptions`](#vectorsearchoptions)
17
+ - [`AsyncActionResponse`](#asyncactionresponse)
18
+ - [`QueryFrame` (0x10)](#queryframe-0x10)
19
+ - [`ActionFrame` (0x11)](#actionframe-0x11)
20
+ - [`NwpClient`](#nwpclient)
21
+
22
+ ---
23
+
24
+ ## `QueryOrderClause`
25
+
26
+ ```typescript
27
+ interface QueryOrderClause {
28
+ field: string;
29
+ dir: "asc" | "desc";
30
+ }
31
+ ```
32
+
33
+ ---
34
+
35
+ ## `VectorSearchOptions`
36
+
37
+ ```typescript
38
+ interface VectorSearchOptions {
39
+ vector: readonly number[];
40
+ topK?: number;
41
+ minScore?: number;
42
+ vectorField?: string;
43
+ }
44
+ ```
45
+
46
+ 当目标 Memory Node 广告 `nwp:vector` 时,附加到 `QueryFrame.vectorSearch`。
47
+
48
+ ---
49
+
50
+ ## `AsyncActionResponse`
51
+
52
+ ```typescript
53
+ interface AsyncActionResponse {
54
+ taskId: string;
55
+ status: string; // "pending" | "running" | …
56
+ pollUrl?: string;
57
+ }
58
+
59
+ function asyncActionResponseFromDict(
60
+ data: Record<string, unknown>,
61
+ ): AsyncActionResponse;
62
+ ```
63
+
64
+ 当 `ActionFrame` 以 `async_ === true` 提交时,由 `NwpClient.invoke` 返回。
65
+
66
+ ---
67
+
68
+ ## `QueryFrame` (0x10)
69
+
70
+ 针对 Memory Node 的结构化读取。
71
+
72
+ ```typescript
73
+ class QueryFrame {
74
+ readonly frameType: FrameType.QUERY;
75
+ readonly preferredTier: EncodingTier.MSGPACK;
76
+
77
+ constructor(
78
+ public readonly anchorRef?: string,
79
+ public readonly filter?: Record<string, unknown>,
80
+ public readonly limit?: number,
81
+ public readonly offset?: number,
82
+ public readonly orderBy?: readonly QueryOrderClause[],
83
+ public readonly fields?: readonly string[],
84
+ public readonly vectorSearch?: VectorSearchOptions,
85
+ public readonly depth?: number,
86
+ );
87
+
88
+ toDict(): Record<string, unknown>;
89
+ static fromDict(data: Record<string, unknown>): QueryFrame;
90
+ }
91
+ ```
92
+
93
+ 线路形式(由 `toDict` 发出)使用 snake-case 键:
94
+ `anchor_ref`、`order_by`、`vector_search` 等。
95
+
96
+ ---
97
+
98
+ ## `ActionFrame` (0x11)
99
+
100
+ 针对 Action / Complex / Gateway Node 的操作调用。
101
+
102
+ ```typescript
103
+ class ActionFrame {
104
+ readonly frameType: FrameType.ACTION;
105
+ readonly preferredTier: EncodingTier.MSGPACK;
106
+
107
+ constructor(
108
+ public readonly actionId: string,
109
+ public readonly params?: Record<string, unknown>,
110
+ public readonly async_?: boolean,
111
+ public readonly idempotencyKey?: string,
112
+ public readonly timeoutMs?: number,
113
+ );
114
+
115
+ toDict(): Record<string, unknown>;
116
+ static fromDict(data: Record<string, unknown>): ActionFrame;
117
+ }
118
+ ```
119
+
120
+ - `actionId` 是 Action Node 的 `.nwm.json` → `endpoints.actions[].id` 中
121
+ 声明的标识符。
122
+ - `async_` 在线路上序列化为 `"async"`(尾下划线避免与 JS 关键字冲突)。
123
+ - `idempotencyKey` —— 存在则节点必须在其重放窗口内去重。
124
+
125
+ ---
126
+
127
+ ## `NwpClient`
128
+
129
+ 使用 `Content-Type: application/x-nps-frame` 与 NWP 节点通信的异步 HTTP 客户端。
130
+
131
+ ```typescript
132
+ class NwpClient {
133
+ constructor(
134
+ baseUrl: string,
135
+ options?: {
136
+ defaultTier?: EncodingTier; // 默认:MSGPACK
137
+ maxPayload?: number; // 默认:65 535
138
+ registry?: FrameRegistry; // 默认:NCP + NWP 帧
139
+ },
140
+ );
141
+
142
+ async sendAnchor(frame: AnchorFrame): Promise<void>;
143
+ async query(frame: QueryFrame): Promise<CapsFrame>;
144
+ stream(frame: QueryFrame): AsyncGenerator<StreamFrame>;
145
+ async invoke(frame: ActionFrame): Promise<unknown>;
146
+ }
147
+ ```
148
+
149
+ `baseUrl` 的尾斜杠自动去除。
150
+
151
+ ### HTTP 路由
152
+
153
+ | 方法 | 路径 | 请求体 | 响应 |
154
+ |------|------|--------|------|
155
+ | `sendAnchor` | `POST /anchor` | `AnchorFrame` 线路字节 | `204 No Content` |
156
+ | `query` | `POST /query` | `QueryFrame` 线路字节 | `CapsFrame` 线路字节 |
157
+ | `stream` | `POST /stream` | `QueryFrame` 线路字节 | 分块 `StreamFrame` 直至 `is_last=true` |
158
+ | `invoke` | `POST /invoke` | `ActionFrame` 线路字节 | 动作结果 |
159
+
160
+ `invoke()` 按 `frame.async_` 分派:
161
+
162
+ - `false` → 依据 content-type 解码响应。若服务器返回
163
+ `application/x-nps-frame`,body 由编解码器解码;否则按 JSON 解析。
164
+ - `true` → 解析 JSON 返回 `AsyncActionResponse`。轮询 `pollUrl`
165
+ 观察进度。
166
+
167
+ 所有方法在非 2xx HTTP 状态时抛普通 `Error`。流帧被独立发出 ——
168
+ 用 `for await` 消费生成器。
169
+
170
+ ---
171
+
172
+ ## 端到端示例
173
+
174
+ ```typescript
175
+ import {
176
+ NwpClient, QueryFrame, QueryOrderClause,
177
+ ActionFrame, AsyncActionResponse,
178
+ VectorSearchOptions,
179
+ } from "@labacacia/nps-sdk/nwp";
180
+
181
+ const nwp = new NwpClient("https://products.example.com");
182
+
183
+ // 1) 一次性上传 anchor
184
+ await nwp.sendAnchor({
185
+ frame: "0x01",
186
+ anchor_id: "sha256:…",
187
+ schema: { fields: [
188
+ { name: "id", type: "uint64" },
189
+ { name: "price", type: "decimal", semantic: "commerce.price.usd" },
190
+ ]},
191
+ ttl: 3600,
192
+ });
193
+
194
+ // 2) 查询一页
195
+ const caps = await nwp.query(new QueryFrame(
196
+ "sha256:…",
197
+ { price: { $lt: "100.00" } },
198
+ 50,
199
+ undefined,
200
+ [{ field: "price", dir: "asc" }],
201
+ ));
202
+ console.log(caps.count, "行, cursor:", caps.next_cursor);
203
+
204
+ // 3) 流式获取全量
205
+ for await (const chunk of nwp.stream(new QueryFrame("sha256:…"))) {
206
+ for (const row of chunk.data) { /* … */ }
207
+ if (chunk.isLast) break;
208
+ }
209
+
210
+ // 4) 触发异步动作
211
+ const resp = await nwp.invoke(new ActionFrame(
212
+ "restock",
213
+ { sku: "sku-4242", qty: 100 },
214
+ true,
215
+ ));
216
+ // resp 是 AsyncActionResponse
217
+ ```
@@ -0,0 +1,224 @@
1
+ English | [中文版](./nps-sdk.nwp.cn.md)
2
+
3
+ # `@labacacia/nps-sdk/nwp` — Class and Method Reference
4
+
5
+ > Spec: [NPS-2 NWP v0.4](https://github.com/labacacia/nps/blob/main/spec/NPS-2-NWP.md)
6
+
7
+ NWP is the HTTP-of-AI. This module ships the two NWP frames
8
+ (`QueryFrame`, `ActionFrame`), the async `NwpClient`, and supporting
9
+ dataclass-like interfaces for query ordering, vector search, and async
10
+ action responses.
11
+
12
+ ---
13
+
14
+ ## Table of contents
15
+
16
+ - [`QueryOrderClause`](#queryorderclause)
17
+ - [`VectorSearchOptions`](#vectorsearchoptions)
18
+ - [`AsyncActionResponse`](#asyncactionresponse)
19
+ - [`QueryFrame` (0x10)](#queryframe-0x10)
20
+ - [`ActionFrame` (0x11)](#actionframe-0x11)
21
+ - [`NwpClient`](#nwpclient)
22
+
23
+ ---
24
+
25
+ ## `QueryOrderClause`
26
+
27
+ ```typescript
28
+ interface QueryOrderClause {
29
+ field: string;
30
+ dir: "asc" | "desc";
31
+ }
32
+ ```
33
+
34
+ ---
35
+
36
+ ## `VectorSearchOptions`
37
+
38
+ ```typescript
39
+ interface VectorSearchOptions {
40
+ vector: readonly number[];
41
+ topK?: number;
42
+ minScore?: number;
43
+ vectorField?: string;
44
+ }
45
+ ```
46
+
47
+ Attached to a `QueryFrame.vectorSearch` when the target Memory Node
48
+ advertises `nwp:vector`.
49
+
50
+ ---
51
+
52
+ ## `AsyncActionResponse`
53
+
54
+ ```typescript
55
+ interface AsyncActionResponse {
56
+ taskId: string;
57
+ status: string; // "pending" | "running" | …
58
+ pollUrl?: string;
59
+ }
60
+
61
+ function asyncActionResponseFromDict(
62
+ data: Record<string, unknown>,
63
+ ): AsyncActionResponse;
64
+ ```
65
+
66
+ Returned by `NwpClient.invoke` when the `ActionFrame` was submitted with
67
+ `async_ === true`.
68
+
69
+ ---
70
+
71
+ ## `QueryFrame` (0x10)
72
+
73
+ Structured read against a Memory Node.
74
+
75
+ ```typescript
76
+ class QueryFrame {
77
+ readonly frameType: FrameType.QUERY;
78
+ readonly preferredTier: EncodingTier.MSGPACK;
79
+
80
+ constructor(
81
+ public readonly anchorRef?: string,
82
+ public readonly filter?: Record<string, unknown>,
83
+ public readonly limit?: number,
84
+ public readonly offset?: number,
85
+ public readonly orderBy?: readonly QueryOrderClause[],
86
+ public readonly fields?: readonly string[],
87
+ public readonly vectorSearch?: VectorSearchOptions,
88
+ public readonly depth?: number,
89
+ );
90
+
91
+ toDict(): Record<string, unknown>;
92
+ static fromDict(data: Record<string, unknown>): QueryFrame;
93
+ }
94
+ ```
95
+
96
+ Wire form (emitted by `toDict`) uses snake-case keys:
97
+ `anchor_ref`, `order_by`, `vector_search` etc.
98
+
99
+ ---
100
+
101
+ ## `ActionFrame` (0x11)
102
+
103
+ Operation invocation against an Action / Complex / Gateway Node.
104
+
105
+ ```typescript
106
+ class ActionFrame {
107
+ readonly frameType: FrameType.ACTION;
108
+ readonly preferredTier: EncodingTier.MSGPACK;
109
+
110
+ constructor(
111
+ public readonly actionId: string,
112
+ public readonly params?: Record<string, unknown>,
113
+ public readonly async_?: boolean,
114
+ public readonly idempotencyKey?: string,
115
+ public readonly timeoutMs?: number,
116
+ );
117
+
118
+ toDict(): Record<string, unknown>;
119
+ static fromDict(data: Record<string, unknown>): ActionFrame;
120
+ }
121
+ ```
122
+
123
+ - `actionId` is the identifier declared under the Action Node's
124
+ `.nwm.json` → `endpoints.actions[].id`.
125
+ - `async_` is serialised as `"async"` on the wire (trailing underscore
126
+ avoids shadowing the JS keyword).
127
+ - `idempotencyKey` — if present, the node MUST deduplicate within its
128
+ replay window.
129
+
130
+ ---
131
+
132
+ ## `NwpClient`
133
+
134
+ Async HTTP client that talks to an NWP node using
135
+ `Content-Type: application/x-nps-frame`.
136
+
137
+ ```typescript
138
+ class NwpClient {
139
+ constructor(
140
+ baseUrl: string,
141
+ options?: {
142
+ defaultTier?: EncodingTier; // default: MSGPACK
143
+ maxPayload?: number; // default: 65 535
144
+ registry?: FrameRegistry; // default: NCP + NWP frames
145
+ },
146
+ );
147
+
148
+ async sendAnchor(frame: AnchorFrame): Promise<void>;
149
+ async query(frame: QueryFrame): Promise<CapsFrame>;
150
+ stream(frame: QueryFrame): AsyncGenerator<StreamFrame>;
151
+ async invoke(frame: ActionFrame): Promise<unknown>;
152
+ }
153
+ ```
154
+
155
+ `baseUrl` trailing slashes are trimmed automatically.
156
+
157
+ ### HTTP routes
158
+
159
+ | Method | Path | Body | Response |
160
+ |--------|------|------|----------|
161
+ | `sendAnchor` | `POST /anchor` | `AnchorFrame` wire bytes | `204 No Content` |
162
+ | `query` | `POST /query` | `QueryFrame` wire bytes | `CapsFrame` wire bytes |
163
+ | `stream` | `POST /stream` | `QueryFrame` wire bytes | chunked `StreamFrame`s until `is_last=true` |
164
+ | `invoke` | `POST /invoke` | `ActionFrame` wire bytes | action result |
165
+
166
+ `invoke()` dispatches by `frame.async_`:
167
+
168
+ - `false` → decodes response based on content-type. If the server
169
+ responds with `application/x-nps-frame`, the body is decoded by the
170
+ codec; otherwise the response is parsed as JSON.
171
+ - `true` → parses JSON and returns `AsyncActionResponse`. Poll
172
+ `pollUrl` to observe progress.
173
+
174
+ All methods throw a plain `Error` on non-2xx HTTP status. Stream frames
175
+ are emitted individually — consume the generator with `for await`.
176
+
177
+ ---
178
+
179
+ ## End-to-end example
180
+
181
+ ```typescript
182
+ import {
183
+ NwpClient, QueryFrame, QueryOrderClause,
184
+ ActionFrame, AsyncActionResponse,
185
+ VectorSearchOptions,
186
+ } from "@labacacia/nps-sdk/nwp";
187
+
188
+ const nwp = new NwpClient("https://products.example.com");
189
+
190
+ // 1) Upload an anchor once
191
+ await nwp.sendAnchor({
192
+ frame: "0x01",
193
+ anchor_id: "sha256:…",
194
+ schema: { fields: [
195
+ { name: "id", type: "uint64" },
196
+ { name: "price", type: "decimal", semantic: "commerce.price.usd" },
197
+ ]},
198
+ ttl: 3600,
199
+ });
200
+
201
+ // 2) Query a page
202
+ const caps = await nwp.query(new QueryFrame(
203
+ "sha256:…",
204
+ { price: { $lt: "100.00" } },
205
+ 50,
206
+ undefined,
207
+ [{ field: "price", dir: "asc" }],
208
+ ));
209
+ console.log(caps.count, "rows, cursor:", caps.next_cursor);
210
+
211
+ // 3) Stream the full set
212
+ for await (const chunk of nwp.stream(new QueryFrame("sha256:…"))) {
213
+ for (const row of chunk.data) { /* … */ }
214
+ if (chunk.isLast) break;
215
+ }
216
+
217
+ // 4) Fire an async action
218
+ const resp = await nwp.invoke(new ActionFrame(
219
+ "restock",
220
+ { sku: "sku-4242", qty: 100 },
221
+ true,
222
+ ));
223
+ // resp is AsyncActionResponse
224
+ ```
@@ -0,0 +1,149 @@
1
+ [English Version](./overview.md) | 中文版
2
+
3
+ # `@labacacia/nps-sdk` — API 参考总览
4
+
5
+ [![npm](https://img.shields.io/npm/v/@labacacia/nps-sdk)](https://www.npmjs.com/package/@labacacia/nps-sdk)
6
+
7
+ NPS TypeScript SDK 是 .NET 参考实现的双格式(ESM + CJS)移植。本文档是
8
+ 各模块 API 参考的入口 —— 每个协议有独立文件,见下。
9
+
10
+ ---
11
+
12
+ ## 包结构
13
+
14
+ ```
15
+ @labacacia/nps-sdk
16
+ ├── / # 根:VERSION、createDefaultRegistry、createFullRegistry
17
+ ├── /core # 线缆原语:FrameHeader、编解码、AnchorFrame 缓存、错误
18
+ ├── /ncp # NCP 帧 + 握手 + 流管理
19
+ ├── /nwp # NWP 帧 + 异步 NwpClient
20
+ ├── /nip # NIP 帧 + NipIdentity(Ed25519)
21
+ ├── /ndp # NDP 帧 + InMemoryNdpRegistry + 验证器
22
+ └── /nop # NOP 帧 + TaskDag 模型 + NopClient
23
+ ```
24
+
25
+ ## 参考文档
26
+
27
+ | 子路径 | 模块 | 参考文档 |
28
+ |--------|------|----------|
29
+ | — | 根辅助函数与注册表工厂 | 本文件 |
30
+ | `@labacacia/nps-sdk/core` | 帧头、编解码、AnchorFrame 缓存、异常 | [`nps-sdk.core.cn.md`](./nps-sdk.core.cn.md) |
31
+ | `@labacacia/nps-sdk/ncp` | NCP 帧集(`AnchorFrame`、`DiffFrame`、`StreamFrame`、`CapsFrame`、`ErrorFrame`、`HelloFrame`) | [`nps-sdk.ncp.cn.md`](./nps-sdk.ncp.cn.md) |
32
+ | `@labacacia/nps-sdk/nwp` | `QueryFrame`、`ActionFrame`、`NwpClient` | [`nps-sdk.nwp.cn.md`](./nps-sdk.nwp.cn.md) |
33
+ | `@labacacia/nps-sdk/nip` | `IdentFrame`、`TrustFrame`、`RevokeFrame`、`NipIdentity` | [`nps-sdk.nip.cn.md`](./nps-sdk.nip.cn.md) |
34
+ | `@labacacia/nps-sdk/ndp` | `AnnounceFrame`、`ResolveFrame`、`GraphFrame`、注册表、验证器 | [`nps-sdk.ndp.cn.md`](./nps-sdk.ndp.cn.md) |
35
+ | `@labacacia/nps-sdk/nop` | Task DAG、`TaskFrame`、`DelegateFrame`、`SyncFrame`、`AlignStreamFrame`、`NopClient` | [`nps-sdk.nop.cn.md`](./nps-sdk.nop.cn.md) |
36
+
37
+ ---
38
+
39
+ ## 安装
40
+
41
+ ```bash
42
+ npm install @labacacia/nps-sdk
43
+ ```
44
+
45
+ 要求 **Node.js 18+**(Web Crypto)或支持原生 `crypto.subtle` 的
46
+ 现代浏览器。
47
+
48
+ ---
49
+
50
+ ## 根模块
51
+
52
+ ```typescript
53
+ import { VERSION, createDefaultRegistry, createFullRegistry } from "@labacacia/nps-sdk";
54
+ ```
55
+
56
+ - `VERSION` —— SDK 版本常量。
57
+ - `createDefaultRegistry()` —— 只含 NCP 帧(`ANCHOR`、`DIFF`、`STREAM`、`CAPS`、`ERROR`)的新 `FrameRegistry`。
58
+ - `createFullRegistry()` —— 预注册全部五个协议(NCP + NWP + NIP + NDP + NOP)的新 `FrameRegistry`。
59
+ 解码任意帧时使用这个。
60
+
61
+ ```typescript
62
+ const registry = createFullRegistry();
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 最小端到端示例
68
+
69
+ ```typescript
70
+ import { NwpClient, QueryFrame, ActionFrame } from "@labacacia/nps-sdk/nwp";
71
+
72
+ const client = new NwpClient("http://node.example.com:17433");
73
+
74
+ // 分页查询
75
+ const caps = await client.query(
76
+ new QueryFrame("sha256:<anchor-id>", { active: true }, 50),
77
+ );
78
+ console.log(caps.count, caps.data);
79
+
80
+ // 流式查询
81
+ for await (const chunk of client.stream(new QueryFrame("sha256:<anchor-id>"))) {
82
+ console.log(chunk.seq, chunk.data);
83
+ if (chunk.isLast) break;
84
+ }
85
+
86
+ // Action 调用
87
+ const result = await client.invoke(
88
+ new ActionFrame("summarise", { maxTokens: 500 }),
89
+ );
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 编码分层
95
+
96
+ 每个帧都有 `preferredTier`。MsgPack 是生产默认;JSON Tier
97
+ 留给诊断。
98
+
99
+ | Tier | `EncodingTier` 值 | 说明 |
100
+ |------|---------------------|------|
101
+ | Tier-1 JSON | `0x00` | UTF-8 JSON。开发与兼容。 |
102
+ | Tier-2 MsgPack | `0x01` | MessagePack 二进制。**生产默认** —— 约小 60%。 |
103
+
104
+ ```typescript
105
+ import { EncodingTier } from "@labacacia/nps-sdk/core";
106
+ import { NpsFrameCodec } from "@labacacia/nps-sdk/core";
107
+
108
+ const codec = new NpsFrameCodec(createFullRegistry());
109
+ const wire = codec.encode(frame, { overrideTier: EncodingTier.JSON });
110
+ ```
111
+
112
+ ---
113
+
114
+ ## 异步约定
115
+
116
+ - 所有面向网络的客户端(`NwpClient`、`NopClient`)返回 `Promise<T>`。
117
+ - 流式使用 `AsyncGenerator<StreamFrame>` —— 用
118
+ `for await (const chunk of …) { … }` 消费。
119
+ - 客户端 **无需** 显式 dispose —— 底层 `fetch` 拥有连接生命周期。
120
+
121
+ ---
122
+
123
+ ## 错误层级
124
+
125
+ ```
126
+ Error
127
+ └── NpsError 来自 "@labacacia/nps-sdk/core"
128
+ ├── NpsFrameError —— 帧头解析 / 结构错误
129
+ ├── NpsCodecError —— 编解码失败
130
+ ├── NpsAnchorNotFoundError —— AnchorFrame 不在缓存
131
+ ├── NpsAnchorPoisonError —— anchor_id / schema 不一致
132
+ └── NpsStreamError —— 流序号跳跃 / 未知流 id
133
+ ```
134
+
135
+ `NcpError`(来自 `@labacacia/nps-sdk/core`)是一个独立的协议级错误,
136
+ 携带机器可读的 `code`(如 `NCP-ANCHOR-NOT-FOUND`、
137
+ `NCP-STREAM-SEQ-GAP`)。
138
+
139
+ ---
140
+
141
+ ## 参考规范
142
+
143
+ | 模块 | 规范 |
144
+ |------|------|
145
+ | `core` + `ncp` | [NPS-1 NCP v0.4](https://github.com/labacacia/NPS-Release/blob/main/spec/NPS-1-NCP.cn.md) |
146
+ | `nwp` | [NPS-2 NWP v0.4](https://github.com/labacacia/NPS-Release/blob/main/spec/NPS-2-NWP.cn.md) |
147
+ | `nip` | [NPS-3 NIP v0.2](https://github.com/labacacia/NPS-Release/blob/main/spec/NPS-3-NIP.cn.md) |
148
+ | `ndp` | [NPS-4 NDP v0.2](https://github.com/labacacia/NPS-Release/blob/main/spec/NPS-4-NDP.cn.md) |
149
+ | `nop` | [NPS-5 NOP v0.3](https://github.com/labacacia/NPS-Release/blob/main/spec/NPS-5-NOP.cn.md) |