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