@cloudflare/sandbox 0.0.0-6704961 → 0.0.0-68d9bc5
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 +62 -0
- package/Dockerfile +40 -31
- package/README.md +743 -24
- package/container_src/bun.lock +122 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/handler/exec.ts +340 -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 +377 -2917
- package/container_src/jupyter-server.ts +579 -0
- package/container_src/jupyter-service.ts +448 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/startup.sh +83 -0
- package/container_src/types.ts +108 -0
- package/package.json +5 -9
- package/src/client.ts +254 -1322
- package/src/errors.ts +218 -0
- package/src/index.ts +47 -7
- package/src/interpreter-types.ts +383 -0
- package/src/interpreter.ts +150 -0
- package/src/jupyter-client.ts +349 -0
- package/src/request-handler.ts +69 -20
- package/src/sandbox.ts +616 -96
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +401 -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,23 +15,6 @@ export interface ExecuteResponse {
|
|
|
13
15
|
stderr: string;
|
|
14
16
|
exitCode: number;
|
|
15
17
|
command: string;
|
|
16
|
-
args: string[];
|
|
17
|
-
timestamp: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
interface SessionResponse {
|
|
21
|
-
sessionId: string;
|
|
22
|
-
message: string;
|
|
23
|
-
timestamp: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
interface SessionListResponse {
|
|
27
|
-
sessions: Array<{
|
|
28
|
-
sessionId: string;
|
|
29
|
-
hasActiveProcess: boolean;
|
|
30
|
-
createdAt: string;
|
|
31
|
-
}>;
|
|
32
|
-
count: number;
|
|
33
18
|
timestamp: string;
|
|
34
19
|
}
|
|
35
20
|
|
|
@@ -176,32 +161,11 @@ interface PingResponse {
|
|
|
176
161
|
timestamp: string;
|
|
177
162
|
}
|
|
178
163
|
|
|
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
164
|
interface HttpClientOptions {
|
|
201
165
|
stub?: Sandbox;
|
|
202
166
|
baseUrl?: string;
|
|
203
167
|
port?: number;
|
|
204
|
-
onCommandStart?: (command: string
|
|
168
|
+
onCommandStart?: (command: string) => void;
|
|
205
169
|
onOutput?: (
|
|
206
170
|
stream: "stdout" | "stderr",
|
|
207
171
|
data: string,
|
|
@@ -212,11 +176,9 @@ interface HttpClientOptions {
|
|
|
212
176
|
exitCode: number,
|
|
213
177
|
stdout: string,
|
|
214
178
|
stderr: string,
|
|
215
|
-
command: string
|
|
216
|
-
args: string[]
|
|
179
|
+
command: string
|
|
217
180
|
) => void;
|
|
218
|
-
onError?: (error: string, command?: string
|
|
219
|
-
onStreamEvent?: (event: StreamEvent) => void;
|
|
181
|
+
onError?: (error: string, command?: string) => void;
|
|
220
182
|
}
|
|
221
183
|
|
|
222
184
|
export class HttpClient {
|
|
@@ -231,7 +193,7 @@ export class HttpClient {
|
|
|
231
193
|
this.baseUrl = this.options.baseUrl!;
|
|
232
194
|
}
|
|
233
195
|
|
|
234
|
-
|
|
196
|
+
protected async doFetch(
|
|
235
197
|
path: string,
|
|
236
198
|
options?: RequestInit
|
|
237
199
|
): Promise<Response> {
|
|
@@ -271,119 +233,22 @@ export class HttpClient {
|
|
|
271
233
|
throw error;
|
|
272
234
|
}
|
|
273
235
|
}
|
|
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
236
|
|
|
371
237
|
async execute(
|
|
372
238
|
command: string,
|
|
373
|
-
|
|
374
|
-
sessionId?: string,
|
|
375
|
-
background: boolean = false,
|
|
239
|
+
options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
|
|
376
240
|
): Promise<ExecuteResponse> {
|
|
377
241
|
try {
|
|
378
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
242
|
+
const targetSessionId = options.sessionId || this.sessionId;
|
|
243
|
+
const executeRequest = {
|
|
244
|
+
command,
|
|
245
|
+
sessionId: targetSessionId,
|
|
246
|
+
cwd: options.cwd,
|
|
247
|
+
env: options.env,
|
|
248
|
+
} satisfies ExecuteRequest;
|
|
379
249
|
|
|
380
250
|
const response = await this.doFetch(`/api/execute`, {
|
|
381
|
-
body: JSON.stringify(
|
|
382
|
-
args,
|
|
383
|
-
command,
|
|
384
|
-
background,
|
|
385
|
-
sessionId: targetSessionId,
|
|
386
|
-
} as ExecuteRequest),
|
|
251
|
+
body: JSON.stringify(executeRequest),
|
|
387
252
|
headers: {
|
|
388
253
|
"Content-Type": "application/json",
|
|
389
254
|
},
|
|
@@ -410,8 +275,7 @@ export class HttpClient {
|
|
|
410
275
|
data.exitCode,
|
|
411
276
|
data.stdout,
|
|
412
277
|
data.stderr,
|
|
413
|
-
data.command
|
|
414
|
-
data.args
|
|
278
|
+
data.command
|
|
415
279
|
);
|
|
416
280
|
|
|
417
281
|
return data;
|
|
@@ -419,31 +283,27 @@ export class HttpClient {
|
|
|
419
283
|
console.error("[HTTP Client] Error executing command:", error);
|
|
420
284
|
this.options.onError?.(
|
|
421
285
|
error instanceof Error ? error.message : "Unknown error",
|
|
422
|
-
command
|
|
423
|
-
args
|
|
286
|
+
command
|
|
424
287
|
);
|
|
425
288
|
throw error;
|
|
426
289
|
}
|
|
427
290
|
}
|
|
428
291
|
|
|
429
|
-
async
|
|
292
|
+
async executeCommandStream(
|
|
430
293
|
command: string,
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
background: boolean = false
|
|
434
|
-
): Promise<void> {
|
|
294
|
+
sessionId?: string
|
|
295
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
435
296
|
try {
|
|
436
297
|
const targetSessionId = sessionId || this.sessionId;
|
|
437
298
|
|
|
438
299
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
439
300
|
body: JSON.stringify({
|
|
440
|
-
args,
|
|
441
301
|
command,
|
|
442
|
-
background,
|
|
443
302
|
sessionId: targetSessionId,
|
|
444
303
|
}),
|
|
445
304
|
headers: {
|
|
446
305
|
"Content-Type": "application/json",
|
|
306
|
+
Accept: "text/event-stream",
|
|
447
307
|
},
|
|
448
308
|
method: "POST",
|
|
449
309
|
});
|
|
@@ -461,94 +321,11 @@ export class HttpClient {
|
|
|
461
321
|
throw new Error("No response body for streaming request");
|
|
462
322
|
}
|
|
463
323
|
|
|
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
|
-
}
|
|
324
|
+
console.log(`[HTTP Client] Started command stream: ${command}`);
|
|
325
|
+
|
|
326
|
+
return response.body;
|
|
545
327
|
} catch (error) {
|
|
546
|
-
console.error("[HTTP Client] Error in
|
|
547
|
-
this.options.onError?.(
|
|
548
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
549
|
-
command,
|
|
550
|
-
args
|
|
551
|
-
);
|
|
328
|
+
console.error("[HTTP Client] Error in command stream:", error);
|
|
552
329
|
throw error;
|
|
553
330
|
}
|
|
554
331
|
}
|
|
@@ -596,135 +373,6 @@ export class HttpClient {
|
|
|
596
373
|
}
|
|
597
374
|
}
|
|
598
375
|
|
|
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
|
-
|
|
728
376
|
async mkdir(
|
|
729
377
|
path: string,
|
|
730
378
|
recursive: boolean = false,
|
|
@@ -766,20 +414,22 @@ export class HttpClient {
|
|
|
766
414
|
}
|
|
767
415
|
}
|
|
768
416
|
|
|
769
|
-
async
|
|
417
|
+
async writeFile(
|
|
770
418
|
path: string,
|
|
771
|
-
|
|
419
|
+
content: string,
|
|
420
|
+
encoding: string = "utf-8",
|
|
772
421
|
sessionId?: string
|
|
773
|
-
): Promise<
|
|
422
|
+
): Promise<WriteFileResponse> {
|
|
774
423
|
try {
|
|
775
424
|
const targetSessionId = sessionId || this.sessionId;
|
|
776
425
|
|
|
777
|
-
const response = await this.doFetch(`/api/
|
|
426
|
+
const response = await this.doFetch(`/api/write`, {
|
|
778
427
|
body: JSON.stringify({
|
|
428
|
+
content,
|
|
429
|
+
encoding,
|
|
779
430
|
path,
|
|
780
|
-
recursive,
|
|
781
431
|
sessionId: targetSessionId,
|
|
782
|
-
} as
|
|
432
|
+
} as WriteFileRequest),
|
|
783
433
|
headers: {
|
|
784
434
|
"Content-Type": "application/json",
|
|
785
435
|
},
|
|
@@ -795,116 +445,32 @@ export class HttpClient {
|
|
|
795
445
|
);
|
|
796
446
|
}
|
|
797
447
|
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
448
|
+
const data: WriteFileResponse = await response.json();
|
|
449
|
+
console.log(
|
|
450
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}`
|
|
451
|
+
);
|
|
801
452
|
|
|
802
|
-
|
|
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
|
-
}
|
|
453
|
+
return data;
|
|
881
454
|
} catch (error) {
|
|
882
|
-
console.error("[HTTP Client] Error
|
|
883
|
-
this.options.onError?.(
|
|
884
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
885
|
-
"mkdir",
|
|
886
|
-
recursive ? ["-p", path] : [path]
|
|
887
|
-
);
|
|
455
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
888
456
|
throw error;
|
|
889
457
|
}
|
|
890
458
|
}
|
|
891
459
|
|
|
892
|
-
async
|
|
460
|
+
async readFile(
|
|
893
461
|
path: string,
|
|
894
|
-
content: string,
|
|
895
462
|
encoding: string = "utf-8",
|
|
896
463
|
sessionId?: string
|
|
897
|
-
): Promise<
|
|
464
|
+
): Promise<ReadFileResponse> {
|
|
898
465
|
try {
|
|
899
466
|
const targetSessionId = sessionId || this.sessionId;
|
|
900
467
|
|
|
901
|
-
const response = await this.doFetch(`/api/
|
|
468
|
+
const response = await this.doFetch(`/api/read`, {
|
|
902
469
|
body: JSON.stringify({
|
|
903
|
-
content,
|
|
904
470
|
encoding,
|
|
905
471
|
path,
|
|
906
472
|
sessionId: targetSessionId,
|
|
907
|
-
} as
|
|
473
|
+
} as ReadFileRequest),
|
|
908
474
|
headers: {
|
|
909
475
|
"Content-Type": "application/json",
|
|
910
476
|
},
|
|
@@ -920,34 +486,30 @@ export class HttpClient {
|
|
|
920
486
|
);
|
|
921
487
|
}
|
|
922
488
|
|
|
923
|
-
const data:
|
|
489
|
+
const data: ReadFileResponse = await response.json();
|
|
924
490
|
console.log(
|
|
925
|
-
`[HTTP Client] File
|
|
491
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
|
|
926
492
|
);
|
|
927
493
|
|
|
928
494
|
return data;
|
|
929
495
|
} catch (error) {
|
|
930
|
-
console.error("[HTTP Client] Error
|
|
496
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
931
497
|
throw error;
|
|
932
498
|
}
|
|
933
499
|
}
|
|
934
500
|
|
|
935
|
-
async
|
|
501
|
+
async deleteFile(
|
|
936
502
|
path: string,
|
|
937
|
-
content: string,
|
|
938
|
-
encoding: string = "utf-8",
|
|
939
503
|
sessionId?: string
|
|
940
|
-
): Promise<
|
|
504
|
+
): Promise<DeleteFileResponse> {
|
|
941
505
|
try {
|
|
942
506
|
const targetSessionId = sessionId || this.sessionId;
|
|
943
507
|
|
|
944
|
-
const response = await this.doFetch(`/api/
|
|
508
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
945
509
|
body: JSON.stringify({
|
|
946
|
-
content,
|
|
947
|
-
encoding,
|
|
948
510
|
path,
|
|
949
511
|
sessionId: targetSessionId,
|
|
950
|
-
} as
|
|
512
|
+
} as DeleteFileRequest),
|
|
951
513
|
headers: {
|
|
952
514
|
"Content-Type": "application/json",
|
|
953
515
|
},
|
|
@@ -963,114 +525,32 @@ export class HttpClient {
|
|
|
963
525
|
);
|
|
964
526
|
}
|
|
965
527
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
528
|
+
const data: DeleteFileResponse = await response.json();
|
|
529
|
+
console.log(
|
|
530
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
531
|
+
);
|
|
969
532
|
|
|
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
|
-
}
|
|
533
|
+
return data;
|
|
1049
534
|
} 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
|
-
);
|
|
535
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
1056
536
|
throw error;
|
|
1057
537
|
}
|
|
1058
538
|
}
|
|
1059
539
|
|
|
1060
|
-
async
|
|
1061
|
-
|
|
1062
|
-
|
|
540
|
+
async renameFile(
|
|
541
|
+
oldPath: string,
|
|
542
|
+
newPath: string,
|
|
1063
543
|
sessionId?: string
|
|
1064
|
-
): Promise<
|
|
544
|
+
): Promise<RenameFileResponse> {
|
|
1065
545
|
try {
|
|
1066
546
|
const targetSessionId = sessionId || this.sessionId;
|
|
1067
547
|
|
|
1068
|
-
const response = await this.doFetch(`/api/
|
|
548
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
1069
549
|
body: JSON.stringify({
|
|
1070
|
-
|
|
1071
|
-
|
|
550
|
+
newPath,
|
|
551
|
+
oldPath,
|
|
1072
552
|
sessionId: targetSessionId,
|
|
1073
|
-
} as
|
|
553
|
+
} as RenameFileRequest),
|
|
1074
554
|
headers: {
|
|
1075
555
|
"Content-Type": "application/json",
|
|
1076
556
|
},
|
|
@@ -1086,32 +566,32 @@ export class HttpClient {
|
|
|
1086
566
|
);
|
|
1087
567
|
}
|
|
1088
568
|
|
|
1089
|
-
const data:
|
|
569
|
+
const data: RenameFileResponse = await response.json();
|
|
1090
570
|
console.log(
|
|
1091
|
-
`[HTTP Client] File
|
|
571
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
1092
572
|
);
|
|
1093
573
|
|
|
1094
574
|
return data;
|
|
1095
575
|
} catch (error) {
|
|
1096
|
-
console.error("[HTTP Client] Error
|
|
576
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
1097
577
|
throw error;
|
|
1098
578
|
}
|
|
1099
579
|
}
|
|
1100
580
|
|
|
1101
|
-
async
|
|
1102
|
-
|
|
1103
|
-
|
|
581
|
+
async moveFile(
|
|
582
|
+
sourcePath: string,
|
|
583
|
+
destinationPath: string,
|
|
1104
584
|
sessionId?: string
|
|
1105
|
-
): Promise<
|
|
585
|
+
): Promise<MoveFileResponse> {
|
|
1106
586
|
try {
|
|
1107
587
|
const targetSessionId = sessionId || this.sessionId;
|
|
1108
588
|
|
|
1109
|
-
const response = await this.doFetch(`/api/
|
|
589
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1110
590
|
body: JSON.stringify({
|
|
1111
|
-
|
|
1112
|
-
path,
|
|
591
|
+
destinationPath,
|
|
1113
592
|
sessionId: targetSessionId,
|
|
1114
|
-
|
|
593
|
+
sourcePath,
|
|
594
|
+
} as MoveFileRequest),
|
|
1115
595
|
headers: {
|
|
1116
596
|
"Content-Type": "application/json",
|
|
1117
597
|
},
|
|
@@ -1127,104 +607,25 @@ export class HttpClient {
|
|
|
1127
607
|
);
|
|
1128
608
|
}
|
|
1129
609
|
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
610
|
+
const data: MoveFileResponse = await response.json();
|
|
611
|
+
console.log(
|
|
612
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
613
|
+
);
|
|
1133
614
|
|
|
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
|
-
}
|
|
615
|
+
return data;
|
|
1205
616
|
} 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
|
-
);
|
|
617
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1212
618
|
throw error;
|
|
1213
619
|
}
|
|
1214
620
|
}
|
|
1215
621
|
|
|
1216
|
-
async
|
|
1217
|
-
path: string,
|
|
1218
|
-
sessionId?: string
|
|
1219
|
-
): Promise<DeleteFileResponse> {
|
|
622
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1220
623
|
try {
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
624
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1224
625
|
body: JSON.stringify({
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
}
|
|
626
|
+
port,
|
|
627
|
+
name,
|
|
628
|
+
}),
|
|
1228
629
|
headers: {
|
|
1229
630
|
"Content-Type": "application/json",
|
|
1230
631
|
},
|
|
@@ -1235,36 +636,36 @@ export class HttpClient {
|
|
|
1235
636
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1236
637
|
error?: string;
|
|
1237
638
|
};
|
|
639
|
+
console.log(errorData);
|
|
1238
640
|
throw new Error(
|
|
1239
641
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1240
642
|
);
|
|
1241
643
|
}
|
|
1242
644
|
|
|
1243
|
-
const data:
|
|
645
|
+
const data: ExposePortResponse = await response.json();
|
|
1244
646
|
console.log(
|
|
1245
|
-
`[HTTP Client]
|
|
647
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
648
|
+
name ? ` (${name})` : ""
|
|
649
|
+
}, Success: ${data.success}`
|
|
1246
650
|
);
|
|
1247
651
|
|
|
1248
652
|
return data;
|
|
1249
653
|
} catch (error) {
|
|
1250
|
-
console.error("[HTTP Client] Error
|
|
654
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1251
655
|
throw error;
|
|
1252
656
|
}
|
|
1253
657
|
}
|
|
1254
658
|
|
|
1255
|
-
async
|
|
659
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1256
660
|
try {
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1259
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
661
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1260
662
|
body: JSON.stringify({
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
} as DeleteFileRequest),
|
|
663
|
+
port,
|
|
664
|
+
}),
|
|
1264
665
|
headers: {
|
|
1265
666
|
"Content-Type": "application/json",
|
|
1266
667
|
},
|
|
1267
|
-
method: "
|
|
668
|
+
method: "DELETE",
|
|
1268
669
|
});
|
|
1269
670
|
|
|
1270
671
|
if (!response.ok) {
|
|
@@ -1276,105 +677,25 @@ export class HttpClient {
|
|
|
1276
677
|
);
|
|
1277
678
|
}
|
|
1278
679
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
680
|
+
const data: UnexposePortResponse = await response.json();
|
|
681
|
+
console.log(
|
|
682
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
683
|
+
);
|
|
1282
684
|
|
|
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
|
-
}
|
|
685
|
+
return data;
|
|
1349
686
|
} 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
|
-
);
|
|
687
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
1356
688
|
throw error;
|
|
1357
689
|
}
|
|
1358
690
|
}
|
|
1359
691
|
|
|
1360
|
-
async
|
|
1361
|
-
oldPath: string,
|
|
1362
|
-
newPath: string,
|
|
1363
|
-
sessionId?: string
|
|
1364
|
-
): Promise<RenameFileResponse> {
|
|
692
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
1365
693
|
try {
|
|
1366
|
-
const
|
|
1367
|
-
|
|
1368
|
-
const response = await this.doFetch(`/api/rename`, {
|
|
1369
|
-
body: JSON.stringify({
|
|
1370
|
-
newPath,
|
|
1371
|
-
oldPath,
|
|
1372
|
-
sessionId: targetSessionId,
|
|
1373
|
-
} as RenameFileRequest),
|
|
694
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
1374
695
|
headers: {
|
|
1375
696
|
"Content-Type": "application/json",
|
|
1376
697
|
},
|
|
1377
|
-
method: "
|
|
698
|
+
method: "GET",
|
|
1378
699
|
});
|
|
1379
700
|
|
|
1380
701
|
if (!response.ok) {
|
|
@@ -1386,145 +707,98 @@ export class HttpClient {
|
|
|
1386
707
|
);
|
|
1387
708
|
}
|
|
1388
709
|
|
|
1389
|
-
const data:
|
|
1390
|
-
console.log(
|
|
1391
|
-
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
1392
|
-
);
|
|
710
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
711
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
1393
712
|
|
|
1394
713
|
return data;
|
|
1395
714
|
} catch (error) {
|
|
1396
|
-
console.error("[HTTP Client] Error
|
|
715
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
1397
716
|
throw error;
|
|
1398
717
|
}
|
|
1399
718
|
}
|
|
1400
719
|
|
|
1401
|
-
async
|
|
1402
|
-
oldPath: string,
|
|
1403
|
-
newPath: string,
|
|
1404
|
-
sessionId?: string
|
|
1405
|
-
): Promise<void> {
|
|
720
|
+
async ping(): Promise<string> {
|
|
1406
721
|
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),
|
|
722
|
+
const response = await this.doFetch(`/api/ping`, {
|
|
1415
723
|
headers: {
|
|
1416
724
|
"Content-Type": "application/json",
|
|
1417
725
|
},
|
|
1418
|
-
method: "
|
|
726
|
+
method: "GET",
|
|
1419
727
|
});
|
|
1420
728
|
|
|
1421
729
|
if (!response.ok) {
|
|
1422
|
-
|
|
1423
|
-
error?: string;
|
|
1424
|
-
};
|
|
1425
|
-
throw new Error(
|
|
1426
|
-
errorData.error || `HTTP error! status: ${response.status}`
|
|
1427
|
-
);
|
|
730
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1428
731
|
}
|
|
1429
732
|
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
733
|
+
const data: PingResponse = await response.json();
|
|
734
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
735
|
+
return data.timestamp;
|
|
736
|
+
} catch (error) {
|
|
737
|
+
console.error("[HTTP Client] Error pinging server:", error);
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
1433
741
|
|
|
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();
|
|
742
|
+
async getCommands(): Promise<string[]> {
|
|
743
|
+
try {
|
|
744
|
+
const response = await fetch(`${this.baseUrl}/api/commands`, {
|
|
745
|
+
headers: {
|
|
746
|
+
"Content-Type": "application/json",
|
|
747
|
+
},
|
|
748
|
+
method: "GET",
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
if (!response.ok) {
|
|
752
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1502
753
|
}
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
"rename",
|
|
1508
|
-
[oldPath, newPath]
|
|
754
|
+
|
|
755
|
+
const data: CommandsResponse = await response.json();
|
|
756
|
+
console.log(
|
|
757
|
+
`[HTTP Client] Available commands: ${data.availableCommands.length}`
|
|
1509
758
|
);
|
|
759
|
+
return data.availableCommands;
|
|
760
|
+
} catch (error) {
|
|
761
|
+
console.error("[HTTP Client] Error getting commands:", error);
|
|
1510
762
|
throw error;
|
|
1511
763
|
}
|
|
1512
764
|
}
|
|
1513
765
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
):
|
|
766
|
+
getSessionId(): string | null {
|
|
767
|
+
return this.sessionId;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
setSessionId(sessionId: string): void {
|
|
771
|
+
this.sessionId = sessionId;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
clearSession(): void {
|
|
775
|
+
this.sessionId = null;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Process management methods
|
|
779
|
+
async startProcess(
|
|
780
|
+
command: string,
|
|
781
|
+
options?: {
|
|
782
|
+
processId?: string;
|
|
783
|
+
sessionId?: string;
|
|
784
|
+
timeout?: number;
|
|
785
|
+
env?: Record<string, string>;
|
|
786
|
+
cwd?: string;
|
|
787
|
+
encoding?: string;
|
|
788
|
+
autoCleanup?: boolean;
|
|
789
|
+
}
|
|
790
|
+
): Promise<StartProcessResponse> {
|
|
1519
791
|
try {
|
|
1520
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
792
|
+
const targetSessionId = options?.sessionId || this.sessionId;
|
|
1521
793
|
|
|
1522
|
-
const response = await this.doFetch(
|
|
794
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1523
795
|
body: JSON.stringify({
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
796
|
+
command,
|
|
797
|
+
options: {
|
|
798
|
+
...options,
|
|
799
|
+
sessionId: targetSessionId,
|
|
800
|
+
},
|
|
801
|
+
} as StartProcessRequest),
|
|
1528
802
|
headers: {
|
|
1529
803
|
"Content-Type": "application/json",
|
|
1530
804
|
},
|
|
@@ -1540,36 +814,25 @@ export class HttpClient {
|
|
|
1540
814
|
);
|
|
1541
815
|
}
|
|
1542
816
|
|
|
1543
|
-
const data:
|
|
817
|
+
const data: StartProcessResponse = await response.json();
|
|
1544
818
|
console.log(
|
|
1545
|
-
`[HTTP Client]
|
|
819
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
1546
820
|
);
|
|
1547
821
|
|
|
1548
822
|
return data;
|
|
1549
823
|
} catch (error) {
|
|
1550
|
-
console.error("[HTTP Client] Error
|
|
824
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1551
825
|
throw error;
|
|
1552
826
|
}
|
|
1553
827
|
}
|
|
1554
828
|
|
|
1555
|
-
async
|
|
1556
|
-
sourcePath: string,
|
|
1557
|
-
destinationPath: string,
|
|
1558
|
-
sessionId?: string
|
|
1559
|
-
): Promise<void> {
|
|
829
|
+
async listProcesses(): Promise<ListProcessesResponse> {
|
|
1560
830
|
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),
|
|
831
|
+
const response = await this.doFetch("/api/process/list", {
|
|
1569
832
|
headers: {
|
|
1570
833
|
"Content-Type": "application/json",
|
|
1571
834
|
},
|
|
1572
|
-
method: "
|
|
835
|
+
method: "GET",
|
|
1573
836
|
});
|
|
1574
837
|
|
|
1575
838
|
if (!response.ok) {
|
|
@@ -1581,134 +844,53 @@ export class HttpClient {
|
|
|
1581
844
|
);
|
|
1582
845
|
}
|
|
1583
846
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
}
|
|
847
|
+
const data: ListProcessesResponse = await response.json();
|
|
848
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
1587
849
|
|
|
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
|
-
}
|
|
850
|
+
return data;
|
|
1660
851
|
} 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
|
-
);
|
|
852
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1667
853
|
throw error;
|
|
1668
854
|
}
|
|
1669
855
|
}
|
|
1670
856
|
|
|
1671
|
-
async
|
|
857
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1672
858
|
try {
|
|
1673
|
-
const response = await this.doFetch(`/api/
|
|
1674
|
-
body: JSON.stringify({
|
|
1675
|
-
port,
|
|
1676
|
-
name,
|
|
1677
|
-
}),
|
|
859
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1678
860
|
headers: {
|
|
1679
861
|
"Content-Type": "application/json",
|
|
1680
862
|
},
|
|
1681
|
-
method: "
|
|
863
|
+
method: "GET",
|
|
1682
864
|
});
|
|
1683
865
|
|
|
1684
866
|
if (!response.ok) {
|
|
1685
867
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1686
868
|
error?: string;
|
|
1687
869
|
};
|
|
1688
|
-
console.log(errorData);
|
|
1689
870
|
throw new Error(
|
|
1690
871
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1691
872
|
);
|
|
1692
873
|
}
|
|
1693
874
|
|
|
1694
|
-
const data:
|
|
875
|
+
const data: GetProcessResponse = await response.json();
|
|
1695
876
|
console.log(
|
|
1696
|
-
`[HTTP Client]
|
|
877
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
878
|
+
data.process?.status || "not found"
|
|
879
|
+
}`
|
|
1697
880
|
);
|
|
1698
881
|
|
|
1699
882
|
return data;
|
|
1700
883
|
} catch (error) {
|
|
1701
|
-
console.error("[HTTP Client] Error
|
|
884
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1702
885
|
throw error;
|
|
1703
886
|
}
|
|
1704
887
|
}
|
|
1705
888
|
|
|
1706
|
-
async
|
|
889
|
+
async killProcess(
|
|
890
|
+
processId: string
|
|
891
|
+
): Promise<{ success: boolean; message: string }> {
|
|
1707
892
|
try {
|
|
1708
|
-
const response = await this.doFetch(`/api/
|
|
1709
|
-
body: JSON.stringify({
|
|
1710
|
-
port,
|
|
1711
|
-
}),
|
|
893
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1712
894
|
headers: {
|
|
1713
895
|
"Content-Type": "application/json",
|
|
1714
896
|
},
|
|
@@ -1724,25 +906,30 @@ export class HttpClient {
|
|
|
1724
906
|
);
|
|
1725
907
|
}
|
|
1726
908
|
|
|
1727
|
-
const data
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
909
|
+
const data = (await response.json()) as {
|
|
910
|
+
success: boolean;
|
|
911
|
+
message: string;
|
|
912
|
+
};
|
|
913
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1731
914
|
|
|
1732
915
|
return data;
|
|
1733
916
|
} catch (error) {
|
|
1734
|
-
console.error("[HTTP Client] Error
|
|
917
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
1735
918
|
throw error;
|
|
1736
919
|
}
|
|
1737
920
|
}
|
|
1738
921
|
|
|
1739
|
-
async
|
|
922
|
+
async killAllProcesses(): Promise<{
|
|
923
|
+
success: boolean;
|
|
924
|
+
killedCount: number;
|
|
925
|
+
message: string;
|
|
926
|
+
}> {
|
|
1740
927
|
try {
|
|
1741
|
-
const response = await this.doFetch(
|
|
928
|
+
const response = await this.doFetch("/api/process/kill-all", {
|
|
1742
929
|
headers: {
|
|
1743
930
|
"Content-Type": "application/json",
|
|
1744
931
|
},
|
|
1745
|
-
method: "
|
|
932
|
+
method: "DELETE",
|
|
1746
933
|
});
|
|
1747
934
|
|
|
1748
935
|
if (!response.ok) {
|
|
@@ -1754,21 +941,23 @@ export class HttpClient {
|
|
|
1754
941
|
);
|
|
1755
942
|
}
|
|
1756
943
|
|
|
1757
|
-
const data
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
944
|
+
const data = (await response.json()) as {
|
|
945
|
+
success: boolean;
|
|
946
|
+
killedCount: number;
|
|
947
|
+
message: string;
|
|
948
|
+
};
|
|
949
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
1761
950
|
|
|
1762
951
|
return data;
|
|
1763
952
|
} catch (error) {
|
|
1764
|
-
console.error("[HTTP Client] Error
|
|
953
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
1765
954
|
throw error;
|
|
1766
955
|
}
|
|
1767
956
|
}
|
|
1768
957
|
|
|
1769
|
-
async
|
|
958
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
1770
959
|
try {
|
|
1771
|
-
const response = await this.doFetch(`/api/
|
|
960
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
1772
961
|
headers: {
|
|
1773
962
|
"Content-Type": "application/json",
|
|
1774
963
|
},
|
|
@@ -1776,314 +965,57 @@ export class HttpClient {
|
|
|
1776
965
|
});
|
|
1777
966
|
|
|
1778
967
|
if (!response.ok) {
|
|
1779
|
-
|
|
968
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
969
|
+
error?: string;
|
|
970
|
+
};
|
|
971
|
+
throw new Error(
|
|
972
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
973
|
+
);
|
|
1780
974
|
}
|
|
1781
975
|
|
|
1782
|
-
const data:
|
|
1783
|
-
console.log(`[HTTP Client]
|
|
1784
|
-
|
|
976
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
977
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
978
|
+
|
|
979
|
+
return data;
|
|
1785
980
|
} catch (error) {
|
|
1786
|
-
console.error("[HTTP Client] Error
|
|
981
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1787
982
|
throw error;
|
|
1788
983
|
}
|
|
1789
984
|
}
|
|
1790
985
|
|
|
1791
|
-
async
|
|
986
|
+
async streamProcessLogs(
|
|
987
|
+
processId: string
|
|
988
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
1792
989
|
try {
|
|
1793
|
-
const response = await
|
|
990
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
1794
991
|
headers: {
|
|
1795
|
-
|
|
992
|
+
Accept: "text/event-stream",
|
|
993
|
+
"Cache-Control": "no-cache",
|
|
1796
994
|
},
|
|
1797
995
|
method: "GET",
|
|
1798
996
|
});
|
|
1799
997
|
|
|
1800
998
|
if (!response.ok) {
|
|
1801
|
-
|
|
999
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1000
|
+
error?: string;
|
|
1001
|
+
};
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (!response.body) {
|
|
1008
|
+
throw new Error("No response body for streaming request");
|
|
1802
1009
|
}
|
|
1803
1010
|
|
|
1804
|
-
const data: CommandsResponse = await response.json();
|
|
1805
1011
|
console.log(
|
|
1806
|
-
`[HTTP Client]
|
|
1012
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1807
1013
|
);
|
|
1808
|
-
|
|
1014
|
+
|
|
1015
|
+
return response.body;
|
|
1809
1016
|
} catch (error) {
|
|
1810
|
-
console.error("[HTTP Client] Error
|
|
1017
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1811
1018
|
throw error;
|
|
1812
1019
|
}
|
|
1813
1020
|
}
|
|
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
1021
|
}
|