paseo-plugin-zcode-provider 0.0.0-stage → 0.1.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 (134) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +29 -0
  3. package/README.md +98 -2
  4. package/client/diagnostics.tsx +397 -0
  5. package/client/setup.tsx +347 -0
  6. package/icon.svg +1 -0
  7. package/index.client.tsx +22 -0
  8. package/index.server.ts +21 -0
  9. package/package.json +75 -4
  10. package/paseo-plugin.json +6 -0
  11. package/server/attachments.ts +150 -0
  12. package/server/build-info.ts +2 -0
  13. package/server/conversation.ts +319 -0
  14. package/server/diagnostics.ts +235 -0
  15. package/server/discovery/discover.ts +225 -0
  16. package/server/discovery/manifest.ts +36 -0
  17. package/server/discovery/types.ts +30 -0
  18. package/server/errors.ts +36 -0
  19. package/server/host/bridge.ts +463 -0
  20. package/server/host/schemas.ts +119 -0
  21. package/server/logger.ts +19 -0
  22. package/server/mapping.ts +270 -0
  23. package/server/models.ts +68 -0
  24. package/server/persistence.ts +146 -0
  25. package/server/presentation.ts +428 -0
  26. package/server/provider.ts +831 -0
  27. package/server/runtime/managed.ts +501 -0
  28. package/server/runtime/pins.ts +72 -0
  29. package/server/runtime-setup.ts +102 -0
  30. package/server/session-types.ts +49 -0
  31. package/server/session.ts +866 -0
  32. package/server/status.ts +89 -0
  33. package/server/timeline.ts +18 -0
  34. package/server/vendor/zcode/LICENSE +202 -0
  35. package/server/vendor/zcode/NOTICE.md +70 -0
  36. package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
  37. package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
  38. package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
  39. package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
  40. package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
  41. package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
  42. package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
  43. package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
  44. package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
  45. package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
  46. package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
  47. package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
  48. package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
  49. package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
  50. package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
  51. package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
  52. package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
  53. package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
  54. package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
  55. package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
  56. package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
  57. package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
  58. package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
  59. package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
  60. package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
  61. package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
  62. package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
  63. package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
  64. package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
  65. package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
  66. package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
  67. package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
  68. package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
  69. package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
  70. package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
  71. package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
  72. package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
  73. package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
  74. package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
  75. package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
  76. package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
  77. package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
  78. package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
  79. package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
  80. package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
  81. package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
  82. package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
  83. package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
  84. package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
  85. package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
  86. package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
  87. package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
  88. package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
  89. package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
  90. package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
  91. package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
  92. package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
  93. package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
  94. package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
  95. package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
  96. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
  97. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
  98. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
  99. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
  100. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
  101. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
  102. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
  103. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
  104. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
  105. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
  106. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
  107. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
  108. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
  109. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
  110. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
  111. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
  112. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
  113. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
  114. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
  115. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
  116. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
  117. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
  118. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
  119. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
  120. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
  121. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
  122. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
  123. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
  124. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
  125. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
  126. package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
  127. package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
  128. package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
  129. package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
  130. package/server/vendor/zcode/provenance.json +405 -0
  131. package/shared/diagnostics.ts +52 -0
  132. package/shared/runtime-setup.ts +75 -0
  133. package/vendor/acp-registry/LICENSE +176 -0
  134. package/vendor/paseo/LICENSE +211 -0
@@ -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
+ }
@@ -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
+ }