paseo-plugin-zcode-provider 0.0.0-stage → 0.2.0

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 (141) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +29 -0
  3. package/README.md +112 -2
  4. package/client/account.tsx +827 -0
  5. package/client/diagnostics.tsx +379 -0
  6. package/client/runtime.tsx +296 -0
  7. package/client/settings.tsx +84 -0
  8. package/client/value.tsx +20 -0
  9. package/icon.svg +1 -0
  10. package/index.client.tsx +22 -0
  11. package/index.server.ts +39 -0
  12. package/package.json +77 -4
  13. package/paseo-plugin.json +6 -0
  14. package/server/account.ts +781 -0
  15. package/server/attachments.ts +150 -0
  16. package/server/build-info.ts +2 -0
  17. package/server/conversation.ts +319 -0
  18. package/server/diagnostics.ts +252 -0
  19. package/server/discovery/discover.ts +225 -0
  20. package/server/discovery/manifest.ts +36 -0
  21. package/server/discovery/types.ts +30 -0
  22. package/server/errors.ts +36 -0
  23. package/server/host/bridge.ts +490 -0
  24. package/server/host/schemas.ts +119 -0
  25. package/server/logger.ts +19 -0
  26. package/server/mapping.ts +270 -0
  27. package/server/models.ts +68 -0
  28. package/server/persistence.ts +146 -0
  29. package/server/presentation.ts +470 -0
  30. package/server/provider-status.ts +81 -0
  31. package/server/provider.ts +838 -0
  32. package/server/runtime/managed.ts +501 -0
  33. package/server/runtime/pins.ts +72 -0
  34. package/server/runtime-setup.ts +77 -0
  35. package/server/session-types.ts +49 -0
  36. package/server/session.ts +866 -0
  37. package/server/status.ts +89 -0
  38. package/server/timeline.ts +18 -0
  39. package/server/usage.ts +362 -0
  40. package/server/vendor/zcode/LICENSE +202 -0
  41. package/server/vendor/zcode/NOTICE.md +70 -0
  42. package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
  43. package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
  44. package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
  45. package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
  46. package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
  47. package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
  48. package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
  49. package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
  50. package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
  51. package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
  52. package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
  53. package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
  54. package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
  55. package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
  56. package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
  57. package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
  58. package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
  59. package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
  60. package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
  61. package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
  62. package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
  63. package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
  64. package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
  65. package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
  66. package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
  67. package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
  68. package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
  69. package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
  70. package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
  71. package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
  72. package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
  73. package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
  74. package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
  75. package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
  76. package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
  77. package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
  78. package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
  79. package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
  80. package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
  81. package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
  82. package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
  83. package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
  84. package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
  85. package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
  86. package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
  87. package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
  88. package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
  89. package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
  90. package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
  91. package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
  92. package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
  93. package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
  94. package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
  95. package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
  96. package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
  97. package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
  98. package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
  99. package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
  100. package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
  101. package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
  102. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
  103. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
  104. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
  105. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
  106. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
  107. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
  108. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
  109. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
  110. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
  111. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
  112. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
  113. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
  114. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
  115. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
  116. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
  117. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
  118. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
  119. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
  120. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
  121. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
  122. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
  123. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
  124. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
  125. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
  126. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
  127. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
  128. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
  129. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
  130. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
  131. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
  132. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
  133. package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
  134. package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
  135. package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
  136. package/server/vendor/zcode/provenance.json +405 -0
  137. package/shared/account.ts +201 -0
  138. package/shared/diagnostics.ts +52 -0
  139. package/shared/runtime-setup.ts +73 -0
  140. package/vendor/acp-registry/LICENSE +176 -0
  141. package/vendor/paseo/LICENSE +211 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Layer 0: 基础设施
