@cloudflare/sandbox 0.0.0-2f85e95 → 0.0.0-3053156
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 +74 -0
- package/Dockerfile +101 -11
- 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 +395 -2645
- package/container_src/jupyter-server.ts +579 -0
- package/container_src/jupyter-service.ts +458 -0
- package/container_src/jupyter_config.py +48 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/startup.sh +84 -0
- package/container_src/types.ts +108 -0
- package/package.json +5 -9
- package/src/client.ts +331 -1270
- package/src/errors.ts +218 -0
- package/src/index.ts +54 -136
- 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 +144 -0
- package/src/sandbox.ts +778 -0
- 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,10 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExecuteRequest } from "../container_src/types";
|
|
2
2
|
import type { Sandbox } from "./index";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import type {
|
|
4
|
+
BaseExecOptions,
|
|
5
|
+
GetProcessLogsResponse,
|
|
6
|
+
GetProcessResponse,
|
|
7
|
+
ListProcessesResponse,
|
|
8
|
+
StartProcessRequest,
|
|
9
|
+
StartProcessResponse,
|
|
10
|
+
} from "./types";
|
|
8
11
|
|
|
9
12
|
export interface ExecuteResponse {
|
|
10
13
|
success: boolean;
|
|
@@ -12,23 +15,6 @@ export interface ExecuteResponse {
|
|
|
12
15
|
stderr: string;
|
|
13
16
|
exitCode: number;
|
|
14
17
|
command: string;
|
|
15
|
-
args: string[];
|
|
16
|
-
timestamp: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface SessionResponse {
|
|
20
|
-
sessionId: string;
|
|
21
|
-
message: string;
|
|
22
|
-
timestamp: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface SessionListResponse {
|
|
26
|
-
sessions: Array<{
|
|
27
|
-
sessionId: string;
|
|
28
|
-
hasActiveProcess: boolean;
|
|
29
|
-
createdAt: string;
|
|
30
|
-
}>;
|
|
31
|
-
count: number;
|
|
32
18
|
timestamp: string;
|
|
33
19
|
}
|
|
34
20
|
|
|
@@ -139,37 +125,47 @@ export interface MoveFileResponse {
|
|
|
139
125
|
timestamp: string;
|
|
140
126
|
}
|
|
141
127
|
|
|
142
|
-
interface
|
|
143
|
-
|
|
128
|
+
interface PreviewInfo {
|
|
129
|
+
url: string;
|
|
130
|
+
port: number;
|
|
131
|
+
name?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ExposedPort extends PreviewInfo {
|
|
135
|
+
exposedAt: string;
|
|
136
|
+
timestamp: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface ExposePortResponse {
|
|
140
|
+
success: boolean;
|
|
141
|
+
port: number;
|
|
142
|
+
name?: string;
|
|
143
|
+
exposedAt: string;
|
|
144
|
+
timestamp: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface UnexposePortResponse {
|
|
148
|
+
success: boolean;
|
|
149
|
+
port: number;
|
|
150
|
+
timestamp: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface GetExposedPortsResponse {
|
|
154
|
+
ports: ExposedPort[];
|
|
155
|
+
count: number;
|
|
144
156
|
timestamp: string;
|
|
145
157
|
}
|
|
146
158
|
|
|
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;
|
|
159
|
+
interface PingResponse {
|
|
160
|
+
message: string;
|
|
161
|
+
timestamp: string;
|
|
166
162
|
}
|
|
167
163
|
|
|
168
164
|
interface HttpClientOptions {
|
|
169
165
|
stub?: Sandbox;
|
|
170
166
|
baseUrl?: string;
|
|
171
167
|
port?: number;
|
|
172
|
-
onCommandStart?: (command: string
|
|
168
|
+
onCommandStart?: (command: string) => void;
|
|
173
169
|
onOutput?: (
|
|
174
170
|
stream: "stdout" | "stderr",
|
|
175
171
|
data: string,
|
|
@@ -180,11 +176,9 @@ interface HttpClientOptions {
|
|
|
180
176
|
exitCode: number,
|
|
181
177
|
stdout: string,
|
|
182
178
|
stderr: string,
|
|
183
|
-
command: string
|
|
184
|
-
args: string[]
|
|
179
|
+
command: string
|
|
185
180
|
) => void;
|
|
186
|
-
onError?: (error: string, command?: string
|
|
187
|
-
onStreamEvent?: (event: StreamEvent) => void;
|
|
181
|
+
onError?: (error: string, command?: string) => void;
|
|
188
182
|
}
|
|
189
183
|
|
|
190
184
|
export class HttpClient {
|
|
@@ -199,7 +193,7 @@ export class HttpClient {
|
|
|
199
193
|
this.baseUrl = this.options.baseUrl!;
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
|
|
196
|
+
protected async doFetch(
|
|
203
197
|
path: string,
|
|
204
198
|
options?: RequestInit
|
|
205
199
|
): Promise<Response> {
|
|
@@ -239,117 +233,22 @@ export class HttpClient {
|
|
|
239
233
|
throw error;
|
|
240
234
|
}
|
|
241
235
|
}
|
|
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
236
|
|
|
339
237
|
async execute(
|
|
340
238
|
command: string,
|
|
341
|
-
|
|
342
|
-
sessionId?: string
|
|
239
|
+
options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
|
|
343
240
|
): Promise<ExecuteResponse> {
|
|
344
241
|
try {
|
|
345
|
-
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;
|
|
346
249
|
|
|
347
250
|
const response = await this.doFetch(`/api/execute`, {
|
|
348
|
-
body: JSON.stringify(
|
|
349
|
-
args,
|
|
350
|
-
command,
|
|
351
|
-
sessionId: targetSessionId,
|
|
352
|
-
}),
|
|
251
|
+
body: JSON.stringify(executeRequest),
|
|
353
252
|
headers: {
|
|
354
253
|
"Content-Type": "application/json",
|
|
355
254
|
},
|
|
@@ -376,8 +275,7 @@ export class HttpClient {
|
|
|
376
275
|
data.exitCode,
|
|
377
276
|
data.stdout,
|
|
378
277
|
data.stderr,
|
|
379
|
-
data.command
|
|
380
|
-
data.args
|
|
278
|
+
data.command
|
|
381
279
|
);
|
|
382
280
|
|
|
383
281
|
return data;
|
|
@@ -385,29 +283,27 @@ export class HttpClient {
|
|
|
385
283
|
console.error("[HTTP Client] Error executing command:", error);
|
|
386
284
|
this.options.onError?.(
|
|
387
285
|
error instanceof Error ? error.message : "Unknown error",
|
|
388
|
-
command
|
|
389
|
-
args
|
|
286
|
+
command
|
|
390
287
|
);
|
|
391
288
|
throw error;
|
|
392
289
|
}
|
|
393
290
|
}
|
|
394
291
|
|
|
395
|
-
async
|
|
292
|
+
async executeCommandStream(
|
|
396
293
|
command: string,
|
|
397
|
-
args: string[] = [],
|
|
398
294
|
sessionId?: string
|
|
399
|
-
): Promise<
|
|
295
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
400
296
|
try {
|
|
401
297
|
const targetSessionId = sessionId || this.sessionId;
|
|
402
298
|
|
|
403
299
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
404
300
|
body: JSON.stringify({
|
|
405
|
-
args,
|
|
406
301
|
command,
|
|
407
302
|
sessionId: targetSessionId,
|
|
408
303
|
}),
|
|
409
304
|
headers: {
|
|
410
305
|
"Content-Type": "application/json",
|
|
306
|
+
Accept: "text/event-stream",
|
|
411
307
|
},
|
|
412
308
|
method: "POST",
|
|
413
309
|
});
|
|
@@ -425,95 +321,11 @@ export class HttpClient {
|
|
|
425
321
|
throw new Error("No response body for streaming request");
|
|
426
322
|
}
|
|
427
323
|
|
|
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
|
-
}
|
|
324
|
+
console.log(`[HTTP Client] Started command stream: ${command}`);
|
|
325
|
+
|
|
326
|
+
return response.body;
|
|
510
327
|
} catch (error) {
|
|
511
|
-
console.error("[HTTP Client] Error in
|
|
512
|
-
this.options.onError?.(
|
|
513
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
514
|
-
command,
|
|
515
|
-
args
|
|
516
|
-
);
|
|
328
|
+
console.error("[HTTP Client] Error in command stream:", error);
|
|
517
329
|
throw error;
|
|
518
330
|
}
|
|
519
331
|
}
|
|
@@ -533,7 +345,7 @@ export class HttpClient {
|
|
|
533
345
|
repoUrl,
|
|
534
346
|
sessionId: targetSessionId,
|
|
535
347
|
targetDir,
|
|
536
|
-
}),
|
|
348
|
+
} as GitCheckoutRequest),
|
|
537
349
|
headers: {
|
|
538
350
|
"Content-Type": "application/json",
|
|
539
351
|
},
|
|
@@ -561,22 +373,20 @@ export class HttpClient {
|
|
|
561
373
|
}
|
|
562
374
|
}
|
|
563
375
|
|
|
564
|
-
async
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
targetDir?: string,
|
|
376
|
+
async mkdir(
|
|
377
|
+
path: string,
|
|
378
|
+
recursive: boolean = false,
|
|
568
379
|
sessionId?: string
|
|
569
|
-
): Promise<
|
|
380
|
+
): Promise<MkdirResponse> {
|
|
570
381
|
try {
|
|
571
382
|
const targetSessionId = sessionId || this.sessionId;
|
|
572
383
|
|
|
573
|
-
const response = await this.doFetch(`/api/
|
|
384
|
+
const response = await this.doFetch(`/api/mkdir`, {
|
|
574
385
|
body: JSON.stringify({
|
|
575
|
-
|
|
576
|
-
|
|
386
|
+
path,
|
|
387
|
+
recursive,
|
|
577
388
|
sessionId: targetSessionId,
|
|
578
|
-
|
|
579
|
-
}),
|
|
389
|
+
} as MkdirRequest),
|
|
580
390
|
headers: {
|
|
581
391
|
"Content-Type": "application/json",
|
|
582
392
|
},
|
|
@@ -592,119 +402,34 @@ export class HttpClient {
|
|
|
592
402
|
);
|
|
593
403
|
}
|
|
594
404
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
405
|
+
const data: MkdirResponse = await response.json();
|
|
406
|
+
console.log(
|
|
407
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`
|
|
408
|
+
);
|
|
598
409
|
|
|
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
|
-
}
|
|
410
|
+
return data;
|
|
683
411
|
} 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
|
-
);
|
|
412
|
+
console.error("[HTTP Client] Error creating directory:", error);
|
|
690
413
|
throw error;
|
|
691
414
|
}
|
|
692
415
|
}
|
|
693
416
|
|
|
694
|
-
async
|
|
417
|
+
async writeFile(
|
|
695
418
|
path: string,
|
|
696
|
-
|
|
419
|
+
content: string,
|
|
420
|
+
encoding: string = "utf-8",
|
|
697
421
|
sessionId?: string
|
|
698
|
-
): Promise<
|
|
422
|
+
): Promise<WriteFileResponse> {
|
|
699
423
|
try {
|
|
700
424
|
const targetSessionId = sessionId || this.sessionId;
|
|
701
425
|
|
|
702
|
-
const response = await this.doFetch(`/api/
|
|
426
|
+
const response = await this.doFetch(`/api/write`, {
|
|
703
427
|
body: JSON.stringify({
|
|
428
|
+
content,
|
|
429
|
+
encoding,
|
|
704
430
|
path,
|
|
705
|
-
recursive,
|
|
706
431
|
sessionId: targetSessionId,
|
|
707
|
-
}),
|
|
432
|
+
} as WriteFileRequest),
|
|
708
433
|
headers: {
|
|
709
434
|
"Content-Type": "application/json",
|
|
710
435
|
},
|
|
@@ -720,32 +445,32 @@ export class HttpClient {
|
|
|
720
445
|
);
|
|
721
446
|
}
|
|
722
447
|
|
|
723
|
-
const data:
|
|
448
|
+
const data: WriteFileResponse = await response.json();
|
|
724
449
|
console.log(
|
|
725
|
-
`[HTTP Client]
|
|
450
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}`
|
|
726
451
|
);
|
|
727
452
|
|
|
728
453
|
return data;
|
|
729
454
|
} catch (error) {
|
|
730
|
-
console.error("[HTTP Client] Error
|
|
455
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
731
456
|
throw error;
|
|
732
457
|
}
|
|
733
458
|
}
|
|
734
459
|
|
|
735
|
-
async
|
|
460
|
+
async readFile(
|
|
736
461
|
path: string,
|
|
737
|
-
|
|
462
|
+
encoding: string = "utf-8",
|
|
738
463
|
sessionId?: string
|
|
739
|
-
): Promise<
|
|
464
|
+
): Promise<ReadFileResponse> {
|
|
740
465
|
try {
|
|
741
466
|
const targetSessionId = sessionId || this.sessionId;
|
|
742
467
|
|
|
743
|
-
const response = await this.doFetch(`/api/
|
|
468
|
+
const response = await this.doFetch(`/api/read`, {
|
|
744
469
|
body: JSON.stringify({
|
|
470
|
+
encoding,
|
|
745
471
|
path,
|
|
746
|
-
recursive,
|
|
747
472
|
sessionId: targetSessionId,
|
|
748
|
-
}),
|
|
473
|
+
} as ReadFileRequest),
|
|
749
474
|
headers: {
|
|
750
475
|
"Content-Type": "application/json",
|
|
751
476
|
},
|
|
@@ -761,117 +486,30 @@ export class HttpClient {
|
|
|
761
486
|
);
|
|
762
487
|
}
|
|
763
488
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
489
|
+
const data: ReadFileResponse = await response.json();
|
|
490
|
+
console.log(
|
|
491
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
|
|
492
|
+
);
|
|
767
493
|
|
|
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
|
-
}
|
|
494
|
+
return data;
|
|
848
495
|
} 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
|
-
);
|
|
496
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
855
497
|
throw error;
|
|
856
498
|
}
|
|
857
499
|
}
|
|
858
500
|
|
|
859
|
-
async
|
|
501
|
+
async deleteFile(
|
|
860
502
|
path: string,
|
|
861
|
-
content: string,
|
|
862
|
-
encoding: string = "utf-8",
|
|
863
503
|
sessionId?: string
|
|
864
|
-
): Promise<
|
|
504
|
+
): Promise<DeleteFileResponse> {
|
|
865
505
|
try {
|
|
866
506
|
const targetSessionId = sessionId || this.sessionId;
|
|
867
507
|
|
|
868
|
-
const response = await this.doFetch(`/api/
|
|
508
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
869
509
|
body: JSON.stringify({
|
|
870
|
-
content,
|
|
871
|
-
encoding,
|
|
872
510
|
path,
|
|
873
511
|
sessionId: targetSessionId,
|
|
874
|
-
}),
|
|
512
|
+
} as DeleteFileRequest),
|
|
875
513
|
headers: {
|
|
876
514
|
"Content-Type": "application/json",
|
|
877
515
|
},
|
|
@@ -887,34 +525,32 @@ export class HttpClient {
|
|
|
887
525
|
);
|
|
888
526
|
}
|
|
889
527
|
|
|
890
|
-
const data:
|
|
528
|
+
const data: DeleteFileResponse = await response.json();
|
|
891
529
|
console.log(
|
|
892
|
-
`[HTTP Client] File
|
|
530
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
893
531
|
);
|
|
894
532
|
|
|
895
533
|
return data;
|
|
896
534
|
} catch (error) {
|
|
897
|
-
console.error("[HTTP Client] Error
|
|
535
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
898
536
|
throw error;
|
|
899
537
|
}
|
|
900
538
|
}
|
|
901
539
|
|
|
902
|
-
async
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
encoding: string = "utf-8",
|
|
540
|
+
async renameFile(
|
|
541
|
+
oldPath: string,
|
|
542
|
+
newPath: string,
|
|
906
543
|
sessionId?: string
|
|
907
|
-
): Promise<
|
|
544
|
+
): Promise<RenameFileResponse> {
|
|
908
545
|
try {
|
|
909
546
|
const targetSessionId = sessionId || this.sessionId;
|
|
910
547
|
|
|
911
|
-
const response = await this.doFetch(`/api/
|
|
548
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
912
549
|
body: JSON.stringify({
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
path,
|
|
550
|
+
newPath,
|
|
551
|
+
oldPath,
|
|
916
552
|
sessionId: targetSessionId,
|
|
917
|
-
}),
|
|
553
|
+
} as RenameFileRequest),
|
|
918
554
|
headers: {
|
|
919
555
|
"Content-Type": "application/json",
|
|
920
556
|
},
|
|
@@ -930,114 +566,32 @@ export class HttpClient {
|
|
|
930
566
|
);
|
|
931
567
|
}
|
|
932
568
|
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
569
|
+
const data: RenameFileResponse = await response.json();
|
|
570
|
+
console.log(
|
|
571
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
572
|
+
);
|
|
936
573
|
|
|
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
|
-
}
|
|
574
|
+
return data;
|
|
1016
575
|
} 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
|
-
);
|
|
576
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
1023
577
|
throw error;
|
|
1024
578
|
}
|
|
1025
579
|
}
|
|
1026
580
|
|
|
1027
|
-
async
|
|
1028
|
-
|
|
1029
|
-
|
|
581
|
+
async moveFile(
|
|
582
|
+
sourcePath: string,
|
|
583
|
+
destinationPath: string,
|
|
1030
584
|
sessionId?: string
|
|
1031
|
-
): Promise<
|
|
585
|
+
): Promise<MoveFileResponse> {
|
|
1032
586
|
try {
|
|
1033
587
|
const targetSessionId = sessionId || this.sessionId;
|
|
1034
588
|
|
|
1035
|
-
const response = await this.doFetch(`/api/
|
|
589
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1036
590
|
body: JSON.stringify({
|
|
1037
|
-
|
|
1038
|
-
path,
|
|
591
|
+
destinationPath,
|
|
1039
592
|
sessionId: targetSessionId,
|
|
1040
|
-
|
|
593
|
+
sourcePath,
|
|
594
|
+
} as MoveFileRequest),
|
|
1041
595
|
headers: {
|
|
1042
596
|
"Content-Type": "application/json",
|
|
1043
597
|
},
|
|
@@ -1053,31 +607,24 @@ export class HttpClient {
|
|
|
1053
607
|
);
|
|
1054
608
|
}
|
|
1055
609
|
|
|
1056
|
-
const data:
|
|
610
|
+
const data: MoveFileResponse = await response.json();
|
|
1057
611
|
console.log(
|
|
1058
|
-
`[HTTP Client] File
|
|
612
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
1059
613
|
);
|
|
1060
614
|
|
|
1061
615
|
return data;
|
|
1062
616
|
} catch (error) {
|
|
1063
|
-
console.error("[HTTP Client] Error
|
|
617
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1064
618
|
throw error;
|
|
1065
619
|
}
|
|
1066
620
|
}
|
|
1067
621
|
|
|
1068
|
-
async
|
|
1069
|
-
path: string,
|
|
1070
|
-
encoding: string = "utf-8",
|
|
1071
|
-
sessionId?: string
|
|
1072
|
-
): Promise<void> {
|
|
622
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1073
623
|
try {
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
const response = await this.doFetch(`/api/read/stream`, {
|
|
624
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1077
625
|
body: JSON.stringify({
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
sessionId: targetSessionId,
|
|
626
|
+
port,
|
|
627
|
+
name,
|
|
1081
628
|
}),
|
|
1082
629
|
headers: {
|
|
1083
630
|
"Content-Type": "application/json",
|
|
@@ -1089,115 +636,36 @@ export class HttpClient {
|
|
|
1089
636
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1090
637
|
error?: string;
|
|
1091
638
|
};
|
|
639
|
+
console.log(errorData);
|
|
1092
640
|
throw new Error(
|
|
1093
641
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1094
642
|
);
|
|
1095
643
|
}
|
|
1096
644
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
645
|
+
const data: ExposePortResponse = await response.json();
|
|
646
|
+
console.log(
|
|
647
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
648
|
+
name ? ` (${name})` : ""
|
|
649
|
+
}, Success: ${data.success}`
|
|
650
|
+
);
|
|
1100
651
|
|
|
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
|
-
}
|
|
652
|
+
return data;
|
|
1174
653
|
} 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
|
-
);
|
|
654
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1181
655
|
throw error;
|
|
1182
656
|
}
|
|
1183
657
|
}
|
|
1184
658
|
|
|
1185
|
-
async
|
|
1186
|
-
path: string,
|
|
1187
|
-
sessionId?: string
|
|
1188
|
-
): Promise<DeleteFileResponse> {
|
|
659
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1189
660
|
try {
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
661
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1193
662
|
body: JSON.stringify({
|
|
1194
|
-
|
|
1195
|
-
sessionId: targetSessionId,
|
|
663
|
+
port,
|
|
1196
664
|
}),
|
|
1197
665
|
headers: {
|
|
1198
666
|
"Content-Type": "application/json",
|
|
1199
667
|
},
|
|
1200
|
-
method: "
|
|
668
|
+
method: "DELETE",
|
|
1201
669
|
});
|
|
1202
670
|
|
|
1203
671
|
if (!response.ok) {
|
|
@@ -1209,31 +677,25 @@ export class HttpClient {
|
|
|
1209
677
|
);
|
|
1210
678
|
}
|
|
1211
679
|
|
|
1212
|
-
const data:
|
|
680
|
+
const data: UnexposePortResponse = await response.json();
|
|
1213
681
|
console.log(
|
|
1214
|
-
`[HTTP Client]
|
|
682
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
1215
683
|
);
|
|
1216
684
|
|
|
1217
685
|
return data;
|
|
1218
686
|
} catch (error) {
|
|
1219
|
-
console.error("[HTTP Client] Error
|
|
687
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
1220
688
|
throw error;
|
|
1221
689
|
}
|
|
1222
690
|
}
|
|
1223
691
|
|
|
1224
|
-
async
|
|
692
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
1225
693
|
try {
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1228
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
1229
|
-
body: JSON.stringify({
|
|
1230
|
-
path,
|
|
1231
|
-
sessionId: targetSessionId,
|
|
1232
|
-
}),
|
|
694
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
1233
695
|
headers: {
|
|
1234
696
|
"Content-Type": "application/json",
|
|
1235
697
|
},
|
|
1236
|
-
method: "
|
|
698
|
+
method: "GET",
|
|
1237
699
|
});
|
|
1238
700
|
|
|
1239
701
|
if (!response.ok) {
|
|
@@ -1245,101 +707,98 @@ export class HttpClient {
|
|
|
1245
707
|
);
|
|
1246
708
|
}
|
|
1247
709
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
710
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
711
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
712
|
+
|
|
713
|
+
return data;
|
|
714
|
+
} catch (error) {
|
|
715
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
716
|
+
throw error;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
async ping(): Promise<string> {
|
|
721
|
+
try {
|
|
722
|
+
const response = await this.doFetch(`/api/ping`, {
|
|
723
|
+
headers: {
|
|
724
|
+
"Content-Type": "application/json",
|
|
725
|
+
},
|
|
726
|
+
method: "GET",
|
|
727
|
+
});
|
|
1251
728
|
|
|
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();
|
|
729
|
+
if (!response.ok) {
|
|
730
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1317
731
|
}
|
|
732
|
+
|
|
733
|
+
const data: PingResponse = await response.json();
|
|
734
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
735
|
+
return data.timestamp;
|
|
1318
736
|
} catch (error) {
|
|
1319
|
-
console.error("[HTTP Client] Error
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
737
|
+
console.error("[HTTP Client] Error pinging server:", error);
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
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}`);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const data: CommandsResponse = await response.json();
|
|
756
|
+
console.log(
|
|
757
|
+
`[HTTP Client] Available commands: ${data.availableCommands.length}`
|
|
1324
758
|
);
|
|
759
|
+
return data.availableCommands;
|
|
760
|
+
} catch (error) {
|
|
761
|
+
console.error("[HTTP Client] Error getting commands:", error);
|
|
1325
762
|
throw error;
|
|
1326
763
|
}
|
|
1327
764
|
}
|
|
1328
765
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
):
|
|
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> {
|
|
1334
791
|
try {
|
|
1335
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
792
|
+
const targetSessionId = options?.sessionId || this.sessionId;
|
|
1336
793
|
|
|
1337
|
-
const response = await this.doFetch(
|
|
794
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1338
795
|
body: JSON.stringify({
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
796
|
+
command,
|
|
797
|
+
options: {
|
|
798
|
+
...options,
|
|
799
|
+
sessionId: targetSessionId,
|
|
800
|
+
},
|
|
801
|
+
} as StartProcessRequest),
|
|
1343
802
|
headers: {
|
|
1344
803
|
"Content-Type": "application/json",
|
|
1345
804
|
},
|
|
@@ -1355,36 +814,25 @@ export class HttpClient {
|
|
|
1355
814
|
);
|
|
1356
815
|
}
|
|
1357
816
|
|
|
1358
|
-
const data:
|
|
817
|
+
const data: StartProcessResponse = await response.json();
|
|
1359
818
|
console.log(
|
|
1360
|
-
`[HTTP Client]
|
|
819
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
1361
820
|
);
|
|
1362
821
|
|
|
1363
822
|
return data;
|
|
1364
823
|
} catch (error) {
|
|
1365
|
-
console.error("[HTTP Client] Error
|
|
824
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1366
825
|
throw error;
|
|
1367
826
|
}
|
|
1368
827
|
}
|
|
1369
828
|
|
|
1370
|
-
async
|
|
1371
|
-
oldPath: string,
|
|
1372
|
-
newPath: string,
|
|
1373
|
-
sessionId?: string
|
|
1374
|
-
): Promise<void> {
|
|
829
|
+
async listProcesses(): Promise<ListProcessesResponse> {
|
|
1375
830
|
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
|
-
}),
|
|
831
|
+
const response = await this.doFetch("/api/process/list", {
|
|
1384
832
|
headers: {
|
|
1385
833
|
"Content-Type": "application/json",
|
|
1386
834
|
},
|
|
1387
|
-
method: "
|
|
835
|
+
method: "GET",
|
|
1388
836
|
});
|
|
1389
837
|
|
|
1390
838
|
if (!response.ok) {
|
|
@@ -1396,108 +844,23 @@ export class HttpClient {
|
|
|
1396
844
|
);
|
|
1397
845
|
}
|
|
1398
846
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
}
|
|
847
|
+
const data: ListProcessesResponse = await response.json();
|
|
848
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
1402
849
|
|
|
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
|
-
}
|
|
850
|
+
return data;
|
|
1472
851
|
} 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
|
-
);
|
|
852
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1479
853
|
throw error;
|
|
1480
854
|
}
|
|
1481
855
|
}
|
|
1482
856
|
|
|
1483
|
-
async
|
|
1484
|
-
sourcePath: string,
|
|
1485
|
-
destinationPath: string,
|
|
1486
|
-
sessionId?: string
|
|
1487
|
-
): Promise<MoveFileResponse> {
|
|
857
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1488
858
|
try {
|
|
1489
|
-
const
|
|
1490
|
-
|
|
1491
|
-
const response = await this.doFetch(`/api/move`, {
|
|
1492
|
-
body: JSON.stringify({
|
|
1493
|
-
destinationPath,
|
|
1494
|
-
sessionId: targetSessionId,
|
|
1495
|
-
sourcePath,
|
|
1496
|
-
}),
|
|
859
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1497
860
|
headers: {
|
|
1498
861
|
"Content-Type": "application/json",
|
|
1499
862
|
},
|
|
1500
|
-
method: "
|
|
863
|
+
method: "GET",
|
|
1501
864
|
});
|
|
1502
865
|
|
|
1503
866
|
if (!response.ok) {
|
|
@@ -1509,36 +872,29 @@ export class HttpClient {
|
|
|
1509
872
|
);
|
|
1510
873
|
}
|
|
1511
874
|
|
|
1512
|
-
const data:
|
|
875
|
+
const data: GetProcessResponse = await response.json();
|
|
1513
876
|
console.log(
|
|
1514
|
-
`[HTTP Client]
|
|
877
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
878
|
+
data.process?.status || "not found"
|
|
879
|
+
}`
|
|
1515
880
|
);
|
|
1516
881
|
|
|
1517
882
|
return data;
|
|
1518
883
|
} catch (error) {
|
|
1519
|
-
console.error("[HTTP Client] Error
|
|
884
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1520
885
|
throw error;
|
|
1521
886
|
}
|
|
1522
887
|
}
|
|
1523
888
|
|
|
1524
|
-
async
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
sessionId?: string
|
|
1528
|
-
): Promise<void> {
|
|
889
|
+
async killProcess(
|
|
890
|
+
processId: string
|
|
891
|
+
): Promise<{ success: boolean; message: string }> {
|
|
1529
892
|
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
|
-
}),
|
|
893
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1538
894
|
headers: {
|
|
1539
895
|
"Content-Type": "application/json",
|
|
1540
896
|
},
|
|
1541
|
-
method: "
|
|
897
|
+
method: "DELETE",
|
|
1542
898
|
});
|
|
1543
899
|
|
|
1544
900
|
if (!response.ok) {
|
|
@@ -1550,118 +906,58 @@ export class HttpClient {
|
|
|
1550
906
|
);
|
|
1551
907
|
}
|
|
1552
908
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
909
|
+
const data = (await response.json()) as {
|
|
910
|
+
success: boolean;
|
|
911
|
+
message: string;
|
|
912
|
+
};
|
|
913
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1556
914
|
|
|
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
|
-
}
|
|
915
|
+
return data;
|
|
1629
916
|
} 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
|
-
);
|
|
917
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
1636
918
|
throw error;
|
|
1637
919
|
}
|
|
1638
920
|
}
|
|
1639
921
|
|
|
1640
|
-
async
|
|
922
|
+
async killAllProcesses(): Promise<{
|
|
923
|
+
success: boolean;
|
|
924
|
+
killedCount: number;
|
|
925
|
+
message: string;
|
|
926
|
+
}> {
|
|
1641
927
|
try {
|
|
1642
|
-
const response = await this.doFetch(
|
|
928
|
+
const response = await this.doFetch("/api/process/kill-all", {
|
|
1643
929
|
headers: {
|
|
1644
930
|
"Content-Type": "application/json",
|
|
1645
931
|
},
|
|
1646
|
-
method: "
|
|
932
|
+
method: "DELETE",
|
|
1647
933
|
});
|
|
1648
934
|
|
|
1649
935
|
if (!response.ok) {
|
|
1650
|
-
|
|
936
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
937
|
+
error?: string;
|
|
938
|
+
};
|
|
939
|
+
throw new Error(
|
|
940
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
941
|
+
);
|
|
1651
942
|
}
|
|
1652
943
|
|
|
1653
|
-
const data
|
|
1654
|
-
|
|
1655
|
-
|
|
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`);
|
|
950
|
+
|
|
951
|
+
return data;
|
|
1656
952
|
} catch (error) {
|
|
1657
|
-
console.error("[HTTP Client] Error
|
|
953
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
1658
954
|
throw error;
|
|
1659
955
|
}
|
|
1660
956
|
}
|
|
1661
957
|
|
|
1662
|
-
async
|
|
958
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
1663
959
|
try {
|
|
1664
|
-
const response = await
|
|
960
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
1665
961
|
headers: {
|
|
1666
962
|
"Content-Type": "application/json",
|
|
1667
963
|
},
|
|
@@ -1669,292 +965,57 @@ export class HttpClient {
|
|
|
1669
965
|
});
|
|
1670
966
|
|
|
1671
967
|
if (!response.ok) {
|
|
1672
|
-
|
|
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
|
+
);
|
|
1673
974
|
}
|
|
1674
975
|
|
|
1675
|
-
const data:
|
|
1676
|
-
console.log(
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
return data.availableCommands;
|
|
976
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
977
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
978
|
+
|
|
979
|
+
return data;
|
|
1680
980
|
} catch (error) {
|
|
1681
|
-
console.error("[HTTP Client] Error getting
|
|
981
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1682
982
|
throw error;
|
|
1683
983
|
}
|
|
1684
984
|
}
|
|
1685
985
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
// Example usage and utility functions
|
|
1700
|
-
export function createClient(options?: HttpClientOptions): HttpClient {
|
|
1701
|
-
return new HttpClient(options);
|
|
1702
|
-
}
|
|
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
|
-
}
|
|
986
|
+
async streamProcessLogs(
|
|
987
|
+
processId: string
|
|
988
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
989
|
+
try {
|
|
990
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
991
|
+
headers: {
|
|
992
|
+
Accept: "text/event-stream",
|
|
993
|
+
"Cache-Control": "no-cache",
|
|
994
|
+
},
|
|
995
|
+
method: "GET",
|
|
996
|
+
});
|
|
1897
997
|
|
|
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
|
-
}
|
|
998
|
+
if (!response.ok) {
|
|
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
|
+
}
|
|
1913
1006
|
|
|
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
|
-
}
|
|
1007
|
+
if (!response.body) {
|
|
1008
|
+
throw new Error("No response body for streaming request");
|
|
1009
|
+
}
|
|
1929
1010
|
|
|
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
|
-
}
|
|
1011
|
+
console.log(
|
|
1012
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1013
|
+
);
|
|
1945
1014
|
|
|
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();
|
|
1015
|
+
return response.body;
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1959
1020
|
}
|
|
1960
1021
|
}
|