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,275 @@
1
+ import { VSBuffer } from "./buffer.js";
2
+ import { CancellationToken, Event, Emitter, type IDisposable } from "./foundation.js";
3
+ import { BufferReader, BufferWriter, deserialize, serialize } from "./serialization.js";
4
+ import type { IMessagePassingProtocol } from "./protocol.js";
5
+ import {
6
+ type IChannel,
7
+ type IChannelClient,
8
+ type IHandler,
9
+ type IRawResponse,
10
+ RequestType,
11
+ ResponseType,
12
+ } from "./channels.shared.js";
13
+
14
+ enum State {
15
+ Uninitialized,
16
+ Idle,
17
+ }
18
+
19
+ export class ChannelClient implements IChannelClient, IDisposable {
20
+ private state = State.Uninitialized;
21
+ private isDisposed = false;
22
+ private activeRequests = new Set<IDisposable>();
23
+ private handlers = new Map<number, IHandler>();
24
+ // Promise 请求和事件监听共用 handlers,但只有前者需要在连接终结时 reject。
25
+ // 单独维护 reject map,避免 dispose 把事件订阅误当成挂起的 RPC 请求。
26
+ private pendingRejections = new Map<number, (error: Error) => void>();
27
+ private lastRequestId = 0;
28
+ private protocolListener: IDisposable | null;
29
+
30
+ private readonly _onDidInitialize = new Emitter<void>();
31
+ readonly onDidInitialize = this._onDidInitialize.event;
32
+
33
+ constructor(private protocol: IMessagePassingProtocol) {
34
+ this.protocolListener = this.protocol.onMessage((msg) => this.onBuffer(msg));
35
+ }
36
+
37
+ getChannel<T extends IChannel>(channelName: string): T {
38
+ return {
39
+ call: (command: string, arg?: any, cancellationToken?: CancellationToken) => {
40
+ if (this.isDisposed) {
41
+ return Promise.reject(new Error("ChannelClient is disposed"));
42
+ }
43
+ return this.requestPromise(channelName, command, arg, cancellationToken);
44
+ },
45
+ listen: (event: string, arg?: any) => {
46
+ if (this.isDisposed) {
47
+ return Event.None;
48
+ }
49
+ return this.requestEvent(channelName, event, arg);
50
+ },
51
+ } as T;
52
+ }
53
+
54
+ private requestPromise(
55
+ channelName: string,
56
+ name: string,
57
+ arg?: any,
58
+ cancellationToken = CancellationToken.None,
59
+ ): Promise<any> {
60
+ const id = this.lastRequestId++;
61
+
62
+ if (cancellationToken.isCancellationRequested) {
63
+ return Promise.reject(new Error("Cancelled"));
64
+ }
65
+
66
+ let disposable: IDisposable | undefined;
67
+ const result = new Promise<any>((resolve, reject) => {
68
+ this.pendingRejections.set(id, reject);
69
+ const doRequest = () => {
70
+ // dispose/cancel 可能发生在 Initialize 之前;此时不能再把已经 rejected
71
+ // 的请求发送到新连接或已终结的传输上。
72
+ if (this.isDisposed || !this.pendingRejections.has(id)) {
73
+ return;
74
+ }
75
+
76
+ const handler: IHandler = (response) => {
77
+ switch (response.type) {
78
+ case ResponseType.PromiseSuccess:
79
+ this.handlers.delete(id);
80
+ this.pendingRejections.delete(id);
81
+ resolve(response.data);
82
+ return;
83
+ case ResponseType.PromiseError: {
84
+ this.handlers.delete(id);
85
+ this.pendingRejections.delete(id);
86
+ const error = new Error(response.data.message) as Error & Record<string, unknown>;
87
+ error.name = response.data.name;
88
+ if (response.data.stack) {
89
+ error.stack = response.data.stack.join("\n");
90
+ }
91
+ const passthroughKeys = [
92
+ "code",
93
+ "kind",
94
+ "status",
95
+ "retryAfterMs",
96
+ "data",
97
+ "detail",
98
+ "details",
99
+ "taskId",
100
+ "traceId",
101
+ ] as const;
102
+ for (const key of passthroughKeys) {
103
+ const value = response.data[key];
104
+ if (value !== undefined) {
105
+ error[key] = value;
106
+ }
107
+ }
108
+ reject(error);
109
+ return;
110
+ }
111
+ case ResponseType.PromiseErrorObj:
112
+ this.handlers.delete(id);
113
+ this.pendingRejections.delete(id);
114
+ reject(response.data);
115
+ return;
116
+ }
117
+ };
118
+
119
+ this.handlers.set(id, handler);
120
+ this.sendRequest(RequestType.Promise, id, channelName, name, arg);
121
+ };
122
+
123
+ if (this.state === State.Idle) {
124
+ doRequest();
125
+ } else {
126
+ this.whenInitialized().then(doRequest);
127
+ }
128
+
129
+ disposable = cancellationToken.onCancellationRequested(() => {
130
+ if (!this.pendingRejections.has(id)) {
131
+ return;
132
+ }
133
+ this.sendCancelOrDispose(RequestType.PromiseCancel, id);
134
+ this.handlers.delete(id);
135
+ this.pendingRejections.delete(id);
136
+ reject(new Error("Cancelled"));
137
+ });
138
+ this.activeRequests.add(disposable);
139
+ });
140
+
141
+ return result.finally(() => {
142
+ disposable?.dispose();
143
+ if (disposable) {
144
+ this.activeRequests.delete(disposable);
145
+ }
146
+ });
147
+ }
148
+
149
+ private requestEvent(channelName: string, name: string, arg?: any): Event<any> {
150
+ const id = this.lastRequestId++;
151
+ const emitter = new Emitter<any>({
152
+ onWillAddFirstListener: () => {
153
+ const doRequest = () => {
154
+ this.activeRequests.add(emitter);
155
+ this.sendRequest(RequestType.EventListen, id, channelName, name, arg);
156
+ };
157
+
158
+ if (this.state === State.Idle) {
159
+ doRequest();
160
+ } else {
161
+ this.whenInitialized().then(doRequest);
162
+ }
163
+ },
164
+ onDidRemoveLastListener: () => {
165
+ this.activeRequests.delete(emitter);
166
+ this.sendCancelOrDispose(RequestType.EventDispose, id);
167
+ this.handlers.delete(id);
168
+ },
169
+ });
170
+
171
+ this.handlers.set(id, (response) => {
172
+ emitter.fire((response as { data: any }).data);
173
+ });
174
+
175
+ return emitter.event;
176
+ }
177
+
178
+ private sendRequest(
179
+ type: RequestType,
180
+ id: number,
181
+ channelName: string,
182
+ name: string,
183
+ arg?: any,
184
+ ): void {
185
+ const writer = new BufferWriter();
186
+ serialize(writer, [type, id, channelName, name]);
187
+ serialize(writer, arg);
188
+ try {
189
+ this.protocol.send(writer.buffer);
190
+ } catch {
191
+ /* noop */
192
+ }
193
+ }
194
+
195
+ private sendCancelOrDispose(
196
+ type: RequestType.PromiseCancel | RequestType.EventDispose,
197
+ id: number,
198
+ ): void {
199
+ const writer = new BufferWriter();
200
+ serialize(writer, [type, id]);
201
+ serialize(writer, undefined);
202
+ try {
203
+ this.protocol.send(writer.buffer);
204
+ } catch {
205
+ /* noop */
206
+ }
207
+ }
208
+
209
+ private onBuffer(message: VSBuffer): void {
210
+ const reader = new BufferReader(message);
211
+ const header = deserialize(reader);
212
+ const body = deserialize(reader);
213
+ const type = header[0] as ResponseType;
214
+
215
+ switch (type) {
216
+ case ResponseType.Initialize:
217
+ this.onResponse({ type: ResponseType.Initialize });
218
+ return;
219
+ case ResponseType.PromiseSuccess:
220
+ case ResponseType.PromiseError:
221
+ case ResponseType.EventFire:
222
+ case ResponseType.PromiseErrorObj:
223
+ this.onResponse({
224
+ type,
225
+ id: header[1],
226
+ data: body,
227
+ } as IRawResponse);
228
+ return;
229
+ }
230
+ }
231
+
232
+ private onResponse(response: IRawResponse): void {
233
+ if (response.type === ResponseType.Initialize) {
234
+ this.state = State.Idle;
235
+ this._onDidInitialize.fire();
236
+ return;
237
+ }
238
+
239
+ this.handlers.get(response.id)?.(response);
240
+ }
241
+
242
+ private whenInitialized(): Promise<void> {
243
+ if (this.state === State.Idle) {
244
+ return Promise.resolve();
245
+ }
246
+ return Event.toPromise(this.onDidInitialize);
247
+ }
248
+
249
+ dispose(reason?: Error): void {
250
+ if (this.isDisposed) {
251
+ return;
252
+ }
253
+ this.isDisposed = true;
254
+ this.protocolListener?.dispose();
255
+ this.protocolListener = null;
256
+
257
+ const rejection = reason ?? new Error("ChannelClient disposed");
258
+ if (!reason) {
259
+ rejection.name = "ConnectionClosed";
260
+ }
261
+ // 传输已终结时,所有已发出以及排队等待 Initialize 的 Promise 请求都必须
262
+ // fail-closed。否则上层的 in-flight 去重 Promise 会永久占用 workspace key。
263
+ for (const [id, reject] of this.pendingRejections) {
264
+ this.pendingRejections.delete(id);
265
+ this.handlers.delete(id);
266
+ reject(rejection);
267
+ }
268
+ for (const disposable of this.activeRequests) {
269
+ disposable.dispose();
270
+ }
271
+ this.activeRequests.clear();
272
+ this.pendingRejections.clear();
273
+ this._onDidInitialize.dispose();
274
+ }
275
+ }
@@ -0,0 +1,283 @@
1
+ import { VSBuffer } from "./buffer.js";
2
+ import { type IDisposable, CancellationTokenSource, toDisposable } from "./foundation.js";
3
+ import { BufferReader, BufferWriter, deserialize, serialize } from "./serialization.js";
4
+ import type { IMessagePassingProtocol } from "./protocol.js";
5
+ import {
6
+ type IChannelServer,
7
+ type IRawResponse,
8
+ type IServerChannel,
9
+ RequestType,
10
+ ResponseType,
11
+ } from "./channels.shared.js";
12
+
13
+ export class ChannelServer<TContext = string> implements IChannelServer<TContext>, IDisposable {
14
+ private channels = new Map<string, IServerChannel<TContext>>();
15
+ private activeRequests = new Map<number, IDisposable>();
16
+ private pendingRequests = new Map<
17
+ string,
18
+ { request: any; timer: ReturnType<typeof setTimeout> }[]
19
+ >();
20
+ private protocolListener: IDisposable | null;
21
+
22
+ constructor(
23
+ private protocol: IMessagePassingProtocol,
24
+ private ctx: TContext,
25
+ private timeoutDelay = 1000,
26
+ private deferInit = false,
27
+ ) {
28
+ this.protocolListener = this.protocol.onMessage((msg) => this.onRawMessage(msg));
29
+ if (!this.deferInit) {
30
+ this.sendResponse({ type: ResponseType.Initialize });
31
+ }
32
+ }
33
+
34
+ ready(): void {
35
+ this.sendResponse({ type: ResponseType.Initialize });
36
+ }
37
+
38
+ registerChannel(channelName: string, channel: IServerChannel<TContext>): void {
39
+ this.channels.set(channelName, channel);
40
+ setTimeout(() => this.flushPendingRequests(channelName), 0);
41
+ }
42
+
43
+ private sendResponse(response: IRawResponse): void {
44
+ switch (response.type) {
45
+ case ResponseType.Initialize:
46
+ this.send([response.type]);
47
+ return;
48
+ case ResponseType.PromiseSuccess:
49
+ case ResponseType.PromiseError:
50
+ case ResponseType.EventFire:
51
+ case ResponseType.PromiseErrorObj:
52
+ this.send([response.type, response.id], response.data);
53
+ return;
54
+ }
55
+ }
56
+
57
+ private send(header: any, body: any = undefined): void {
58
+ const writer = new BufferWriter();
59
+ serialize(writer, header);
60
+ serialize(writer, body);
61
+ try {
62
+ this.protocol.send(writer.buffer);
63
+ } catch {
64
+ /* noop */
65
+ }
66
+ }
67
+
68
+ private onRawMessage(message: VSBuffer): void {
69
+ const reader = new BufferReader(message);
70
+ const header = deserialize(reader);
71
+ const body = deserialize(reader);
72
+ const type = header[0] as RequestType;
73
+
74
+ switch (type) {
75
+ case RequestType.Promise:
76
+ this.onPromise({
77
+ type,
78
+ id: header[1],
79
+ channelName: header[2],
80
+ name: header[3],
81
+ arg: body,
82
+ });
83
+ return;
84
+ case RequestType.EventListen:
85
+ this.onEventListen({
86
+ type,
87
+ id: header[1],
88
+ channelName: header[2],
89
+ name: header[3],
90
+ arg: body,
91
+ });
92
+ return;
93
+ case RequestType.PromiseCancel:
94
+ case RequestType.EventDispose:
95
+ this.disposeActiveRequest(header[1]);
96
+ return;
97
+ }
98
+ }
99
+
100
+ private onPromise(request: {
101
+ type: RequestType.Promise;
102
+ id: number;
103
+ channelName: string;
104
+ name: string;
105
+ arg: any;
106
+ }): void {
107
+ const channel = this.channels.get(request.channelName);
108
+ if (!channel) {
109
+ this.collectPendingRequest(request);
110
+ return;
111
+ }
112
+
113
+ const cts = new CancellationTokenSource();
114
+ let promise: Promise<any>;
115
+
116
+ try {
117
+ promise = channel.call(this.ctx, request.name, request.arg, cts.token);
118
+ } catch (error) {
119
+ promise = Promise.reject(error);
120
+ }
121
+
122
+ const disposable = toDisposable(() => cts.cancel());
123
+ this.activeRequests.set(request.id, disposable);
124
+
125
+ promise
126
+ .then(
127
+ (data) => {
128
+ this.sendResponse({
129
+ id: request.id,
130
+ data,
131
+ type: ResponseType.PromiseSuccess,
132
+ });
133
+ },
134
+ (error) => {
135
+ if (error instanceof Error) {
136
+ const rpcErrorPayload: {
137
+ message: string;
138
+ name: string;
139
+ stack: string[] | undefined;
140
+ code?: unknown;
141
+ kind?: unknown;
142
+ status?: unknown;
143
+ retryAfterMs?: unknown;
144
+ data?: unknown;
145
+ detail?: unknown;
146
+ details?: unknown;
147
+ taskId?: unknown;
148
+ traceId?: unknown;
149
+ } = {
150
+ message: error.message,
151
+ name: error.name,
152
+ stack: error.stack ? error.stack.split("\n") : undefined,
153
+ };
154
+ const errorRecord = error as Error & Record<string, unknown>;
155
+ const passthroughKeys = [
156
+ "code",
157
+ "kind",
158
+ "status",
159
+ "retryAfterMs",
160
+ "data",
161
+ "detail",
162
+ "details",
163
+ "taskId",
164
+ "traceId",
165
+ ] as const;
166
+ for (const key of passthroughKeys) {
167
+ const value = errorRecord[key];
168
+ if (value !== undefined) {
169
+ rpcErrorPayload[key] = value;
170
+ }
171
+ }
172
+ this.sendResponse({
173
+ id: request.id,
174
+ data: rpcErrorPayload,
175
+ type: ResponseType.PromiseError,
176
+ });
177
+ return;
178
+ }
179
+
180
+ this.sendResponse({
181
+ id: request.id,
182
+ data: error,
183
+ type: ResponseType.PromiseErrorObj,
184
+ });
185
+ },
186
+ )
187
+ .finally(() => {
188
+ disposable.dispose();
189
+ this.activeRequests.delete(request.id);
190
+ });
191
+ }
192
+
193
+ private onEventListen(request: {
194
+ type: RequestType.EventListen;
195
+ id: number;
196
+ channelName: string;
197
+ name: string;
198
+ arg: any;
199
+ }): void {
200
+ const channel = this.channels.get(request.channelName);
201
+ if (!channel) {
202
+ this.collectPendingRequest(request);
203
+ return;
204
+ }
205
+
206
+ const disposable = channel.listen(
207
+ this.ctx,
208
+ request.name,
209
+ request.arg,
210
+ )((data) => {
211
+ this.sendResponse({
212
+ id: request.id,
213
+ data,
214
+ type: ResponseType.EventFire,
215
+ });
216
+ });
217
+ this.activeRequests.set(request.id, disposable);
218
+ }
219
+
220
+ private disposeActiveRequest(id: number): void {
221
+ const disposable = this.activeRequests.get(id);
222
+ if (!disposable) {
223
+ return;
224
+ }
225
+ disposable.dispose();
226
+ this.activeRequests.delete(id);
227
+ }
228
+
229
+ private collectPendingRequest(request: any): void {
230
+ const pendingRequests = this.pendingRequests.get(request.channelName) ?? [];
231
+ if (pendingRequests.length === 0) {
232
+ this.pendingRequests.set(request.channelName, pendingRequests);
233
+ }
234
+
235
+ const timer = setTimeout(() => {
236
+ console.error(`Unknown channel: ${request.channelName}`);
237
+ if (request.type !== RequestType.Promise) {
238
+ return;
239
+ }
240
+
241
+ this.sendResponse({
242
+ id: request.id,
243
+ data: {
244
+ name: "Unknown channel",
245
+ message: `Channel name '${request.channelName}' timed out after ${this.timeoutDelay}ms`,
246
+ stack: undefined,
247
+ },
248
+ type: ResponseType.PromiseError,
249
+ });
250
+ }, this.timeoutDelay);
251
+
252
+ pendingRequests.push({ request, timer });
253
+ }
254
+
255
+ private flushPendingRequests(channelName: string): void {
256
+ const requests = this.pendingRequests.get(channelName);
257
+ if (!requests) {
258
+ return;
259
+ }
260
+
261
+ for (const { request, timer } of requests) {
262
+ clearTimeout(timer);
263
+ switch (request.type) {
264
+ case RequestType.Promise:
265
+ this.onPromise(request);
266
+ break;
267
+ case RequestType.EventListen:
268
+ this.onEventListen(request);
269
+ break;
270
+ }
271
+ }
272
+ this.pendingRequests.delete(channelName);
273
+ }
274
+
275
+ dispose(): void {
276
+ this.protocolListener?.dispose();
277
+ this.protocolListener = null;
278
+ for (const disposable of this.activeRequests.values()) {
279
+ disposable.dispose();
280
+ }
281
+ this.activeRequests.clear();
282
+ }
283
+ }
@@ -0,0 +1,66 @@
1
+ import type { CancellationToken, Event } from "./foundation.js";
2
+
3
+ export interface IChannel {
4
+ call<T>(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<T>;
5
+ listen<T>(event: string, arg?: any): Event<T>;
6
+ }
7
+
8
+ export interface IServerChannel<TContext = string> {
9
+ call<T>(
10
+ ctx: TContext,
11
+ command: string,
12
+ arg?: any,
13
+ cancellationToken?: CancellationToken,
14
+ ): Promise<T>;
15
+ listen<T>(ctx: TContext, event: string, arg?: any): Event<T>;
16
+ }
17
+
18
+ export const enum RequestType {
19
+ Promise = 100,
20
+ PromiseCancel = 101,
21
+ EventListen = 102,
22
+ EventDispose = 103,
23
+ }
24
+
25
+ export const enum ResponseType {
26
+ Initialize = 200,
27
+ PromiseSuccess = 201,
28
+ PromiseError = 202,
29
+ PromiseErrorObj = 203,
30
+ EventFire = 204,
31
+ }
32
+
33
+ export type IRawResponse =
34
+ | { type: ResponseType.Initialize }
35
+ | { type: ResponseType.PromiseSuccess; id: number; data: any }
36
+ | {
37
+ type: ResponseType.PromiseError;
38
+ id: number;
39
+ data: {
40
+ message: string;
41
+ name: string;
42
+ stack: string[] | undefined;
43
+ code?: unknown;
44
+ kind?: unknown;
45
+ status?: unknown;
46
+ retryAfterMs?: unknown;
47
+ data?: unknown;
48
+ detail?: unknown;
49
+ details?: unknown;
50
+ taskId?: unknown;
51
+ traceId?: unknown;
52
+ };
53
+ }
54
+ | { type: ResponseType.PromiseErrorObj; id: number; data: any }
55
+ | { type: ResponseType.EventFire; id: number; data: any };
56
+
57
+ export type IHandler = (response: IRawResponse) => void;
58
+
59
+ export interface IChannelServer<TContext = string> {
60
+ registerChannel(channelName: string, channel: IServerChannel<TContext>): void;
61
+ ready?(): void;
62
+ }
63
+
64
+ export interface IChannelClient {
65
+ getChannel<T extends IChannel>(channelName: string): T;
66
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./channels.shared.js";
2
+ export { ChannelServer } from "./channelServer.js";
3
+ export { ChannelClient } from "./channelClient.js";
4
+ export { getDelayedChannel } from "./delayedChannel.js";
@@ -0,0 +1,17 @@
1
+ import { type CancellationToken, Event, Relay } from "./foundation.js";
2
+ import type { IChannel } from "./channels.shared.js";
3
+
4
+ export function getDelayedChannel<T extends IChannel>(promise: Promise<T>): T {
5
+ return {
6
+ call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise<any> {
7
+ return promise.then((channel) => channel.call(command, arg, cancellationToken));
8
+ },
9
+ listen(event: string, arg?: any): Event<any> {
10
+ const relay = new Relay<any>();
11
+ promise.then((channel) => {
12
+ relay.input = channel.listen(event, arg);
13
+ });
14
+ return relay.event;
15
+ },
16
+ } as T;
17
+ }