@hachej/boring-execution 0.0.0-stage → 0.2.1
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/README.md +97 -2
- package/dist/aws-code-interpreter.d.ts +149 -0
- package/dist/aws-code-interpreter.js +453 -0
- package/dist/bounded-error.d.ts +8 -0
- package/dist/bounded-error.js +12 -0
- package/dist/contracts.d.ts +46 -0
- package/dist/contracts.js +1 -0
- package/dist/fs-readers.d.ts +7 -0
- package/dist/fs-readers.js +72 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/native.d.ts +8 -0
- package/dist/native.js +1 -0
- package/dist/remote-files-handler.d.ts +21 -0
- package/dist/remote-files-handler.js +215 -0
- package/dist/remote-files-io.d.ts +8 -0
- package/dist/remote-files-io.js +46 -0
- package/dist/remote-files-positional.d.ts +12 -0
- package/dist/remote-files-positional.js +208 -0
- package/dist/remote-files-protocol.d.ts +342 -0
- package/dist/remote-files-protocol.js +113 -0
- package/dist/remote-files-watch.d.ts +29 -0
- package/dist/remote-files-watch.js +154 -0
- package/dist/remote-files.d.ts +17 -0
- package/dist/remote-files.js +238 -0
- package/dist/remote-shell-handler.d.ts +25 -0
- package/dist/remote-shell-handler.js +131 -0
- package/dist/remote-shell-protocol.d.ts +50 -0
- package/dist/remote-shell-protocol.js +163 -0
- package/dist/remote-shell-window.d.ts +14 -0
- package/dist/remote-shell-window.js +82 -0
- package/dist/remote-shell.d.ts +15 -0
- package/dist/remote-shell.js +127 -0
- package/dist/virtual-filesystem.d.ts +67 -0
- package/dist/virtual-filesystem.js +218 -0
- package/dist/virtual-git.d.ts +60 -0
- package/dist/virtual-git.js +116 -0
- package/dist/virtual-sqlite.d.ts +42 -0
- package/dist/virtual-sqlite.js +50 -0
- package/dist/virtual.d.ts +34 -0
- package/dist/virtual.js +277 -0
- package/package.json +94 -4
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { FileError } from '@earendil-works/pi-durable/env';
|
|
3
|
+
import type { FileInfo, FileSystem, Result, WatchChange, WatchTarget } from '@earendil-works/pi-durable/env';
|
|
4
|
+
import type { Context } from '@earendil-works/chord';
|
|
5
|
+
export { identity, sameIdentity, positiveLimit, nativeVersion } from './remote-shell-protocol.js';
|
|
6
|
+
export declare const schema = "boring.remote-files";
|
|
7
|
+
export declare const version = 3;
|
|
8
|
+
export declare const streamType = "application/x-ndjson";
|
|
9
|
+
/**
|
|
10
|
+
* Native methods sent over the wire unchanged. Pi's stateful readers (`openBinaryReader`, `openDirReader`) hold an open handle; a
|
|
11
|
+
* stateless request cannot, so they travel as the request-bound methods of `PositionalCall`, and `watch` as one streamed request.
|
|
12
|
+
*/
|
|
13
|
+
export type FileMethod = Exclude<keyof FileSystem, 'id' | 'cwd' | 'cleanup' | 'openBinaryReader' | 'openDirReader' | 'watch'>;
|
|
14
|
+
type Arguments<Method> = Method extends (...args: [...infer Input, Context]) => unknown ? Input : never;
|
|
15
|
+
type NativeCall = {
|
|
16
|
+
[Method in FileMethod]: {
|
|
17
|
+
readonly method: Method;
|
|
18
|
+
readonly args: Arguments<FileSystem[Method]>;
|
|
19
|
+
};
|
|
20
|
+
}[FileMethod];
|
|
21
|
+
/** What a file looked like when the client opened it. A positional request fails when the file no longer matches (`FileInfo` has no inode). */
|
|
22
|
+
export interface ExpectedFile {
|
|
23
|
+
readonly size: number;
|
|
24
|
+
readonly mtimeMs: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ReaderOptions {
|
|
27
|
+
readonly noFollow?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface DirPage {
|
|
30
|
+
readonly entries: FileInfo[];
|
|
31
|
+
readonly done: boolean;
|
|
32
|
+
readonly mtimeMs: number;
|
|
33
|
+
}
|
|
34
|
+
/** Each call opens the native reader, answers one bounded request and closes it, so no reader outlives a request. */
|
|
35
|
+
export type PositionalCall = {
|
|
36
|
+
readonly method: 'binaryInfo';
|
|
37
|
+
readonly args: readonly [path: string, options: ReaderOptions | undefined];
|
|
38
|
+
} | {
|
|
39
|
+
readonly method: 'readRange';
|
|
40
|
+
readonly args: readonly [path: string, options: ReaderOptions | undefined, offset: number, length: number, expected: ExpectedFile | undefined];
|
|
41
|
+
} | {
|
|
42
|
+
readonly method: 'scanLines';
|
|
43
|
+
readonly args: readonly [path: string, options: ReaderOptions | undefined, lines: {
|
|
44
|
+
readonly startLine: number;
|
|
45
|
+
readonly endLine?: number;
|
|
46
|
+
}, expected: ExpectedFile | undefined];
|
|
47
|
+
}
|
|
48
|
+
/** `maxEntries` 0 only opens the directory (native open errors, `mtimeMs`); `offset` counts entries the native reader already returned. */
|
|
49
|
+
| {
|
|
50
|
+
readonly method: 'readDirPage';
|
|
51
|
+
readonly args: readonly [path: string, offset: number, maxEntries: number, expectedMtimeMs: number | undefined];
|
|
52
|
+
};
|
|
53
|
+
export type WatchCall = {
|
|
54
|
+
readonly method: 'watch';
|
|
55
|
+
readonly args: readonly [targets: readonly WatchTarget[]];
|
|
56
|
+
};
|
|
57
|
+
export type RemoteFileSystemCall = NativeCall | PositionalCall | WatchCall;
|
|
58
|
+
/** Largest `readRange` the response limit can carry as canonical base64 inside its envelope. */
|
|
59
|
+
export declare function maxRangeBytes(maxResponseBytes: number): number;
|
|
60
|
+
export declare const maxDirPageEntries = 1000, maxWatchTargets = 64, maxWatchExcludeNames = 256;
|
|
61
|
+
/** Bytes travel as canonical base64 text; JSON number arrays multiplied the size by three to four. */
|
|
62
|
+
export declare function encodeBytes(value: Uint8Array): string;
|
|
63
|
+
export declare function decodeBytes(value: string): Uint8Array;
|
|
64
|
+
export declare const bytes: z.ZodPipe<z.ZodString, z.ZodTransform<Uint8Array<ArrayBufferLike>, string>>;
|
|
65
|
+
declare const requestSchema: z.ZodObject<{
|
|
66
|
+
cwd: z.ZodString;
|
|
67
|
+
call: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
68
|
+
method: z.ZodLiteral<"absolutePath">;
|
|
69
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
70
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
71
|
+
method: z.ZodLiteral<"readTextFile">;
|
|
72
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
73
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
74
|
+
method: z.ZodLiteral<"openTextLineReader">;
|
|
75
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
76
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
77
|
+
method: z.ZodLiteral<"readBinaryFile">;
|
|
78
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
79
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
80
|
+
method: z.ZodLiteral<"flushFile">;
|
|
81
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
82
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
83
|
+
method: z.ZodLiteral<"fileInfo">;
|
|
84
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
85
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
86
|
+
method: z.ZodLiteral<"listDir">;
|
|
87
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
88
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
89
|
+
method: z.ZodLiteral<"canonicalPath">;
|
|
90
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
91
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
92
|
+
method: z.ZodLiteral<"exists">;
|
|
93
|
+
args: z.ZodTuple<[z.ZodString], null>;
|
|
94
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
95
|
+
method: z.ZodLiteral<"joinPath">;
|
|
96
|
+
args: z.ZodTuple<[z.ZodArray<z.ZodString>], null>;
|
|
97
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
98
|
+
method: z.ZodLiteral<"readTextLines">;
|
|
99
|
+
args: z.ZodTuple<[z.ZodString, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
100
|
+
maxLines: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
102
|
+
maxLines?: never;
|
|
103
|
+
} | {
|
|
104
|
+
maxLines: number;
|
|
105
|
+
} | undefined, {
|
|
106
|
+
maxLines?: number | undefined;
|
|
107
|
+
} | null>>], null>;
|
|
108
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
109
|
+
method: z.ZodLiteral<"writeFile">;
|
|
110
|
+
args: z.ZodTuple<[z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodPipe<z.ZodObject<{
|
|
111
|
+
bytes: z.ZodPipe<z.ZodString, z.ZodTransform<Uint8Array<ArrayBufferLike>, string>>;
|
|
112
|
+
}, z.core.$strict>, z.ZodTransform<Uint8Array<ArrayBufferLike>, {
|
|
113
|
+
bytes: Uint8Array<ArrayBufferLike>;
|
|
114
|
+
}>>]>], null>;
|
|
115
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
116
|
+
method: z.ZodLiteral<"appendFile">;
|
|
117
|
+
args: z.ZodTuple<[z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodPipe<z.ZodObject<{
|
|
118
|
+
bytes: z.ZodPipe<z.ZodString, z.ZodTransform<Uint8Array<ArrayBufferLike>, string>>;
|
|
119
|
+
}, z.core.$strict>, z.ZodTransform<Uint8Array<ArrayBufferLike>, {
|
|
120
|
+
bytes: Uint8Array<ArrayBufferLike>;
|
|
121
|
+
}>>]>], null>;
|
|
122
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
123
|
+
method: z.ZodLiteral<"truncateFile">;
|
|
124
|
+
args: z.ZodTuple<[z.ZodString, z.ZodNumber], null>;
|
|
125
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
126
|
+
method: z.ZodLiteral<"renameFile">;
|
|
127
|
+
args: z.ZodTuple<[z.ZodString, z.ZodString], null>;
|
|
128
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
129
|
+
method: z.ZodLiteral<"createDir">;
|
|
130
|
+
args: z.ZodTuple<[z.ZodString, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
131
|
+
recursive: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
133
|
+
recursive?: never;
|
|
134
|
+
} | {
|
|
135
|
+
recursive: boolean;
|
|
136
|
+
} | undefined, {
|
|
137
|
+
recursive?: boolean | undefined;
|
|
138
|
+
} | null>>], null>;
|
|
139
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
140
|
+
method: z.ZodLiteral<"remove">;
|
|
141
|
+
args: z.ZodTuple<[z.ZodString, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
142
|
+
recursive: z.ZodOptional<z.ZodBoolean>;
|
|
143
|
+
force: z.ZodOptional<z.ZodBoolean>;
|
|
144
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
145
|
+
force?: boolean;
|
|
146
|
+
recursive?: boolean;
|
|
147
|
+
} | undefined, {
|
|
148
|
+
recursive?: boolean | undefined;
|
|
149
|
+
force?: boolean | undefined;
|
|
150
|
+
} | null>>], null>;
|
|
151
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
152
|
+
method: z.ZodLiteral<"createTempDir">;
|
|
153
|
+
args: z.ZodTuple<[z.ZodPipe<z.ZodNullable<z.ZodString>, z.ZodTransform<string | undefined, string | null>>], null>;
|
|
154
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
155
|
+
method: z.ZodLiteral<"createTempFile">;
|
|
156
|
+
args: z.ZodTuple<[z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
157
|
+
prefix: z.ZodOptional<z.ZodString>;
|
|
158
|
+
suffix: z.ZodOptional<z.ZodString>;
|
|
159
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
160
|
+
suffix?: string;
|
|
161
|
+
prefix?: string;
|
|
162
|
+
} | undefined, {
|
|
163
|
+
prefix?: string | undefined;
|
|
164
|
+
suffix?: string | undefined;
|
|
165
|
+
} | null>>], null>;
|
|
166
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
167
|
+
method: z.ZodLiteral<"binaryInfo">;
|
|
168
|
+
args: z.ZodTuple<[z.ZodString, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
169
|
+
noFollow: z.ZodOptional<z.ZodBoolean>;
|
|
170
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
171
|
+
noFollow?: never;
|
|
172
|
+
} | {
|
|
173
|
+
noFollow: boolean;
|
|
174
|
+
} | undefined, {
|
|
175
|
+
noFollow?: boolean | undefined;
|
|
176
|
+
} | null>>], null>;
|
|
177
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
178
|
+
method: z.ZodLiteral<"readRange">;
|
|
179
|
+
args: z.ZodTuple<[z.ZodString, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
180
|
+
noFollow: z.ZodOptional<z.ZodBoolean>;
|
|
181
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
182
|
+
noFollow?: never;
|
|
183
|
+
} | {
|
|
184
|
+
noFollow: boolean;
|
|
185
|
+
} | undefined, {
|
|
186
|
+
noFollow?: boolean | undefined;
|
|
187
|
+
} | null>>, z.ZodNumber, z.ZodNumber, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
188
|
+
size: z.ZodNumber;
|
|
189
|
+
mtimeMs: z.ZodNumber;
|
|
190
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
191
|
+
size: number;
|
|
192
|
+
mtimeMs: number;
|
|
193
|
+
} | undefined, {
|
|
194
|
+
size: number;
|
|
195
|
+
mtimeMs: number;
|
|
196
|
+
} | null>>], null>;
|
|
197
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
198
|
+
method: z.ZodLiteral<"scanLines">;
|
|
199
|
+
args: z.ZodTuple<[z.ZodString, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
200
|
+
noFollow: z.ZodOptional<z.ZodBoolean>;
|
|
201
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
202
|
+
noFollow?: never;
|
|
203
|
+
} | {
|
|
204
|
+
noFollow: boolean;
|
|
205
|
+
} | undefined, {
|
|
206
|
+
noFollow?: boolean | undefined;
|
|
207
|
+
} | null>>, z.ZodPipe<z.ZodObject<{
|
|
208
|
+
startLine: z.ZodNumber;
|
|
209
|
+
endLine: z.ZodOptional<z.ZodNumber>;
|
|
210
|
+
}, z.core.$strict>, z.ZodTransform<{
|
|
211
|
+
startLine: number;
|
|
212
|
+
endLine?: never;
|
|
213
|
+
} | {
|
|
214
|
+
startLine: number;
|
|
215
|
+
endLine: number;
|
|
216
|
+
}, {
|
|
217
|
+
startLine: number;
|
|
218
|
+
endLine?: number | undefined;
|
|
219
|
+
}>>, z.ZodPipe<z.ZodNullable<z.ZodObject<{
|
|
220
|
+
size: z.ZodNumber;
|
|
221
|
+
mtimeMs: z.ZodNumber;
|
|
222
|
+
}, z.core.$strict>>, z.ZodTransform<{
|
|
223
|
+
size: number;
|
|
224
|
+
mtimeMs: number;
|
|
225
|
+
} | undefined, {
|
|
226
|
+
size: number;
|
|
227
|
+
mtimeMs: number;
|
|
228
|
+
} | null>>], null>;
|
|
229
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
230
|
+
method: z.ZodLiteral<"readDirPage">;
|
|
231
|
+
args: z.ZodTuple<[z.ZodString, z.ZodNumber, z.ZodNumber, z.ZodPipe<z.ZodNullable<z.ZodNumber>, z.ZodTransform<number | undefined, number | null>>], null>;
|
|
232
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
233
|
+
method: z.ZodLiteral<"watch">;
|
|
234
|
+
args: z.ZodTuple<[z.ZodArray<z.ZodPipe<z.ZodObject<{
|
|
235
|
+
path: z.ZodString;
|
|
236
|
+
recursive: z.ZodOptional<z.ZodBoolean>;
|
|
237
|
+
exclude: z.ZodOptional<z.ZodObject<{
|
|
238
|
+
hidden: z.ZodOptional<z.ZodBoolean>;
|
|
239
|
+
names: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
240
|
+
}, z.core.$strict>>;
|
|
241
|
+
}, z.core.$strict>, z.ZodTransform<WatchTarget, {
|
|
242
|
+
path: string;
|
|
243
|
+
recursive?: boolean | undefined;
|
|
244
|
+
exclude?: {
|
|
245
|
+
hidden?: boolean | undefined;
|
|
246
|
+
names?: string[] | undefined;
|
|
247
|
+
} | undefined;
|
|
248
|
+
}>>>], null>;
|
|
249
|
+
}, z.core.$strict>], "method">;
|
|
250
|
+
schema: z.ZodLiteral<"boring.remote-files">;
|
|
251
|
+
version: z.ZodLiteral<3>;
|
|
252
|
+
nativeVersion: z.ZodLiteral<"pi-durable@1.1.0">;
|
|
253
|
+
requestId: z.ZodString;
|
|
254
|
+
identity: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./contracts.js").WorkspaceIdentity, unknown>>;
|
|
255
|
+
filesystemId: z.ZodString;
|
|
256
|
+
}, z.core.$strict>;
|
|
257
|
+
export declare function requestInput(value: unknown): Omit<z.infer<typeof requestSchema>, 'call'> & {
|
|
258
|
+
readonly call: RemoteFileSystemCall;
|
|
259
|
+
};
|
|
260
|
+
export declare const envelope: z.ZodObject<{
|
|
261
|
+
result: z.ZodUnknown;
|
|
262
|
+
schema: z.ZodLiteral<"boring.remote-files">;
|
|
263
|
+
version: z.ZodLiteral<3>;
|
|
264
|
+
nativeVersion: z.ZodLiteral<"pi-durable@1.1.0">;
|
|
265
|
+
requestId: z.ZodString;
|
|
266
|
+
identity: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./contracts.js").WorkspaceIdentity, unknown>>;
|
|
267
|
+
filesystemId: z.ZodString;
|
|
268
|
+
}, z.core.$strict>;
|
|
269
|
+
export declare const fileInfo: z.ZodObject<{
|
|
270
|
+
name: z.ZodString;
|
|
271
|
+
path: z.ZodString;
|
|
272
|
+
kind: z.ZodEnum<{
|
|
273
|
+
file: "file";
|
|
274
|
+
directory: "directory";
|
|
275
|
+
symlink: "symlink";
|
|
276
|
+
}>;
|
|
277
|
+
size: z.ZodNumber;
|
|
278
|
+
mtimeMs: z.ZodNumber;
|
|
279
|
+
}, z.core.$strict>;
|
|
280
|
+
export declare const lineScan: z.ZodObject<{
|
|
281
|
+
newlines: z.ZodNumber;
|
|
282
|
+
start: z.ZodNumber;
|
|
283
|
+
end: z.ZodNumber;
|
|
284
|
+
firstLineEnd: z.ZodNumber;
|
|
285
|
+
lastLineStart: z.ZodNumber;
|
|
286
|
+
selectedBytes: z.ZodNumber;
|
|
287
|
+
firstLineBytes: z.ZodNumber;
|
|
288
|
+
}, z.core.$strict>;
|
|
289
|
+
export declare const dirPage: z.ZodObject<{
|
|
290
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
291
|
+
name: z.ZodString;
|
|
292
|
+
path: z.ZodString;
|
|
293
|
+
kind: z.ZodEnum<{
|
|
294
|
+
file: "file";
|
|
295
|
+
directory: "directory";
|
|
296
|
+
symlink: "symlink";
|
|
297
|
+
}>;
|
|
298
|
+
size: z.ZodNumber;
|
|
299
|
+
mtimeMs: z.ZodNumber;
|
|
300
|
+
}, z.core.$strict>>;
|
|
301
|
+
done: z.ZodBoolean;
|
|
302
|
+
mtimeMs: z.ZodNumber;
|
|
303
|
+
}, z.core.$strict>;
|
|
304
|
+
export declare const line: z.ZodObject<{
|
|
305
|
+
text: z.ZodString;
|
|
306
|
+
terminated: z.ZodBoolean;
|
|
307
|
+
}, z.core.$strict>;
|
|
308
|
+
export declare const nothing: z.ZodPipe<z.ZodNull, z.ZodTransform<undefined, null>>;
|
|
309
|
+
export declare function result<Value>(value: unknown, output: z.ZodType<Value>): Result<Value, FileError>;
|
|
310
|
+
export declare function wireResult(value: Result<unknown, FileError>): unknown;
|
|
311
|
+
export declare function parseWatchChange(value: unknown): WatchChange;
|
|
312
|
+
export declare function wireWatchChange(change: WatchChange): unknown;
|
|
313
|
+
export declare const streamFrame: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
314
|
+
sequence: z.ZodLiteral<0>;
|
|
315
|
+
type: z.ZodLiteral<"opened">;
|
|
316
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
317
|
+
native: "native";
|
|
318
|
+
polling: "polling";
|
|
319
|
+
}>>;
|
|
320
|
+
schema: z.ZodLiteral<"boring.remote-files">;
|
|
321
|
+
version: z.ZodLiteral<3>;
|
|
322
|
+
nativeVersion: z.ZodLiteral<"pi-durable@1.1.0">;
|
|
323
|
+
requestId: z.ZodString;
|
|
324
|
+
identity: z.ZodPipe<z.ZodUnknown, z.ZodTransform<import("./contracts.js").WorkspaceIdentity, unknown>>;
|
|
325
|
+
filesystemId: z.ZodString;
|
|
326
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
327
|
+
requestId: z.ZodString;
|
|
328
|
+
sequence: z.ZodNumber;
|
|
329
|
+
type: z.ZodLiteral<"change">;
|
|
330
|
+
change: z.ZodUnknown;
|
|
331
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
332
|
+
requestId: z.ZodString;
|
|
333
|
+
sequence: z.ZodNumber;
|
|
334
|
+
type: z.ZodLiteral<"line">;
|
|
335
|
+
result: z.ZodUnknown;
|
|
336
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
337
|
+
requestId: z.ZodString;
|
|
338
|
+
sequence: z.ZodNumber;
|
|
339
|
+
type: z.ZodLiteral<"end">;
|
|
340
|
+
}, z.core.$strict>], "type">;
|
|
341
|
+
export declare function wireCall(call: RemoteFileSystemCall): unknown;
|
|
342
|
+
//# sourceMappingURL=remote-files-protocol.d.ts.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { FileError, ok, err } from '@earendil-works/pi-durable/env';
|
|
3
|
+
import { identity, nativeVersion } from './remote-shell-protocol.js';
|
|
4
|
+
export { identity, sameIdentity, positiveLimit, nativeVersion } from './remote-shell-protocol.js';
|
|
5
|
+
export const schema = 'boring.remote-files';
|
|
6
|
+
export const version = 3;
|
|
7
|
+
export const streamType = 'application/x-ndjson';
|
|
8
|
+
/** Largest `readRange` the response limit can carry as canonical base64 inside its envelope. */
|
|
9
|
+
export function maxRangeBytes(maxResponseBytes) { return Math.max(1, Math.floor((maxResponseBytes - 2048) / 4) * 3); }
|
|
10
|
+
export const maxDirPageEntries = 1000, maxWatchTargets = 64, maxWatchExcludeNames = 256;
|
|
11
|
+
const text = z.string(), number = z.number().finite();
|
|
12
|
+
/** Bytes travel as canonical base64 text; JSON number arrays multiplied the size by three to four. */
|
|
13
|
+
export function encodeBytes(value) {
|
|
14
|
+
let text = '';
|
|
15
|
+
for (let index = 0; index < value.length; index += 0x8000)
|
|
16
|
+
text += String.fromCharCode(...value.subarray(index, index + 0x8000));
|
|
17
|
+
return btoa(text);
|
|
18
|
+
}
|
|
19
|
+
export function decodeBytes(value) {
|
|
20
|
+
let text;
|
|
21
|
+
try {
|
|
22
|
+
text = atob(value);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new TypeError('Invalid resource bytes');
|
|
26
|
+
}
|
|
27
|
+
const result = new Uint8Array(text.length);
|
|
28
|
+
for (let index = 0; index < text.length; index++)
|
|
29
|
+
result[index] = text.charCodeAt(index);
|
|
30
|
+
if (encodeBytes(result) !== value)
|
|
31
|
+
throw new TypeError('Invalid resource bytes');
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
export const bytes = z.string().transform(decodeBytes);
|
|
35
|
+
const content = z.union([text, z.strictObject({ bytes }).transform(value => value.bytes)]);
|
|
36
|
+
const optionalText = text.nullable().transform(value => value ?? undefined);
|
|
37
|
+
const recursive = z.strictObject({ recursive: z.boolean().optional() }).nullable().transform(value => value === null ? undefined : value.recursive === undefined ? {} : { recursive: value.recursive });
|
|
38
|
+
const removeOptions = z.strictObject({ recursive: z.boolean().optional(), force: z.boolean().optional() }).nullable().transform(value => value === null ? undefined : ({ ...(value.recursive === undefined ? {} : { recursive: value.recursive }), ...(value.force === undefined ? {} : { force: value.force }) }));
|
|
39
|
+
const lineOptions = z.strictObject({ maxLines: number.optional() }).nullable().transform(value => value === null ? undefined : value.maxLines === undefined ? {} : { maxLines: value.maxLines });
|
|
40
|
+
const tempOptions = z.strictObject({ prefix: text.optional(), suffix: text.optional() }).nullable().transform(value => value === null ? undefined : ({ ...(value.prefix === undefined ? {} : { prefix: value.prefix }), ...(value.suffix === undefined ? {} : { suffix: value.suffix }) }));
|
|
41
|
+
const int = z.number().int().nonnegative();
|
|
42
|
+
const readerOptions = z.strictObject({ noFollow: z.boolean().optional() }).nullable().transform(value => value === null ? undefined : value.noFollow === undefined ? {} : { noFollow: value.noFollow });
|
|
43
|
+
const expectedFile = z.strictObject({ size: number.nonnegative(), mtimeMs: number }).nullable().transform(value => value ?? undefined);
|
|
44
|
+
const lineRange = z.strictObject({ startLine: int, endLine: int.optional() }).transform(value => value.endLine === undefined ? { startLine: value.startLine } : { startLine: value.startLine, endLine: value.endLine });
|
|
45
|
+
const watchTarget = z.strictObject({
|
|
46
|
+
path: text, recursive: z.boolean().optional(),
|
|
47
|
+
exclude: z.strictObject({ hidden: z.boolean().optional(), names: z.array(text).max(maxWatchExcludeNames).optional() }).optional(),
|
|
48
|
+
}).transform((value) => ({
|
|
49
|
+
path: value.path, ...(value.recursive === undefined ? {} : { recursive: value.recursive }),
|
|
50
|
+
...(value.exclude === undefined ? {} : { exclude: { ...(value.exclude.hidden === undefined ? {} : { hidden: value.exclude.hidden }), ...(value.exclude.names === undefined ? {} : { names: value.exclude.names }) } }),
|
|
51
|
+
}));
|
|
52
|
+
const pathCall = (method) => z.strictObject({ method: z.literal(method), args: z.tuple([text]) });
|
|
53
|
+
const callSchema = z.discriminatedUnion('method', [
|
|
54
|
+
pathCall('absolutePath'), pathCall('readTextFile'), pathCall('openTextLineReader'), pathCall('readBinaryFile'),
|
|
55
|
+
pathCall('flushFile'), pathCall('fileInfo'), pathCall('listDir'), pathCall('canonicalPath'), pathCall('exists'),
|
|
56
|
+
z.strictObject({ method: z.literal('joinPath'), args: z.tuple([z.array(text)]) }),
|
|
57
|
+
z.strictObject({ method: z.literal('readTextLines'), args: z.tuple([text, lineOptions]) }),
|
|
58
|
+
z.strictObject({ method: z.literal('writeFile'), args: z.tuple([text, content]) }),
|
|
59
|
+
z.strictObject({ method: z.literal('appendFile'), args: z.tuple([text, content]) }),
|
|
60
|
+
z.strictObject({ method: z.literal('truncateFile'), args: z.tuple([text, number]) }),
|
|
61
|
+
z.strictObject({ method: z.literal('renameFile'), args: z.tuple([text, text]) }),
|
|
62
|
+
z.strictObject({ method: z.literal('createDir'), args: z.tuple([text, recursive]) }),
|
|
63
|
+
z.strictObject({ method: z.literal('remove'), args: z.tuple([text, removeOptions]) }),
|
|
64
|
+
z.strictObject({ method: z.literal('createTempDir'), args: z.tuple([optionalText]) }),
|
|
65
|
+
z.strictObject({ method: z.literal('createTempFile'), args: z.tuple([tempOptions]) }),
|
|
66
|
+
z.strictObject({ method: z.literal('binaryInfo'), args: z.tuple([text, readerOptions]) }),
|
|
67
|
+
z.strictObject({ method: z.literal('readRange'), args: z.tuple([text, readerOptions, int, int, expectedFile]) }),
|
|
68
|
+
z.strictObject({ method: z.literal('scanLines'), args: z.tuple([text, readerOptions, lineRange, expectedFile]) }),
|
|
69
|
+
z.strictObject({ method: z.literal('readDirPage'), args: z.tuple([text, int, int.max(maxDirPageEntries), z.number().finite().nullable().transform(value => value ?? undefined)]) }),
|
|
70
|
+
z.strictObject({ method: z.literal('watch'), args: z.tuple([z.array(watchTarget).min(1).max(maxWatchTargets)]) }),
|
|
71
|
+
]);
|
|
72
|
+
const binding = { schema: z.literal(schema), version: z.literal(version), nativeVersion: z.literal(nativeVersion),
|
|
73
|
+
requestId: text.min(1).max(128), identity: z.unknown().transform(identity), filesystemId: text.min(1) };
|
|
74
|
+
const requestSchema = z.strictObject({ ...binding, cwd: text, call: callSchema });
|
|
75
|
+
export function requestInput(value) { return requestSchema.parse(value); }
|
|
76
|
+
export const envelope = z.strictObject({ ...binding, result: z.unknown() });
|
|
77
|
+
export const fileInfo = z.strictObject({ name: text, path: text, kind: z.enum(['file', 'directory', 'symlink']), size: number.nonnegative(), mtimeMs: number });
|
|
78
|
+
export const lineScan = z.strictObject({ newlines: int, start: int, end: int, firstLineEnd: int, lastLineStart: int, selectedBytes: int, firstLineBytes: int });
|
|
79
|
+
export const dirPage = z.strictObject({ entries: z.array(fileInfo), done: z.boolean(), mtimeMs: number });
|
|
80
|
+
export const line = z.strictObject({ text, terminated: z.boolean() });
|
|
81
|
+
export const nothing = z.null().transform(() => undefined);
|
|
82
|
+
const fileError = z.strictObject({ code: z.enum(['aborted', 'not_found', 'permission_denied', 'not_directory', 'is_directory', 'invalid', 'not_supported', 'unknown']), message: text, path: text.optional() });
|
|
83
|
+
export function result(value, output) {
|
|
84
|
+
const parsed = z.discriminatedUnion('ok', [z.strictObject({ ok: z.literal(true), value: output }), z.strictObject({ ok: z.literal(false), error: fileError })]).parse(value);
|
|
85
|
+
return parsed.ok ? ok(parsed.value) : err(new FileError(parsed.error.code, parsed.error.message, parsed.error.path));
|
|
86
|
+
}
|
|
87
|
+
export function wireResult(value) {
|
|
88
|
+
if (value.ok)
|
|
89
|
+
return { ok: true, value: value.value instanceof Uint8Array ? encodeBytes(value.value) : value.value ?? null };
|
|
90
|
+
return { ok: false, error: { code: value.error.code, message: value.error.message, ...(value.error.path === undefined ? {} : { path: value.error.path }) } };
|
|
91
|
+
}
|
|
92
|
+
const watchChange = z.union([
|
|
93
|
+
z.strictObject({ paths: z.array(text) }),
|
|
94
|
+
z.strictObject({ overflow: z.literal(true) }),
|
|
95
|
+
z.strictObject({ error: fileError }).transform(value => ({ error: new FileError(value.error.code, value.error.message, value.error.path) })),
|
|
96
|
+
]);
|
|
97
|
+
export function parseWatchChange(value) { return watchChange.parse(value); }
|
|
98
|
+
export function wireWatchChange(change) {
|
|
99
|
+
if ('error' in change)
|
|
100
|
+
return { error: { code: change.error.code, message: change.error.message, ...(change.error.path === undefined ? {} : { path: change.error.path }) } };
|
|
101
|
+
return change;
|
|
102
|
+
}
|
|
103
|
+
export const streamFrame = z.discriminatedUnion('type', [
|
|
104
|
+
z.strictObject({ ...binding, sequence: z.literal(0), type: z.literal('opened'), mode: z.enum(['native', 'polling']).optional() }),
|
|
105
|
+
z.strictObject({ requestId: binding.requestId, sequence: z.number().int().positive(), type: z.literal('change'), change: z.unknown() }),
|
|
106
|
+
z.strictObject({ requestId: binding.requestId, sequence: z.number().int().positive(), type: z.literal('line'), result: z.unknown() }),
|
|
107
|
+
z.strictObject({ requestId: binding.requestId, sequence: z.number().int().positive(), type: z.literal('end') }),
|
|
108
|
+
]);
|
|
109
|
+
export function wireCall(call) {
|
|
110
|
+
// JSON has no Infinity; an unbounded line count is the same request as no limit.
|
|
111
|
+
const unbounded = call.method === 'readTextLines' && call.args[1]?.maxLines === Infinity;
|
|
112
|
+
return { method: call.method, args: call.args.map((value, index) => unbounded && index === 1 ? {} : value instanceof Uint8Array ? { bytes: encodeBytes(value) } : value ?? null) };
|
|
113
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Context } from '@earendil-works/chord';
|
|
2
|
+
import { FileError } from '@earendil-works/pi-durable/env';
|
|
3
|
+
import type { FileSystem, FileWatcher, Result, WatchChange, WatchTarget } from '@earendil-works/pi-durable/env';
|
|
4
|
+
export interface ServeWatch {
|
|
5
|
+
readonly fs: FileSystem;
|
|
6
|
+
readonly targets: readonly WatchTarget[];
|
|
7
|
+
readonly binding: object;
|
|
8
|
+
readonly requestId: string;
|
|
9
|
+
readonly context: Context;
|
|
10
|
+
readonly cleanup: Context;
|
|
11
|
+
readonly maxFrameBytes: number;
|
|
12
|
+
/** Re-check authorization and binding before each disclosure. */
|
|
13
|
+
readonly permitted: () => Promise<boolean>;
|
|
14
|
+
/** Close the watcher's lease facade; called once after the watcher has closed. */
|
|
15
|
+
readonly release: () => Promise<void>;
|
|
16
|
+
readonly onCleanupError?: ((error: unknown) => void) | undefined;
|
|
17
|
+
readonly stop: () => void;
|
|
18
|
+
}
|
|
19
|
+
/** Start the native watcher. A failure to establish coverage is a normal result; otherwise a stream response that owns the watcher. */
|
|
20
|
+
export declare function serveWatch(o: ServeWatch): Promise<Result<Response, FileError>>;
|
|
21
|
+
/** Client side: consume an `opened` + `change` stream into Pi's `FileWatcher`. `deliver` is never called after `close` resolves. */
|
|
22
|
+
export declare function watcherFromFrames(options: {
|
|
23
|
+
readonly mode: 'native' | 'polling';
|
|
24
|
+
readonly next: () => Promise<unknown>;
|
|
25
|
+
readonly requestId: string;
|
|
26
|
+
readonly onChange: (change: WatchChange) => void;
|
|
27
|
+
readonly dispose: () => Promise<void>;
|
|
28
|
+
}): FileWatcher;
|
|
29
|
+
//# sourceMappingURL=remote-files-watch.d.ts.map
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { FileError, ok } from '@earendil-works/pi-durable/env';
|
|
2
|
+
import { parseWatchChange, streamFrame, streamType, wireWatchChange } from './remote-files-protocol.js';
|
|
3
|
+
const maxQueuedChanges = 256;
|
|
4
|
+
const encode = (value) => new TextEncoder().encode(JSON.stringify(value) + '\n');
|
|
5
|
+
/** Start the native watcher. A failure to establish coverage is a normal result; otherwise a stream response that owns the watcher. */
|
|
6
|
+
export async function serveWatch(o) {
|
|
7
|
+
const queue = [];
|
|
8
|
+
let wake, ended = false, finished = false;
|
|
9
|
+
// A native `error` ends the watcher and must reach the client whatever happened to the queued events before it.
|
|
10
|
+
let terminal;
|
|
11
|
+
const signal = o.context.abortSignal;
|
|
12
|
+
const push = (change) => {
|
|
13
|
+
if (ended || terminal !== undefined)
|
|
14
|
+
return;
|
|
15
|
+
if ('error' in change)
|
|
16
|
+
terminal = change;
|
|
17
|
+
else if (queue.length >= maxQueuedChanges) {
|
|
18
|
+
queue.length = 0;
|
|
19
|
+
queue.push({ overflow: true });
|
|
20
|
+
}
|
|
21
|
+
else
|
|
22
|
+
queue.push(change);
|
|
23
|
+
wake?.();
|
|
24
|
+
};
|
|
25
|
+
const opened = await o.fs.watch(o.targets, push, o.context);
|
|
26
|
+
if (!opened.ok)
|
|
27
|
+
return opened;
|
|
28
|
+
const watcher = opened.value;
|
|
29
|
+
let closing;
|
|
30
|
+
const close = () => closing ??= (async () => {
|
|
31
|
+
ended = true;
|
|
32
|
+
wake?.();
|
|
33
|
+
try {
|
|
34
|
+
await watcher.close(o.cleanup);
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
await o.release();
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
let controller;
|
|
41
|
+
let sequence = 1;
|
|
42
|
+
const interrupt = () => {
|
|
43
|
+
if (finished)
|
|
44
|
+
return;
|
|
45
|
+
finished = true;
|
|
46
|
+
signal?.removeEventListener('abort', interrupt);
|
|
47
|
+
controller.error(new Error('Remote file watch interrupted'));
|
|
48
|
+
void close().catch(error => o.onCleanupError?.(error));
|
|
49
|
+
};
|
|
50
|
+
const frameFor = (change) => {
|
|
51
|
+
const frame = (value) => encode({ requestId: o.requestId, sequence, type: 'change', change: wireWatchChange(value) });
|
|
52
|
+
const bytes = frame(change);
|
|
53
|
+
if (bytes.length <= o.maxFrameBytes)
|
|
54
|
+
return bytes;
|
|
55
|
+
if ('error' in change) {
|
|
56
|
+
// Never turn a terminal error into an event: bound its text, then fall back to a fixed message.
|
|
57
|
+
const bounded = (limit) => ({ error: new FileError(change.error.code, change.error.message.slice(0, limit), change.error.path === undefined ? undefined : change.error.path.slice(0, limit)) });
|
|
58
|
+
for (const limit of [512, 64]) {
|
|
59
|
+
const shorter = frame(bounded(limit));
|
|
60
|
+
if (shorter.length <= o.maxFrameBytes)
|
|
61
|
+
return shorter;
|
|
62
|
+
}
|
|
63
|
+
return frame({ error: new FileError('unknown', 'Watcher failed') });
|
|
64
|
+
}
|
|
65
|
+
return frame({ overflow: true });
|
|
66
|
+
};
|
|
67
|
+
const body = new ReadableStream({
|
|
68
|
+
start(value) {
|
|
69
|
+
controller = value;
|
|
70
|
+
controller.enqueue(encode({ ...o.binding, type: 'opened', sequence: 0, mode: watcher.mode }));
|
|
71
|
+
signal?.addEventListener('abort', interrupt, { once: true });
|
|
72
|
+
if (signal?.aborted)
|
|
73
|
+
interrupt();
|
|
74
|
+
},
|
|
75
|
+
async pull() {
|
|
76
|
+
try {
|
|
77
|
+
while (!finished && queue.length === 0 && terminal === undefined && !ended)
|
|
78
|
+
await new Promise(resolve => { wake = resolve; });
|
|
79
|
+
wake = undefined;
|
|
80
|
+
if (finished)
|
|
81
|
+
return;
|
|
82
|
+
const change = queue.shift() ?? terminal;
|
|
83
|
+
if (change === undefined) {
|
|
84
|
+
interrupt();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!await o.permitted()) {
|
|
88
|
+
interrupt();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
controller.enqueue(frameFor(change));
|
|
92
|
+
sequence++;
|
|
93
|
+
// Pi: after an `error` change no calls follow.
|
|
94
|
+
if ('error' in change) {
|
|
95
|
+
finished = true;
|
|
96
|
+
signal?.removeEventListener('abort', interrupt);
|
|
97
|
+
await close();
|
|
98
|
+
controller.enqueue(encode({ requestId: o.requestId, sequence: sequence++, type: 'end' }));
|
|
99
|
+
controller.close();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
interrupt();
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
async cancel() { finished = true; signal?.removeEventListener('abort', interrupt); o.stop(); await close(); },
|
|
107
|
+
}, { highWaterMark: 1 });
|
|
108
|
+
return ok(new Response(body, { headers: { 'content-type': streamType, 'cache-control': 'no-store' } }));
|
|
109
|
+
}
|
|
110
|
+
/** Client side: consume an `opened` + `change` stream into Pi's `FileWatcher`. `deliver` is never called after `close` resolves. */
|
|
111
|
+
export function watcherFromFrames(options) {
|
|
112
|
+
let stopped = false;
|
|
113
|
+
const deliver = (change) => { if (!stopped) {
|
|
114
|
+
try {
|
|
115
|
+
options.onChange(change);
|
|
116
|
+
}
|
|
117
|
+
catch { /* a throwing listener must not stop the watcher */ }
|
|
118
|
+
} };
|
|
119
|
+
void (async () => {
|
|
120
|
+
let sequence = 1;
|
|
121
|
+
try {
|
|
122
|
+
while (!stopped) {
|
|
123
|
+
const raw = await options.next();
|
|
124
|
+
if (stopped)
|
|
125
|
+
return;
|
|
126
|
+
if (raw === undefined)
|
|
127
|
+
throw new TypeError('Watch stream ended without a terminal frame');
|
|
128
|
+
const frame = streamFrame.parse(raw);
|
|
129
|
+
if (frame.requestId !== options.requestId || frame.sequence !== sequence++ || frame.type === 'opened')
|
|
130
|
+
throw new TypeError('Watch frame sequence mismatch');
|
|
131
|
+
// The server sends `end` only after an `error` change; a bare `end` is a lost terminal error, never a quiet stop.
|
|
132
|
+
if (frame.type === 'end')
|
|
133
|
+
throw new TypeError('Watch stream ended without an error change');
|
|
134
|
+
if (frame.type !== 'change')
|
|
135
|
+
throw new TypeError('Unexpected watch frame');
|
|
136
|
+
const change = parseWatchChange(frame.change);
|
|
137
|
+
deliver(change);
|
|
138
|
+
if ('error' in change) {
|
|
139
|
+
stopped = true;
|
|
140
|
+
await options.dispose();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
if (stopped)
|
|
147
|
+
return;
|
|
148
|
+
deliver({ error: new FileError('unknown', 'Remote file watch was interrupted') });
|
|
149
|
+
stopped = true;
|
|
150
|
+
await options.dispose();
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
return { mode: options.mode, close: async () => { stopped = true; await options.dispose(); } };
|
|
154
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FileSystem } from '@earendil-works/pi-durable/env';
|
|
2
|
+
import type { WorkspaceIdentity, WorkspaceLease } from './contracts.js';
|
|
3
|
+
export { createRemoteFileSystemHandler } from './remote-files-handler.js';
|
|
4
|
+
export type { RemoteFileSystemAccess, RemoteFileSystemHandlerOptions } from './remote-files-handler.js';
|
|
5
|
+
export type { RemoteFileSystemCall } from './remote-files-protocol.js';
|
|
6
|
+
export interface RemoteFileSystemOptions {
|
|
7
|
+
readonly identity: WorkspaceIdentity;
|
|
8
|
+
readonly filesystemId: string;
|
|
9
|
+
readonly cwd: string;
|
|
10
|
+
readonly endpoint: string | URL;
|
|
11
|
+
readonly fetch: (request: Request) => Promise<Response>;
|
|
12
|
+
readonly maxRequestBytes?: number;
|
|
13
|
+
readonly maxResponseBytes?: number;
|
|
14
|
+
readonly maxFrameBytes?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function createRemoteFileSystemLease(options: RemoteFileSystemOptions): WorkspaceLease<FileSystem>;
|
|
17
|
+
//# sourceMappingURL=remote-files.d.ts.map
|