@cloudflare/sandbox 0.0.0-c5bd973 → 0.0.0-c87db11
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 +86 -0
- package/Dockerfile +101 -11
- package/README.md +743 -24
- package/container_src/bun.lock +122 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/handler/exec.ts +340 -0
- package/container_src/handler/file.ts +844 -0
- package/container_src/handler/git.ts +182 -0
- package/container_src/handler/ports.ts +314 -0
- package/container_src/handler/process.ts +640 -0
- package/container_src/index.ts +395 -2645
- package/container_src/jupyter-server.ts +579 -0
- package/container_src/jupyter-service.ts +458 -0
- package/container_src/jupyter_config.py +48 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/startup.sh +84 -0
- package/container_src/types.ts +108 -0
- package/package.json +5 -9
- package/src/client.ts +346 -1275
- 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,11 +193,13 @@ 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
|
-
const url = this.options.stub
|
|
200
|
+
const url = this.options.stub
|
|
201
|
+
? `http://localhost:${this.options.port}${path}`
|
|
202
|
+
: `${this.baseUrl}${path}`;
|
|
207
203
|
const method = options?.method || "GET";
|
|
208
204
|
|
|
209
205
|
console.log(`[HTTP Client] Making ${method} request to ${url}`);
|
|
@@ -212,15 +208,23 @@ export class HttpClient {
|
|
|
212
208
|
let response: Response;
|
|
213
209
|
|
|
214
210
|
if (this.options.stub) {
|
|
215
|
-
response = await this.options.stub.containerFetch(
|
|
211
|
+
response = await this.options.stub.containerFetch(
|
|
212
|
+
url,
|
|
213
|
+
options,
|
|
214
|
+
this.options.port
|
|
215
|
+
);
|
|
216
216
|
} else {
|
|
217
|
-
response = await fetch(
|
|
217
|
+
response = await fetch(url, options);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
console.log(
|
|
220
|
+
console.log(
|
|
221
|
+
`[HTTP Client] Response: ${response.status} ${response.statusText}`
|
|
222
|
+
);
|
|
221
223
|
|
|
222
224
|
if (!response.ok) {
|
|
223
|
-
console.error(
|
|
225
|
+
console.error(
|
|
226
|
+
`[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`
|
|
227
|
+
);
|
|
224
228
|
}
|
|
225
229
|
|
|
226
230
|
return response;
|
|
@@ -229,117 +233,22 @@ export class HttpClient {
|
|
|
229
233
|
throw error;
|
|
230
234
|
}
|
|
231
235
|
}
|
|
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
236
|
|
|
329
237
|
async execute(
|
|
330
238
|
command: string,
|
|
331
|
-
|
|
332
|
-
sessionId?: string
|
|
239
|
+
options: Pick<BaseExecOptions, "sessionId" | "cwd" | "env">
|
|
333
240
|
): Promise<ExecuteResponse> {
|
|
334
241
|
try {
|
|
335
|
-
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;
|
|
336
249
|
|
|
337
250
|
const response = await this.doFetch(`/api/execute`, {
|
|
338
|
-
body: JSON.stringify(
|
|
339
|
-
args,
|
|
340
|
-
command,
|
|
341
|
-
sessionId: targetSessionId,
|
|
342
|
-
}),
|
|
251
|
+
body: JSON.stringify(executeRequest),
|
|
343
252
|
headers: {
|
|
344
253
|
"Content-Type": "application/json",
|
|
345
254
|
},
|
|
@@ -366,8 +275,7 @@ export class HttpClient {
|
|
|
366
275
|
data.exitCode,
|
|
367
276
|
data.stdout,
|
|
368
277
|
data.stderr,
|
|
369
|
-
data.command
|
|
370
|
-
data.args
|
|
278
|
+
data.command
|
|
371
279
|
);
|
|
372
280
|
|
|
373
281
|
return data;
|
|
@@ -375,29 +283,27 @@ export class HttpClient {
|
|
|
375
283
|
console.error("[HTTP Client] Error executing command:", error);
|
|
376
284
|
this.options.onError?.(
|
|
377
285
|
error instanceof Error ? error.message : "Unknown error",
|
|
378
|
-
command
|
|
379
|
-
args
|
|
286
|
+
command
|
|
380
287
|
);
|
|
381
288
|
throw error;
|
|
382
289
|
}
|
|
383
290
|
}
|
|
384
291
|
|
|
385
|
-
async
|
|
292
|
+
async executeCommandStream(
|
|
386
293
|
command: string,
|
|
387
|
-
args: string[] = [],
|
|
388
294
|
sessionId?: string
|
|
389
|
-
): Promise<
|
|
295
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
390
296
|
try {
|
|
391
297
|
const targetSessionId = sessionId || this.sessionId;
|
|
392
298
|
|
|
393
299
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
394
300
|
body: JSON.stringify({
|
|
395
|
-
args,
|
|
396
301
|
command,
|
|
397
302
|
sessionId: targetSessionId,
|
|
398
303
|
}),
|
|
399
304
|
headers: {
|
|
400
305
|
"Content-Type": "application/json",
|
|
306
|
+
Accept: "text/event-stream",
|
|
401
307
|
},
|
|
402
308
|
method: "POST",
|
|
403
309
|
});
|
|
@@ -415,95 +321,11 @@ export class HttpClient {
|
|
|
415
321
|
throw new Error("No response body for streaming request");
|
|
416
322
|
}
|
|
417
323
|
|
|
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
|
-
}
|
|
324
|
+
console.log(`[HTTP Client] Started command stream: ${command}`);
|
|
325
|
+
|
|
326
|
+
return response.body;
|
|
500
327
|
} catch (error) {
|
|
501
|
-
console.error("[HTTP Client] Error in
|
|
502
|
-
this.options.onError?.(
|
|
503
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
504
|
-
command,
|
|
505
|
-
args
|
|
506
|
-
);
|
|
328
|
+
console.error("[HTTP Client] Error in command stream:", error);
|
|
507
329
|
throw error;
|
|
508
330
|
}
|
|
509
331
|
}
|
|
@@ -523,7 +345,7 @@ export class HttpClient {
|
|
|
523
345
|
repoUrl,
|
|
524
346
|
sessionId: targetSessionId,
|
|
525
347
|
targetDir,
|
|
526
|
-
}),
|
|
348
|
+
} as GitCheckoutRequest),
|
|
527
349
|
headers: {
|
|
528
350
|
"Content-Type": "application/json",
|
|
529
351
|
},
|
|
@@ -551,22 +373,20 @@ export class HttpClient {
|
|
|
551
373
|
}
|
|
552
374
|
}
|
|
553
375
|
|
|
554
|
-
async
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
targetDir?: string,
|
|
376
|
+
async mkdir(
|
|
377
|
+
path: string,
|
|
378
|
+
recursive: boolean = false,
|
|
558
379
|
sessionId?: string
|
|
559
|
-
): Promise<
|
|
380
|
+
): Promise<MkdirResponse> {
|
|
560
381
|
try {
|
|
561
382
|
const targetSessionId = sessionId || this.sessionId;
|
|
562
383
|
|
|
563
|
-
const response = await this.doFetch(`/api/
|
|
384
|
+
const response = await this.doFetch(`/api/mkdir`, {
|
|
564
385
|
body: JSON.stringify({
|
|
565
|
-
|
|
566
|
-
|
|
386
|
+
path,
|
|
387
|
+
recursive,
|
|
567
388
|
sessionId: targetSessionId,
|
|
568
|
-
|
|
569
|
-
}),
|
|
389
|
+
} as MkdirRequest),
|
|
570
390
|
headers: {
|
|
571
391
|
"Content-Type": "application/json",
|
|
572
392
|
},
|
|
@@ -582,119 +402,34 @@ export class HttpClient {
|
|
|
582
402
|
);
|
|
583
403
|
}
|
|
584
404
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
405
|
+
const data: MkdirResponse = await response.json();
|
|
406
|
+
console.log(
|
|
407
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`
|
|
408
|
+
);
|
|
588
409
|
|
|
589
|
-
|
|
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
|
-
}
|
|
410
|
+
return data;
|
|
673
411
|
} catch (error) {
|
|
674
|
-
console.error("[HTTP Client] Error
|
|
675
|
-
this.options.onError?.(
|
|
676
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
677
|
-
"git clone",
|
|
678
|
-
[branch, repoUrl, targetDir || ""]
|
|
679
|
-
);
|
|
412
|
+
console.error("[HTTP Client] Error creating directory:", error);
|
|
680
413
|
throw error;
|
|
681
414
|
}
|
|
682
415
|
}
|
|
683
416
|
|
|
684
|
-
async
|
|
417
|
+
async writeFile(
|
|
685
418
|
path: string,
|
|
686
|
-
|
|
419
|
+
content: string,
|
|
420
|
+
encoding: string = "utf-8",
|
|
687
421
|
sessionId?: string
|
|
688
|
-
): Promise<
|
|
422
|
+
): Promise<WriteFileResponse> {
|
|
689
423
|
try {
|
|
690
424
|
const targetSessionId = sessionId || this.sessionId;
|
|
691
425
|
|
|
692
|
-
const response = await this.doFetch(`/api/
|
|
426
|
+
const response = await this.doFetch(`/api/write`, {
|
|
693
427
|
body: JSON.stringify({
|
|
428
|
+
content,
|
|
429
|
+
encoding,
|
|
694
430
|
path,
|
|
695
|
-
recursive,
|
|
696
431
|
sessionId: targetSessionId,
|
|
697
|
-
}),
|
|
432
|
+
} as WriteFileRequest),
|
|
698
433
|
headers: {
|
|
699
434
|
"Content-Type": "application/json",
|
|
700
435
|
},
|
|
@@ -710,32 +445,32 @@ export class HttpClient {
|
|
|
710
445
|
);
|
|
711
446
|
}
|
|
712
447
|
|
|
713
|
-
const data:
|
|
448
|
+
const data: WriteFileResponse = await response.json();
|
|
714
449
|
console.log(
|
|
715
|
-
`[HTTP Client]
|
|
450
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}`
|
|
716
451
|
);
|
|
717
452
|
|
|
718
453
|
return data;
|
|
719
454
|
} catch (error) {
|
|
720
|
-
console.error("[HTTP Client] Error
|
|
455
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
721
456
|
throw error;
|
|
722
457
|
}
|
|
723
458
|
}
|
|
724
459
|
|
|
725
|
-
async
|
|
460
|
+
async readFile(
|
|
726
461
|
path: string,
|
|
727
|
-
|
|
462
|
+
encoding: string = "utf-8",
|
|
728
463
|
sessionId?: string
|
|
729
|
-
): Promise<
|
|
464
|
+
): Promise<ReadFileResponse> {
|
|
730
465
|
try {
|
|
731
466
|
const targetSessionId = sessionId || this.sessionId;
|
|
732
467
|
|
|
733
|
-
const response = await this.doFetch(`/api/
|
|
468
|
+
const response = await this.doFetch(`/api/read`, {
|
|
734
469
|
body: JSON.stringify({
|
|
470
|
+
encoding,
|
|
735
471
|
path,
|
|
736
|
-
recursive,
|
|
737
472
|
sessionId: targetSessionId,
|
|
738
|
-
}),
|
|
473
|
+
} as ReadFileRequest),
|
|
739
474
|
headers: {
|
|
740
475
|
"Content-Type": "application/json",
|
|
741
476
|
},
|
|
@@ -751,117 +486,30 @@ export class HttpClient {
|
|
|
751
486
|
);
|
|
752
487
|
}
|
|
753
488
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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
|
+
);
|
|
757
493
|
|
|
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
|
-
}
|
|
494
|
+
return data;
|
|
838
495
|
} 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
|
-
);
|
|
496
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
845
497
|
throw error;
|
|
846
498
|
}
|
|
847
499
|
}
|
|
848
500
|
|
|
849
|
-
async
|
|
501
|
+
async deleteFile(
|
|
850
502
|
path: string,
|
|
851
|
-
content: string,
|
|
852
|
-
encoding: string = "utf-8",
|
|
853
503
|
sessionId?: string
|
|
854
|
-
): Promise<
|
|
504
|
+
): Promise<DeleteFileResponse> {
|
|
855
505
|
try {
|
|
856
506
|
const targetSessionId = sessionId || this.sessionId;
|
|
857
507
|
|
|
858
|
-
const response = await this.doFetch(`/api/
|
|
508
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
859
509
|
body: JSON.stringify({
|
|
860
|
-
content,
|
|
861
|
-
encoding,
|
|
862
510
|
path,
|
|
863
511
|
sessionId: targetSessionId,
|
|
864
|
-
}),
|
|
512
|
+
} as DeleteFileRequest),
|
|
865
513
|
headers: {
|
|
866
514
|
"Content-Type": "application/json",
|
|
867
515
|
},
|
|
@@ -877,34 +525,32 @@ export class HttpClient {
|
|
|
877
525
|
);
|
|
878
526
|
}
|
|
879
527
|
|
|
880
|
-
const data:
|
|
528
|
+
const data: DeleteFileResponse = await response.json();
|
|
881
529
|
console.log(
|
|
882
|
-
`[HTTP Client] File
|
|
530
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
883
531
|
);
|
|
884
532
|
|
|
885
533
|
return data;
|
|
886
534
|
} catch (error) {
|
|
887
|
-
console.error("[HTTP Client] Error
|
|
535
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
888
536
|
throw error;
|
|
889
537
|
}
|
|
890
538
|
}
|
|
891
539
|
|
|
892
|
-
async
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
encoding: string = "utf-8",
|
|
540
|
+
async renameFile(
|
|
541
|
+
oldPath: string,
|
|
542
|
+
newPath: string,
|
|
896
543
|
sessionId?: string
|
|
897
|
-
): Promise<
|
|
544
|
+
): Promise<RenameFileResponse> {
|
|
898
545
|
try {
|
|
899
546
|
const targetSessionId = sessionId || this.sessionId;
|
|
900
547
|
|
|
901
|
-
const response = await this.doFetch(`/api/
|
|
548
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
902
549
|
body: JSON.stringify({
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
path,
|
|
550
|
+
newPath,
|
|
551
|
+
oldPath,
|
|
906
552
|
sessionId: targetSessionId,
|
|
907
|
-
}),
|
|
553
|
+
} as RenameFileRequest),
|
|
908
554
|
headers: {
|
|
909
555
|
"Content-Type": "application/json",
|
|
910
556
|
},
|
|
@@ -920,114 +566,32 @@ export class HttpClient {
|
|
|
920
566
|
);
|
|
921
567
|
}
|
|
922
568
|
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
569
|
+
const data: RenameFileResponse = await response.json();
|
|
570
|
+
console.log(
|
|
571
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`
|
|
572
|
+
);
|
|
926
573
|
|
|
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
|
-
}
|
|
574
|
+
return data;
|
|
1006
575
|
} 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
|
-
);
|
|
576
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
1013
577
|
throw error;
|
|
1014
578
|
}
|
|
1015
579
|
}
|
|
1016
580
|
|
|
1017
|
-
async
|
|
1018
|
-
|
|
1019
|
-
|
|
581
|
+
async moveFile(
|
|
582
|
+
sourcePath: string,
|
|
583
|
+
destinationPath: string,
|
|
1020
584
|
sessionId?: string
|
|
1021
|
-
): Promise<
|
|
585
|
+
): Promise<MoveFileResponse> {
|
|
1022
586
|
try {
|
|
1023
587
|
const targetSessionId = sessionId || this.sessionId;
|
|
1024
588
|
|
|
1025
|
-
const response = await this.doFetch(`/api/
|
|
589
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1026
590
|
body: JSON.stringify({
|
|
1027
|
-
|
|
1028
|
-
path,
|
|
591
|
+
destinationPath,
|
|
1029
592
|
sessionId: targetSessionId,
|
|
1030
|
-
|
|
593
|
+
sourcePath,
|
|
594
|
+
} as MoveFileRequest),
|
|
1031
595
|
headers: {
|
|
1032
596
|
"Content-Type": "application/json",
|
|
1033
597
|
},
|
|
@@ -1043,31 +607,24 @@ export class HttpClient {
|
|
|
1043
607
|
);
|
|
1044
608
|
}
|
|
1045
609
|
|
|
1046
|
-
const data:
|
|
610
|
+
const data: MoveFileResponse = await response.json();
|
|
1047
611
|
console.log(
|
|
1048
|
-
`[HTTP Client] File
|
|
612
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
1049
613
|
);
|
|
1050
614
|
|
|
1051
615
|
return data;
|
|
1052
616
|
} catch (error) {
|
|
1053
|
-
console.error("[HTTP Client] Error
|
|
617
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1054
618
|
throw error;
|
|
1055
619
|
}
|
|
1056
620
|
}
|
|
1057
621
|
|
|
1058
|
-
async
|
|
1059
|
-
path: string,
|
|
1060
|
-
encoding: string = "utf-8",
|
|
1061
|
-
sessionId?: string
|
|
1062
|
-
): Promise<void> {
|
|
622
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1063
623
|
try {
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
const response = await this.doFetch(`/api/read/stream`, {
|
|
624
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1067
625
|
body: JSON.stringify({
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
sessionId: targetSessionId,
|
|
626
|
+
port,
|
|
627
|
+
name,
|
|
1071
628
|
}),
|
|
1072
629
|
headers: {
|
|
1073
630
|
"Content-Type": "application/json",
|
|
@@ -1079,115 +636,36 @@ export class HttpClient {
|
|
|
1079
636
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1080
637
|
error?: string;
|
|
1081
638
|
};
|
|
639
|
+
console.log(errorData);
|
|
1082
640
|
throw new Error(
|
|
1083
641
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1084
642
|
);
|
|
1085
643
|
}
|
|
1086
644
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
645
|
+
const data: ExposePortResponse = await response.json();
|
|
646
|
+
console.log(
|
|
647
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
648
|
+
name ? ` (${name})` : ""
|
|
649
|
+
}, Success: ${data.success}`
|
|
650
|
+
);
|
|
1090
651
|
|
|
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
|
-
}
|
|
652
|
+
return data;
|
|
1164
653
|
} 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
|
-
);
|
|
654
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1171
655
|
throw error;
|
|
1172
656
|
}
|
|
1173
657
|
}
|
|
1174
658
|
|
|
1175
|
-
async
|
|
1176
|
-
path: string,
|
|
1177
|
-
sessionId?: string
|
|
1178
|
-
): Promise<DeleteFileResponse> {
|
|
659
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1179
660
|
try {
|
|
1180
|
-
const
|
|
1181
|
-
|
|
1182
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
661
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1183
662
|
body: JSON.stringify({
|
|
1184
|
-
|
|
1185
|
-
sessionId: targetSessionId,
|
|
663
|
+
port,
|
|
1186
664
|
}),
|
|
1187
665
|
headers: {
|
|
1188
666
|
"Content-Type": "application/json",
|
|
1189
667
|
},
|
|
1190
|
-
method: "
|
|
668
|
+
method: "DELETE",
|
|
1191
669
|
});
|
|
1192
670
|
|
|
1193
671
|
if (!response.ok) {
|
|
@@ -1199,31 +677,25 @@ export class HttpClient {
|
|
|
1199
677
|
);
|
|
1200
678
|
}
|
|
1201
679
|
|
|
1202
|
-
const data:
|
|
680
|
+
const data: UnexposePortResponse = await response.json();
|
|
1203
681
|
console.log(
|
|
1204
|
-
`[HTTP Client]
|
|
682
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
1205
683
|
);
|
|
1206
684
|
|
|
1207
685
|
return data;
|
|
1208
686
|
} catch (error) {
|
|
1209
|
-
console.error("[HTTP Client] Error
|
|
687
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
1210
688
|
throw error;
|
|
1211
689
|
}
|
|
1212
690
|
}
|
|
1213
691
|
|
|
1214
|
-
async
|
|
692
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
1215
693
|
try {
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
1219
|
-
body: JSON.stringify({
|
|
1220
|
-
path,
|
|
1221
|
-
sessionId: targetSessionId,
|
|
1222
|
-
}),
|
|
694
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
1223
695
|
headers: {
|
|
1224
696
|
"Content-Type": "application/json",
|
|
1225
697
|
},
|
|
1226
|
-
method: "
|
|
698
|
+
method: "GET",
|
|
1227
699
|
});
|
|
1228
700
|
|
|
1229
701
|
if (!response.ok) {
|
|
@@ -1235,101 +707,98 @@ export class HttpClient {
|
|
|
1235
707
|
);
|
|
1236
708
|
}
|
|
1237
709
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
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
|
+
});
|
|
1241
728
|
|
|
1242
|
-
|
|
1243
|
-
|
|
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();
|
|
729
|
+
if (!response.ok) {
|
|
730
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
1307
731
|
}
|
|
732
|
+
|
|
733
|
+
const data: PingResponse = await response.json();
|
|
734
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
735
|
+
return data.timestamp;
|
|
1308
736
|
} catch (error) {
|
|
1309
|
-
console.error("[HTTP Client] Error
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
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}`
|
|
1314
758
|
);
|
|
759
|
+
return data.availableCommands;
|
|
760
|
+
} catch (error) {
|
|
761
|
+
console.error("[HTTP Client] Error getting commands:", error);
|
|
1315
762
|
throw error;
|
|
1316
763
|
}
|
|
1317
764
|
}
|
|
1318
765
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
):
|
|
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> {
|
|
1324
791
|
try {
|
|
1325
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
792
|
+
const targetSessionId = options?.sessionId || this.sessionId;
|
|
1326
793
|
|
|
1327
|
-
const response = await this.doFetch(
|
|
794
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1328
795
|
body: JSON.stringify({
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
796
|
+
command,
|
|
797
|
+
options: {
|
|
798
|
+
...options,
|
|
799
|
+
sessionId: targetSessionId,
|
|
800
|
+
},
|
|
801
|
+
} as StartProcessRequest),
|
|
1333
802
|
headers: {
|
|
1334
803
|
"Content-Type": "application/json",
|
|
1335
804
|
},
|
|
@@ -1345,36 +814,25 @@ export class HttpClient {
|
|
|
1345
814
|
);
|
|
1346
815
|
}
|
|
1347
816
|
|
|
1348
|
-
const data:
|
|
817
|
+
const data: StartProcessResponse = await response.json();
|
|
1349
818
|
console.log(
|
|
1350
|
-
`[HTTP Client]
|
|
819
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
1351
820
|
);
|
|
1352
821
|
|
|
1353
822
|
return data;
|
|
1354
823
|
} catch (error) {
|
|
1355
|
-
console.error("[HTTP Client] Error
|
|
824
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1356
825
|
throw error;
|
|
1357
826
|
}
|
|
1358
827
|
}
|
|
1359
828
|
|
|
1360
|
-
async
|
|
1361
|
-
oldPath: string,
|
|
1362
|
-
newPath: string,
|
|
1363
|
-
sessionId?: string
|
|
1364
|
-
): Promise<void> {
|
|
829
|
+
async listProcesses(): Promise<ListProcessesResponse> {
|
|
1365
830
|
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
|
-
}),
|
|
831
|
+
const response = await this.doFetch("/api/process/list", {
|
|
1374
832
|
headers: {
|
|
1375
833
|
"Content-Type": "application/json",
|
|
1376
834
|
},
|
|
1377
|
-
method: "
|
|
835
|
+
method: "GET",
|
|
1378
836
|
});
|
|
1379
837
|
|
|
1380
838
|
if (!response.ok) {
|
|
@@ -1386,108 +844,23 @@ export class HttpClient {
|
|
|
1386
844
|
);
|
|
1387
845
|
}
|
|
1388
846
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
}
|
|
847
|
+
const data: ListProcessesResponse = await response.json();
|
|
848
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
1392
849
|
|
|
1393
|
-
|
|
1394
|
-
const decoder = new TextDecoder();
|
|
1395
|
-
|
|
1396
|
-
try {
|
|
1397
|
-
while (true) {
|
|
1398
|
-
const { done, value } = await reader.read();
|
|
1399
|
-
|
|
1400
|
-
if (done) {
|
|
1401
|
-
break;
|
|
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();
|
|
1461
|
-
}
|
|
850
|
+
return data;
|
|
1462
851
|
} catch (error) {
|
|
1463
|
-
console.error("[HTTP Client] Error
|
|
1464
|
-
this.options.onError?.(
|
|
1465
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1466
|
-
"rename",
|
|
1467
|
-
[oldPath, newPath]
|
|
1468
|
-
);
|
|
852
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1469
853
|
throw error;
|
|
1470
854
|
}
|
|
1471
855
|
}
|
|
1472
856
|
|
|
1473
|
-
async
|
|
1474
|
-
sourcePath: string,
|
|
1475
|
-
destinationPath: string,
|
|
1476
|
-
sessionId?: string
|
|
1477
|
-
): Promise<MoveFileResponse> {
|
|
857
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1478
858
|
try {
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
const response = await this.doFetch(`/api/move`, {
|
|
1482
|
-
body: JSON.stringify({
|
|
1483
|
-
destinationPath,
|
|
1484
|
-
sessionId: targetSessionId,
|
|
1485
|
-
sourcePath,
|
|
1486
|
-
}),
|
|
859
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1487
860
|
headers: {
|
|
1488
861
|
"Content-Type": "application/json",
|
|
1489
862
|
},
|
|
1490
|
-
method: "
|
|
863
|
+
method: "GET",
|
|
1491
864
|
});
|
|
1492
865
|
|
|
1493
866
|
if (!response.ok) {
|
|
@@ -1499,36 +872,29 @@ export class HttpClient {
|
|
|
1499
872
|
);
|
|
1500
873
|
}
|
|
1501
874
|
|
|
1502
|
-
const data:
|
|
875
|
+
const data: GetProcessResponse = await response.json();
|
|
1503
876
|
console.log(
|
|
1504
|
-
`[HTTP Client]
|
|
877
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
878
|
+
data.process?.status || "not found"
|
|
879
|
+
}`
|
|
1505
880
|
);
|
|
1506
881
|
|
|
1507
882
|
return data;
|
|
1508
883
|
} catch (error) {
|
|
1509
|
-
console.error("[HTTP Client] Error
|
|
884
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1510
885
|
throw error;
|
|
1511
886
|
}
|
|
1512
887
|
}
|
|
1513
888
|
|
|
1514
|
-
async
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
sessionId?: string
|
|
1518
|
-
): Promise<void> {
|
|
889
|
+
async killProcess(
|
|
890
|
+
processId: string
|
|
891
|
+
): Promise<{ success: boolean; message: string }> {
|
|
1519
892
|
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
|
-
}),
|
|
893
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1528
894
|
headers: {
|
|
1529
895
|
"Content-Type": "application/json",
|
|
1530
896
|
},
|
|
1531
|
-
method: "
|
|
897
|
+
method: "DELETE",
|
|
1532
898
|
});
|
|
1533
899
|
|
|
1534
900
|
if (!response.ok) {
|
|
@@ -1540,118 +906,58 @@ export class HttpClient {
|
|
|
1540
906
|
);
|
|
1541
907
|
}
|
|
1542
908
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
909
|
+
const data = (await response.json()) as {
|
|
910
|
+
success: boolean;
|
|
911
|
+
message: string;
|
|
912
|
+
};
|
|
913
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1546
914
|
|
|
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
|
-
}
|
|
915
|
+
return data;
|
|
1619
916
|
} 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
|
-
);
|
|
917
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
1626
918
|
throw error;
|
|
1627
919
|
}
|
|
1628
920
|
}
|
|
1629
921
|
|
|
1630
|
-
async
|
|
922
|
+
async killAllProcesses(): Promise<{
|
|
923
|
+
success: boolean;
|
|
924
|
+
killedCount: number;
|
|
925
|
+
message: string;
|
|
926
|
+
}> {
|
|
1631
927
|
try {
|
|
1632
|
-
const response = await this.doFetch(
|
|
928
|
+
const response = await this.doFetch("/api/process/kill-all", {
|
|
1633
929
|
headers: {
|
|
1634
930
|
"Content-Type": "application/json",
|
|
1635
931
|
},
|
|
1636
|
-
method: "
|
|
932
|
+
method: "DELETE",
|
|
1637
933
|
});
|
|
1638
934
|
|
|
1639
935
|
if (!response.ok) {
|
|
1640
|
-
|
|
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
|
+
);
|
|
1641
942
|
}
|
|
1642
943
|
|
|
1643
|
-
const data
|
|
1644
|
-
|
|
1645
|
-
|
|
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;
|
|
1646
952
|
} catch (error) {
|
|
1647
|
-
console.error("[HTTP Client] Error
|
|
953
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
1648
954
|
throw error;
|
|
1649
955
|
}
|
|
1650
956
|
}
|
|
1651
957
|
|
|
1652
|
-
async
|
|
958
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
1653
959
|
try {
|
|
1654
|
-
const response = await
|
|
960
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
1655
961
|
headers: {
|
|
1656
962
|
"Content-Type": "application/json",
|
|
1657
963
|
},
|
|
@@ -1659,292 +965,57 @@ export class HttpClient {
|
|
|
1659
965
|
});
|
|
1660
966
|
|
|
1661
967
|
if (!response.ok) {
|
|
1662
|
-
|
|
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
|
+
);
|
|
1663
974
|
}
|
|
1664
975
|
|
|
1665
|
-
const data:
|
|
1666
|
-
console.log(
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
return data.availableCommands;
|
|
976
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
977
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
978
|
+
|
|
979
|
+
return data;
|
|
1670
980
|
} catch (error) {
|
|
1671
|
-
console.error("[HTTP Client] Error getting
|
|
981
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1672
982
|
throw error;
|
|
1673
983
|
}
|
|
1674
984
|
}
|
|
1675
985
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
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
|
-
}
|
|
1758
|
-
|
|
1759
|
-
// Convenience function for quick streaming git checkout
|
|
1760
|
-
export async function quickGitCheckoutStream(
|
|
1761
|
-
repoUrl: string,
|
|
1762
|
-
branch: string = "main",
|
|
1763
|
-
targetDir?: string,
|
|
1764
|
-
options?: HttpClientOptions
|
|
1765
|
-
): Promise<void> {
|
|
1766
|
-
const client = createClient(options);
|
|
1767
|
-
await client.createSession();
|
|
1768
|
-
|
|
1769
|
-
try {
|
|
1770
|
-
await client.gitCheckoutStream(repoUrl, branch, targetDir);
|
|
1771
|
-
} finally {
|
|
1772
|
-
client.clearSession();
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
// Convenience function for quick streaming directory creation
|
|
1777
|
-
export async function quickMkdirStream(
|
|
1778
|
-
path: string,
|
|
1779
|
-
recursive: boolean = false,
|
|
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
|
-
}
|
|
1791
|
-
|
|
1792
|
-
// Convenience function for quick file writing
|
|
1793
|
-
export async function quickWriteFile(
|
|
1794
|
-
path: string,
|
|
1795
|
-
content: string,
|
|
1796
|
-
encoding: string = "utf-8",
|
|
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();
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
|
-
// Convenience function for quick streaming file writing
|
|
1810
|
-
export async function quickWriteFileStream(
|
|
1811
|
-
path: string,
|
|
1812
|
-
content: string,
|
|
1813
|
-
encoding: string = "utf-8",
|
|
1814
|
-
options?: HttpClientOptions
|
|
1815
|
-
): Promise<void> {
|
|
1816
|
-
const client = createClient(options);
|
|
1817
|
-
await client.createSession();
|
|
1818
|
-
|
|
1819
|
-
try {
|
|
1820
|
-
await client.writeFileStream(path, content, encoding);
|
|
1821
|
-
} finally {
|
|
1822
|
-
client.clearSession();
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
// Convenience function for quick file reading
|
|
1827
|
-
export async function quickReadFile(
|
|
1828
|
-
path: string,
|
|
1829
|
-
encoding: string = "utf-8",
|
|
1830
|
-
options?: HttpClientOptions
|
|
1831
|
-
): Promise<ReadFileResponse> {
|
|
1832
|
-
const client = createClient(options);
|
|
1833
|
-
await client.createSession();
|
|
1834
|
-
|
|
1835
|
-
try {
|
|
1836
|
-
return await client.readFile(path, encoding);
|
|
1837
|
-
} finally {
|
|
1838
|
-
client.clearSession();
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
|
|
1842
|
-
// Convenience function for quick streaming file reading
|
|
1843
|
-
export async function quickReadFileStream(
|
|
1844
|
-
path: string,
|
|
1845
|
-
encoding: string = "utf-8",
|
|
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
|
-
}
|
|
1857
|
-
|
|
1858
|
-
// Convenience function for quick file deletion
|
|
1859
|
-
export async function quickDeleteFile(
|
|
1860
|
-
path: string,
|
|
1861
|
-
options?: HttpClientOptions
|
|
1862
|
-
): Promise<DeleteFileResponse> {
|
|
1863
|
-
const client = createClient(options);
|
|
1864
|
-
await client.createSession();
|
|
1865
|
-
|
|
1866
|
-
try {
|
|
1867
|
-
return await client.deleteFile(path);
|
|
1868
|
-
} finally {
|
|
1869
|
-
client.clearSession();
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
// Convenience function for quick streaming file deletion
|
|
1874
|
-
export async function quickDeleteFileStream(
|
|
1875
|
-
path: string,
|
|
1876
|
-
options?: HttpClientOptions
|
|
1877
|
-
): Promise<void> {
|
|
1878
|
-
const client = createClient(options);
|
|
1879
|
-
await client.createSession();
|
|
1880
|
-
|
|
1881
|
-
try {
|
|
1882
|
-
await client.deleteFileStream(path);
|
|
1883
|
-
} finally {
|
|
1884
|
-
client.clearSession();
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
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
|
+
});
|
|
1887
997
|
|
|
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
|
-
}
|
|
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
|
+
}
|
|
1903
1006
|
|
|
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
|
-
}
|
|
1007
|
+
if (!response.body) {
|
|
1008
|
+
throw new Error("No response body for streaming request");
|
|
1009
|
+
}
|
|
1919
1010
|
|
|
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
|
-
}
|
|
1011
|
+
console.log(
|
|
1012
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1013
|
+
);
|
|
1935
1014
|
|
|
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();
|
|
1015
|
+
return response.body;
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1949
1020
|
}
|
|
1950
1021
|
}
|