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