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