3
+ * - IDisposable / DisposableStore: 资源生命周期管理
4
+ * - Event / Emitter: 事件系统
5
+ * - CancellationToken: 取消令牌
6
+ *
7
+ * 这些是整个 IPC 框架的地基,所有上层模块都依赖它们。
8
+ */
9
+
10
+ // ============================================================================
11
+ // Disposable - 资源释放模式
12
+ // ============================================================================
13
+
14
+ export interface IDisposable {
15
+ dispose(): void;
16
+ }
17
+
18
+ export function toDisposable(fn: () => void): IDisposable {
19
+ return { dispose: once(fn) };
20
+ }
21
+
22
+ function once(fn: () => void): () => void {
23
+ let called = false;
24
+ return () => {
25
+ if (!called) {
26
+ called = true;
27
+ fn();
28
+ }
29
+ };
30
+ }
31
+
32
+ /**
33
+ * DisposableStore 收集多个 IDisposable,统一释放。
34
+ * VS Code 里几乎每个类都有一个 DisposableStore 来管理子资源。
35
+ */
36
+ export class DisposableStore implements IDisposable {
37
+ private items = new Set<IDisposable>();
38
+ private isDisposed = false;
39
+
40
+ add<T extends IDisposable>(item: T): T {
41
+ if (this.isDisposed) {
42
+ console.warn("Adding to a disposed DisposableStore");
43
+ item.dispose();
44
+ return item;
45
+ }
46
+ this.items.add(item);
47
+ return item;
48
+ }
49
+
50
+ dispose(): void {
51
+ if (this.isDisposed) {
52
+ return;
53
+ }
54
+ this.isDisposed = true;
55
+ for (const item of this.items) {
56
+ item.dispose();
57
+ }
58
+ this.items.clear();
59
+ }
60
+ }
61
+
62
+ // ============================================================================
63
+ // Event System - 事件系统
64
+ // ============================================================================
65
+
66
+ /**
67
+ * Event<T> 就是一个函数签名:传入 listener,返回一个 IDisposable 用于取消订阅。
68
+ * 这是整个 IPC 框架中事件流转的核心类型。
69
+ */
70
+ export type Event<T> = (listener: (e: T) => void) => IDisposable;
71
+
72
+ export namespace Event {
73
+ /** 永远不触发的事件 */
74
+ export const None: Event<any> = () => ({ dispose() {} });
75
+
76
+ /** 只触发一次就自动取消订阅 */
77
+ export function once<T>(event: Event<T>): Event<T> {
78
+ return (listener) => {
79
+ let fired = false;
80
+ const disposable = event((e) => {
81
+ if (!fired) {
82
+ fired = true;
83
+ disposable.dispose();
84
+ listener(e);
85
+ }
86
+ });
87
+ return disposable;
88
+ };
89
+ }
90
+
91
+ /** 把事件转为 Promise,resolve 后自动取消订阅 */
92
+ export function toPromise<T>(event: Event<T>): Promise<T> {
93
+ return new Promise((resolve) => once(event)(resolve));
94
+ }
95
+
96
+ /** 过滤事件 */
97
+ export function filter<T>(event: Event<T>, fn: (e: T) => boolean): Event<T> {
98
+ return (listener) =>
99
+ event((e) => {
100
+ if (fn(e)) {
101
+ listener(e);
102
+ }
103
+ });
104
+ }
105
+
106
+ /** 映射事件 */
107
+ export function map<T, R>(event: Event<T>, fn: (e: T) => R): Event<R> {
108
+ return (listener) => event((e) => listener(fn(e)));
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Emitter<T> 是事件的发射器。
114
+ *
115
+ * 关键设计:
116
+ * - onWillAddFirstListener: 第一个订阅者到来时触发(懒初始化资源)
117
+ * - onDidRemoveLastListener: 最后一个订阅者离开时触发(释放资源)
118
+ *
119
+ * 这个"懒订阅"机制在 IPC 框架中至关重要——
120
+ * ChannelClient 的 requestEvent 正是利用它来实现
121
+ * "有人监听才发送 EventListen 请求,无人监听就发 EventDispose"。
122
+ */
123
+ export class Emitter<T> implements IDisposable {
124
+ private listeners = new Set<(e: T) => void>();
125
+ private disposed = false;
126
+ private options?: EmitterOptions;
127
+
128
+ constructor(options?: EmitterOptions) {
129
+ this.options = options;
130
+ }
131
+
132
+ get event(): Event<T> {
133
+ return (listener: (e: T) => void) => {
134
+ if (this.disposed) {
135
+ return { dispose() {} };
136
+ }
137
+
138
+ const isFirst = this.listeners.size === 0;
139
+ this.listeners.add(listener);
140
+
141
+ if (isFirst) {
142
+ this.options?.onWillAddFirstListener?.();
143
+ }
144
+
145
+ return toDisposable(() => {
146
+ this.listeners.delete(listener);
147
+ if (this.listeners.size === 0) {
148
+ this.options?.onDidRemoveLastListener?.();
149
+ }
150
+ });
151
+ };
152
+ }
153
+
154
+ fire(event: T): void {
155
+ if (this.disposed) {
156
+ return;
157
+ }
158
+ for (const listener of [...this.listeners]) {
159
+ listener(event);
160
+ }
161
+ }
162
+
163
+ dispose(): void {
164
+ this.disposed = true;
165
+ this.listeners.clear();
166
+ }
167
+ }
168
+
169
+ interface EmitterOptions {
170
+ onWillAddFirstListener?: () => void;
171
+ onDidRemoveLastListener?: () => void;
172
+ }
173
+
174
+ /**
175
+ * Relay 是一个"事件中继器",可以动态切换输入源。
176
+ * 用于 getDelayedChannel 中:先创建 Relay,等 channel promise resolve 后切换 input。
177
+ */
178
+ export class Relay<T> implements IDisposable {
179
+ private emitter = new Emitter<T>();
180
+ private inputDisposable: IDisposable = { dispose() {} };
181
+
182
+ readonly event = this.emitter.event;
183
+
184
+ set input(event: Event<T>) {
185
+ this.inputDisposable.dispose();
186
+ this.inputDisposable = event((e) => this.emitter.fire(e));
187
+ }
188
+
189
+ dispose(): void {
190
+ this.inputDisposable.dispose();
191
+ this.emitter.dispose();
192
+ }
193
+ }
194
+
195
+ /**
196
+ * EventMultiplexer 聚合多个事件源为一个事件。
197
+ * IPCServer 的 getMulticastEvent 用它来聚合所有客户端的同名事件。
198
+ */
199
+ export class EventMultiplexer<T> implements IDisposable {
200
+ private readonly emitter = new Emitter<T>();
201
+ private readonly disposables: IDisposable[] = [];
202
+
203
+ readonly event = this.emitter.event;
204
+
205
+ add(event: Event<T>): IDisposable {
206
+ const d = event((e) => this.emitter.fire(e));
207
+ this.disposables.push(d);
208
+ return d;
209
+ }
210
+
211
+ dispose(): void {
212
+ for (const d of this.disposables) {
213
+ d.dispose();
214
+ }
215
+ this.emitter.dispose();
216
+ }
217
+ }
218
+
219
+ // ============================================================================
220
+ // CancellationToken - 取消令牌
221
+ // ============================================================================
222
+
223
+ export interface CancellationToken {
224
+ readonly isCancellationRequested: boolean;
225
+ readonly onCancellationRequested: Event<void>;
226
+ }
227
+
228
+ export namespace CancellationToken {
229
+ export const None: CancellationToken = {
230
+ isCancellationRequested: false,
231
+ onCancellationRequested: Event.None,
232
+ };
233
+ }
234
+
235
+ export class CancellationTokenSource implements IDisposable {
236
+ private _token?: CancellationToken;
237
+ private emitter = new Emitter<void>();
238
+ private _isCancelled = false;
239
+
240
+ get token(): CancellationToken {
241
+ if (!this._token) {
242
+ this._token = {
243
+ isCancellationRequested: false,
244
+ onCancellationRequested: this.emitter.event,
245
+ };
246
+ }
247
+ return this._token;
248
+ }
249
+
250
+ cancel(): void {
251
+ if (!this._isCancelled) {
252
+ this._isCancelled = true;
253
+ this.emitter.fire();
254
+ }
255
+ }
256
+
257
+ dispose(): void {
258
+ this.emitter.dispose();
259
+ }
260
+ }
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Layer 2: 传输协议抽象
3
+ *
4
+ * IMessagePassingProtocol 是整个 IPC 框架的"腰部"——
5
+ * 它上面是 Channel RPC 层(完全传输无关),
6
+ * 它下面是各种具体传输实现(Electron/MessagePort/Socket/ChildProcess)。
7
+ *
8
+ * 只要实现 send() 和 onMessage,就能接入整个 RPC 框架。
9
+ */
10
+
11
+ import { VSBuffer } from "./buffer.js";
12
+ import { Event, Emitter, IDisposable, DisposableStore } from "./foundation.js";
13
+
14
+ // ============================================================================
15
+ // 核心传输接口
16
+ // ============================================================================
17
+
18
+ /**
19
+ * IMessagePassingProtocol —— 整个框架的核心抽象
20
+ *
21
+ * 这就是 VS Code 通信能力的秘密:上层代码只看到 send/onMessage,
22
+ * 不管底层是 Electron IPC、MessagePort、WebSocket 还是 TCP Socket。
23
+ */
24
+ export interface IMessagePassingProtocol {
25
+ send(buffer: VSBuffer): void;
26
+ readonly onMessage: Event<VSBuffer>;
27
+ drain?(): Promise<void>;
28
+ }
29
+
30
+ /**
31
+ * 连接级只读流控观察面。
32
+ *
33
+ * transport 负责维护未确认字节与状态边沿;业务层只能订阅,不能伪造 ACK 或直接改水位。
34
+ */
35
+ export interface ConnectionFlowControl {
36
+ readonly unacknowledgedBytes: number;
37
+ readonly onSaturated: Event<void>;
38
+ readonly onDrained: Event<void>;
39
+ }
40
+
41
+ export type MessagePortFlowState = "saturated" | "drained";
42
+
43
+ export interface MessagePortFlowControl {
44
+ __zcodeRpcControl: "connection-flow-v1";
45
+ state: MessagePortFlowState;
46
+ }
47
+
48
+ export type MessagePortPayload = Uint8Array | MessagePortFlowControl;
49
+
50
+ function isMessagePortFlowControl(value: unknown): value is MessagePortFlowControl {
51
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
52
+ const record = value as Record<string, unknown>;
53
+ return (
54
+ Object.keys(record).length === 2 &&
55
+ record.__zcodeRpcControl === "connection-flow-v1" &&
56
+ (record.state === "saturated" || record.state === "drained")
57
+ );
58
+ }
59
+
60
+ // ============================================================================
61
+ // Socket 接口(用于 TCP/WebSocket 等流式传输)
62
+ // ============================================================================
63
+
64
+ /**
65
+ * ISocket 抽象了底层的网络 socket。
66
+ * 在 Node.js 环境是 net.Socket,在浏览器环境是 WebSocket。
67
+ */
68
+ export interface ISocket extends IDisposable {
69
+ onData: Event<VSBuffer>;
70
+ onClose: Event<void>;
71
+ onEnd: Event<void>;
72
+ write(buffer: VSBuffer): void;
73
+ end(): void;
74
+ drain(): Promise<void>;
75
+ }
76
+
77
+ // ============================================================================
78
+ // ChunkStream —— 处理 TCP 的分片和粘包
79
+ // ============================================================================
80
+
81
+ /**
82
+ * TCP 是流式协议,一次 write 不代表对面一次 read 就能完整收到。
83
+ * ChunkStream 把收到的碎片攒起来,按需读取指定字节数。
84
+ */
85
+ export class ChunkStream {
86
+ private chunks: VSBuffer[] = [];
87
+ private totalLength = 0;
88
+
89
+ get byteLength(): number {
90
+ return this.totalLength;
91
+ }
92
+
93
+ acceptChunk(chunk: VSBuffer): void {
94
+ this.chunks.push(chunk);
95
+ this.totalLength += chunk.byteLength;
96
+ }
97
+
98
+ /**
99
+ * 预览前 byteCount 字节,但不消费底层缓冲。
100
+ *
101
+ * Socket/stdio 传输天然可能分片。
102
+ * 之前协议层在 body 还没收全时就先把 header read 掉,后续再来的 body
103
+ * 会失去对应的帧头,导致消息永远卡住。这里提供 peek,让调用方先判断
104
+ * “整帧是否已经到齐”,确认足够后再真正消费。
105
+ */
106
+ peek(byteCount: number): VSBuffer | null {
107
+ if (this.totalLength < byteCount) {
108
+ return null;
109
+ }
110
+
111
+ if (this.chunks[0].byteLength >= byteCount) {
112
+ return this.chunks[0].slice(0, byteCount);
113
+ }
114
+
115
+ const result = VSBuffer.alloc(byteCount);
116
+ let offset = 0;
117
+ for (const chunk of this.chunks) {
118
+ if (offset >= byteCount) {
119
+ break;
120
+ }
121
+
122
+ const remaining = byteCount - offset;
123
+ const copyLength = Math.min(chunk.byteLength, remaining);
124
+ result.set(copyLength === chunk.byteLength ? chunk : chunk.slice(0, copyLength), offset);
125
+ offset += copyLength;
126
+ }
127
+
128
+ return result;
129
+ }
130
+
131
+ /** 丢弃前 byteCount 字节 */
132
+ skip(byteCount: number): void {
133
+ const discarded = this.read(byteCount);
134
+ if (!discarded) {
135
+ throw new Error(`ChunkStream.skip(${byteCount}) 超出可读范围`);
136
+ }
137
+ }
138
+
139
+ /** 读取 byteCount 字节,不够就返回 null */
140
+ read(byteCount: number): VSBuffer | null {
141
+ if (this.totalLength < byteCount) {
142
+ return null;
143
+ }
144
+
145
+ if (this.chunks[0].byteLength === byteCount) {
146
+ const result = this.chunks.shift()!;
147
+ this.totalLength -= byteCount;
148
+ return result;
149
+ }
150
+
151
+ if (this.chunks[0].byteLength > byteCount) {
152
+ const result = this.chunks[0].slice(0, byteCount);
153
+ this.chunks[0] = this.chunks[0].slice(byteCount);
154
+ this.totalLength -= byteCount;
155
+ return result;
156
+ }
157
+
158
+ // 需要跨多个 chunk 拼接
159
+ const result = VSBuffer.alloc(byteCount);
160
+ let offset = 0;
161
+ while (offset < byteCount) {
162
+ const chunk = this.chunks[0];
163
+ const needed = byteCount - offset;
164
+ if (chunk.byteLength <= needed) {
165
+ result.set(chunk, offset);
166
+ offset += chunk.byteLength;
167
+ this.chunks.shift();
168
+ } else {
169
+ result.set(chunk.slice(0, needed), offset);
170
+ this.chunks[0] = chunk.slice(needed);
171
+ offset += needed;
172
+ }
173
+ }
174
+ this.totalLength -= byteCount;
175
+ return result;
176
+ }
177
+ }
178
+
179
+ // ============================================================================
180
+ // Protocol —— 在 ISocket 上实现 IMessagePassingProtocol
181
+ // ============================================================================
182
+
183
+ /**
184
+ * 消息帧格式 (13 bytes header):
185
+ *
186
+ * ┌─────────┬──────────┬──────────┬──────────────┐
187
+ * │ type(1) │ id(4) │ ack(4) │ length(4) │
188
+ * └─────────┴──────────┴──────────┴──────────────┘
189
+ *
190
+ * type: 消息类型(Regular, Ack, KeepAlive 等)
191
+ * id: 消息序号
192
+ * ack: 确认号(告诉对方"我已收到你的消息到第 ack 号")
193
+ * length: payload 长度
194
+ */
195
+ export enum ProtocolMessageType {
196
+ None = 0,
197
+ Regular = 1,
198
+ Control = 2,
199
+ Ack = 3,
200
+ Disconnect = 5,
201
+ ReplayRequest = 6,
202
+ Pause = 7,
203
+ Resume = 8,
204
+ KeepAlive = 9,
205
+ }
206
+
207
+ export const HEADER_SIZE = 13; // 1 + 4 + 4 + 4
208
+
209
+ export class ProtocolMessage {
210
+ constructor(
211
+ public readonly type: ProtocolMessageType,
212
+ public readonly id: number,
213
+ public readonly ack: number,
214
+ public readonly data: VSBuffer,
215
+ ) {}
216
+
217
+ get byteLength(): number {
218
+ return HEADER_SIZE + this.data.byteLength;
219
+ }
220
+ }
221
+
222
+ export function writeProtocolMessage(msg: ProtocolMessage): VSBuffer {
223
+ const result = VSBuffer.alloc(HEADER_SIZE + msg.data.byteLength);
224
+ result.writeUInt8(msg.type, 0);
225
+ result.writeUInt32BE(msg.id, 1);
226
+ result.writeUInt32BE(msg.ack, 5);
227
+ result.writeUInt32BE(msg.data.byteLength, 9);
228
+ result.set(msg.data, HEADER_SIZE);
229
+ return result;
230
+ }
231
+
232
+ /**
233
+ * 基础 Protocol: 在 ISocket 上加消息帧,实现 IMessagePassingProtocol。
234
+ * 只做消息分帧,不做 ACK/重连(那是 PersistentProtocol 的事)。
235
+ */
236
+ export class SocketProtocol implements IMessagePassingProtocol {
237
+ private readonly _onMessage = new Emitter<VSBuffer>();
238
+ readonly onMessage = this._onMessage.event;
239
+
240
+ private readonly chunkStream = new ChunkStream();
241
+ private readonly disposables = new DisposableStore();
242
+
243
+ constructor(private socket: ISocket) {
244
+ this.disposables.add(
245
+ socket.onData((data) => {
246
+ this.chunkStream.acceptChunk(data);
247
+ this.readMessages();
248
+ }),
249
+ );
250
+ }
251
+
252
+ send(buffer: VSBuffer): void {
253
+ this.writeMessage(new ProtocolMessage(ProtocolMessageType.Regular, 0, 0, buffer));
254
+ }
255
+
256
+ private writeMessage(msg: ProtocolMessage): void {
257
+ this.socket.write(writeProtocolMessage(msg));
258
+ }
259
+
260
+ private readMessages(): void {
261
+ while (true) {
262
+ const header = this.chunkStream.peek(HEADER_SIZE);
263
+ if (!header) {
264
+ break;
265
+ }
266
+
267
+ const type = header.readUInt8(0) as ProtocolMessageType;
268
+ const _id = header.readUInt32BE(1);
269
+ const _ack = header.readUInt32BE(5);
270
+ const length = header.readUInt32BE(9);
271
+
272
+ const totalFrameLength = HEADER_SIZE + length;
273
+ if (this.chunkStream.byteLength < totalFrameLength) {
274
+ // 不能在 body 未到齐时提前消费 header,否则下一段数据拼上来后
275
+ // 已经找不到这帧的长度信息,调用方就会一直等待一个永远不会完成的 Promise。
276
+ break;
277
+ }
278
+
279
+ this.chunkStream.skip(HEADER_SIZE);
280
+
281
+ if (length === 0) {
282
+ if (type === ProtocolMessageType.Regular) {
283
+ this._onMessage.fire(VSBuffer.alloc(0));
284
+ }
285
+ continue;
286
+ }
287
+
288
+ const body = this.chunkStream.read(length);
289
+ if (!body) {
290
+ throw new Error("SocketProtocol 读取到完整帧长度后 body 不应为空");
291
+ }
292
+
293
+ if (type === ProtocolMessageType.Regular) {
294
+ this._onMessage.fire(body);
295
+ }
296
+ }
297
+ }
298
+
299
+ async drain(): Promise<void> {
300
+ return this.socket.drain();
301
+ }
302
+
303
+ dispose(): void {
304
+ this.disposables.dispose();
305
+ this._onMessage.dispose();
306
+ }
307
+ }
308
+
309
+ // ============================================================================
310
+ // QueueProtocol —— 内存中的协议对,用于测试
311
+ // ============================================================================
312
+
313
+ /**
314
+ * 创建一对通过内存队列连接的 protocol,
315
+ * 一端 send 的消息会出现在另一端的 onMessage。
316
+ * 非常适合单元测试,不需要真正的网络连接。
317
+ */
318
+ export function createQueuePair(): [IMessagePassingProtocol, IMessagePassingProtocol] {
319
+ const emitterA = new Emitter<VSBuffer>();
320
+ const emitterB = new Emitter<VSBuffer>();
321
+
322
+ const protocolA: IMessagePassingProtocol = {
323
+ send: (buffer: VSBuffer) => {
324
+ // A 发送的消息 → B 收到
325
+ setTimeout(() => emitterB.fire(buffer), 0);
326
+ },
327
+ onMessage: emitterA.event,
328
+ };
329
+
330
+ const protocolB: IMessagePassingProtocol = {
331
+ send: (buffer: VSBuffer) => {
332
+ // B 发送的消息 → A 收到
333
+ setTimeout(() => emitterA.fire(buffer), 0);
334
+ },
335
+ onMessage: emitterB.event,
336
+ };
337
+
338
+ return [protocolA, protocolB];
339
+ }
340
+
341
+ // ============================================================================
342
+ // MessagePort Protocol —— 用于 Web Worker / Electron sandbox
343
+ // ============================================================================
344
+
345
+ /**
346
+ * MessagePort 接口的最小声明,
347
+ * 使得这个 Protocol 可以同时在浏览器和 Electron 中使用。
348
+ */
349
+ export interface MessagePortLike {
350
+ addEventListener(type: "message", listener: (e: { data: MessagePortPayload }) => void): void;
351
+ removeEventListener(type: "message", listener: (e: { data: MessagePortPayload }) => void): void;
352
+ postMessage(message: MessagePortPayload): void;
353
+ start(): void;
354
+ close(): void;
355
+ }
356
+
357
+ /**
358
+ * 在 MessagePort 上实现 IMessagePassingProtocol。
359
+ * 这是最简单的传输实现——不需要分帧,因为 MessagePort 本身就是消息边界的。
360
+ */
361
+ export class MessagePortProtocol implements IMessagePassingProtocol {
362
+ private readonly _onMessage = new Emitter<VSBuffer>();
363
+ readonly onMessage = this._onMessage.event;
364
+ private readonly _onFlowState = new Emitter<MessagePortFlowState>();
365
+ readonly onFlowState = this._onFlowState.event;
366
+
367
+ private readonly handler: (e: { data: MessagePortPayload }) => void;
368
+
369
+ constructor(private port: MessagePortLike) {
370
+ this.handler = (e: { data: MessagePortPayload }) => {
371
+ if (isMessagePortFlowControl(e.data)) {
372
+ this._onFlowState.fire(e.data.state);
373
+ return;
374
+ }
375
+ // MessagePort control object 不能进入 Channel deserialize;未知对象和伪造
376
+ // connection-flow-v1 一律丢弃,只有真实 Uint8Array 才是 RPC binary。
377
+ if (e.data instanceof Uint8Array) this._onMessage.fire(VSBuffer.wrap(e.data));
378
+ };
379
+ this.port.addEventListener("message", this.handler);
380
+ this.port.start();
381
+ }
382
+
383
+ send(buffer: VSBuffer): void {
384
+ this.port.postMessage(buffer.buffer);
385
+ }
386
+
387
+ sendFlowState(state: MessagePortFlowState): void {
388
+ this.port.postMessage({ __zcodeRpcControl: "connection-flow-v1", state });
389
+ }
390
+
391
+ disconnect(): void {
392
+ this.port.removeEventListener("message", this.handler);
393
+ this.port.close();
394
+ this._onMessage.dispose();
395
+ this._onFlowState.dispose();
396
+ }
397
+ }