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