@cloudflare/sandbox 0.0.0-d1c7c99 → 0.0.0-d670ba2
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 +197 -0
- package/Dockerfile +97 -10
- package/README.md +858 -24
- package/container_src/bun.lock +76 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/control-process.ts +784 -0
- package/container_src/handler/exec.ts +185 -0
- package/container_src/handler/file.ts +457 -0
- package/container_src/handler/git.ts +130 -0
- package/container_src/handler/ports.ts +314 -0
- package/container_src/handler/process.ts +568 -0
- package/container_src/handler/session.ts +92 -0
- package/container_src/index.ts +433 -2738
- package/container_src/interpreter-service.ts +276 -0
- package/container_src/isolation.ts +1213 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/runtime/executors/javascript/node_executor.ts +123 -0
- package/container_src/runtime/executors/python/ipython_executor.py +338 -0
- package/container_src/runtime/executors/typescript/ts_executor.ts +138 -0
- package/container_src/runtime/process-pool.ts +464 -0
- package/container_src/shell-escape.ts +42 -0
- package/container_src/startup.sh +11 -0
- package/container_src/types.ts +131 -0
- package/package.json +5 -9
- package/src/client.ts +448 -1360
- package/src/errors.ts +219 -0
- package/src/file-stream.ts +162 -0
- package/src/index.ts +78 -133
- package/src/interpreter-client.ts +352 -0
- package/src/interpreter-types.ts +390 -0
- package/src/interpreter.ts +150 -0
- package/src/request-handler.ts +144 -0
- package/src/sandbox.ts +756 -0
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +571 -0
- package/tsconfig.json +1 -1
- package/tests/client.example.ts +0 -308
- package/tests/connection-test.ts +0 -81
- package/tests/simple-test.ts +0 -81
- package/tests/test1.ts +0 -281
- package/tests/test2.ts +0 -929
package/src/client.ts
CHANGED
|
@@ -1,36 +1,23 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExecuteRequest } from "../container_src/types";
|
|
2
2
|
import type { Sandbox } from "./index";
|
|
3
|
+
import type {
|
|
4
|
+
BaseExecOptions,
|
|
5
|
+
DeleteFileResponse,
|
|
6
|
+
ExecuteResponse,
|
|
7
|
+
GetProcessLogsResponse,
|
|
8
|
+
GetProcessResponse,
|
|
9
|
+
GitCheckoutResponse,
|
|
10
|
+
ListFilesResponse,
|
|
11
|
+
ListProcessesResponse,
|
|
12
|
+
MkdirResponse,
|
|
13
|
+
MoveFileResponse,
|
|
14
|
+
ReadFileResponse,
|
|
15
|
+
RenameFileResponse,
|
|
16
|
+
StartProcessRequest,
|
|
17
|
+
StartProcessResponse,
|
|
18
|
+
WriteFileResponse,
|
|
19
|
+
} from "./types";
|
|
3
20
|
|
|
4
|
-
interface ExecuteRequest {
|
|
5
|
-
command: string;
|
|
6
|
-
args?: string[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ExecuteResponse {
|
|
10
|
-
success: boolean;
|
|
11
|
-
stdout: string;
|
|
12
|
-
stderr: string;
|
|
13
|
-
exitCode: number;
|
|
14
|
-
command: string;
|
|
15
|
-
args: string[];
|
|
16
|
-
timestamp: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface SessionResponse {
|
|
20
|
-
sessionId: string;
|
|
21
|
-
message: string;
|
|
22
|
-
timestamp: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface SessionListResponse {
|
|
26
|
-
sessions: Array<{
|
|
27
|
-
sessionId: string;
|
|
28
|
-
hasActiveProcess: boolean;
|
|
29
|
-
createdAt: string;
|
|
30
|
-
}>;
|
|
31
|
-
count: number;
|
|
32
|
-
timestamp: string;
|
|
33
|
-
}
|
|
34
21
|
|
|
35
22
|
interface CommandsResponse {
|
|
36
23
|
availableCommands: string[];
|
|
@@ -41,135 +28,103 @@ interface GitCheckoutRequest {
|
|
|
41
28
|
repoUrl: string;
|
|
42
29
|
branch?: string;
|
|
43
30
|
targetDir?: string;
|
|
44
|
-
sessionId
|
|
31
|
+
sessionId: string;
|
|
45
32
|
}
|
|
46
33
|
|
|
47
|
-
export interface GitCheckoutResponse {
|
|
48
|
-
success: boolean;
|
|
49
|
-
stdout: string;
|
|
50
|
-
stderr: string;
|
|
51
|
-
exitCode: number;
|
|
52
|
-
repoUrl: string;
|
|
53
|
-
branch: string;
|
|
54
|
-
targetDir: string;
|
|
55
|
-
timestamp: string;
|
|
56
|
-
}
|
|
57
34
|
|
|
58
35
|
interface MkdirRequest {
|
|
59
36
|
path: string;
|
|
60
37
|
recursive?: boolean;
|
|
61
|
-
sessionId
|
|
38
|
+
sessionId: string;
|
|
62
39
|
}
|
|
63
40
|
|
|
64
|
-
export interface MkdirResponse {
|
|
65
|
-
success: boolean;
|
|
66
|
-
stdout: string;
|
|
67
|
-
stderr: string;
|
|
68
|
-
exitCode: number;
|
|
69
|
-
path: string;
|
|
70
|
-
recursive: boolean;
|
|
71
|
-
timestamp: string;
|
|
72
|
-
}
|
|
73
41
|
|
|
74
42
|
interface WriteFileRequest {
|
|
75
43
|
path: string;
|
|
76
44
|
content: string;
|
|
77
45
|
encoding?: string;
|
|
78
|
-
sessionId
|
|
46
|
+
sessionId: string;
|
|
79
47
|
}
|
|
80
48
|
|
|
81
|
-
export interface WriteFileResponse {
|
|
82
|
-
success: boolean;
|
|
83
|
-
exitCode: number;
|
|
84
|
-
path: string;
|
|
85
|
-
timestamp: string;
|
|
86
|
-
}
|
|
87
49
|
|
|
88
50
|
interface ReadFileRequest {
|
|
89
51
|
path: string;
|
|
90
52
|
encoding?: string;
|
|
91
|
-
sessionId
|
|
53
|
+
sessionId: string;
|
|
92
54
|
}
|
|
93
55
|
|
|
94
|
-
export interface ReadFileResponse {
|
|
95
|
-
success: boolean;
|
|
96
|
-
exitCode: number;
|
|
97
|
-
path: string;
|
|
98
|
-
content: string;
|
|
99
|
-
timestamp: string;
|
|
100
|
-
}
|
|
101
56
|
|
|
102
57
|
interface DeleteFileRequest {
|
|
103
58
|
path: string;
|
|
104
|
-
sessionId
|
|
59
|
+
sessionId: string;
|
|
105
60
|
}
|
|
106
61
|
|
|
107
|
-
export interface DeleteFileResponse {
|
|
108
|
-
success: boolean;
|
|
109
|
-
exitCode: number;
|
|
110
|
-
path: string;
|
|
111
|
-
timestamp: string;
|
|
112
|
-
}
|
|
113
62
|
|
|
114
63
|
interface RenameFileRequest {
|
|
115
64
|
oldPath: string;
|
|
116
65
|
newPath: string;
|
|
117
|
-
sessionId
|
|
66
|
+
sessionId: string;
|
|
118
67
|
}
|
|
119
68
|
|
|
120
|
-
export interface RenameFileResponse {
|
|
121
|
-
success: boolean;
|
|
122
|
-
exitCode: number;
|
|
123
|
-
oldPath: string;
|
|
124
|
-
newPath: string;
|
|
125
|
-
timestamp: string;
|
|
126
|
-
}
|
|
127
69
|
|
|
128
70
|
interface MoveFileRequest {
|
|
129
71
|
sourcePath: string;
|
|
130
72
|
destinationPath: string;
|
|
131
|
-
sessionId
|
|
73
|
+
sessionId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
interface ListFilesRequest {
|
|
78
|
+
path: string;
|
|
79
|
+
options?: {
|
|
80
|
+
recursive?: boolean;
|
|
81
|
+
includeHidden?: boolean;
|
|
82
|
+
};
|
|
83
|
+
sessionId: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
interface PreviewInfo {
|
|
88
|
+
url: string;
|
|
89
|
+
port: number;
|
|
90
|
+
name?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface ExposedPort extends PreviewInfo {
|
|
94
|
+
exposedAt: string;
|
|
95
|
+
timestamp: string;
|
|
132
96
|
}
|
|
133
97
|
|
|
134
|
-
|
|
98
|
+
interface ExposePortResponse {
|
|
135
99
|
success: boolean;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
100
|
+
port: number;
|
|
101
|
+
name?: string;
|
|
102
|
+
exposedAt: string;
|
|
139
103
|
timestamp: string;
|
|
140
104
|
}
|
|
141
105
|
|
|
142
|
-
interface
|
|
143
|
-
|
|
106
|
+
interface UnexposePortResponse {
|
|
107
|
+
success: boolean;
|
|
108
|
+
port: number;
|
|
109
|
+
timestamp: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface GetExposedPortsResponse {
|
|
113
|
+
ports: ExposedPort[];
|
|
114
|
+
count: number;
|
|
144
115
|
timestamp: string;
|
|
145
116
|
}
|
|
146
117
|
|
|
147
|
-
interface
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
args?: string[];
|
|
151
|
-
stream?: "stdout" | "stderr";
|
|
152
|
-
data?: string;
|
|
153
|
-
message?: string;
|
|
154
|
-
path?: string;
|
|
155
|
-
oldPath?: string;
|
|
156
|
-
newPath?: string;
|
|
157
|
-
sourcePath?: string;
|
|
158
|
-
destinationPath?: string;
|
|
159
|
-
content?: string;
|
|
160
|
-
success?: boolean;
|
|
161
|
-
exitCode?: number;
|
|
162
|
-
stdout?: string;
|
|
163
|
-
stderr?: string;
|
|
164
|
-
error?: string;
|
|
165
|
-
timestamp?: string;
|
|
118
|
+
interface PingResponse {
|
|
119
|
+
message: string;
|
|
120
|
+
timestamp: string;
|
|
166
121
|
}
|
|
167
122
|
|
|
168
123
|
interface HttpClientOptions {
|
|
169
124
|
stub?: Sandbox;
|
|
170
125
|
baseUrl?: string;
|
|
171
126
|
port?: number;
|
|
172
|
-
onCommandStart?: (command: string
|
|
127
|
+
onCommandStart?: (command: string) => void;
|
|
173
128
|
onOutput?: (
|
|
174
129
|
stream: "stdout" | "stderr",
|
|
175
130
|
data: string,
|
|
@@ -180,17 +135,14 @@ interface HttpClientOptions {
|
|
|
180
135
|
exitCode: number,
|
|
181
136
|
stdout: string,
|
|
182
137
|
stderr: string,
|
|
183
|
-
command: string
|
|
184
|
-
args: string[]
|
|
138
|
+
command: string
|
|
185
139
|
) => void;
|
|
186
|
-
onError?: (error: string, command?: string
|
|
187
|
-
onStreamEvent?: (event: StreamEvent) => void;
|
|
140
|
+
onError?: (error: string, command?: string) => void;
|
|
188
141
|
}
|
|
189
142
|
|
|
190
143
|
export class HttpClient {
|
|
191
144
|
private baseUrl: string;
|
|
192
145
|
private options: HttpClientOptions;
|
|
193
|
-
private sessionId: string | null = null;
|
|
194
146
|
|
|
195
147
|
constructor(options: HttpClientOptions = {}) {
|
|
196
148
|
this.options = {
|
|
@@ -199,7 +151,7 @@ export class HttpClient {
|
|
|
199
151
|
this.baseUrl = this.options.baseUrl!;
|
|
200
152
|
}
|
|
201
153
|
|
|
202
|
-
|
|
154
|
+
protected async doFetch(
|
|
203
155
|
path: string,
|
|
204
156
|
options?: RequestInit
|
|
205
157
|
): Promise<Response> {
|
|
@@ -239,121 +191,53 @@ export class HttpClient {
|
|
|
239
191
|
throw error;
|
|
240
192
|
}
|
|
241
193
|
}
|
|
242
|
-
// Public methods to set event handlers
|
|
243
|
-
setOnOutput(
|
|
244
|
-
handler: (
|
|
245
|
-
stream: "stdout" | "stderr",
|
|
246
|
-
data: string,
|
|
247
|
-
command: string
|
|
248
|
-
) => void
|
|
249
|
-
): void {
|
|
250
|
-
this.options.onOutput = handler;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
setOnCommandComplete(
|
|
254
|
-
handler: (
|
|
255
|
-
success: boolean,
|
|
256
|
-
exitCode: number,
|
|
257
|
-
stdout: string,
|
|
258
|
-
stderr: string,
|
|
259
|
-
command: string,
|
|
260
|
-
args: string[]
|
|
261
|
-
) => void
|
|
262
|
-
): void {
|
|
263
|
-
this.options.onCommandComplete = handler;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
setOnStreamEvent(handler: (event: StreamEvent) => void): void {
|
|
267
|
-
this.options.onStreamEvent = handler;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Public getter methods
|
|
271
|
-
getOnOutput():
|
|
272
|
-
| ((stream: "stdout" | "stderr", data: string, command: string) => void)
|
|
273
|
-
| undefined {
|
|
274
|
-
return this.options.onOutput;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
getOnCommandComplete():
|
|
278
|
-
| ((
|
|
279
|
-
success: boolean,
|
|
280
|
-
exitCode: number,
|
|
281
|
-
stdout: string,
|
|
282
|
-
stderr: string,
|
|
283
|
-
command: string,
|
|
284
|
-
args: string[]
|
|
285
|
-
) => void)
|
|
286
|
-
| undefined {
|
|
287
|
-
return this.options.onCommandComplete;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
getOnStreamEvent(): ((event: StreamEvent) => void) | undefined {
|
|
291
|
-
return this.options.onStreamEvent;
|
|
292
|
-
}
|
|
293
194
|
|
|
294
|
-
async createSession(
|
|
195
|
+
async createSession(options: {
|
|
196
|
+
id: string;
|
|
197
|
+
env?: Record<string, string>;
|
|
198
|
+
cwd?: string;
|
|
199
|
+
isolation?: boolean;
|
|
200
|
+
}): Promise<{ success: boolean; id: string; message: string }> {
|
|
295
201
|
try {
|
|
296
202
|
const response = await this.doFetch(`/api/session/create`, {
|
|
297
|
-
headers: {
|
|
298
|
-
"Content-Type": "application/json",
|
|
299
|
-
},
|
|
300
203
|
method: "POST",
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
if (!response.ok) {
|
|
304
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const data: SessionResponse = await response.json();
|
|
308
|
-
this.sessionId = data.sessionId;
|
|
309
|
-
console.log(`[HTTP Client] Created session: ${this.sessionId}`);
|
|
310
|
-
return this.sessionId;
|
|
311
|
-
} catch (error) {
|
|
312
|
-
console.error("[HTTP Client] Error creating session:", error);
|
|
313
|
-
throw error;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
async listSessions(): Promise<SessionListResponse> {
|
|
318
|
-
try {
|
|
319
|
-
const response = await this.doFetch(`/api/session/list`, {
|
|
320
204
|
headers: {
|
|
321
205
|
"Content-Type": "application/json",
|
|
322
206
|
},
|
|
323
|
-
|
|
207
|
+
body: JSON.stringify(options),
|
|
324
208
|
});
|
|
325
209
|
|
|
326
210
|
if (!response.ok) {
|
|
327
|
-
|
|
211
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
212
|
+
error?: string;
|
|
213
|
+
};
|
|
214
|
+
throw new Error(
|
|
215
|
+
errorData.error || `Failed to create session: ${response.status}`
|
|
216
|
+
);
|
|
328
217
|
}
|
|
329
218
|
|
|
330
|
-
const data
|
|
331
|
-
console.log(`[HTTP Client]
|
|
219
|
+
const data = await response.json() as { success: boolean; id: string; message: string };
|
|
220
|
+
console.log(`[HTTP Client] Session created: ${options.id}`);
|
|
332
221
|
return data;
|
|
333
222
|
} catch (error) {
|
|
334
|
-
console.error("[HTTP Client] Error
|
|
223
|
+
console.error("[HTTP Client] Error creating session:", error);
|
|
335
224
|
throw error;
|
|
336
225
|
}
|
|
337
226
|
}
|
|
338
227
|
|
|
339
|
-
async
|
|
228
|
+
async exec(
|
|
229
|
+
sessionId: string,
|
|
340
230
|
command: string,
|
|
341
|
-
|
|
342
|
-
sessionId?: string
|
|
231
|
+
options?: Pick<BaseExecOptions, "cwd" | "env">
|
|
343
232
|
): Promise<ExecuteResponse> {
|
|
344
233
|
try {
|
|
345
|
-
|
|
346
|
-
|
|
234
|
+
// Always use session-specific endpoint
|
|
347
235
|
const response = await this.doFetch(`/api/execute`, {
|
|
348
|
-
|
|
349
|
-
args,
|
|
350
|
-
command,
|
|
351
|
-
sessionId: targetSessionId,
|
|
352
|
-
}),
|
|
236
|
+
method: "POST",
|
|
353
237
|
headers: {
|
|
354
238
|
"Content-Type": "application/json",
|
|
355
239
|
},
|
|
356
|
-
|
|
240
|
+
body: JSON.stringify({ id: sessionId, command }),
|
|
357
241
|
});
|
|
358
242
|
|
|
359
243
|
if (!response.ok) {
|
|
@@ -361,55 +245,57 @@ export class HttpClient {
|
|
|
361
245
|
error?: string;
|
|
362
246
|
};
|
|
363
247
|
throw new Error(
|
|
364
|
-
errorData.error || `
|
|
248
|
+
errorData.error || `Failed to execute in session: ${response.status}`
|
|
365
249
|
);
|
|
366
250
|
}
|
|
367
251
|
|
|
368
|
-
const data
|
|
252
|
+
const data = await response.json() as { stdout: string; stderr: string; exitCode: number; success: boolean };
|
|
369
253
|
console.log(
|
|
370
|
-
`[HTTP Client] Command executed
|
|
254
|
+
`[HTTP Client] Command executed in session ${sessionId}: ${command}`
|
|
371
255
|
);
|
|
256
|
+
|
|
257
|
+
// Convert to ExecuteResponse format for consistency
|
|
258
|
+
const executeResponse: ExecuteResponse = {
|
|
259
|
+
...data,
|
|
260
|
+
command,
|
|
261
|
+
timestamp: new Date().toISOString()
|
|
262
|
+
};
|
|
372
263
|
|
|
373
264
|
// Call the callback if provided
|
|
374
265
|
this.options.onCommandComplete?.(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
data.args
|
|
266
|
+
executeResponse.success,
|
|
267
|
+
executeResponse.exitCode,
|
|
268
|
+
executeResponse.stdout,
|
|
269
|
+
executeResponse.stderr,
|
|
270
|
+
executeResponse.command
|
|
381
271
|
);
|
|
382
272
|
|
|
383
|
-
return
|
|
273
|
+
return executeResponse;
|
|
384
274
|
} catch (error) {
|
|
385
|
-
console.error("[HTTP Client] Error executing
|
|
275
|
+
console.error("[HTTP Client] Error executing in session:", error);
|
|
386
276
|
this.options.onError?.(
|
|
387
277
|
error instanceof Error ? error.message : "Unknown error",
|
|
388
|
-
command
|
|
389
|
-
args
|
|
278
|
+
command
|
|
390
279
|
);
|
|
391
280
|
throw error;
|
|
392
281
|
}
|
|
393
282
|
}
|
|
394
283
|
|
|
395
|
-
async
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
): Promise<void> {
|
|
284
|
+
async execStream(
|
|
285
|
+
sessionId: string,
|
|
286
|
+
command: string
|
|
287
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
400
288
|
try {
|
|
401
|
-
|
|
402
|
-
|
|
289
|
+
// Always use session-specific streaming endpoint
|
|
403
290
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
404
|
-
|
|
405
|
-
args,
|
|
406
|
-
command,
|
|
407
|
-
sessionId: targetSessionId,
|
|
408
|
-
}),
|
|
291
|
+
method: "POST",
|
|
409
292
|
headers: {
|
|
410
293
|
"Content-Type": "application/json",
|
|
411
294
|
},
|
|
412
|
-
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
id: sessionId,
|
|
297
|
+
command
|
|
298
|
+
}),
|
|
413
299
|
});
|
|
414
300
|
|
|
415
301
|
if (!response.ok) {
|
|
@@ -417,123 +303,38 @@ export class HttpClient {
|
|
|
417
303
|
error?: string;
|
|
418
304
|
};
|
|
419
305
|
throw new Error(
|
|
420
|
-
errorData.error || `
|
|
306
|
+
errorData.error || `Failed to stream execute in session: ${response.status}`
|
|
421
307
|
);
|
|
422
308
|
}
|
|
423
309
|
|
|
424
310
|
if (!response.body) {
|
|
425
|
-
throw new Error("No response body for streaming
|
|
311
|
+
throw new Error("No response body for streaming execution");
|
|
426
312
|
}
|
|
427
313
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
try {
|
|
432
|
-
while (true) {
|
|
433
|
-
const { done, value } = await reader.read();
|
|
434
|
-
|
|
435
|
-
if (done) {
|
|
436
|
-
break;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
440
|
-
const lines = chunk.split("\n");
|
|
441
|
-
|
|
442
|
-
for (const line of lines) {
|
|
443
|
-
if (line.startsWith("data: ")) {
|
|
444
|
-
try {
|
|
445
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
446
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
447
|
-
|
|
448
|
-
console.log(`[HTTP Client] Stream event: ${event.type}`);
|
|
449
|
-
this.options.onStreamEvent?.(event);
|
|
450
|
-
|
|
451
|
-
switch (event.type) {
|
|
452
|
-
case "command_start":
|
|
453
|
-
console.log(
|
|
454
|
-
`[HTTP Client] Command started: ${
|
|
455
|
-
event.command
|
|
456
|
-
} ${event.args?.join(" ")}`
|
|
457
|
-
);
|
|
458
|
-
this.options.onCommandStart?.(
|
|
459
|
-
event.command!,
|
|
460
|
-
event.args || []
|
|
461
|
-
);
|
|
462
|
-
break;
|
|
463
|
-
|
|
464
|
-
case "output":
|
|
465
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
466
|
-
this.options.onOutput?.(
|
|
467
|
-
event.stream!,
|
|
468
|
-
event.data!,
|
|
469
|
-
event.command!
|
|
470
|
-
);
|
|
471
|
-
break;
|
|
472
|
-
|
|
473
|
-
case "command_complete":
|
|
474
|
-
console.log(
|
|
475
|
-
`[HTTP Client] Command completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
476
|
-
);
|
|
477
|
-
this.options.onCommandComplete?.(
|
|
478
|
-
event.success!,
|
|
479
|
-
event.exitCode!,
|
|
480
|
-
event.stdout!,
|
|
481
|
-
event.stderr!,
|
|
482
|
-
event.command!,
|
|
483
|
-
event.args || []
|
|
484
|
-
);
|
|
485
|
-
break;
|
|
486
|
-
|
|
487
|
-
case "error":
|
|
488
|
-
console.error(
|
|
489
|
-
`[HTTP Client] Command error: ${event.error}`
|
|
490
|
-
);
|
|
491
|
-
this.options.onError?.(
|
|
492
|
-
event.error!,
|
|
493
|
-
event.command,
|
|
494
|
-
event.args
|
|
495
|
-
);
|
|
496
|
-
break;
|
|
497
|
-
}
|
|
498
|
-
} catch (parseError) {
|
|
499
|
-
console.warn(
|
|
500
|
-
"[HTTP Client] Failed to parse stream event:",
|
|
501
|
-
parseError
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
} finally {
|
|
508
|
-
reader.releaseLock();
|
|
509
|
-
}
|
|
510
|
-
} catch (error) {
|
|
511
|
-
console.error("[HTTP Client] Error in streaming execution:", error);
|
|
512
|
-
this.options.onError?.(
|
|
513
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
514
|
-
command,
|
|
515
|
-
args
|
|
314
|
+
console.log(
|
|
315
|
+
`[HTTP Client] Started streaming command in session ${sessionId}: ${command}`
|
|
516
316
|
);
|
|
317
|
+
return response.body;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error("[HTTP Client] Error streaming execute in session:", error);
|
|
517
320
|
throw error;
|
|
518
321
|
}
|
|
519
322
|
}
|
|
520
323
|
|
|
521
324
|
async gitCheckout(
|
|
522
325
|
repoUrl: string,
|
|
326
|
+
sessionId: string,
|
|
523
327
|
branch: string = "main",
|
|
524
|
-
targetDir?: string
|
|
525
|
-
sessionId?: string
|
|
328
|
+
targetDir?: string
|
|
526
329
|
): Promise<GitCheckoutResponse> {
|
|
527
330
|
try {
|
|
528
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
529
|
-
|
|
530
331
|
const response = await this.doFetch(`/api/git/checkout`, {
|
|
531
332
|
body: JSON.stringify({
|
|
532
333
|
branch,
|
|
533
334
|
repoUrl,
|
|
534
|
-
sessionId: targetSessionId,
|
|
535
335
|
targetDir,
|
|
536
|
-
|
|
336
|
+
sessionId,
|
|
337
|
+
} as GitCheckoutRequest),
|
|
537
338
|
headers: {
|
|
538
339
|
"Content-Type": "application/json",
|
|
539
340
|
},
|
|
@@ -561,150 +362,18 @@ export class HttpClient {
|
|
|
561
362
|
}
|
|
562
363
|
}
|
|
563
364
|
|
|
564
|
-
async gitCheckoutStream(
|
|
565
|
-
repoUrl: string,
|
|
566
|
-
branch: string = "main",
|
|
567
|
-
targetDir?: string,
|
|
568
|
-
sessionId?: string
|
|
569
|
-
): Promise<void> {
|
|
570
|
-
try {
|
|
571
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
572
|
-
|
|
573
|
-
const response = await this.doFetch(`/api/git/checkout/stream`, {
|
|
574
|
-
body: JSON.stringify({
|
|
575
|
-
branch,
|
|
576
|
-
repoUrl,
|
|
577
|
-
sessionId: targetSessionId,
|
|
578
|
-
targetDir,
|
|
579
|
-
}),
|
|
580
|
-
headers: {
|
|
581
|
-
"Content-Type": "application/json",
|
|
582
|
-
},
|
|
583
|
-
method: "POST",
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
if (!response.ok) {
|
|
587
|
-
const errorData = (await response.json().catch(() => ({}))) as {
|
|
588
|
-
error?: string;
|
|
589
|
-
};
|
|
590
|
-
throw new Error(
|
|
591
|
-
errorData.error || `HTTP error! status: ${response.status}`
|
|
592
|
-
);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
if (!response.body) {
|
|
596
|
-
throw new Error("No response body for streaming request");
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
const reader = response.body.getReader();
|
|
600
|
-
const decoder = new TextDecoder();
|
|
601
|
-
|
|
602
|
-
try {
|
|
603
|
-
while (true) {
|
|
604
|
-
const { done, value } = await reader.read();
|
|
605
|
-
|
|
606
|
-
if (done) {
|
|
607
|
-
break;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
611
|
-
const lines = chunk.split("\n");
|
|
612
|
-
|
|
613
|
-
for (const line of lines) {
|
|
614
|
-
if (line.startsWith("data: ")) {
|
|
615
|
-
try {
|
|
616
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
617
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
618
|
-
|
|
619
|
-
console.log(
|
|
620
|
-
`[HTTP Client] Git checkout stream event: ${event.type}`
|
|
621
|
-
);
|
|
622
|
-
this.options.onStreamEvent?.(event);
|
|
623
|
-
|
|
624
|
-
switch (event.type) {
|
|
625
|
-
case "command_start":
|
|
626
|
-
console.log(
|
|
627
|
-
`[HTTP Client] Git checkout started: ${
|
|
628
|
-
event.command
|
|
629
|
-
} ${event.args?.join(" ")}`
|
|
630
|
-
);
|
|
631
|
-
this.options.onCommandStart?.(
|
|
632
|
-
event.command!,
|
|
633
|
-
event.args || []
|
|
634
|
-
);
|
|
635
|
-
break;
|
|
636
|
-
|
|
637
|
-
case "output":
|
|
638
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
639
|
-
this.options.onOutput?.(
|
|
640
|
-
event.stream!,
|
|
641
|
-
event.data!,
|
|
642
|
-
event.command!
|
|
643
|
-
);
|
|
644
|
-
break;
|
|
645
|
-
|
|
646
|
-
case "command_complete":
|
|
647
|
-
console.log(
|
|
648
|
-
`[HTTP Client] Git checkout completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
649
|
-
);
|
|
650
|
-
this.options.onCommandComplete?.(
|
|
651
|
-
event.success!,
|
|
652
|
-
event.exitCode!,
|
|
653
|
-
event.stdout!,
|
|
654
|
-
event.stderr!,
|
|
655
|
-
event.command!,
|
|
656
|
-
event.args || []
|
|
657
|
-
);
|
|
658
|
-
break;
|
|
659
|
-
|
|
660
|
-
case "error":
|
|
661
|
-
console.error(
|
|
662
|
-
`[HTTP Client] Git checkout error: ${event.error}`
|
|
663
|
-
);
|
|
664
|
-
this.options.onError?.(
|
|
665
|
-
event.error!,
|
|
666
|
-
event.command,
|
|
667
|
-
event.args
|
|
668
|
-
);
|
|
669
|
-
break;
|
|
670
|
-
}
|
|
671
|
-
} catch (parseError) {
|
|
672
|
-
console.warn(
|
|
673
|
-
"[HTTP Client] Failed to parse git checkout stream event:",
|
|
674
|
-
parseError
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
} finally {
|
|
681
|
-
reader.releaseLock();
|
|
682
|
-
}
|
|
683
|
-
} catch (error) {
|
|
684
|
-
console.error("[HTTP Client] Error in streaming git checkout:", error);
|
|
685
|
-
this.options.onError?.(
|
|
686
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
687
|
-
"git clone",
|
|
688
|
-
[branch, repoUrl, targetDir || ""]
|
|
689
|
-
);
|
|
690
|
-
throw error;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
365
|
async mkdir(
|
|
695
366
|
path: string,
|
|
696
367
|
recursive: boolean = false,
|
|
697
|
-
sessionId
|
|
368
|
+
sessionId: string
|
|
698
369
|
): Promise<MkdirResponse> {
|
|
699
370
|
try {
|
|
700
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
701
|
-
|
|
702
371
|
const response = await this.doFetch(`/api/mkdir`, {
|
|
703
372
|
body: JSON.stringify({
|
|
704
373
|
path,
|
|
705
374
|
recursive,
|
|
706
|
-
sessionId
|
|
707
|
-
}),
|
|
375
|
+
sessionId,
|
|
376
|
+
} as MkdirRequest),
|
|
708
377
|
headers: {
|
|
709
378
|
"Content-Type": "application/json",
|
|
710
379
|
},
|
|
@@ -722,7 +391,7 @@ export class HttpClient {
|
|
|
722
391
|
|
|
723
392
|
const data: MkdirResponse = await response.json();
|
|
724
393
|
console.log(
|
|
725
|
-
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`
|
|
394
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
726
395
|
);
|
|
727
396
|
|
|
728
397
|
return data;
|
|
@@ -732,20 +401,20 @@ export class HttpClient {
|
|
|
732
401
|
}
|
|
733
402
|
}
|
|
734
403
|
|
|
735
|
-
async
|
|
404
|
+
async writeFile(
|
|
736
405
|
path: string,
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
406
|
+
content: string,
|
|
407
|
+
encoding: string = "utf-8",
|
|
408
|
+
sessionId: string
|
|
409
|
+
): Promise<WriteFileResponse> {
|
|
740
410
|
try {
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
const response = await this.doFetch(`/api/mkdir/stream`, {
|
|
411
|
+
const response = await this.doFetch(`/api/write`, {
|
|
744
412
|
body: JSON.stringify({
|
|
413
|
+
content,
|
|
414
|
+
encoding,
|
|
745
415
|
path,
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}),
|
|
416
|
+
sessionId,
|
|
417
|
+
} as WriteFileRequest),
|
|
749
418
|
headers: {
|
|
750
419
|
"Content-Type": "application/json",
|
|
751
420
|
},
|
|
@@ -761,117 +430,30 @@ export class HttpClient {
|
|
|
761
430
|
);
|
|
762
431
|
}
|
|
763
432
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
433
|
+
const data: WriteFileResponse = await response.json();
|
|
434
|
+
console.log(
|
|
435
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
436
|
+
);
|
|
767
437
|
|
|
768
|
-
|
|
769
|
-
const decoder = new TextDecoder();
|
|
770
|
-
|
|
771
|
-
try {
|
|
772
|
-
while (true) {
|
|
773
|
-
const { done, value } = await reader.read();
|
|
774
|
-
|
|
775
|
-
if (done) {
|
|
776
|
-
break;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
780
|
-
const lines = chunk.split("\n");
|
|
781
|
-
|
|
782
|
-
for (const line of lines) {
|
|
783
|
-
if (line.startsWith("data: ")) {
|
|
784
|
-
try {
|
|
785
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
786
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
787
|
-
|
|
788
|
-
console.log(`[HTTP Client] Mkdir stream event: ${event.type}`);
|
|
789
|
-
this.options.onStreamEvent?.(event);
|
|
790
|
-
|
|
791
|
-
switch (event.type) {
|
|
792
|
-
case "command_start":
|
|
793
|
-
console.log(
|
|
794
|
-
`[HTTP Client] Mkdir started: ${
|
|
795
|
-
event.command
|
|
796
|
-
} ${event.args?.join(" ")}`
|
|
797
|
-
);
|
|
798
|
-
this.options.onCommandStart?.(
|
|
799
|
-
event.command!,
|
|
800
|
-
event.args || []
|
|
801
|
-
);
|
|
802
|
-
break;
|
|
803
|
-
|
|
804
|
-
case "output":
|
|
805
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
806
|
-
this.options.onOutput?.(
|
|
807
|
-
event.stream!,
|
|
808
|
-
event.data!,
|
|
809
|
-
event.command!
|
|
810
|
-
);
|
|
811
|
-
break;
|
|
812
|
-
|
|
813
|
-
case "command_complete":
|
|
814
|
-
console.log(
|
|
815
|
-
`[HTTP Client] Mkdir completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
816
|
-
);
|
|
817
|
-
this.options.onCommandComplete?.(
|
|
818
|
-
event.success!,
|
|
819
|
-
event.exitCode!,
|
|
820
|
-
event.stdout!,
|
|
821
|
-
event.stderr!,
|
|
822
|
-
event.command!,
|
|
823
|
-
event.args || []
|
|
824
|
-
);
|
|
825
|
-
break;
|
|
826
|
-
|
|
827
|
-
case "error":
|
|
828
|
-
console.error(`[HTTP Client] Mkdir error: ${event.error}`);
|
|
829
|
-
this.options.onError?.(
|
|
830
|
-
event.error!,
|
|
831
|
-
event.command,
|
|
832
|
-
event.args
|
|
833
|
-
);
|
|
834
|
-
break;
|
|
835
|
-
}
|
|
836
|
-
} catch (parseError) {
|
|
837
|
-
console.warn(
|
|
838
|
-
"[HTTP Client] Failed to parse mkdir stream event:",
|
|
839
|
-
parseError
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
} finally {
|
|
846
|
-
reader.releaseLock();
|
|
847
|
-
}
|
|
438
|
+
return data;
|
|
848
439
|
} catch (error) {
|
|
849
|
-
console.error("[HTTP Client] Error
|
|
850
|
-
this.options.onError?.(
|
|
851
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
852
|
-
"mkdir",
|
|
853
|
-
recursive ? ["-p", path] : [path]
|
|
854
|
-
);
|
|
440
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
855
441
|
throw error;
|
|
856
442
|
}
|
|
857
443
|
}
|
|
858
444
|
|
|
859
|
-
async
|
|
445
|
+
async readFile(
|
|
860
446
|
path: string,
|
|
861
|
-
content: string,
|
|
862
447
|
encoding: string = "utf-8",
|
|
863
|
-
sessionId
|
|
864
|
-
): Promise<
|
|
448
|
+
sessionId: string
|
|
449
|
+
): Promise<ReadFileResponse> {
|
|
865
450
|
try {
|
|
866
|
-
const
|
|
867
|
-
|
|
868
|
-
const response = await this.doFetch(`/api/write`, {
|
|
451
|
+
const response = await this.doFetch(`/api/read`, {
|
|
869
452
|
body: JSON.stringify({
|
|
870
|
-
content,
|
|
871
453
|
encoding,
|
|
872
454
|
path,
|
|
873
|
-
sessionId
|
|
874
|
-
}),
|
|
455
|
+
sessionId,
|
|
456
|
+
} as ReadFileRequest),
|
|
875
457
|
headers: {
|
|
876
458
|
"Content-Type": "application/json",
|
|
877
459
|
},
|
|
@@ -887,38 +469,32 @@ export class HttpClient {
|
|
|
887
469
|
);
|
|
888
470
|
}
|
|
889
471
|
|
|
890
|
-
const data:
|
|
472
|
+
const data: ReadFileResponse = await response.json();
|
|
891
473
|
console.log(
|
|
892
|
-
`[HTTP Client] File
|
|
474
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
893
475
|
);
|
|
894
476
|
|
|
895
477
|
return data;
|
|
896
478
|
} catch (error) {
|
|
897
|
-
console.error("[HTTP Client] Error
|
|
479
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
898
480
|
throw error;
|
|
899
481
|
}
|
|
900
482
|
}
|
|
901
483
|
|
|
902
|
-
async
|
|
484
|
+
async readFileStream(
|
|
903
485
|
path: string,
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
sessionId?: string
|
|
907
|
-
): Promise<void> {
|
|
486
|
+
sessionId: string
|
|
487
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
908
488
|
try {
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
const response = await this.doFetch(`/api/write/stream`, {
|
|
912
|
-
body: JSON.stringify({
|
|
913
|
-
content,
|
|
914
|
-
encoding,
|
|
915
|
-
path,
|
|
916
|
-
sessionId: targetSessionId,
|
|
917
|
-
}),
|
|
489
|
+
const response = await this.doFetch(`/api/read/stream`, {
|
|
490
|
+
method: "POST",
|
|
918
491
|
headers: {
|
|
919
492
|
"Content-Type": "application/json",
|
|
920
493
|
},
|
|
921
|
-
|
|
494
|
+
body: JSON.stringify({
|
|
495
|
+
path,
|
|
496
|
+
sessionId,
|
|
497
|
+
} as ReadFileRequest),
|
|
922
498
|
});
|
|
923
499
|
|
|
924
500
|
if (!response.ok) {
|
|
@@ -931,113 +507,29 @@ export class HttpClient {
|
|
|
931
507
|
}
|
|
932
508
|
|
|
933
509
|
if (!response.body) {
|
|
934
|
-
throw new Error("No response body for streaming
|
|
510
|
+
throw new Error("No response body for file streaming");
|
|
935
511
|
}
|
|
936
512
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
try {
|
|
941
|
-
while (true) {
|
|
942
|
-
const { done, value } = await reader.read();
|
|
943
|
-
|
|
944
|
-
if (done) {
|
|
945
|
-
break;
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
949
|
-
const lines = chunk.split("\n");
|
|
950
|
-
|
|
951
|
-
for (const line of lines) {
|
|
952
|
-
if (line.startsWith("data: ")) {
|
|
953
|
-
try {
|
|
954
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
955
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
956
|
-
|
|
957
|
-
console.log(
|
|
958
|
-
`[HTTP Client] Write file stream event: ${event.type}`
|
|
959
|
-
);
|
|
960
|
-
this.options.onStreamEvent?.(event);
|
|
961
|
-
|
|
962
|
-
switch (event.type) {
|
|
963
|
-
case "command_start":
|
|
964
|
-
console.log(
|
|
965
|
-
`[HTTP Client] Write file started: ${event.path}`
|
|
966
|
-
);
|
|
967
|
-
this.options.onCommandStart?.("write", [
|
|
968
|
-
path,
|
|
969
|
-
content,
|
|
970
|
-
encoding,
|
|
971
|
-
]);
|
|
972
|
-
break;
|
|
973
|
-
|
|
974
|
-
case "output":
|
|
975
|
-
console.log(`[output] ${event.message}`);
|
|
976
|
-
this.options.onOutput?.("stdout", event.message!, "write");
|
|
977
|
-
break;
|
|
978
|
-
|
|
979
|
-
case "command_complete":
|
|
980
|
-
console.log(
|
|
981
|
-
`[HTTP Client] Write file completed: ${event.path}, Success: ${event.success}`
|
|
982
|
-
);
|
|
983
|
-
this.options.onCommandComplete?.(
|
|
984
|
-
event.success!,
|
|
985
|
-
0,
|
|
986
|
-
"",
|
|
987
|
-
"",
|
|
988
|
-
"write",
|
|
989
|
-
[path, content, encoding]
|
|
990
|
-
);
|
|
991
|
-
break;
|
|
992
|
-
|
|
993
|
-
case "error":
|
|
994
|
-
console.error(
|
|
995
|
-
`[HTTP Client] Write file error: ${event.error}`
|
|
996
|
-
);
|
|
997
|
-
this.options.onError?.(event.error!, "write", [
|
|
998
|
-
path,
|
|
999
|
-
content,
|
|
1000
|
-
encoding,
|
|
1001
|
-
]);
|
|
1002
|
-
break;
|
|
1003
|
-
}
|
|
1004
|
-
} catch (parseError) {
|
|
1005
|
-
console.warn(
|
|
1006
|
-
"[HTTP Client] Failed to parse write file stream event:",
|
|
1007
|
-
parseError
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
} finally {
|
|
1014
|
-
reader.releaseLock();
|
|
1015
|
-
}
|
|
1016
|
-
} catch (error) {
|
|
1017
|
-
console.error("[HTTP Client] Error in streaming write file:", error);
|
|
1018
|
-
this.options.onError?.(
|
|
1019
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1020
|
-
"write",
|
|
1021
|
-
[path, content, encoding]
|
|
513
|
+
console.log(
|
|
514
|
+
`[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1022
515
|
);
|
|
516
|
+
return response.body;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error("[HTTP Client] Error streaming file:", error);
|
|
1023
519
|
throw error;
|
|
1024
520
|
}
|
|
1025
521
|
}
|
|
1026
522
|
|
|
1027
|
-
async
|
|
523
|
+
async deleteFile(
|
|
1028
524
|
path: string,
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
): Promise<ReadFileResponse> {
|
|
525
|
+
sessionId: string
|
|
526
|
+
): Promise<DeleteFileResponse> {
|
|
1032
527
|
try {
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
const response = await this.doFetch(`/api/read`, {
|
|
528
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
1036
529
|
body: JSON.stringify({
|
|
1037
|
-
encoding,
|
|
1038
530
|
path,
|
|
1039
|
-
sessionId
|
|
1040
|
-
}),
|
|
531
|
+
sessionId,
|
|
532
|
+
} as DeleteFileRequest),
|
|
1041
533
|
headers: {
|
|
1042
534
|
"Content-Type": "application/json",
|
|
1043
535
|
},
|
|
@@ -1053,32 +545,30 @@ export class HttpClient {
|
|
|
1053
545
|
);
|
|
1054
546
|
}
|
|
1055
547
|
|
|
1056
|
-
const data:
|
|
548
|
+
const data: DeleteFileResponse = await response.json();
|
|
1057
549
|
console.log(
|
|
1058
|
-
`[HTTP Client] File
|
|
550
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1059
551
|
);
|
|
1060
552
|
|
|
1061
553
|
return data;
|
|
1062
554
|
} catch (error) {
|
|
1063
|
-
console.error("[HTTP Client] Error
|
|
555
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
1064
556
|
throw error;
|
|
1065
557
|
}
|
|
1066
558
|
}
|
|
1067
559
|
|
|
1068
|
-
async
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
sessionId
|
|
1072
|
-
): Promise<
|
|
560
|
+
async renameFile(
|
|
561
|
+
oldPath: string,
|
|
562
|
+
newPath: string,
|
|
563
|
+
sessionId: string
|
|
564
|
+
): Promise<RenameFileResponse> {
|
|
1073
565
|
try {
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
const response = await this.doFetch(`/api/read/stream`, {
|
|
566
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
1077
567
|
body: JSON.stringify({
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
sessionId
|
|
1081
|
-
}),
|
|
568
|
+
newPath,
|
|
569
|
+
oldPath,
|
|
570
|
+
sessionId,
|
|
571
|
+
} as RenameFileRequest),
|
|
1082
572
|
headers: {
|
|
1083
573
|
"Content-Type": "application/json",
|
|
1084
574
|
},
|
|
@@ -1094,106 +584,30 @@ export class HttpClient {
|
|
|
1094
584
|
);
|
|
1095
585
|
}
|
|
1096
586
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
587
|
+
const data: RenameFileResponse = await response.json();
|
|
588
|
+
console.log(
|
|
589
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
590
|
+
);
|
|
1100
591
|
|
|
1101
|
-
|
|
1102
|
-
const decoder = new TextDecoder();
|
|
1103
|
-
|
|
1104
|
-
try {
|
|
1105
|
-
while (true) {
|
|
1106
|
-
const { done, value } = await reader.read();
|
|
1107
|
-
|
|
1108
|
-
if (done) {
|
|
1109
|
-
break;
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1113
|
-
const lines = chunk.split("\n");
|
|
1114
|
-
|
|
1115
|
-
for (const line of lines) {
|
|
1116
|
-
if (line.startsWith("data: ")) {
|
|
1117
|
-
try {
|
|
1118
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1119
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1120
|
-
|
|
1121
|
-
console.log(
|
|
1122
|
-
`[HTTP Client] Read file stream event: ${event.type}`
|
|
1123
|
-
);
|
|
1124
|
-
this.options.onStreamEvent?.(event);
|
|
1125
|
-
|
|
1126
|
-
switch (event.type) {
|
|
1127
|
-
case "command_start":
|
|
1128
|
-
console.log(
|
|
1129
|
-
`[HTTP Client] Read file started: ${event.path}`
|
|
1130
|
-
);
|
|
1131
|
-
this.options.onCommandStart?.("read", [path, encoding]);
|
|
1132
|
-
break;
|
|
1133
|
-
|
|
1134
|
-
case "command_complete":
|
|
1135
|
-
console.log(
|
|
1136
|
-
`[HTTP Client] Read file completed: ${
|
|
1137
|
-
event.path
|
|
1138
|
-
}, Success: ${event.success}, Content length: ${
|
|
1139
|
-
event.content?.length || 0
|
|
1140
|
-
}`
|
|
1141
|
-
);
|
|
1142
|
-
this.options.onCommandComplete?.(
|
|
1143
|
-
event.success!,
|
|
1144
|
-
0,
|
|
1145
|
-
event.content || "",
|
|
1146
|
-
"",
|
|
1147
|
-
"read",
|
|
1148
|
-
[path, encoding]
|
|
1149
|
-
);
|
|
1150
|
-
break;
|
|
1151
|
-
|
|
1152
|
-
case "error":
|
|
1153
|
-
console.error(
|
|
1154
|
-
`[HTTP Client] Read file error: ${event.error}`
|
|
1155
|
-
);
|
|
1156
|
-
this.options.onError?.(event.error!, "read", [
|
|
1157
|
-
path,
|
|
1158
|
-
encoding,
|
|
1159
|
-
]);
|
|
1160
|
-
break;
|
|
1161
|
-
}
|
|
1162
|
-
} catch (parseError) {
|
|
1163
|
-
console.warn(
|
|
1164
|
-
"[HTTP Client] Failed to parse read file stream event:",
|
|
1165
|
-
parseError
|
|
1166
|
-
);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
} finally {
|
|
1172
|
-
reader.releaseLock();
|
|
1173
|
-
}
|
|
592
|
+
return data;
|
|
1174
593
|
} catch (error) {
|
|
1175
|
-
console.error("[HTTP Client] Error
|
|
1176
|
-
this.options.onError?.(
|
|
1177
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1178
|
-
"read",
|
|
1179
|
-
[path, encoding]
|
|
1180
|
-
);
|
|
594
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
1181
595
|
throw error;
|
|
1182
596
|
}
|
|
1183
597
|
}
|
|
1184
598
|
|
|
1185
|
-
async
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
599
|
+
async moveFile(
|
|
600
|
+
sourcePath: string,
|
|
601
|
+
destinationPath: string,
|
|
602
|
+
sessionId: string
|
|
603
|
+
): Promise<MoveFileResponse> {
|
|
1189
604
|
try {
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
605
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1193
606
|
body: JSON.stringify({
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
607
|
+
destinationPath,
|
|
608
|
+
sourcePath,
|
|
609
|
+
sessionId,
|
|
610
|
+
} as MoveFileRequest),
|
|
1197
611
|
headers: {
|
|
1198
612
|
"Content-Type": "application/json",
|
|
1199
613
|
},
|
|
@@ -1209,27 +623,33 @@ export class HttpClient {
|
|
|
1209
623
|
);
|
|
1210
624
|
}
|
|
1211
625
|
|
|
1212
|
-
const data:
|
|
626
|
+
const data: MoveFileResponse = await response.json();
|
|
1213
627
|
console.log(
|
|
1214
|
-
`[HTTP Client] File
|
|
628
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1215
629
|
);
|
|
1216
630
|
|
|
1217
631
|
return data;
|
|
1218
632
|
} catch (error) {
|
|
1219
|
-
console.error("[HTTP Client] Error
|
|
633
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1220
634
|
throw error;
|
|
1221
635
|
}
|
|
1222
636
|
}
|
|
1223
637
|
|
|
1224
|
-
async
|
|
638
|
+
async listFiles(
|
|
639
|
+
path: string,
|
|
640
|
+
sessionId: string,
|
|
641
|
+
options?: {
|
|
642
|
+
recursive?: boolean;
|
|
643
|
+
includeHidden?: boolean;
|
|
644
|
+
}
|
|
645
|
+
): Promise<ListFilesResponse> {
|
|
1225
646
|
try {
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1228
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
647
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
1229
648
|
body: JSON.stringify({
|
|
1230
649
|
path,
|
|
1231
|
-
|
|
1232
|
-
|
|
650
|
+
options,
|
|
651
|
+
sessionId,
|
|
652
|
+
} as ListFilesRequest),
|
|
1233
653
|
headers: {
|
|
1234
654
|
"Content-Type": "application/json",
|
|
1235
655
|
},
|
|
@@ -1245,100 +665,24 @@ export class HttpClient {
|
|
|
1245
665
|
);
|
|
1246
666
|
}
|
|
1247
667
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
668
|
+
const data: ListFilesResponse = await response.json();
|
|
669
|
+
console.log(
|
|
670
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
671
|
+
);
|
|
1251
672
|
|
|
1252
|
-
|
|
1253
|
-
const decoder = new TextDecoder();
|
|
1254
|
-
|
|
1255
|
-
try {
|
|
1256
|
-
while (true) {
|
|
1257
|
-
const { done, value } = await reader.read();
|
|
1258
|
-
|
|
1259
|
-
if (done) {
|
|
1260
|
-
break;
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1264
|
-
const lines = chunk.split("\n");
|
|
1265
|
-
|
|
1266
|
-
for (const line of lines) {
|
|
1267
|
-
if (line.startsWith("data: ")) {
|
|
1268
|
-
try {
|
|
1269
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1270
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1271
|
-
|
|
1272
|
-
console.log(
|
|
1273
|
-
`[HTTP Client] Delete file stream event: ${event.type}`
|
|
1274
|
-
);
|
|
1275
|
-
this.options.onStreamEvent?.(event);
|
|
1276
|
-
|
|
1277
|
-
switch (event.type) {
|
|
1278
|
-
case "command_start":
|
|
1279
|
-
console.log(
|
|
1280
|
-
`[HTTP Client] Delete file started: ${event.path}`
|
|
1281
|
-
);
|
|
1282
|
-
this.options.onCommandStart?.("delete", [path]);
|
|
1283
|
-
break;
|
|
1284
|
-
|
|
1285
|
-
case "command_complete":
|
|
1286
|
-
console.log(
|
|
1287
|
-
`[HTTP Client] Delete file completed: ${event.path}, Success: ${event.success}`
|
|
1288
|
-
);
|
|
1289
|
-
this.options.onCommandComplete?.(
|
|
1290
|
-
event.success!,
|
|
1291
|
-
0,
|
|
1292
|
-
"",
|
|
1293
|
-
"",
|
|
1294
|
-
"delete",
|
|
1295
|
-
[path]
|
|
1296
|
-
);
|
|
1297
|
-
break;
|
|
1298
|
-
|
|
1299
|
-
case "error":
|
|
1300
|
-
console.error(
|
|
1301
|
-
`[HTTP Client] Delete file error: ${event.error}`
|
|
1302
|
-
);
|
|
1303
|
-
this.options.onError?.(event.error!, "delete", [path]);
|
|
1304
|
-
break;
|
|
1305
|
-
}
|
|
1306
|
-
} catch (parseError) {
|
|
1307
|
-
console.warn(
|
|
1308
|
-
"[HTTP Client] Failed to parse delete file stream event:",
|
|
1309
|
-
parseError
|
|
1310
|
-
);
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
} finally {
|
|
1316
|
-
reader.releaseLock();
|
|
1317
|
-
}
|
|
673
|
+
return data;
|
|
1318
674
|
} catch (error) {
|
|
1319
|
-
console.error("[HTTP Client] Error
|
|
1320
|
-
this.options.onError?.(
|
|
1321
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1322
|
-
"delete",
|
|
1323
|
-
[path]
|
|
1324
|
-
);
|
|
675
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
1325
676
|
throw error;
|
|
1326
677
|
}
|
|
1327
678
|
}
|
|
1328
679
|
|
|
1329
|
-
async
|
|
1330
|
-
oldPath: string,
|
|
1331
|
-
newPath: string,
|
|
1332
|
-
sessionId?: string
|
|
1333
|
-
): Promise<RenameFileResponse> {
|
|
680
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1334
681
|
try {
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
const response = await this.doFetch(`/api/rename`, {
|
|
682
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1338
683
|
body: JSON.stringify({
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
sessionId: targetSessionId,
|
|
684
|
+
port,
|
|
685
|
+
name,
|
|
1342
686
|
}),
|
|
1343
687
|
headers: {
|
|
1344
688
|
"Content-Type": "application/json",
|
|
@@ -1350,41 +694,36 @@ export class HttpClient {
|
|
|
1350
694
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1351
695
|
error?: string;
|
|
1352
696
|
};
|
|
697
|
+
console.log(errorData);
|
|
1353
698
|
throw new Error(
|
|
1354
699
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1355
700
|
);
|
|
1356
701
|
}
|
|
1357
702
|
|
|
1358
|
-
const data:
|
|
703
|
+
const data: ExposePortResponse = await response.json();
|
|
1359
704
|
console.log(
|
|
1360
|
-
`[HTTP Client]
|
|
705
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
706
|
+
name ? ` (${name})` : ""
|
|
707
|
+
}, Success: ${data.success}`
|
|
1361
708
|
);
|
|
1362
709
|
|
|
1363
710
|
return data;
|
|
1364
711
|
} catch (error) {
|
|
1365
|
-
console.error("[HTTP Client] Error
|
|
712
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1366
713
|
throw error;
|
|
1367
714
|
}
|
|
1368
715
|
}
|
|
1369
716
|
|
|
1370
|
-
async
|
|
1371
|
-
oldPath: string,
|
|
1372
|
-
newPath: string,
|
|
1373
|
-
sessionId?: string
|
|
1374
|
-
): Promise<void> {
|
|
717
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1375
718
|
try {
|
|
1376
|
-
const
|
|
1377
|
-
|
|
1378
|
-
const response = await this.doFetch(`/api/rename/stream`, {
|
|
719
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1379
720
|
body: JSON.stringify({
|
|
1380
|
-
|
|
1381
|
-
oldPath,
|
|
1382
|
-
sessionId: targetSessionId,
|
|
721
|
+
port,
|
|
1383
722
|
}),
|
|
1384
723
|
headers: {
|
|
1385
724
|
"Content-Type": "application/json",
|
|
1386
725
|
},
|
|
1387
|
-
method: "
|
|
726
|
+
method: "DELETE",
|
|
1388
727
|
});
|
|
1389
728
|
|
|
1390
729
|
if (!response.ok) {
|
|
@@ -1396,108 +735,25 @@ export class HttpClient {
|
|
|
1396
735
|
);
|
|
1397
736
|
}
|
|
1398
737
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
738
|
+
const data: UnexposePortResponse = await response.json();
|
|
739
|
+
console.log(
|
|
740
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
741
|
+
);
|
|
1402
742
|
|
|
1403
|
-
|
|
1404
|
-
const decoder = new TextDecoder();
|
|
1405
|
-
|
|
1406
|
-
try {
|
|
1407
|
-
while (true) {
|
|
1408
|
-
const { done, value } = await reader.read();
|
|
1409
|
-
|
|
1410
|
-
if (done) {
|
|
1411
|
-
break;
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1415
|
-
const lines = chunk.split("\n");
|
|
1416
|
-
|
|
1417
|
-
for (const line of lines) {
|
|
1418
|
-
if (line.startsWith("data: ")) {
|
|
1419
|
-
try {
|
|
1420
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1421
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1422
|
-
|
|
1423
|
-
console.log(
|
|
1424
|
-
`[HTTP Client] Rename file stream event: ${event.type}`
|
|
1425
|
-
);
|
|
1426
|
-
this.options.onStreamEvent?.(event);
|
|
1427
|
-
|
|
1428
|
-
switch (event.type) {
|
|
1429
|
-
case "command_start":
|
|
1430
|
-
console.log(
|
|
1431
|
-
`[HTTP Client] Rename file started: ${event.oldPath} -> ${event.newPath}`
|
|
1432
|
-
);
|
|
1433
|
-
this.options.onCommandStart?.("rename", [oldPath, newPath]);
|
|
1434
|
-
break;
|
|
1435
|
-
|
|
1436
|
-
case "command_complete":
|
|
1437
|
-
console.log(
|
|
1438
|
-
`[HTTP Client] Rename file completed: ${event.oldPath} -> ${event.newPath}, Success: ${event.success}`
|
|
1439
|
-
);
|
|
1440
|
-
this.options.onCommandComplete?.(
|
|
1441
|
-
event.success!,
|
|
1442
|
-
0,
|
|
1443
|
-
"",
|
|
1444
|
-
"",
|
|
1445
|
-
"rename",
|
|
1446
|
-
[oldPath, newPath]
|
|
1447
|
-
);
|
|
1448
|
-
break;
|
|
1449
|
-
|
|
1450
|
-
case "error":
|
|
1451
|
-
console.error(
|
|
1452
|
-
`[HTTP Client] Rename file error: ${event.error}`
|
|
1453
|
-
);
|
|
1454
|
-
this.options.onError?.(event.error!, "rename", [
|
|
1455
|
-
oldPath,
|
|
1456
|
-
newPath,
|
|
1457
|
-
]);
|
|
1458
|
-
break;
|
|
1459
|
-
}
|
|
1460
|
-
} catch (parseError) {
|
|
1461
|
-
console.warn(
|
|
1462
|
-
"[HTTP Client] Failed to parse rename file stream event:",
|
|
1463
|
-
parseError
|
|
1464
|
-
);
|
|
1465
|
-
}
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
} finally {
|
|
1470
|
-
reader.releaseLock();
|
|
1471
|
-
}
|
|
743
|
+
return data;
|
|
1472
744
|
} catch (error) {
|
|
1473
|
-
console.error("[HTTP Client] Error
|
|
1474
|
-
this.options.onError?.(
|
|
1475
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1476
|
-
"rename",
|
|
1477
|
-
[oldPath, newPath]
|
|
1478
|
-
);
|
|
745
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
1479
746
|
throw error;
|
|
1480
747
|
}
|
|
1481
748
|
}
|
|
1482
749
|
|
|
1483
|
-
async
|
|
1484
|
-
sourcePath: string,
|
|
1485
|
-
destinationPath: string,
|
|
1486
|
-
sessionId?: string
|
|
1487
|
-
): Promise<MoveFileResponse> {
|
|
750
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
1488
751
|
try {
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
const response = await this.doFetch(`/api/move`, {
|
|
1492
|
-
body: JSON.stringify({
|
|
1493
|
-
destinationPath,
|
|
1494
|
-
sessionId: targetSessionId,
|
|
1495
|
-
sourcePath,
|
|
1496
|
-
}),
|
|
752
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
1497
753
|
headers: {
|
|
1498
754
|
"Content-Type": "application/json",
|
|
1499
755
|
},
|
|
1500
|
-
method: "
|
|
756
|
+
method: "GET",
|
|
1501
757
|
});
|
|
1502
758
|
|
|
1503
759
|
if (!response.ok) {
|
|
@@ -1509,32 +765,59 @@ export class HttpClient {
|
|
|
1509
765
|
);
|
|
1510
766
|
}
|
|
1511
767
|
|
|
1512
|
-
const data:
|
|
1513
|
-
console.log(
|
|
1514
|
-
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
1515
|
-
);
|
|
768
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
769
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
1516
770
|
|
|
1517
771
|
return data;
|
|
1518
772
|
} catch (error) {
|
|
1519
|
-
console.error("[HTTP Client] Error
|
|
773
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
1520
774
|
throw error;
|
|
1521
775
|
}
|
|
1522
776
|
}
|
|
1523
777
|
|
|
1524
|
-
async
|
|
1525
|
-
sourcePath: string,
|
|
1526
|
-
destinationPath: string,
|
|
1527
|
-
sessionId?: string
|
|
1528
|
-
): Promise<void> {
|
|
778
|
+
async ping(): Promise<string> {
|
|
1529
779
|
try {
|
|
1530
|
-
const
|
|
780
|
+
const response = await this.doFetch(`/api/ping`, {
|
|
781
|
+
headers: {
|
|
782
|
+
"Content-Type": "application/json",
|
|
783
|
+
},
|
|
784
|
+
method: "GET",
|
|
785
|
+
});
|
|
1531
786
|
|
|
1532
|
-
|
|
787
|
+
if (!response.ok) {
|
|
788
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const data: PingResponse = await response.json();
|
|
792
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
793
|
+
return data.timestamp;
|
|
794
|
+
} catch (error) {
|
|
795
|
+
console.error("[HTTP Client] Error pinging server:", error);
|
|
796
|
+
throw error;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
// Process management methods
|
|
802
|
+
async startProcess(
|
|
803
|
+
command: string,
|
|
804
|
+
sessionId: string,
|
|
805
|
+
options?: {
|
|
806
|
+
processId?: string;
|
|
807
|
+
timeout?: number;
|
|
808
|
+
env?: Record<string, string>;
|
|
809
|
+
cwd?: string;
|
|
810
|
+
encoding?: string;
|
|
811
|
+
autoCleanup?: boolean;
|
|
812
|
+
}
|
|
813
|
+
): Promise<StartProcessResponse> {
|
|
814
|
+
try {
|
|
815
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1533
816
|
body: JSON.stringify({
|
|
1534
|
-
|
|
1535
|
-
sessionId
|
|
1536
|
-
|
|
1537
|
-
}),
|
|
817
|
+
command,
|
|
818
|
+
sessionId,
|
|
819
|
+
options,
|
|
820
|
+
} as StartProcessRequest),
|
|
1538
821
|
headers: {
|
|
1539
822
|
"Content-Type": "application/json",
|
|
1540
823
|
},
|
|
@@ -1550,96 +833,24 @@ export class HttpClient {
|
|
|
1550
833
|
);
|
|
1551
834
|
}
|
|
1552
835
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
836
|
+
const data: StartProcessResponse = await response.json();
|
|
837
|
+
console.log(
|
|
838
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
839
|
+
);
|
|
1556
840
|
|
|
1557
|
-
|
|
1558
|
-
const decoder = new TextDecoder();
|
|
1559
|
-
|
|
1560
|
-
try {
|
|
1561
|
-
while (true) {
|
|
1562
|
-
const { done, value } = await reader.read();
|
|
1563
|
-
|
|
1564
|
-
if (done) {
|
|
1565
|
-
break;
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1569
|
-
const lines = chunk.split("\n");
|
|
1570
|
-
|
|
1571
|
-
for (const line of lines) {
|
|
1572
|
-
if (line.startsWith("data: ")) {
|
|
1573
|
-
try {
|
|
1574
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1575
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1576
|
-
|
|
1577
|
-
console.log(
|
|
1578
|
-
`[HTTP Client] Move file stream event: ${event.type}`
|
|
1579
|
-
);
|
|
1580
|
-
this.options.onStreamEvent?.(event);
|
|
1581
|
-
|
|
1582
|
-
switch (event.type) {
|
|
1583
|
-
case "command_start":
|
|
1584
|
-
console.log(
|
|
1585
|
-
`[HTTP Client] Move file started: ${event.sourcePath} -> ${event.destinationPath}`
|
|
1586
|
-
);
|
|
1587
|
-
this.options.onCommandStart?.("move", [
|
|
1588
|
-
sourcePath,
|
|
1589
|
-
destinationPath,
|
|
1590
|
-
]);
|
|
1591
|
-
break;
|
|
1592
|
-
|
|
1593
|
-
case "command_complete":
|
|
1594
|
-
console.log(
|
|
1595
|
-
`[HTTP Client] Move file completed: ${event.sourcePath} -> ${event.destinationPath}, Success: ${event.success}`
|
|
1596
|
-
);
|
|
1597
|
-
this.options.onCommandComplete?.(
|
|
1598
|
-
event.success!,
|
|
1599
|
-
0,
|
|
1600
|
-
"",
|
|
1601
|
-
"",
|
|
1602
|
-
"move",
|
|
1603
|
-
[sourcePath, destinationPath]
|
|
1604
|
-
);
|
|
1605
|
-
break;
|
|
1606
|
-
|
|
1607
|
-
case "error":
|
|
1608
|
-
console.error(
|
|
1609
|
-
`[HTTP Client] Move file error: ${event.error}`
|
|
1610
|
-
);
|
|
1611
|
-
this.options.onError?.(event.error!, "move", [
|
|
1612
|
-
sourcePath,
|
|
1613
|
-
destinationPath,
|
|
1614
|
-
]);
|
|
1615
|
-
break;
|
|
1616
|
-
}
|
|
1617
|
-
} catch (parseError) {
|
|
1618
|
-
console.warn(
|
|
1619
|
-
"[HTTP Client] Failed to parse move file stream event:",
|
|
1620
|
-
parseError
|
|
1621
|
-
);
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
} finally {
|
|
1627
|
-
reader.releaseLock();
|
|
1628
|
-
}
|
|
841
|
+
return data;
|
|
1629
842
|
} catch (error) {
|
|
1630
|
-
console.error("[HTTP Client] Error
|
|
1631
|
-
this.options.onError?.(
|
|
1632
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1633
|
-
"move",
|
|
1634
|
-
[sourcePath, destinationPath]
|
|
1635
|
-
);
|
|
843
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1636
844
|
throw error;
|
|
1637
845
|
}
|
|
1638
846
|
}
|
|
1639
847
|
|
|
1640
|
-
async
|
|
848
|
+
async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
|
|
1641
849
|
try {
|
|
1642
|
-
const
|
|
850
|
+
const url = sessionId
|
|
851
|
+
? `/api/process/list?session=${encodeURIComponent(sessionId)}`
|
|
852
|
+
: "/api/process/list";
|
|
853
|
+
const response = await this.doFetch(url, {
|
|
1643
854
|
headers: {
|
|
1644
855
|
"Content-Type": "application/json",
|
|
1645
856
|
},
|
|
@@ -1647,21 +858,27 @@ export class HttpClient {
|
|
|
1647
858
|
});
|
|
1648
859
|
|
|
1649
860
|
if (!response.ok) {
|
|
1650
|
-
|
|
861
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
862
|
+
error?: string;
|
|
863
|
+
};
|
|
864
|
+
throw new Error(
|
|
865
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
866
|
+
);
|
|
1651
867
|
}
|
|
1652
868
|
|
|
1653
|
-
const data:
|
|
1654
|
-
console.log(`[HTTP Client]
|
|
1655
|
-
|
|
869
|
+
const data: ListProcessesResponse = await response.json();
|
|
870
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
871
|
+
|
|
872
|
+
return data;
|
|
1656
873
|
} catch (error) {
|
|
1657
|
-
console.error("[HTTP Client] Error
|
|
874
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1658
875
|
throw error;
|
|
1659
876
|
}
|
|
1660
877
|
}
|
|
1661
878
|
|
|
1662
|
-
async
|
|
879
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1663
880
|
try {
|
|
1664
|
-
const response = await
|
|
881
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1665
882
|
headers: {
|
|
1666
883
|
"Content-Type": "application/json",
|
|
1667
884
|
},
|
|
@@ -1669,292 +886,163 @@ export class HttpClient {
|
|
|
1669
886
|
});
|
|
1670
887
|
|
|
1671
888
|
if (!response.ok) {
|
|
1672
|
-
|
|
889
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
890
|
+
error?: string;
|
|
891
|
+
};
|
|
892
|
+
throw new Error(
|
|
893
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
894
|
+
);
|
|
1673
895
|
}
|
|
1674
896
|
|
|
1675
|
-
const data:
|
|
897
|
+
const data: GetProcessResponse = await response.json();
|
|
1676
898
|
console.log(
|
|
1677
|
-
`[HTTP Client]
|
|
899
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
900
|
+
data.process?.status || "not found"
|
|
901
|
+
}`
|
|
1678
902
|
);
|
|
1679
|
-
|
|
903
|
+
|
|
904
|
+
return data;
|
|
1680
905
|
} catch (error) {
|
|
1681
|
-
console.error("[HTTP Client] Error getting
|
|
906
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1682
907
|
throw error;
|
|
1683
908
|
}
|
|
1684
909
|
}
|
|
1685
910
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
// Example usage and utility functions
|
|
1700
|
-
export function createClient(options?: HttpClientOptions): HttpClient {
|
|
1701
|
-
return new HttpClient(options);
|
|
1702
|
-
}
|
|
911
|
+
async killProcess(
|
|
912
|
+
processId: string
|
|
913
|
+
): Promise<{ success: boolean; message: string }> {
|
|
914
|
+
try {
|
|
915
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
916
|
+
headers: {
|
|
917
|
+
"Content-Type": "application/json",
|
|
918
|
+
},
|
|
919
|
+
method: "DELETE",
|
|
920
|
+
});
|
|
1703
921
|
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
try {
|
|
1714
|
-
return await client.execute(command, args);
|
|
1715
|
-
} finally {
|
|
1716
|
-
client.clearSession();
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
922
|
+
if (!response.ok) {
|
|
923
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
924
|
+
error?: string;
|
|
925
|
+
};
|
|
926
|
+
throw new Error(
|
|
927
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
928
|
+
);
|
|
929
|
+
}
|
|
1719
930
|
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
): Promise<void> {
|
|
1726
|
-
const client = createClient(options);
|
|
1727
|
-
await client.createSession();
|
|
1728
|
-
|
|
1729
|
-
try {
|
|
1730
|
-
await client.executeStream(command, args);
|
|
1731
|
-
} finally {
|
|
1732
|
-
client.clearSession();
|
|
1733
|
-
}
|
|
1734
|
-
}
|
|
931
|
+
const data = (await response.json()) as {
|
|
932
|
+
success: boolean;
|
|
933
|
+
message: string;
|
|
934
|
+
};
|
|
935
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1735
936
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
options?: HttpClientOptions
|
|
1742
|
-
): Promise<GitCheckoutResponse> {
|
|
1743
|
-
const client = createClient(options);
|
|
1744
|
-
await client.createSession();
|
|
1745
|
-
|
|
1746
|
-
try {
|
|
1747
|
-
return await client.gitCheckout(repoUrl, branch, targetDir);
|
|
1748
|
-
} finally {
|
|
1749
|
-
client.clearSession();
|
|
937
|
+
return data;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
940
|
+
throw error;
|
|
941
|
+
}
|
|
1750
942
|
}
|
|
1751
|
-
}
|
|
1752
943
|
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
}
|
|
944
|
+
async killAllProcesses(sessionId?: string): Promise<{
|
|
945
|
+
success: boolean;
|
|
946
|
+
killedCount: number;
|
|
947
|
+
message: string;
|
|
948
|
+
}> {
|
|
949
|
+
try {
|
|
950
|
+
const url = sessionId
|
|
951
|
+
? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
|
|
952
|
+
: "/api/process/kill-all";
|
|
953
|
+
const response = await this.doFetch(url, {
|
|
954
|
+
headers: {
|
|
955
|
+
"Content-Type": "application/json",
|
|
956
|
+
},
|
|
957
|
+
method: "DELETE",
|
|
958
|
+
});
|
|
1768
959
|
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
)
|
|
1776
|
-
|
|
1777
|
-
await client.createSession();
|
|
1778
|
-
|
|
1779
|
-
try {
|
|
1780
|
-
await client.gitCheckoutStream(repoUrl, branch, targetDir);
|
|
1781
|
-
} finally {
|
|
1782
|
-
client.clearSession();
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
960
|
+
if (!response.ok) {
|
|
961
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
962
|
+
error?: string;
|
|
963
|
+
};
|
|
964
|
+
throw new Error(
|
|
965
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
966
|
+
);
|
|
967
|
+
}
|
|
1785
968
|
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
const client = createClient(options);
|
|
1793
|
-
await client.createSession();
|
|
1794
|
-
|
|
1795
|
-
try {
|
|
1796
|
-
await client.mkdirStream(path, recursive);
|
|
1797
|
-
} finally {
|
|
1798
|
-
client.clearSession();
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
969
|
+
const data = (await response.json()) as {
|
|
970
|
+
success: boolean;
|
|
971
|
+
killedCount: number;
|
|
972
|
+
message: string;
|
|
973
|
+
};
|
|
974
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
1801
975
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
options?: HttpClientOptions
|
|
1808
|
-
): Promise<WriteFileResponse> {
|
|
1809
|
-
const client = createClient(options);
|
|
1810
|
-
await client.createSession();
|
|
1811
|
-
|
|
1812
|
-
try {
|
|
1813
|
-
return await client.writeFile(path, content, encoding);
|
|
1814
|
-
} finally {
|
|
1815
|
-
client.clearSession();
|
|
976
|
+
return data;
|
|
977
|
+
} catch (error) {
|
|
978
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
1816
981
|
}
|
|
1817
|
-
}
|
|
1818
982
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
await client.createSession();
|
|
1828
|
-
|
|
1829
|
-
try {
|
|
1830
|
-
await client.writeFileStream(path, content, encoding);
|
|
1831
|
-
} finally {
|
|
1832
|
-
client.clearSession();
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
983
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
984
|
+
try {
|
|
985
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
986
|
+
headers: {
|
|
987
|
+
"Content-Type": "application/json",
|
|
988
|
+
},
|
|
989
|
+
method: "GET",
|
|
990
|
+
});
|
|
1835
991
|
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
try {
|
|
1846
|
-
return await client.readFile(path, encoding);
|
|
1847
|
-
} finally {
|
|
1848
|
-
client.clearSession();
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
992
|
+
if (!response.ok) {
|
|
993
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
994
|
+
error?: string;
|
|
995
|
+
};
|
|
996
|
+
throw new Error(
|
|
997
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1851
1000
|
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
path: string,
|
|
1855
|
-
encoding: string = "utf-8",
|
|
1856
|
-
options?: HttpClientOptions
|
|
1857
|
-
): Promise<void> {
|
|
1858
|
-
const client = createClient(options);
|
|
1859
|
-
await client.createSession();
|
|
1860
|
-
|
|
1861
|
-
try {
|
|
1862
|
-
await client.readFileStream(path, encoding);
|
|
1863
|
-
} finally {
|
|
1864
|
-
client.clearSession();
|
|
1865
|
-
}
|
|
1866
|
-
}
|
|
1001
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
1002
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
1867
1003
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
const client = createClient(options);
|
|
1874
|
-
await client.createSession();
|
|
1875
|
-
|
|
1876
|
-
try {
|
|
1877
|
-
return await client.deleteFile(path);
|
|
1878
|
-
} finally {
|
|
1879
|
-
client.clearSession();
|
|
1004
|
+
return data;
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1880
1009
|
}
|
|
1881
|
-
}
|
|
1882
1010
|
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
}
|
|
1011
|
+
async streamProcessLogs(
|
|
1012
|
+
processId: string,
|
|
1013
|
+
options?: { signal?: AbortSignal }
|
|
1014
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
1015
|
+
try {
|
|
1016
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
1017
|
+
headers: {
|
|
1018
|
+
Accept: "text/event-stream",
|
|
1019
|
+
"Cache-Control": "no-cache",
|
|
1020
|
+
},
|
|
1021
|
+
method: "GET",
|
|
1022
|
+
signal: options?.signal,
|
|
1023
|
+
});
|
|
1897
1024
|
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
try {
|
|
1908
|
-
return await client.renameFile(oldPath, newPath);
|
|
1909
|
-
} finally {
|
|
1910
|
-
client.clearSession();
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
1025
|
+
if (!response.ok) {
|
|
1026
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1027
|
+
error?: string;
|
|
1028
|
+
};
|
|
1029
|
+
throw new Error(
|
|
1030
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1913
1033
|
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
newPath: string,
|
|
1918
|
-
options?: HttpClientOptions
|
|
1919
|
-
): Promise<void> {
|
|
1920
|
-
const client = createClient(options);
|
|
1921
|
-
await client.createSession();
|
|
1922
|
-
|
|
1923
|
-
try {
|
|
1924
|
-
await client.renameFileStream(oldPath, newPath);
|
|
1925
|
-
} finally {
|
|
1926
|
-
client.clearSession();
|
|
1927
|
-
}
|
|
1928
|
-
}
|
|
1034
|
+
if (!response.body) {
|
|
1035
|
+
throw new Error("No response body for streaming request");
|
|
1036
|
+
}
|
|
1929
1037
|
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
destinationPath: string,
|
|
1934
|
-
options?: HttpClientOptions
|
|
1935
|
-
): Promise<MoveFileResponse> {
|
|
1936
|
-
const client = createClient(options);
|
|
1937
|
-
await client.createSession();
|
|
1938
|
-
|
|
1939
|
-
try {
|
|
1940
|
-
return await client.moveFile(sourcePath, destinationPath);
|
|
1941
|
-
} finally {
|
|
1942
|
-
client.clearSession();
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1038
|
+
console.log(
|
|
1039
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1040
|
+
);
|
|
1945
1041
|
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
): Promise<void> {
|
|
1952
|
-
const client = createClient(options);
|
|
1953
|
-
await client.createSession();
|
|
1954
|
-
|
|
1955
|
-
try {
|
|
1956
|
-
await client.moveFileStream(sourcePath, destinationPath);
|
|
1957
|
-
} finally {
|
|
1958
|
-
client.clearSession();
|
|
1042
|
+
return response.body;
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1045
|
+
throw error;
|
|
1046
|
+
}
|
|
1959
1047
|
}
|
|
1960
1048
|
}
|