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.
- package/LICENSE +21 -0
- package/NOTICE.md +29 -0
- package/README.md +98 -2
- package/client/diagnostics.tsx +397 -0
- package/client/setup.tsx +347 -0
- package/icon.svg +1 -0
- package/index.client.tsx +22 -0
- package/index.server.ts +21 -0
- package/package.json +75 -4
- package/paseo-plugin.json +6 -0
- package/server/attachments.ts +150 -0
- package/server/build-info.ts +2 -0
- package/server/conversation.ts +319 -0
- package/server/diagnostics.ts +235 -0
- package/server/discovery/discover.ts +225 -0
- package/server/discovery/manifest.ts +36 -0
- package/server/discovery/types.ts +30 -0
- package/server/errors.ts +36 -0
- package/server/host/bridge.ts +463 -0
- package/server/host/schemas.ts +119 -0
- package/server/logger.ts +19 -0
- package/server/mapping.ts +270 -0
- package/server/models.ts +68 -0
- package/server/persistence.ts +146 -0
- package/server/presentation.ts +428 -0
- package/server/provider.ts +831 -0
- package/server/runtime/managed.ts +501 -0
- package/server/runtime/pins.ts +72 -0
- package/server/runtime-setup.ts +102 -0
- package/server/session-types.ts +49 -0
- package/server/session.ts +866 -0
- package/server/status.ts +89 -0
- package/server/timeline.ts +18 -0
- package/server/vendor/zcode/LICENSE +202 -0
- package/server/vendor/zcode/NOTICE.md +70 -0
- package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
- package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
- package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
- package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
- package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
- package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
- package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
- package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
- package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
- package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
- package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
- package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
- package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
- package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
- package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
- package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
- package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
- package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
- package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
- package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
- package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
- package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
- package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
- package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
- package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
- package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
- package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
- package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
- package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
- package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
- package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
- package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
- package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
- package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
- package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
- package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
- package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
- package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
- package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
- package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
- package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
- package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
- package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
- package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
- package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
- package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
- package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
- package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
- package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
- package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
- package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
- package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
- package/server/vendor/zcode/provenance.json +405 -0
- package/shared/diagnostics.ts +52 -0
- package/shared/runtime-setup.ts +75 -0
- package/vendor/acp-registry/LICENSE +176 -0
- package/vendor/paseo/LICENSE +211 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { RestrictedCelError } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export type RestrictedCelTokenKind =
|
|
4
|
+
| "identifier"
|
|
5
|
+
| "string"
|
|
6
|
+
| "number"
|
|
7
|
+
| "operator"
|
|
8
|
+
| "punctuation"
|
|
9
|
+
| "eof";
|
|
10
|
+
|
|
11
|
+
export interface RestrictedCelToken {
|
|
12
|
+
readonly kind: RestrictedCelTokenKind;
|
|
13
|
+
readonly value: string;
|
|
14
|
+
readonly offset: number;
|
|
15
|
+
readonly end: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const DOUBLE_OPERATORS = new Set(["&&", "||", "==", "!=", "<=", ">="]);
|
|
19
|
+
const SINGLE_OPERATORS = new Set(["+", "-", "*", "/", "%", "!", "<", ">"]);
|
|
20
|
+
const PUNCTUATION = new Set(["{", "}", "[", "]", "(", ")", ",", ":", "?", "."]);
|
|
21
|
+
|
|
22
|
+
export function tokenizeRestrictedCel(source: string): readonly RestrictedCelToken[] {
|
|
23
|
+
const tokens: RestrictedCelToken[] = [];
|
|
24
|
+
let offset = 0;
|
|
25
|
+
while (offset < source.length) {
|
|
26
|
+
const character = source[offset]!;
|
|
27
|
+
if (/\s/u.test(character)) {
|
|
28
|
+
offset += 1;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (character === "'" || character === '"') {
|
|
33
|
+
const token = readString(source, offset, character);
|
|
34
|
+
tokens.push(token);
|
|
35
|
+
offset = token.end;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (/[0-9]/u.test(character)) {
|
|
40
|
+
const token = readNumber(source, offset);
|
|
41
|
+
tokens.push(token);
|
|
42
|
+
offset = token.end;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (/[A-Za-z_]/u.test(character)) {
|
|
47
|
+
const end = readWhile(source, offset + 1, /[A-Za-z0-9_]/u);
|
|
48
|
+
tokens.push({ kind: "identifier", value: source.slice(offset, end), offset, end });
|
|
49
|
+
offset = end;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const pair = source.slice(offset, offset + 2);
|
|
54
|
+
if (DOUBLE_OPERATORS.has(pair)) {
|
|
55
|
+
tokens.push({ kind: "operator", value: pair, offset, end: offset + 2 });
|
|
56
|
+
offset += 2;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (SINGLE_OPERATORS.has(character)) {
|
|
60
|
+
tokens.push({ kind: "operator", value: character, offset, end: offset + 1 });
|
|
61
|
+
offset += 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (PUNCTUATION.has(character)) {
|
|
65
|
+
tokens.push({ kind: "punctuation", value: character, offset, end: offset + 1 });
|
|
66
|
+
offset += 1;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
throw new RestrictedCelError(`unsupported token ${JSON.stringify(character)}`, offset);
|
|
70
|
+
}
|
|
71
|
+
tokens.push({ kind: "eof", value: "", offset: source.length, end: source.length });
|
|
72
|
+
return Object.freeze(tokens);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function readNumber(source: string, offset: number): RestrictedCelToken {
|
|
76
|
+
const match = /^(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/u.exec(source.slice(offset));
|
|
77
|
+
if (!match) throw new RestrictedCelError("invalid number literal", offset);
|
|
78
|
+
const value = match[0];
|
|
79
|
+
const end = offset + value.length;
|
|
80
|
+
return { kind: "number", value, offset, end };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readString(source: string, offset: number, quote: "'" | '"'): RestrictedCelToken {
|
|
84
|
+
let cursor = offset + 1;
|
|
85
|
+
let value = "";
|
|
86
|
+
while (cursor < source.length) {
|
|
87
|
+
const character = source[cursor]!;
|
|
88
|
+
if (character === quote) {
|
|
89
|
+
return { kind: "string", value, offset, end: cursor + 1 };
|
|
90
|
+
}
|
|
91
|
+
if (character === "\n" || character === "\r") {
|
|
92
|
+
throw new RestrictedCelError("unterminated string literal", offset);
|
|
93
|
+
}
|
|
94
|
+
if (character !== "\\") {
|
|
95
|
+
value += character;
|
|
96
|
+
cursor += 1;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const escapeOffset = cursor;
|
|
101
|
+
cursor += 1;
|
|
102
|
+
const escaped = source[cursor];
|
|
103
|
+
if (escaped === undefined) {
|
|
104
|
+
throw new RestrictedCelError("unterminated string escape", escapeOffset);
|
|
105
|
+
}
|
|
106
|
+
const simpleEscape = SIMPLE_ESCAPES[escaped];
|
|
107
|
+
if (simpleEscape !== undefined) {
|
|
108
|
+
value += simpleEscape;
|
|
109
|
+
cursor += 1;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (escaped === "u") {
|
|
113
|
+
const digits = source.slice(cursor + 1, cursor + 5);
|
|
114
|
+
if (!/^[0-9A-Fa-f]{4}$/u.test(digits)) {
|
|
115
|
+
throw new RestrictedCelError("invalid unicode escape", escapeOffset);
|
|
116
|
+
}
|
|
117
|
+
value += String.fromCharCode(Number.parseInt(digits, 16));
|
|
118
|
+
cursor += 5;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
throw new RestrictedCelError(`unsupported string escape \\${escaped}`, escapeOffset);
|
|
122
|
+
}
|
|
123
|
+
throw new RestrictedCelError("unterminated string literal", offset);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const SIMPLE_ESCAPES: Readonly<Record<string, string>> = Object.freeze({
|
|
127
|
+
"'": "'",
|
|
128
|
+
'"': '"',
|
|
129
|
+
"\\": "\\",
|
|
130
|
+
b: "\b",
|
|
131
|
+
f: "\f",
|
|
132
|
+
n: "\n",
|
|
133
|
+
r: "\r",
|
|
134
|
+
t: "\t",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
function readWhile(source: string, offset: number, pattern: RegExp): number {
|
|
138
|
+
let cursor = offset;
|
|
139
|
+
while (cursor < source.length && pattern.test(source[cursor]!)) cursor += 1;
|
|
140
|
+
return cursor;
|
|
141
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
2
|
+
export type JsonValue = JsonPrimitive | JsonObject | readonly JsonValue[];
|
|
3
|
+
|
|
4
|
+
export interface JsonObject {
|
|
5
|
+
readonly [key: string]: JsonValue;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type RestrictedCelValue = string | number;
|
|
9
|
+
export type ModelOptionName = "reasoningLevel" | "maxOutputTokens";
|
|
10
|
+
|
|
11
|
+
export interface RestrictedCelProgram {
|
|
12
|
+
readonly source: string;
|
|
13
|
+
evaluate(input: RestrictedCelValue): JsonValue;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ModelOptionMapProgram {
|
|
17
|
+
readonly source: string;
|
|
18
|
+
evaluate(input: RestrictedCelValue): JsonObject;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class RestrictedCelError extends Error {
|
|
22
|
+
readonly offset: number;
|
|
23
|
+
|
|
24
|
+
constructor(message: string, offset: number) {
|
|
25
|
+
super(`${message} at offset ${offset}`);
|
|
26
|
+
this.name = "RestrictedCelError";
|
|
27
|
+
this.offset = offset;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class ModelOptionMapError extends Error {
|
|
32
|
+
constructor(message: string) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "ModelOptionMapError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 0.5: 跨平台 Buffer 抽象
|
|
3
|
+
*
|
|
4
|
+
* VS Code 需要在 Node.js (Buffer) 和浏览器 (Uint8Array) 之间统一二进制操作。
|
|
5
|
+
* VSBuffer 是对 Uint8Array 的薄封装,提供统一的读写接口。
|
|
6
|
+
*
|
|
7
|
+
* 这是序列化层和传输层的基础。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class VSBuffer {
|
|
11
|
+
readonly buffer: Uint8Array;
|
|
12
|
+
readonly byteLength: number;
|
|
13
|
+
|
|
14
|
+
private constructor(buffer: Uint8Array) {
|
|
15
|
+
this.buffer = buffer;
|
|
16
|
+
this.byteLength = buffer.byteLength;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** 分配指定大小的空 buffer */
|
|
20
|
+
static alloc(byteLength: number): VSBuffer {
|
|
21
|
+
return new VSBuffer(new Uint8Array(byteLength));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 包装已有的 Uint8Array */
|
|
25
|
+
static wrap(buffer: Uint8Array): VSBuffer {
|
|
26
|
+
return new VSBuffer(buffer);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 从字符串创建 buffer (UTF-8) */
|
|
30
|
+
static fromString(str: string): VSBuffer {
|
|
31
|
+
const encoder = new TextEncoder();
|
|
32
|
+
return new VSBuffer(encoder.encode(str));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 拼接多个 buffer */
|
|
36
|
+
static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
|
|
37
|
+
const len = totalLength ?? buffers.reduce((sum, b) => sum + b.byteLength, 0);
|
|
38
|
+
const result = VSBuffer.alloc(len);
|
|
39
|
+
let offset = 0;
|
|
40
|
+
for (const buf of buffers) {
|
|
41
|
+
result.set(buf, offset);
|
|
42
|
+
offset += buf.byteLength;
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 转为 UTF-8 字符串 */
|
|
48
|
+
toString(): string {
|
|
49
|
+
const decoder = new TextDecoder();
|
|
50
|
+
return decoder.decode(this.buffer);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** 切片 */
|
|
54
|
+
slice(start: number, end?: number): VSBuffer {
|
|
55
|
+
return new VSBuffer(this.buffer.slice(start, end));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 拷贝数据到 this buffer 的指定位置 */
|
|
59
|
+
set(source: VSBuffer | Uint8Array, offset = 0): void {
|
|
60
|
+
const raw = source instanceof VSBuffer ? source.buffer : source;
|
|
61
|
+
this.buffer.set(raw, offset);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
readUInt8(offset: number): number {
|
|
65
|
+
return this.buffer[offset];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
writeUInt8(value: number, offset: number): void {
|
|
69
|
+
this.buffer[offset] = value;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
readUInt32BE(offset: number): number {
|
|
73
|
+
return (
|
|
74
|
+
((this.buffer[offset] << 24) |
|
|
75
|
+
(this.buffer[offset + 1] << 16) |
|
|
76
|
+
(this.buffer[offset + 2] << 8) |
|
|
77
|
+
this.buffer[offset + 3]) >>>
|
|
78
|
+
0
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
writeUInt32BE(value: number, offset: number): void {
|
|
83
|
+
this.buffer[offset] = (value >>> 24) & 0xff;
|
|
84
|
+
this.buffer[offset + 1] = (value >>> 16) & 0xff;
|
|
85
|
+
this.buffer[offset + 2] = (value >>> 8) & 0xff;
|
|
86
|
+
this.buffer[offset + 3] = value & 0xff;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -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
|
+
}
|