@linxiraos/pi-utils 1.0.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/CHANGELOG.md +449 -0
- package/README.md +36 -0
- package/dist/types/abortable.d.ts +32 -0
- package/dist/types/acp/connection.d.ts +118 -0
- package/dist/types/acp/protocol.d.ts +526 -0
- package/dist/types/acp/schema.d.ts +41 -0
- package/dist/types/acp/stream.d.ts +8 -0
- package/dist/types/acp/transport.d.ts +81 -0
- package/dist/types/acp.d.ts +6 -0
- package/dist/types/async.d.ts +20 -0
- package/dist/types/binary.d.ts +17 -0
- package/dist/types/browsers.d.ts +68 -0
- package/dist/types/chalk.d.ts +125 -0
- package/dist/types/cli.d.ts +134 -0
- package/dist/types/color.d.ts +102 -0
- package/dist/types/dates.d.ts +7 -0
- package/dist/types/dirs.d.ts +282 -0
- package/dist/types/docx/converter.d.ts +46 -0
- package/dist/types/docx/xml.d.ts +26 -0
- package/dist/types/docx/zip.d.ts +6 -0
- package/dist/types/docx.d.ts +11 -0
- package/dist/types/dom/core.d.ts +431 -0
- package/dist/types/dom/parser.d.ts +7 -0
- package/dist/types/dom/selector.d.ts +5 -0
- package/dist/types/dom.d.ts +5 -0
- package/dist/types/env.d.ts +124 -0
- package/dist/types/fetch-retry.d.ts +96 -0
- package/dist/types/file-lock.d.ts +23 -0
- package/dist/types/format.d.ts +37 -0
- package/dist/types/frontmatter.d.ts +46 -0
- package/dist/types/fs-error.d.ts +31 -0
- package/dist/types/glob.d.ts +28 -0
- package/dist/types/headers.d.ts +34 -0
- package/dist/types/index.d.ts +39 -0
- package/dist/types/json-parse.d.ts +74 -0
- package/dist/types/json.d.ts +14 -0
- package/dist/types/logger/rotating-file.d.ts +18 -0
- package/dist/types/logger.d.ts +96 -0
- package/dist/types/loop-phase.d.ts +10 -0
- package/dist/types/lru.d.ts +46 -0
- package/dist/types/marked/core.d.ts +445 -0
- package/dist/types/marked.d.ts +2 -0
- package/dist/types/mermaid-ascii.d.ts +11 -0
- package/dist/types/mime.d.ts +29 -0
- package/dist/types/module-timer.d.ts +1 -0
- package/dist/types/path-tree.d.ts +76 -0
- package/dist/types/path.d.ts +2 -0
- package/dist/types/peek-file.d.ts +29 -0
- package/dist/types/postmortem.d.ts +104 -0
- package/dist/types/process-name.d.ts +7 -0
- package/dist/types/procmgr.d.ts +74 -0
- package/dist/types/prompt.d.ts +18 -0
- package/dist/types/ptree.d.ts +110 -0
- package/dist/types/readability/readability.d.ts +9 -0
- package/dist/types/readability/readerable.d.ts +10 -0
- package/dist/types/readability/types.d.ts +70 -0
- package/dist/types/readability.d.ts +4 -0
- package/dist/types/ring.d.ts +93 -0
- package/dist/types/runtime-install.d.ts +78 -0
- package/dist/types/sanitize-text.d.ts +29 -0
- package/dist/types/snowflake.d.ts +25 -0
- package/dist/types/stderr-guard.d.ts +22 -0
- package/dist/types/stream.d.ts +67 -0
- package/dist/types/tab-spacing.d.ts +24 -0
- package/dist/types/temp.d.ts +17 -0
- package/dist/types/template.d.ts +62 -0
- package/dist/types/timing-buffer.d.ts +22 -0
- package/dist/types/tls-fetch.d.ts +37 -0
- package/dist/types/turndown/gfm.d.ts +11 -0
- package/dist/types/turndown/html.d.ts +5 -0
- package/dist/types/turndown/service.d.ts +21 -0
- package/dist/types/turndown/types.d.ts +70 -0
- package/dist/types/turndown.d.ts +4 -0
- package/dist/types/type-guards.d.ts +5 -0
- package/dist/types/vendor/mermaid-ascii/ascii/ansi.d.ts +41 -0
- package/dist/types/vendor/mermaid-ascii/ascii/canvas.d.ts +89 -0
- package/dist/types/vendor/mermaid-ascii/ascii/class-diagram.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/ascii/converter.d.ts +12 -0
- package/dist/types/vendor/mermaid-ascii/ascii/draw.d.ts +66 -0
- package/dist/types/vendor/mermaid-ascii/ascii/edge-bundling.d.ts +48 -0
- package/dist/types/vendor/mermaid-ascii/ascii/edge-routing.d.ts +43 -0
- package/dist/types/vendor/mermaid-ascii/ascii/er-diagram.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/ascii/grid.d.ts +56 -0
- package/dist/types/vendor/mermaid-ascii/ascii/index.d.ts +65 -0
- package/dist/types/vendor/mermaid-ascii/ascii/multiline-utils.d.ts +27 -0
- package/dist/types/vendor/mermaid-ascii/ascii/pathfinder.d.ts +17 -0
- package/dist/types/vendor/mermaid-ascii/ascii/sequence.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/circle.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/corners.d.ts +34 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/diamond.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/hexagon.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/index.d.ts +26 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/rectangle.d.ts +31 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/rounded.d.ts +11 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/special.d.ts +59 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/stadium.d.ts +17 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/state.d.ts +30 -0
- package/dist/types/vendor/mermaid-ascii/ascii/shapes/types.d.ts +55 -0
- package/dist/types/vendor/mermaid-ascii/ascii/types.d.ts +206 -0
- package/dist/types/vendor/mermaid-ascii/ascii/validate.d.ts +51 -0
- package/dist/types/vendor/mermaid-ascii/ascii/xychart.d.ts +2 -0
- package/dist/types/vendor/mermaid-ascii/class/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/class/types.d.ts +102 -0
- package/dist/types/vendor/mermaid-ascii/er/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/er/types.d.ts +76 -0
- package/dist/types/vendor/mermaid-ascii/index.d.ts +1 -0
- package/dist/types/vendor/mermaid-ascii/multiline-utils.d.ts +9 -0
- package/dist/types/vendor/mermaid-ascii/parser.d.ts +7 -0
- package/dist/types/vendor/mermaid-ascii/sequence/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/sequence/types.d.ts +130 -0
- package/dist/types/vendor/mermaid-ascii/text-metrics.d.ts +21 -0
- package/dist/types/vendor/mermaid-ascii/types.d.ts +114 -0
- package/dist/types/vendor/mermaid-ascii/xychart/colors.d.ts +25 -0
- package/dist/types/vendor/mermaid-ascii/xychart/parser.d.ts +6 -0
- package/dist/types/vendor/mermaid-ascii/xychart/types.d.ts +145 -0
- package/dist/types/version.d.ts +18 -0
- package/dist/types/vterm/buffer.d.ts +99 -0
- package/dist/types/vterm/terminal.d.ts +44 -0
- package/dist/types/vterm.d.ts +8 -0
- package/dist/types/which.d.ts +37 -0
- package/dist/types/worker-host.d.ts +51 -0
- package/dist/types/xml.d.ts +31 -0
- package/package.json +59 -0
- package/src/abortable.ts +109 -0
- package/src/acp/connection.ts +344 -0
- package/src/acp/protocol.ts +466 -0
- package/src/acp/schema.ts +160 -0
- package/src/acp/stream.ts +82 -0
- package/src/acp/transport.ts +213 -0
- package/src/acp.ts +6 -0
- package/src/async.ts +91 -0
- package/src/binary.ts +50 -0
- package/src/browsers.ts +501 -0
- package/src/chalk.ts +312 -0
- package/src/cli.ts +529 -0
- package/src/color.ts +302 -0
- package/src/dates.ts +194 -0
- package/src/dirs.ts +1026 -0
- package/src/docx/converter.ts +681 -0
- package/src/docx/xml.ts +166 -0
- package/src/docx/zip.ts +87 -0
- package/src/docx.ts +20 -0
- package/src/dom/core.ts +1254 -0
- package/src/dom/parser.ts +370 -0
- package/src/dom/selector.ts +290 -0
- package/src/dom.ts +33 -0
- package/src/env.ts +400 -0
- package/src/fetch-retry.ts +379 -0
- package/src/file-lock.ts +69 -0
- package/src/format.ts +113 -0
- package/src/frontmatter.ts +205 -0
- package/src/fs-error.ts +56 -0
- package/src/glob.ts +189 -0
- package/src/headers.ts +167 -0
- package/src/index.ts +59 -0
- package/src/json-parse.ts +821 -0
- package/src/json.ts +23 -0
- package/src/logger/rotating-file.ts +149 -0
- package/src/logger.ts +798 -0
- package/src/loop-phase.ts +49 -0
- package/src/lru.ts +185 -0
- package/src/marked/core.ts +1576 -0
- package/src/marked.ts +2 -0
- package/src/mermaid-ascii.ts +31 -0
- package/src/mime.ts +159 -0
- package/src/module-timer.ts +148 -0
- package/src/path-tree.ts +147 -0
- package/src/path.ts +28 -0
- package/src/peek-file.ts +188 -0
- package/src/postmortem.ts +433 -0
- package/src/process-name.ts +57 -0
- package/src/procmgr.ts +247 -0
- package/src/prompt.ts +545 -0
- package/src/ptree.ts +412 -0
- package/src/readability/readability.ts +533 -0
- package/src/readability/readerable.ts +51 -0
- package/src/readability/types.ts +72 -0
- package/src/readability.ts +11 -0
- package/src/ring.ts +169 -0
- package/src/runtime-install.ts +393 -0
- package/src/sanitize-text.ts +96 -0
- package/src/snowflake.ts +121 -0
- package/src/stderr-guard.ts +155 -0
- package/src/stream.ts +454 -0
- package/src/tab-spacing.ts +336 -0
- package/src/temp.ts +134 -0
- package/src/template.ts +586 -0
- package/src/timing-buffer.ts +47 -0
- package/src/tls-fetch.ts +178 -0
- package/src/turndown/gfm.ts +106 -0
- package/src/turndown/html.ts +257 -0
- package/src/turndown/service.ts +334 -0
- package/src/turndown/types.ts +81 -0
- package/src/turndown.ts +5 -0
- package/src/type-guards.ts +17 -0
- package/src/vendor/mermaid-ascii/NOTICE +33 -0
- package/src/vendor/mermaid-ascii/ascii/ansi.ts +409 -0
- package/src/vendor/mermaid-ascii/ascii/canvas.ts +476 -0
- package/src/vendor/mermaid-ascii/ascii/class-diagram.ts +699 -0
- package/src/vendor/mermaid-ascii/ascii/converter.ts +271 -0
- package/src/vendor/mermaid-ascii/ascii/draw.ts +1382 -0
- package/src/vendor/mermaid-ascii/ascii/edge-bundling.ts +328 -0
- package/src/vendor/mermaid-ascii/ascii/edge-routing.ts +297 -0
- package/src/vendor/mermaid-ascii/ascii/er-diagram.ts +441 -0
- package/src/vendor/mermaid-ascii/ascii/grid.ts +578 -0
- package/src/vendor/mermaid-ascii/ascii/index.ts +187 -0
- package/src/vendor/mermaid-ascii/ascii/multiline-utils.ts +78 -0
- package/src/vendor/mermaid-ascii/ascii/pathfinder.ts +277 -0
- package/src/vendor/mermaid-ascii/ascii/sequence.ts +460 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/circle.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/corners.ts +127 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/diamond.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/hexagon.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/index.ts +101 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/rectangle.ts +175 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/rounded.ts +27 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/special.ts +296 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/stadium.ts +114 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/state.ts +192 -0
- package/src/vendor/mermaid-ascii/ascii/shapes/types.ts +73 -0
- package/src/vendor/mermaid-ascii/ascii/types.ts +273 -0
- package/src/vendor/mermaid-ascii/ascii/validate.ts +120 -0
- package/src/vendor/mermaid-ascii/ascii/xychart.ts +875 -0
- package/src/vendor/mermaid-ascii/class/parser.ts +290 -0
- package/src/vendor/mermaid-ascii/class/types.ts +121 -0
- package/src/vendor/mermaid-ascii/er/parser.ts +181 -0
- package/src/vendor/mermaid-ascii/er/types.ts +91 -0
- package/src/vendor/mermaid-ascii/index.ts +14 -0
- package/src/vendor/mermaid-ascii/multiline-utils.ts +30 -0
- package/src/vendor/mermaid-ascii/parser.ts +645 -0
- package/src/vendor/mermaid-ascii/sequence/parser.ts +207 -0
- package/src/vendor/mermaid-ascii/sequence/types.ts +146 -0
- package/src/vendor/mermaid-ascii/text-metrics.ts +71 -0
- package/src/vendor/mermaid-ascii/types.ts +164 -0
- package/src/vendor/mermaid-ascii/xychart/colors.ts +140 -0
- package/src/vendor/mermaid-ascii/xychart/parser.ts +115 -0
- package/src/vendor/mermaid-ascii/xychart/types.ts +150 -0
- package/src/version.ts +99 -0
- package/src/vterm/buffer.ts +218 -0
- package/src/vterm/terminal.ts +773 -0
- package/src/vterm.ts +8 -0
- package/src/which.ts +234 -0
- package/src/worker-host.ts +100 -0
- package/src/xml.ts +313 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { MaybePromise } from "./protocol";
|
|
2
|
+
import type { Stream } from "./stream";
|
|
3
|
+
|
|
4
|
+
/** JSON-RPC request identifier. */
|
|
5
|
+
export type JsonRpcId = string | number | null;
|
|
6
|
+
/** JSON-RPC request. */
|
|
7
|
+
export interface AnyRequest {
|
|
8
|
+
jsonrpc: "2.0";
|
|
9
|
+
id: JsonRpcId;
|
|
10
|
+
method: string;
|
|
11
|
+
params?: unknown;
|
|
12
|
+
}
|
|
13
|
+
/** JSON-RPC notification. */
|
|
14
|
+
export interface AnyNotification {
|
|
15
|
+
jsonrpc: "2.0";
|
|
16
|
+
method: string;
|
|
17
|
+
params?: unknown;
|
|
18
|
+
}
|
|
19
|
+
/** JSON-RPC error payload. */
|
|
20
|
+
export interface ErrorResponse {
|
|
21
|
+
code: number;
|
|
22
|
+
message: string;
|
|
23
|
+
data?: unknown;
|
|
24
|
+
}
|
|
25
|
+
/** JSON-RPC response. */
|
|
26
|
+
export type AnyResponse = { jsonrpc: "2.0"; id: JsonRpcId } & ({ result: unknown } | { error: ErrorResponse });
|
|
27
|
+
/** Any JSON-RPC wire message. */
|
|
28
|
+
export type AnyMessage = AnyRequest | AnyNotification | AnyResponse;
|
|
29
|
+
|
|
30
|
+
/** Error returned by a JSON-RPC peer or handler. */
|
|
31
|
+
export class RequestError extends Error {
|
|
32
|
+
/** JSON-RPC error code. */
|
|
33
|
+
readonly code: number;
|
|
34
|
+
/** Optional protocol error data. */
|
|
35
|
+
readonly data?: unknown;
|
|
36
|
+
|
|
37
|
+
constructor(code: number, message: string, data?: unknown) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "RequestError";
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.data = data;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Creates a JSON parse error. */
|
|
45
|
+
static parseError(data?: unknown, additionalMessage?: string): RequestError {
|
|
46
|
+
return createStandardError(-32700, "Parse error", data, additionalMessage);
|
|
47
|
+
}
|
|
48
|
+
/** Creates an invalid-request error. */
|
|
49
|
+
static invalidRequest(data?: unknown, additionalMessage?: string): RequestError {
|
|
50
|
+
return createStandardError(-32600, "Invalid request", data, additionalMessage);
|
|
51
|
+
}
|
|
52
|
+
/** Creates a method-not-found error. */
|
|
53
|
+
static methodNotFound(method: string): RequestError {
|
|
54
|
+
return new RequestError(-32601, `"Method not found": ${method}`, { method });
|
|
55
|
+
}
|
|
56
|
+
/** Creates an invalid-parameters error. */
|
|
57
|
+
static invalidParams(data?: unknown, additionalMessage?: string): RequestError {
|
|
58
|
+
return createStandardError(-32602, "Invalid params", data, additionalMessage);
|
|
59
|
+
}
|
|
60
|
+
/** Creates an internal error. */
|
|
61
|
+
static internalError(data?: unknown, additionalMessage?: string): RequestError {
|
|
62
|
+
return createStandardError(-32603, "Internal error", data, additionalMessage);
|
|
63
|
+
}
|
|
64
|
+
/** Creates a cancellation error. */
|
|
65
|
+
static requestCancelled(data?: unknown, additionalMessage?: string): RequestError {
|
|
66
|
+
return createStandardError(-32800, "Request cancelled", data, additionalMessage);
|
|
67
|
+
}
|
|
68
|
+
/** Creates an authentication-required error. */
|
|
69
|
+
static authRequired(data?: unknown, additionalMessage?: string): RequestError {
|
|
70
|
+
return createStandardError(-32000, "Authentication required", data, additionalMessage);
|
|
71
|
+
}
|
|
72
|
+
/** Creates a resource-not-found error. */
|
|
73
|
+
static resourceNotFound(uri?: string): RequestError {
|
|
74
|
+
return new RequestError(
|
|
75
|
+
-32002,
|
|
76
|
+
uri === undefined ? "Resource not found" : `Resource not found: ${uri}`,
|
|
77
|
+
uri === undefined ? undefined : { uri },
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
/** Converts this error into a JSON-RPC result. */
|
|
81
|
+
toResult(): { error: ErrorResponse } {
|
|
82
|
+
return { error: this.toErrorResponse() };
|
|
83
|
+
}
|
|
84
|
+
/** Converts this error into a JSON-RPC error payload. */
|
|
85
|
+
toErrorResponse(): ErrorResponse {
|
|
86
|
+
return { code: this.code, message: this.message, ...(this.data === undefined ? {} : { data: this.data }) };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function createStandardError(
|
|
91
|
+
code: number,
|
|
92
|
+
message: string,
|
|
93
|
+
data: unknown,
|
|
94
|
+
additionalMessage: string | undefined,
|
|
95
|
+
): RequestError {
|
|
96
|
+
return new RequestError(code, additionalMessage ? `${message}: ${additionalMessage}` : message, data);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
type Dispatcher = (method: string, params: unknown, notification: boolean) => MaybePromise<unknown>;
|
|
100
|
+
type Pending = { resolve(value: unknown): void; reject(reason: unknown): void };
|
|
101
|
+
|
|
102
|
+
/** Correlated bidirectional JSON-RPC connection. */
|
|
103
|
+
export class RpcConnection {
|
|
104
|
+
#nextId = 0;
|
|
105
|
+
#pending = new Map<JsonRpcId, Pending>();
|
|
106
|
+
#writable: WritableStream<AnyMessage>;
|
|
107
|
+
#writeTail: Promise<void> = Promise.resolve();
|
|
108
|
+
#abort = new AbortController();
|
|
109
|
+
#closed = Promise.withResolvers<void>();
|
|
110
|
+
#dispatcher: Dispatcher;
|
|
111
|
+
|
|
112
|
+
constructor(stream: Stream, dispatcher: Dispatcher) {
|
|
113
|
+
this.#writable = stream.writable;
|
|
114
|
+
this.#dispatcher = dispatcher;
|
|
115
|
+
void this.#read(stream.readable);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Signal aborted when the stream closes. */
|
|
119
|
+
get signal(): AbortSignal {
|
|
120
|
+
return this.#abort.signal;
|
|
121
|
+
}
|
|
122
|
+
/** Promise resolved when the stream closes. */
|
|
123
|
+
get closed(): Promise<void> {
|
|
124
|
+
return this.#closed.promise;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Sends a correlated JSON-RPC request. */
|
|
128
|
+
request<Response>(method: string, params?: unknown): Promise<Response> {
|
|
129
|
+
if (this.signal.aborted) return Promise.reject(new Error("Connection closed"));
|
|
130
|
+
const id = this.#nextId++;
|
|
131
|
+
const deferred = Promise.withResolvers<unknown>();
|
|
132
|
+
this.#pending.set(id, deferred);
|
|
133
|
+
void this.#write({ jsonrpc: "2.0", id, method, ...(params === undefined ? {} : { params }) }).catch(error => {
|
|
134
|
+
this.#pending.delete(id);
|
|
135
|
+
deferred.reject(error);
|
|
136
|
+
});
|
|
137
|
+
return deferred.promise as Promise<Response>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Sends a JSON-RPC notification. */
|
|
141
|
+
async notify(method: string, params?: unknown): Promise<void> {
|
|
142
|
+
await this.#write({ jsonrpc: "2.0", method, ...(params === undefined ? {} : { params }) });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Closes the connection and rejects outstanding requests. */
|
|
146
|
+
close(error?: unknown): void {
|
|
147
|
+
if (this.signal.aborted) return;
|
|
148
|
+
this.#abort.abort(error);
|
|
149
|
+
for (const pending of this.#pending.values()) pending.reject(error ?? new Error("Connection closed"));
|
|
150
|
+
this.#pending.clear();
|
|
151
|
+
this.#closed.resolve();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#write(message: AnyMessage): Promise<void> {
|
|
155
|
+
const write = this.#writeTail.then(async () => {
|
|
156
|
+
const writer = this.#writable.getWriter();
|
|
157
|
+
try {
|
|
158
|
+
await writer.write(message);
|
|
159
|
+
} finally {
|
|
160
|
+
writer.releaseLock();
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
this.#writeTail = write.catch(() => {});
|
|
164
|
+
return write;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async #read(readable: ReadableStream<AnyMessage>): Promise<void> {
|
|
168
|
+
const reader = readable.getReader();
|
|
169
|
+
try {
|
|
170
|
+
while (true) {
|
|
171
|
+
const next = await reader.read();
|
|
172
|
+
if (next.done) break;
|
|
173
|
+
void this.#handle(next.value).catch(error => this.close(error));
|
|
174
|
+
}
|
|
175
|
+
this.close();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
this.close(error);
|
|
178
|
+
} finally {
|
|
179
|
+
reader.releaseLock();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async #handle(message: AnyMessage): Promise<void> {
|
|
184
|
+
if ("id" in message && !("method" in message)) {
|
|
185
|
+
const pending = this.#pending.get(message.id);
|
|
186
|
+
if (!pending) return;
|
|
187
|
+
this.#pending.delete(message.id);
|
|
188
|
+
if ("error" in message)
|
|
189
|
+
pending.reject(new RequestError(message.error.code, message.error.message, message.error.data));
|
|
190
|
+
else pending.resolve(message.result);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (!("method" in message)) return;
|
|
194
|
+
if (!("id" in message)) {
|
|
195
|
+
try {
|
|
196
|
+
await this.#dispatcher(message.method, message.params, true);
|
|
197
|
+
} catch {
|
|
198
|
+
/* Notifications have no error channel. */
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const result = await this.#dispatcher(message.method, message.params, false);
|
|
204
|
+
await this.#write({ jsonrpc: "2.0", id: message.id, result: result ?? {} });
|
|
205
|
+
} catch (error) {
|
|
206
|
+
const protocolError =
|
|
207
|
+
error instanceof RequestError
|
|
208
|
+
? error
|
|
209
|
+
: RequestError.internalError({ details: error instanceof Error ? error.message : String(error) });
|
|
210
|
+
await this.#write({ jsonrpc: "2.0", id: message.id, error: protocolError.toErrorResponse() });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
package/src/acp.ts
ADDED
package/src/async.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wrap a promise with a timeout and optional abort signal.
|
|
3
|
+
* Rejects with the given message if the timeout fires first.
|
|
4
|
+
* Cleans up all listeners on settlement.
|
|
5
|
+
*/
|
|
6
|
+
export function withTimeout<T>(promise: Promise<T>, ms: number, message: string, signal?: AbortSignal): Promise<T> {
|
|
7
|
+
if (signal?.aborted) {
|
|
8
|
+
const reason = signal.reason instanceof Error ? signal.reason : new Error("Aborted");
|
|
9
|
+
return Promise.reject(reason);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { promise: wrapped, resolve, reject } = Promise.withResolvers<T>();
|
|
13
|
+
let settled = false;
|
|
14
|
+
const timeoutId = setTimeout(() => {
|
|
15
|
+
if (settled) return;
|
|
16
|
+
settled = true;
|
|
17
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
18
|
+
reject(new Error(message));
|
|
19
|
+
}, ms);
|
|
20
|
+
|
|
21
|
+
const onAbort = () => {
|
|
22
|
+
if (settled) return;
|
|
23
|
+
settled = true;
|
|
24
|
+
clearTimeout(timeoutId);
|
|
25
|
+
reject(signal?.reason instanceof Error ? signal.reason : new Error("Aborted"));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (signal) {
|
|
29
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
promise.then(
|
|
33
|
+
value => {
|
|
34
|
+
if (settled) return;
|
|
35
|
+
settled = true;
|
|
36
|
+
clearTimeout(timeoutId);
|
|
37
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
38
|
+
resolve(value);
|
|
39
|
+
},
|
|
40
|
+
err => {
|
|
41
|
+
if (settled) return;
|
|
42
|
+
settled = true;
|
|
43
|
+
clearTimeout(timeoutId);
|
|
44
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
45
|
+
reject(err);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return wrapped;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Coalesces rapid-fire writes into one deferred batch. `push` queues a value
|
|
54
|
+
* and returns a promise for the batch flush; the first push of a batch arms a
|
|
55
|
+
* timer (`delayMs`, or a microtask at 0), and every push before it fires joins
|
|
56
|
+
* the same batch and shares the same promise. Used to keep hot paths off
|
|
57
|
+
* synchronous storage (prompt history, model perf).
|
|
58
|
+
*/
|
|
59
|
+
export class AsyncDrain<T> {
|
|
60
|
+
#queue?: T[];
|
|
61
|
+
#promise = Promise.resolve();
|
|
62
|
+
|
|
63
|
+
constructor(readonly delayMs: number = 0) {}
|
|
64
|
+
|
|
65
|
+
/** Queue `value`; `hnd` receives the whole batch when the window closes. */
|
|
66
|
+
push(value: T, hnd: (values: T[]) => Promise<void> | void): Promise<void> {
|
|
67
|
+
let queue = this.#queue;
|
|
68
|
+
if (!queue) {
|
|
69
|
+
this.#queue = queue = [];
|
|
70
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
71
|
+
const exec = (): void => {
|
|
72
|
+
try {
|
|
73
|
+
if (this.#queue === queue) {
|
|
74
|
+
this.#queue = undefined;
|
|
75
|
+
}
|
|
76
|
+
resolve(hnd(queue!));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
reject(error);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
if (this.delayMs > 0) {
|
|
82
|
+
setTimeout(exec, this.delayMs);
|
|
83
|
+
} else {
|
|
84
|
+
queueMicrotask(exec);
|
|
85
|
+
}
|
|
86
|
+
this.#promise = promise;
|
|
87
|
+
}
|
|
88
|
+
queue.push(value);
|
|
89
|
+
return this.#promise;
|
|
90
|
+
}
|
|
91
|
+
}
|
package/src/binary.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-based binary/text classification for files that are about to be
|
|
3
|
+
* decoded as UTF-8 text and shown to a model or user.
|
|
4
|
+
*
|
|
5
|
+
* The read tool and `@file` auto-read both materialize file bytes as UTF-8
|
|
6
|
+
* strings. For a binary file (font, object, archive, packed blob) that decode
|
|
7
|
+
* is lossy: NUL bytes and invalid sequences survive as control characters and
|
|
8
|
+
* U+FFFD replacements, which corrupt terminal rendering and waste the context
|
|
9
|
+
* window with mojibake. Sniff the header first and refuse instead.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* if (await isProbablyBinary(path)) return "[binary file omitted]";
|
|
13
|
+
* const text = await Bun.file(path).text();
|
|
14
|
+
*/
|
|
15
|
+
import { peekFile, peekFileSync } from "./peek-file";
|
|
16
|
+
|
|
17
|
+
/** Header window sniffed for the binary heuristic; mirrors git's 8000-byte scan. */
|
|
18
|
+
const BINARY_SNIFF_BYTES = 8192;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Classify an in-memory byte header as binary (non-UTF-8-text).
|
|
22
|
+
*
|
|
23
|
+
* Binary when the header contains a NUL byte (true binary, plus UTF-16/UTF-32
|
|
24
|
+
* text whose ASCII range is NUL-padded) or when it is not valid UTF-8. The
|
|
25
|
+
* decode runs in streaming mode so a multibyte sequence truncated at the header
|
|
26
|
+
* boundary is tolerated, while any genuinely invalid byte still fails — matching
|
|
27
|
+
* the strict `fatal` decode the `local://`/`ssh://` read paths already use.
|
|
28
|
+
*/
|
|
29
|
+
export function isProbablyBinaryHeader(header: Uint8Array): boolean {
|
|
30
|
+
if (header.indexOf(0) !== -1) return true;
|
|
31
|
+
try {
|
|
32
|
+
new TextDecoder("utf-8", { fatal: true }).decode(header, { stream: true });
|
|
33
|
+
return false;
|
|
34
|
+
} catch {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sniff the first {@link BINARY_SNIFF_BYTES} of `filePath` and report whether it
|
|
41
|
+
* is binary (non-UTF-8-text). See {@link isProbablyBinaryHeader} for the rule.
|
|
42
|
+
*/
|
|
43
|
+
export function isProbablyBinary(filePath: string, maxBytes = BINARY_SNIFF_BYTES): Promise<boolean> {
|
|
44
|
+
return peekFile(filePath, maxBytes, isProbablyBinaryHeader);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Synchronous {@link isProbablyBinary}. */
|
|
48
|
+
export function isProbablyBinarySync(filePath: string, maxBytes = BINARY_SNIFF_BYTES): boolean {
|
|
49
|
+
return peekFileSync(filePath, maxBytes, isProbablyBinaryHeader);
|
|
50
|
+
}
|