@cloudflare/sandbox 0.0.0-444d2da → 0.0.0-4aceb32
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 +94 -0
- package/Dockerfile +97 -11
- package/README.md +743 -24
- package/container_src/bun.lock +122 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/handler/exec.ts +340 -0
- package/container_src/handler/file.ts +844 -0
- package/container_src/handler/git.ts +182 -0
- package/container_src/handler/ports.ts +314 -0
- package/container_src/handler/process.ts +640 -0
- package/container_src/index.ts +402 -2646
- package/container_src/jupyter-server.ts +579 -0
- package/container_src/jupyter-service.ts +448 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/startup.sh +83 -0
- package/container_src/types.ts +108 -0
- package/package.json +6 -8
- package/src/client.ts +353 -1261
- package/src/errors.ts +218 -0
- package/src/index.ts +54 -129
- package/src/interpreter-types.ts +383 -0
- package/src/interpreter.ts +150 -0
- package/src/jupyter-client.ts +349 -0
- package/src/request-handler.ts +144 -0
- package/src/sandbox.ts +778 -0
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +401 -0
- package/tsconfig.json +1 -1
- package/tests/client.example.ts +0 -308
- package/tests/connection-test.ts +0 -81
- package/tests/simple-test.ts +0 -81
- package/tests/test1.ts +0 -281
- package/tests/test2.ts +0 -929
package/src/client.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExecuteRequest } from "../container_src/types";
|
|
2
2
|
import type { Sandbox } from "./index";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
import type {
|
|
4
|
+
BaseExecOptions,
|
|
5
|
+
GetProcessLogsResponse,
|
|
6
|
+
GetProcessResponse,
|
|
7
|
+
ListProcessesResponse,
|
|
8
|
+
StartProcessRequest,
|
|
9
|
+
StartProcessResponse,
|
|
10
|
+
} from "./types";
|
|
8
11
|
|
|
9
12
|
export interface ExecuteResponse {
|
|
10
13
|
success: boolean;
|
|
@@ -12,23 +15,6 @@ export interface ExecuteResponse {
|
|
|
12
15
|
stderr: string;
|
|
13
16
|
exitCode: number;
|
|
14
17
|
command: string;
|
|
15
|
-
args: string[];
|
|
16
|
-
timestamp: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface SessionResponse {
|
|
20
|
-
sessionId: string;
|
|
21
|
-
message: string;
|
|
22
|
-
timestamp: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface SessionListResponse {
|
|
26
|
-
sessions: Array<{
|
|
27
|
-
sessionId: string;
|
|
28
|
-
hasActiveProcess: boolean;
|
|
29
|
-
createdAt: string;
|
|
30
|
-
}>;
|
|
31
|
-
count: number;
|
|
32
18
|
timestamp: string;
|
|
33
19
|
}
|
|
34
20
|
|
|
@@ -139,37 +125,47 @@ export interface MoveFileResponse {
|
|
|
139
125
|
timestamp: string;
|
|
140
126
|
}
|
|
141
127
|
|
|
142
|
-
interface
|
|
143
|
-
|
|
128
|
+
interface PreviewInfo {
|
|
129
|
+
url: string;
|
|
130
|
+
port: number;
|
|
131
|
+
name?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
interface ExposedPort extends PreviewInfo {
|
|
135
|
+
exposedAt: string;
|
|
136
|
+
timestamp: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface ExposePortResponse {
|
|
140
|
+
success: boolean;
|
|
141
|
+
port: number;
|
|
142
|
+
name?: string;
|
|
143
|
+
exposedAt: string;
|
|
144
|
+
timestamp: string;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface UnexposePortResponse {
|
|
148
|
+
success: boolean;
|
|
149
|
+
port: number;
|
|
150
|
+
timestamp: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface GetExposedPortsResponse {
|
|
154
|
+
ports: ExposedPort[];
|
|
155
|
+
count: number;
|
|
144
156
|
timestamp: string;
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
interface
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
args?: string[];
|
|
151
|
-
stream?: "stdout" | "stderr";
|
|
152
|
-
data?: string;
|
|
153
|
-
message?: string;
|
|
154
|
-
path?: string;
|
|
155
|
-
oldPath?: string;
|
|
156
|
-
newPath?: string;
|
|
157
|
-
sourcePath?: string;
|
|
158
|
-
destinationPath?: string;
|
|
159
|
-
content?: string;
|
|
160
|
-
success?: boolean;
|
|
161
|
-
exitCode?: number;
|
|
162
|
-
stdout?: string;
|
|
163
|
-
stderr?: string;
|
|
164
|
-
error?: string;
|
|
165
|
-
timestamp?: string;
|
|
159
|
+
interface PingResponse {
|
|
160
|
+
message: string;
|
|
161
|
+
timestamp: string;
|
|
166
162
|
}
|
|
167
163
|
|
|
168
164
|
interface HttpClientOptions {
|
|
169
165
|
stub?: Sandbox;
|
|
170
166
|
baseUrl?: string;
|
|
171
167
|
port?: number;
|
|
172
|
-
onCommandStart?: (command: string
|
|
168
|
+
onCommandStart?: (command: string) => void;
|
|
173
169
|
onOutput?: (
|
|
174
170
|
stream: "stdout" | "stderr",
|
|
175
171
|
data: string,
|
|
@@ -180,11 +176,9 @@ interface HttpClientOptions {
|
|
|
180
176
|
exitCode: number,
|
|
181
177
|
stdout: string,
|
|
182
178
|
stderr: string,
|
|
183
|
-
command: string
|
|
184
|
-
args: string[]
|
|
179
|
+
command: string
|
|
185
180
|
) => void;
|
|
186
|
-
onError?: (error: string, command?: string
|
|
187
|
-
onStreamEvent?: (event: StreamEvent) => void;
|
|
181
|
+
onError?: (error: string, command?: string) => void;
|
|
188
182
|
}
|
|
189
183
|
|
|
190
184
|
export class HttpClient {
|
|
@@ -199,126 +193,62 @@ export class HttpClient {
|
|
|
199
193
|
this.baseUrl = this.options.baseUrl!;
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
|
|
196
|
+
protected async doFetch(
|
|
203
197
|
path: string,
|
|
204
198
|
options?: RequestInit
|
|
205
199
|
): Promise<Response> {
|
|
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
|
-
}
|
|
200
|
+
const url = this.options.stub
|
|
201
|
+
? `http://localhost:${this.options.port}${path}`
|
|
202
|
+
: `${this.baseUrl}${path}`;
|
|
203
|
+
const method = options?.method || "GET";
|
|
258
204
|
|
|
259
|
-
|
|
260
|
-
return this.options.onStreamEvent;
|
|
261
|
-
}
|
|
205
|
+
console.log(`[HTTP Client] Making ${method} request to ${url}`);
|
|
262
206
|
|
|
263
|
-
async createSession(): Promise<string> {
|
|
264
207
|
try {
|
|
265
|
-
|
|
266
|
-
headers: {
|
|
267
|
-
"Content-Type": "application/json",
|
|
268
|
-
},
|
|
269
|
-
method: "POST",
|
|
270
|
-
});
|
|
208
|
+
let response: Response;
|
|
271
209
|
|
|
272
|
-
if (
|
|
273
|
-
|
|
210
|
+
if (this.options.stub) {
|
|
211
|
+
response = await this.options.stub.containerFetch(
|
|
212
|
+
url,
|
|
213
|
+
options,
|
|
214
|
+
this.options.port
|
|
215
|
+
);
|
|
216
|
+
} else {
|
|
217
|
+
response = await fetch(url, options);
|
|
274
218
|
}
|
|
275
219
|
|
|
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
|
-
});
|
|
220
|
+
console.log(
|
|
221
|
+
`[HTTP Client] Response: ${response.status} ${response.statusText}`
|
|
222
|
+
);
|
|
294
223
|
|
|
295
224
|
if (!response.ok) {
|
|
296
|
-
|
|
225
|
+
console.error(
|
|
226
|
+
`[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`
|
|
227
|
+
);
|
|
297
228
|
}
|
|
298
229
|
|
|
299
|
-
|
|
300
|
-
console.log(`[HTTP Client] Listed ${data.count} sessions`);
|
|
301
|
-
return data;
|
|
230
|
+
return response;
|
|
302
231
|
} catch (error) {
|
|
303
|
-
console.error(
|
|
232
|
+
console.error(`[HTTP Client] Request error: ${method} ${url}`, error);
|
|
304
233
|
throw error;
|
|
305
234
|
}
|
|
306
235
|
}
|
|
307
236
|
|
|
308
237
|
async execute(
|
|
309
238
|
command: string,
|
|
310
|
-
|
|
311
|
-
sessionId?: string
|
|
239
|
+
options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
|
|
312
240
|
): Promise<ExecuteResponse> {
|
|
313
241
|
try {
|
|
314
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
242
|
+
const targetSessionId = options.sessionId || this.sessionId;
|
|
243
|
+
const executeRequest = {
|
|
244
|
+
command,
|
|
245
|
+
sessionId: targetSessionId,
|
|
246
|
+
cwd: options.cwd,
|
|
247
|
+
env: options.env,
|
|
248
|
+
} satisfies ExecuteRequest;
|
|
315
249
|
|
|
316
250
|
const response = await this.doFetch(`/api/execute`, {
|
|
317
|
-
body: JSON.stringify(
|
|
318
|
-
args,
|
|
319
|
-
command,
|
|
320
|
-
sessionId: targetSessionId,
|
|
321
|
-
}),
|
|
251
|
+
body: JSON.stringify(executeRequest),
|
|
322
252
|
headers: {
|
|
323
253
|
"Content-Type": "application/json",
|
|
324
254
|
},
|
|
@@ -345,8 +275,7 @@ export class HttpClient {
|
|
|
345
275
|
data.exitCode,
|
|
346
276
|
data.stdout,
|
|
347
277
|
data.stderr,
|
|
348
|
-
data.command
|
|
349
|
-
data.args
|
|
278
|
+
data.command
|
|
350
279
|
);
|
|
351
280
|
|
|
352
281
|
return data;
|
|
@@ -354,29 +283,27 @@ export class HttpClient {
|
|
|
354
283
|
console.error("[HTTP Client] Error executing command:", error);
|
|
355
284
|
this.options.onError?.(
|
|
356
285
|
error instanceof Error ? error.message : "Unknown error",
|
|
357
|
-
command
|
|
358
|
-
args
|
|
286
|
+
command
|
|
359
287
|
);
|
|
360
288
|
throw error;
|
|
361
289
|
}
|
|
362
290
|
}
|
|
363
291
|
|
|
364
|
-
async
|
|
292
|
+
async executeCommandStream(
|
|
365
293
|
command: string,
|
|
366
|
-
args: string[] = [],
|
|
367
294
|
sessionId?: string
|
|
368
|
-
): Promise<
|
|
295
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
369
296
|
try {
|
|
370
297
|
const targetSessionId = sessionId || this.sessionId;
|
|
371
298
|
|
|
372
299
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
373
300
|
body: JSON.stringify({
|
|
374
|
-
args,
|
|
375
301
|
command,
|
|
376
302
|
sessionId: targetSessionId,
|
|
377
303
|
}),
|
|
378
304
|
headers: {
|
|
379
305
|
"Content-Type": "application/json",
|
|
306
|
+
Accept: "text/event-stream",
|
|
380
307
|
},
|
|
381
308
|
method: "POST",
|
|
382
309
|
});
|
|
@@ -394,95 +321,11 @@ export class HttpClient {
|
|
|
394
321
|
throw new Error("No response body for streaming request");
|
|
395
322
|
}
|
|
396
323
|
|
|
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
|
-
}
|
|
324
|
+
console.log(`[HTTP Client] Started command stream: ${command}`);
|
|
325
|
+
|
|
326
|
+
return response.body;
|
|
479
327
|
} catch (error) {
|
|
480
|
-
console.error("[HTTP Client] Error in
|
|
481
|
-
this.options.onError?.(
|
|
482
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
483
|
-
command,
|
|
484
|
-
args
|
|
485
|
-
);
|
|
328
|
+
console.error("[HTTP Client] Error in command stream:", error);
|
|
486
329
|
throw error;
|
|
487
330
|
}
|
|
488
331
|
}
|
|
@@ -502,7 +345,7 @@ export class HttpClient {
|
|
|
502
345
|
repoUrl,
|
|
503
346
|
sessionId: targetSessionId,
|
|
504
347
|
targetDir,
|
|
505
|
-
}),
|
|
348
|
+
} as GitCheckoutRequest),
|
|
506
349
|
headers: {
|
|
507
350
|
"Content-Type": "application/json",
|
|
508
351
|
},
|
|
@@ -530,22 +373,20 @@ export class HttpClient {
|
|
|
530
373
|
}
|
|
531
374
|
}
|
|
532
375
|
|
|
533
|
-
async
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
targetDir?: string,
|
|
376
|
+
async mkdir(
|
|
377
|
+
path: string,
|
|
378
|
+
recursive: boolean = false,
|
|
537
379
|
sessionId?: string
|
|
538
|
-
): Promise<
|
|
380
|
+
): Promise<MkdirResponse> {
|
|
539
381
|
try {
|
|
540
382
|
const targetSessionId = sessionId || this.sessionId;
|
|
541
383
|
|
|
542
|
-
const response = await this.doFetch(`/api/
|
|
384
|
+
const response = await this.doFetch(`/api/mkdir`, {
|
|
543
385
|
body: JSON.stringify({
|
|
544
|
-
|
|
545
|
-
|
|
386
|
+
path,
|
|
387
|
+
recursive,
|
|
546
388
|
sessionId: targetSessionId,
|
|
547
|
-
|
|
548
|
-
}),
|
|
389
|
+
} as MkdirRequest),
|
|
549
390
|
headers: {
|
|
550
391
|
"Content-Type": "application/json",
|
|
551
392
|
},
|
|
@@ -561,119 +402,34 @@ export class HttpClient {
|
|
|
561
402
|
);
|
|
562
403
|
}
|
|
563
404
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
405
|
+
const data: MkdirResponse = await response.json();
|
|
406
|
+
console.log(
|
|
407
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`
|
|
408
|
+
);
|
|
567
409
|
|
|
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
|
-
}
|
|
410
|
+
return data;
|
|
652
411
|
} 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
|
-
);
|
|
412
|
+
console.error("[HTTP Client] Error creating directory:", error);
|
|
659
413
|
throw error;
|
|
660
414
|
}
|
|
661
415
|
}
|
|
662
416
|
|
|
663
|
-
async
|
|
417
|
+
async writeFile(
|
|
664
418
|
path: string,
|
|
665
|
-
|
|
419
|
+
content: string,
|
|
420
|
+
encoding: string = "utf-8",
|
|
666
421
|
sessionId?: string
|
|
667
|
-
): Promise<
|
|
422
|
+
): Promise<WriteFileResponse> {
|
|
668
423
|
try {
|
|
669
424
|
const targetSessionId = sessionId || this.sessionId;
|
|
670
425
|
|
|
671
|
-
const response = await this.doFetch(`/api/
|
|
426
|
+
const response = await this.doFetch(`/api/write`, {
|
|
672
427
|
body: JSON.stringify({
|
|
428
|
+
content,
|
|
429
|
+
encoding,
|
|
673
430
|
path,
|
|
674
|
-
recursive,
|
|
675
431
|
sessionId: targetSessionId,
|
|
676
|
-
}),
|
|
432
|
+
} as WriteFileRequest),
|
|
677
433
|
headers: {
|
|
678
434
|
"Content-Type": "application/json",
|
|
679
435
|
},
|
|
@@ -689,32 +445,32 @@ export class HttpClient {
|
|
|
689
445
|
);
|
|
690
446
|
}
|
|
691
447
|
|
|
692
|
-
const data:
|
|
448
|
+
const data: WriteFileResponse = await response.json();
|
|
693
449
|
console.log(
|
|
694
|
-
`[HTTP Client]
|
|
450
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}`
|
|
695
451
|
);
|
|
696
452
|
|
|
697
453
|
return data;
|
|
698
454
|
} catch (error) {
|
|
699
|
-
console.error("[HTTP Client] Error
|
|
455
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
700
456
|
throw error;
|
|
701
457
|
}
|
|
702
458
|
}
|
|
703
459
|
|
|
704
|
-
async
|
|
460
|
+
async readFile(
|
|
705
461
|
path: string,
|
|
706
|
-
|
|
462
|
+
encoding: string = "utf-8",
|
|
707
463
|
sessionId?: string
|
|
708
|
-
): Promise<
|
|
464
|
+
): Promise<ReadFileResponse> {
|
|
709
465
|
try {
|
|
710
466
|
const targetSessionId = sessionId || this.sessionId;
|
|
711
467
|
|
|
712
|
-
const response = await this.doFetch(`/api/
|
|
468
|
+
const response = await this.doFetch(`/api/read`, {
|
|
713
469
|
body: JSON.stringify({
|
|
470
|
+
encoding,
|
|
714
471
|
path,
|
|
715
|
-
recursive,
|
|
716
472
|
sessionId: targetSessionId,
|
|
717
|
-
}),
|
|
473
|
+
} as ReadFileRequest),
|
|
718
474
|
headers: {
|
|
719
475
|
"Content-Type": "application/json",
|
|
720
476
|
},
|
|
@@ -730,117 +486,30 @@ export class HttpClient {
|
|
|
730
486
|
);
|
|
731
487
|
}
|
|
732
488
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
489
|
+
const data: ReadFileResponse = await response.json();
|
|
490
|
+
console.log(
|
|
491
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`
|
|
492
|
+
);
|
|
736
493
|
|
|
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
|
-
}
|
|
494
|
+
return data;
|
|
817
495
|
} 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
|
-
);
|
|
496
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
824
497
|
throw error;
|
|
825
498
|
}
|
|
826
499
|
}
|
|
827
500
|
|
|
828
|
-
async
|
|
501
|
+
async deleteFile(
|
|
829
502
|
path: string,
|
|
830
|
-
content: string,
|
|
831
|
-
encoding: string = "utf-8",
|
|
832
503
|
sessionId?: string
|
|
833
|
-
): Promise<
|
|
504
|
+
): Promise<DeleteFileResponse> {
|
|
834
505
|
try {
|
|
835
506
|
const targetSessionId = sessionId || this.sessionId;
|
|
836
507
|
|
|
837
|
-
const response = await this.doFetch(`/api/
|
|
508
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
838
509
|
body: JSON.stringify({
|
|
839
|
-
content,
|
|
840
|
-
encoding,
|
|
841
510
|
path,
|
|
842
511
|
sessionId: targetSessionId,
|
|
843
|
-
}),
|
|
512
|
+
} as DeleteFileRequest),
|
|
844
513
|
headers: {
|
|
845
514
|
"Content-Type": "application/json",
|
|
846
515
|
},
|
|
@@ -856,34 +525,32 @@ export class HttpClient {
|
|
|
856
525
|
);
|
|
857
526
|
}
|
|
858
527
|
|
|
859
|
-
const data:
|
|
528
|
+
const data: DeleteFileResponse = await response.json();
|
|
860
529
|
console.log(
|
|
861
|
-
`[HTTP Client] File
|
|
530
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
862
531
|
);
|
|
863
532
|
|
|
864
533
|
return data;
|
|
865
534
|
} catch (error) {
|
|
866
|
-
console.error("[HTTP Client] Error
|
|
535
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
867
536
|
throw error;
|
|
868
537
|
}
|
|
869
538
|
}
|
|
870
539
|
|
|
871
|
-
async
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
encoding: string = "utf-8",
|
|
540
|
+
async renameFile(
|
|
541
|
+
oldPath: string,
|
|
542
|
+
newPath: string,
|
|
875
543
|
sessionId?: string
|
|
876
|
-
): Promise<
|
|
544
|
+
): Promise<RenameFileResponse> {
|
|
877
545
|
try {
|
|
878
546
|
const targetSessionId = sessionId || this.sessionId;
|
|
879
547
|
|
|
880
|
-
const response = await this.doFetch(`/api/
|
|
548
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
881
549
|
body: JSON.stringify({
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
path,
|
|
550
|
+
newPath,
|
|
551
|
+
oldPath,
|
|
885
552
|
sessionId: targetSessionId,
|
|
886
|
-
}),
|
|
553
|
+
} as RenameFileRequest),
|
|
887
554
|
headers: {
|
|
888
555
|
"Content-Type": "application/json",
|
|
889
556
|
},
|
|
@@ -899,114 +566,32 @@ export class HttpClient {
|
|
|
899
566
|
);
|
|
900
567
|
}
|
|
901
568
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
569
|
+
const data: RenameFileResponse = await response.json();
|
|
570
|
+
console.log(
|
|
571
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
572
|
+
);
|
|
905
573
|
|
|
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
|
-
}
|
|
574
|
+
return data;
|
|
985
575
|
} 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
|
-
);
|
|
576
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
992
577
|
throw error;
|
|
993
578
|
}
|
|
994
579
|
}
|
|
995
580
|
|
|
996
|
-
async
|
|
997
|
-
|
|
998
|
-
|
|
581
|
+
async moveFile(
|
|
582
|
+
sourcePath: string,
|
|
583
|
+
destinationPath: string,
|
|
999
584
|
sessionId?: string
|
|
1000
|
-
): Promise<
|
|
585
|
+
): Promise<MoveFileResponse> {
|
|
1001
586
|
try {
|
|
1002
587
|
const targetSessionId = sessionId || this.sessionId;
|
|
1003
588
|
|
|
1004
|
-
const response = await this.doFetch(`/api/
|
|
589
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1005
590
|
body: JSON.stringify({
|
|
1006
|
-
|
|
1007
|
-
path,
|
|
591
|
+
destinationPath,
|
|
1008
592
|
sessionId: targetSessionId,
|
|
1009
|
-
|
|
593
|
+
sourcePath,
|
|
594
|
+
} as MoveFileRequest),
|
|
1010
595
|
headers: {
|
|
1011
596
|
"Content-Type": "application/json",
|
|
1012
597
|
},
|
|
@@ -1022,31 +607,24 @@ export class HttpClient {
|
|
|
1022
607
|
);
|
|
1023
608
|
}
|
|
1024
609
|
|
|
1025
|
-
const data:
|
|
610
|
+
const data: MoveFileResponse = await response.json();
|
|
1026
611
|
console.log(
|
|
1027
|
-
`[HTTP Client] File
|
|
612
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
1028
613
|
);
|
|
1029
614
|
|
|
1030
615
|
return data;
|
|
1031
616
|
} catch (error) {
|
|
1032
|
-
console.error("[HTTP Client] Error
|
|
617
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1033
618
|
throw error;
|
|
1034
619
|
}
|
|
1035
620
|
}
|
|
1036
621
|
|
|
1037
|
-
async
|
|
1038
|
-
path: string,
|
|
1039
|
-
encoding: string = "utf-8",
|
|
1040
|
-
sessionId?: string
|
|
1041
|
-
): Promise<void> {
|
|
622
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1042
623
|
try {
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
const response = await this.doFetch(`/api/read/stream`, {
|
|
624
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1046
625
|
body: JSON.stringify({
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
sessionId: targetSessionId,
|
|
626
|
+
port,
|
|
627
|
+
name,
|
|
1050
628
|
}),
|
|
1051
629
|
headers: {
|
|
1052
630
|
"Content-Type": "application/json",
|
|
@@ -1058,115 +636,36 @@ export class HttpClient {
|
|
|
1058
636
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1059
637
|
error?: string;
|
|
1060
638
|
};
|
|
639
|
+
console.log(errorData);
|
|
1061
640
|
throw new Error(
|
|
1062
641
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1063
642
|
);
|
|
1064
643
|
}
|
|
1065
644
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
645
|
+
const data: ExposePortResponse = await response.json();
|
|
646
|
+
console.log(
|
|
647
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
648
|
+
name ? ` (${name})` : ""
|
|
649
|
+
}, Success: ${data.success}`
|
|
650
|
+
);
|
|
1069
651
|
|
|
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
|
-
}
|
|
652
|
+
return data;
|
|
1143
653
|
} 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
|
-
);
|
|
654
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1150
655
|
throw error;
|
|
1151
656
|
}
|
|
1152
657
|
}
|
|
1153
658
|
|
|
1154
|
-
async
|
|
1155
|
-
path: string,
|
|
1156
|
-
sessionId?: string
|
|
1157
|
-
): Promise<DeleteFileResponse> {
|
|
659
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1158
660
|
try {
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
661
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1162
662
|
body: JSON.stringify({
|
|
1163
|
-
|
|
1164
|
-
sessionId: targetSessionId,
|
|
663
|
+
port,
|
|
1165
664
|
}),
|
|
1166
665
|
headers: {
|
|
1167
666
|
"Content-Type": "application/json",
|
|
1168
667
|
},
|
|
1169
|
-
method: "
|
|
668
|
+
method: "DELETE",
|
|
1170
669
|
});
|
|
1171
670
|
|
|
1172
671
|
if (!response.ok) {
|
|
@@ -1178,31 +677,25 @@ export class HttpClient {
|
|
|
1178
677
|
);
|
|
1179
678
|
}
|
|
1180
679
|
|
|
1181
|
-
const data:
|
|
680
|
+
const data: UnexposePortResponse = await response.json();
|
|
1182
681
|
console.log(
|
|
1183
|
-
`[HTTP Client]
|
|
682
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
1184
683
|
);
|
|
1185
684
|
|
|
1186
685
|
return data;
|
|
1187
686
|
} catch (error) {
|
|
1188
|
-
console.error("[HTTP Client] Error
|
|
687
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
1189
688
|
throw error;
|
|
1190
689
|
}
|
|
1191
690
|
}
|
|
1192
691
|
|
|
1193
|
-
async
|
|
692
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
1194
693
|
try {
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
1198
|
-
body: JSON.stringify({
|
|
1199
|
-
path,
|
|
1200
|
-
sessionId: targetSessionId,
|
|
1201
|
-
}),
|
|
694
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
1202
695
|
headers: {
|
|
1203
696
|
"Content-Type": "application/json",
|
|
1204
697
|
},
|
|
1205
|
-
method: "
|
|
698
|
+
method: "GET",
|
|
1206
699
|
});
|
|
1207
700
|
|
|
1208
701
|
if (!response.ok) {
|
|
@@ -1214,101 +707,98 @@ export class HttpClient {
|
|
|
1214
707
|
);
|
|
1215
708
|
}
|
|
1216
709
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
710
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
711
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
712
|
+
|
|
713
|
+
return data;
|
|
714
|
+
} catch (error) {
|
|
715
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
716
|
+
throw error;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
async ping(): Promise<string> {
|
|
721
|
+
try {
|
|
722
|
+
const response = await this.doFetch(`/api/ping`, {
|
|
723
|
+
headers: {
|
|
724
|
+
"Content-Type": "application/json",
|
|
725
|
+
},
|
|
726
|
+
method: "GET",
|
|
727
|
+
});
|
|
1220
728
|
|
|
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();
|
|
729
|
+
if (!response.ok) {
|
|
730
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1286
731
|
}
|
|
732
|
+
|
|
733
|
+
const data: PingResponse = await response.json();
|
|
734
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
735
|
+
return data.timestamp;
|
|
1287
736
|
} catch (error) {
|
|
1288
|
-
console.error("[HTTP Client] Error
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
737
|
+
console.error("[HTTP Client] Error pinging server:", error);
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
async getCommands(): Promise<string[]> {
|
|
743
|
+
try {
|
|
744
|
+
const response = await fetch(`${this.baseUrl}/api/commands`, {
|
|
745
|
+
headers: {
|
|
746
|
+
"Content-Type": "application/json",
|
|
747
|
+
},
|
|
748
|
+
method: "GET",
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
if (!response.ok) {
|
|
752
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
const data: CommandsResponse = await response.json();
|
|
756
|
+
console.log(
|
|
757
|
+
`[HTTP Client] Available commands: ${data.availableCommands.length}`
|
|
1293
758
|
);
|
|
759
|
+
return data.availableCommands;
|
|
760
|
+
} catch (error) {
|
|
761
|
+
console.error("[HTTP Client] Error getting commands:", error);
|
|
1294
762
|
throw error;
|
|
1295
763
|
}
|
|
1296
764
|
}
|
|
1297
765
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
):
|
|
766
|
+
getSessionId(): string | null {
|
|
767
|
+
return this.sessionId;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
setSessionId(sessionId: string): void {
|
|
771
|
+
this.sessionId = sessionId;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
clearSession(): void {
|
|
775
|
+
this.sessionId = null;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Process management methods
|
|
779
|
+
async startProcess(
|
|
780
|
+
command: string,
|
|
781
|
+
options?: {
|
|
782
|
+
processId?: string;
|
|
783
|
+
sessionId?: string;
|
|
784
|
+
timeout?: number;
|
|
785
|
+
env?: Record<string, string>;
|
|
786
|
+
cwd?: string;
|
|
787
|
+
encoding?: string;
|
|
788
|
+
autoCleanup?: boolean;
|
|
789
|
+
}
|
|
790
|
+
): Promise<StartProcessResponse> {
|
|
1303
791
|
try {
|
|
1304
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
792
|
+
const targetSessionId = options?.sessionId || this.sessionId;
|
|
1305
793
|
|
|
1306
|
-
const response = await this.doFetch(
|
|
794
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1307
795
|
body: JSON.stringify({
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
796
|
+
command,
|
|
797
|
+
options: {
|
|
798
|
+
...options,
|
|
799
|
+
sessionId: targetSessionId,
|
|
800
|
+
},
|
|
801
|
+
} as StartProcessRequest),
|
|
1312
802
|
headers: {
|
|
1313
803
|
"Content-Type": "application/json",
|
|
1314
804
|
},
|
|
@@ -1324,36 +814,25 @@ export class HttpClient {
|
|
|
1324
814
|
);
|
|
1325
815
|
}
|
|
1326
816
|
|
|
1327
|
-
const data:
|
|
817
|
+
const data: StartProcessResponse = await response.json();
|
|
1328
818
|
console.log(
|
|
1329
|
-
`[HTTP Client]
|
|
819
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
1330
820
|
);
|
|
1331
821
|
|
|
1332
822
|
return data;
|
|
1333
823
|
} catch (error) {
|
|
1334
|
-
console.error("[HTTP Client] Error
|
|
824
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1335
825
|
throw error;
|
|
1336
826
|
}
|
|
1337
827
|
}
|
|
1338
828
|
|
|
1339
|
-
async
|
|
1340
|
-
oldPath: string,
|
|
1341
|
-
newPath: string,
|
|
1342
|
-
sessionId?: string
|
|
1343
|
-
): Promise<void> {
|
|
829
|
+
async listProcesses(): Promise<ListProcessesResponse> {
|
|
1344
830
|
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
|
-
}),
|
|
831
|
+
const response = await this.doFetch("/api/process/list", {
|
|
1353
832
|
headers: {
|
|
1354
833
|
"Content-Type": "application/json",
|
|
1355
834
|
},
|
|
1356
|
-
method: "
|
|
835
|
+
method: "GET",
|
|
1357
836
|
});
|
|
1358
837
|
|
|
1359
838
|
if (!response.ok) {
|
|
@@ -1365,108 +844,23 @@ export class HttpClient {
|
|
|
1365
844
|
);
|
|
1366
845
|
}
|
|
1367
846
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
}
|
|
847
|
+
const data: ListProcessesResponse = await response.json();
|
|
848
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
1371
849
|
|
|
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
|
-
}
|
|
850
|
+
return data;
|
|
1441
851
|
} 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
|
-
);
|
|
852
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1448
853
|
throw error;
|
|
1449
854
|
}
|
|
1450
855
|
}
|
|
1451
856
|
|
|
1452
|
-
async
|
|
1453
|
-
sourcePath: string,
|
|
1454
|
-
destinationPath: string,
|
|
1455
|
-
sessionId?: string
|
|
1456
|
-
): Promise<MoveFileResponse> {
|
|
857
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1457
858
|
try {
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
const response = await this.doFetch(`/api/move`, {
|
|
1461
|
-
body: JSON.stringify({
|
|
1462
|
-
destinationPath,
|
|
1463
|
-
sessionId: targetSessionId,
|
|
1464
|
-
sourcePath,
|
|
1465
|
-
}),
|
|
859
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1466
860
|
headers: {
|
|
1467
861
|
"Content-Type": "application/json",
|
|
1468
862
|
},
|
|
1469
|
-
method: "
|
|
863
|
+
method: "GET",
|
|
1470
864
|
});
|
|
1471
865
|
|
|
1472
866
|
if (!response.ok) {
|
|
@@ -1478,36 +872,29 @@ export class HttpClient {
|
|
|
1478
872
|
);
|
|
1479
873
|
}
|
|
1480
874
|
|
|
1481
|
-
const data:
|
|
875
|
+
const data: GetProcessResponse = await response.json();
|
|
1482
876
|
console.log(
|
|
1483
|
-
`[HTTP Client]
|
|
877
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
878
|
+
data.process?.status || "not found"
|
|
879
|
+
}`
|
|
1484
880
|
);
|
|
1485
881
|
|
|
1486
882
|
return data;
|
|
1487
883
|
} catch (error) {
|
|
1488
|
-
console.error("[HTTP Client] Error
|
|
884
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1489
885
|
throw error;
|
|
1490
886
|
}
|
|
1491
887
|
}
|
|
1492
888
|
|
|
1493
|
-
async
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
sessionId?: string
|
|
1497
|
-
): Promise<void> {
|
|
889
|
+
async killProcess(
|
|
890
|
+
processId: string
|
|
891
|
+
): Promise<{ success: boolean; message: string }> {
|
|
1498
892
|
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
|
-
}),
|
|
893
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1507
894
|
headers: {
|
|
1508
895
|
"Content-Type": "application/json",
|
|
1509
896
|
},
|
|
1510
|
-
method: "
|
|
897
|
+
method: "DELETE",
|
|
1511
898
|
});
|
|
1512
899
|
|
|
1513
900
|
if (!response.ok) {
|
|
@@ -1519,118 +906,58 @@ export class HttpClient {
|
|
|
1519
906
|
);
|
|
1520
907
|
}
|
|
1521
908
|
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
909
|
+
const data = (await response.json()) as {
|
|
910
|
+
success: boolean;
|
|
911
|
+
message: string;
|
|
912
|
+
};
|
|
913
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1525
914
|
|
|
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
|
-
}
|
|
915
|
+
return data;
|
|
1598
916
|
} 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
|
-
);
|
|
917
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
1605
918
|
throw error;
|
|
1606
919
|
}
|
|
1607
920
|
}
|
|
1608
921
|
|
|
1609
|
-
async
|
|
922
|
+
async killAllProcesses(): Promise<{
|
|
923
|
+
success: boolean;
|
|
924
|
+
killedCount: number;
|
|
925
|
+
message: string;
|
|
926
|
+
}> {
|
|
1610
927
|
try {
|
|
1611
|
-
const response = await this.doFetch(
|
|
928
|
+
const response = await this.doFetch("/api/process/kill-all", {
|
|
1612
929
|
headers: {
|
|
1613
930
|
"Content-Type": "application/json",
|
|
1614
931
|
},
|
|
1615
|
-
method: "
|
|
932
|
+
method: "DELETE",
|
|
1616
933
|
});
|
|
1617
934
|
|
|
1618
935
|
if (!response.ok) {
|
|
1619
|
-
|
|
936
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
937
|
+
error?: string;
|
|
938
|
+
};
|
|
939
|
+
throw new Error(
|
|
940
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
941
|
+
);
|
|
1620
942
|
}
|
|
1621
943
|
|
|
1622
|
-
const data
|
|
1623
|
-
|
|
1624
|
-
|
|
944
|
+
const data = (await response.json()) as {
|
|
945
|
+
success: boolean;
|
|
946
|
+
killedCount: number;
|
|
947
|
+
message: string;
|
|
948
|
+
};
|
|
949
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
950
|
+
|
|
951
|
+
return data;
|
|
1625
952
|
} catch (error) {
|
|
1626
|
-
console.error("[HTTP Client] Error
|
|
953
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
1627
954
|
throw error;
|
|
1628
955
|
}
|
|
1629
956
|
}
|
|
1630
957
|
|
|
1631
|
-
async
|
|
958
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
1632
959
|
try {
|
|
1633
|
-
const response = await
|
|
960
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
1634
961
|
headers: {
|
|
1635
962
|
"Content-Type": "application/json",
|
|
1636
963
|
},
|
|
@@ -1638,292 +965,57 @@ export class HttpClient {
|
|
|
1638
965
|
});
|
|
1639
966
|
|
|
1640
967
|
if (!response.ok) {
|
|
1641
|
-
|
|
968
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
969
|
+
error?: string;
|
|
970
|
+
};
|
|
971
|
+
throw new Error(
|
|
972
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
973
|
+
);
|
|
1642
974
|
}
|
|
1643
975
|
|
|
1644
|
-
const data:
|
|
1645
|
-
console.log(
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
return data.availableCommands;
|
|
976
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
977
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
978
|
+
|
|
979
|
+
return data;
|
|
1649
980
|
} catch (error) {
|
|
1650
|
-
console.error("[HTTP Client] Error getting
|
|
981
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1651
982
|
throw error;
|
|
1652
983
|
}
|
|
1653
984
|
}
|
|
1654
985
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
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
|
-
}
|
|
986
|
+
async streamProcessLogs(
|
|
987
|
+
processId: string
|
|
988
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
989
|
+
try {
|
|
990
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
991
|
+
headers: {
|
|
992
|
+
Accept: "text/event-stream",
|
|
993
|
+
"Cache-Control": "no-cache",
|
|
994
|
+
},
|
|
995
|
+
method: "GET",
|
|
996
|
+
});
|
|
1866
997
|
|
|
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
|
-
}
|
|
998
|
+
if (!response.ok) {
|
|
999
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1000
|
+
error?: string;
|
|
1001
|
+
};
|
|
1002
|
+
throw new Error(
|
|
1003
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1882
1006
|
|
|
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
|
-
}
|
|
1007
|
+
if (!response.body) {
|
|
1008
|
+
throw new Error("No response body for streaming request");
|
|
1009
|
+
}
|
|
1898
1010
|
|
|
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
|
-
}
|
|
1011
|
+
console.log(
|
|
1012
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1013
|
+
);
|
|
1914
1014
|
|
|
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();
|
|
1015
|
+
return response.body;
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1928
1020
|
}
|
|
1929
1021
|
}
|