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