@cloudflare/sandbox 0.0.0-db09b4d → 0.0.0-e1fa354
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 +187 -0
- package/Dockerfile +99 -11
- package/README.md +806 -22
- package/container_src/bun.lock +122 -0
- package/container_src/circuit-breaker.ts +121 -0
- package/container_src/control-process.ts +784 -0
- package/container_src/handler/exec.ts +185 -0
- package/container_src/handler/file.ts +406 -0
- package/container_src/handler/git.ts +130 -0
- package/container_src/handler/ports.ts +314 -0
- package/container_src/handler/process.ts +568 -0
- package/container_src/handler/session.ts +92 -0
- package/container_src/index.ts +448 -2467
- package/container_src/isolation.ts +1038 -0
- package/container_src/jupyter-server.ts +579 -0
- package/container_src/jupyter-service.ts +461 -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/shell-escape.ts +42 -0
- package/container_src/startup.sh +84 -0
- package/container_src/types.ts +131 -0
- package/package.json +6 -8
- package/src/client.ts +477 -1192
- package/src/errors.ts +218 -0
- package/src/index.ts +63 -78
- 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 +747 -0
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +502 -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 -710
package/src/client.ts
CHANGED
|
@@ -1,36 +1,23 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ExecuteRequest } from "../container_src/types";
|
|
2
2
|
import type { Sandbox } from "./index";
|
|
3
|
+
import type {
|
|
4
|
+
BaseExecOptions,
|
|
5
|
+
DeleteFileResponse,
|
|
6
|
+
ExecuteResponse,
|
|
7
|
+
GetProcessLogsResponse,
|
|
8
|
+
GetProcessResponse,
|
|
9
|
+
GitCheckoutResponse,
|
|
10
|
+
ListFilesResponse,
|
|
11
|
+
ListProcessesResponse,
|
|
12
|
+
MkdirResponse,
|
|
13
|
+
MoveFileResponse,
|
|
14
|
+
ReadFileResponse,
|
|
15
|
+
RenameFileResponse,
|
|
16
|
+
StartProcessRequest,
|
|
17
|
+
StartProcessResponse,
|
|
18
|
+
WriteFileResponse,
|
|
19
|
+
} from "./types";
|
|
3
20
|
|
|
4
|
-
interface ExecuteRequest {
|
|
5
|
-
command: string;
|
|
6
|
-
args?: string[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ExecuteResponse {
|
|
10
|
-
success: boolean;
|
|
11
|
-
stdout: string;
|
|
12
|
-
stderr: string;
|
|
13
|
-
exitCode: number;
|
|
14
|
-
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
|
-
timestamp: string;
|
|
33
|
-
}
|
|
34
21
|
|
|
35
22
|
interface CommandsResponse {
|
|
36
23
|
availableCommands: string[];
|
|
@@ -41,120 +28,103 @@ interface GitCheckoutRequest {
|
|
|
41
28
|
repoUrl: string;
|
|
42
29
|
branch?: string;
|
|
43
30
|
targetDir?: string;
|
|
44
|
-
sessionId
|
|
31
|
+
sessionId: string;
|
|
45
32
|
}
|
|
46
33
|
|
|
47
|
-
export interface GitCheckoutResponse {
|
|
48
|
-
success: boolean;
|
|
49
|
-
stdout: string;
|
|
50
|
-
stderr: string;
|
|
51
|
-
exitCode: number;
|
|
52
|
-
repoUrl: string;
|
|
53
|
-
branch: string;
|
|
54
|
-
targetDir: string;
|
|
55
|
-
timestamp: string;
|
|
56
|
-
}
|
|
57
34
|
|
|
58
35
|
interface MkdirRequest {
|
|
59
36
|
path: string;
|
|
60
37
|
recursive?: boolean;
|
|
61
|
-
sessionId
|
|
38
|
+
sessionId: string;
|
|
62
39
|
}
|
|
63
40
|
|
|
64
|
-
export interface MkdirResponse {
|
|
65
|
-
success: boolean;
|
|
66
|
-
stdout: string;
|
|
67
|
-
stderr: string;
|
|
68
|
-
exitCode: number;
|
|
69
|
-
path: string;
|
|
70
|
-
recursive: boolean;
|
|
71
|
-
timestamp: string;
|
|
72
|
-
}
|
|
73
41
|
|
|
74
42
|
interface WriteFileRequest {
|
|
75
43
|
path: string;
|
|
76
44
|
content: string;
|
|
77
45
|
encoding?: string;
|
|
78
|
-
sessionId
|
|
46
|
+
sessionId: string;
|
|
79
47
|
}
|
|
80
48
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
exitCode: number;
|
|
49
|
+
|
|
50
|
+
interface ReadFileRequest {
|
|
84
51
|
path: string;
|
|
85
|
-
|
|
52
|
+
encoding?: string;
|
|
53
|
+
sessionId: string;
|
|
86
54
|
}
|
|
87
55
|
|
|
56
|
+
|
|
88
57
|
interface DeleteFileRequest {
|
|
89
58
|
path: string;
|
|
90
|
-
sessionId
|
|
59
|
+
sessionId: string;
|
|
91
60
|
}
|
|
92
61
|
|
|
93
|
-
export interface DeleteFileResponse {
|
|
94
|
-
success: boolean;
|
|
95
|
-
exitCode: number;
|
|
96
|
-
path: string;
|
|
97
|
-
timestamp: string;
|
|
98
|
-
}
|
|
99
62
|
|
|
100
63
|
interface RenameFileRequest {
|
|
101
64
|
oldPath: string;
|
|
102
65
|
newPath: string;
|
|
103
|
-
sessionId
|
|
66
|
+
sessionId: string;
|
|
104
67
|
}
|
|
105
68
|
|
|
106
|
-
export interface RenameFileResponse {
|
|
107
|
-
success: boolean;
|
|
108
|
-
exitCode: number;
|
|
109
|
-
oldPath: string;
|
|
110
|
-
newPath: string;
|
|
111
|
-
timestamp: string;
|
|
112
|
-
}
|
|
113
69
|
|
|
114
70
|
interface MoveFileRequest {
|
|
115
71
|
sourcePath: string;
|
|
116
72
|
destinationPath: string;
|
|
117
|
-
sessionId
|
|
73
|
+
sessionId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
interface ListFilesRequest {
|
|
78
|
+
path: string;
|
|
79
|
+
options?: {
|
|
80
|
+
recursive?: boolean;
|
|
81
|
+
includeHidden?: boolean;
|
|
82
|
+
};
|
|
83
|
+
sessionId: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
interface PreviewInfo {
|
|
88
|
+
url: string;
|
|
89
|
+
port: number;
|
|
90
|
+
name?: string;
|
|
118
91
|
}
|
|
119
92
|
|
|
120
|
-
|
|
93
|
+
interface ExposedPort extends PreviewInfo {
|
|
94
|
+
exposedAt: string;
|
|
95
|
+
timestamp: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
interface ExposePortResponse {
|
|
121
99
|
success: boolean;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
100
|
+
port: number;
|
|
101
|
+
name?: string;
|
|
102
|
+
exposedAt: string;
|
|
125
103
|
timestamp: string;
|
|
126
104
|
}
|
|
127
105
|
|
|
128
|
-
interface
|
|
129
|
-
|
|
106
|
+
interface UnexposePortResponse {
|
|
107
|
+
success: boolean;
|
|
108
|
+
port: number;
|
|
130
109
|
timestamp: string;
|
|
131
110
|
}
|
|
132
111
|
|
|
133
|
-
interface
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
newPath?: string;
|
|
143
|
-
sourcePath?: string;
|
|
144
|
-
destinationPath?: string;
|
|
145
|
-
success?: boolean;
|
|
146
|
-
exitCode?: number;
|
|
147
|
-
stdout?: string;
|
|
148
|
-
stderr?: string;
|
|
149
|
-
error?: string;
|
|
150
|
-
timestamp?: string;
|
|
112
|
+
interface GetExposedPortsResponse {
|
|
113
|
+
ports: ExposedPort[];
|
|
114
|
+
count: number;
|
|
115
|
+
timestamp: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface PingResponse {
|
|
119
|
+
message: string;
|
|
120
|
+
timestamp: string;
|
|
151
121
|
}
|
|
152
122
|
|
|
153
123
|
interface HttpClientOptions {
|
|
154
124
|
stub?: Sandbox;
|
|
155
125
|
baseUrl?: string;
|
|
156
126
|
port?: number;
|
|
157
|
-
onCommandStart?: (command: string
|
|
127
|
+
onCommandStart?: (command: string) => void;
|
|
158
128
|
onOutput?: (
|
|
159
129
|
stream: "stdout" | "stderr",
|
|
160
130
|
data: string,
|
|
@@ -165,17 +135,14 @@ interface HttpClientOptions {
|
|
|
165
135
|
exitCode: number,
|
|
166
136
|
stdout: string,
|
|
167
137
|
stderr: string,
|
|
168
|
-
command: string
|
|
169
|
-
args: string[]
|
|
138
|
+
command: string
|
|
170
139
|
) => void;
|
|
171
|
-
onError?: (error: string, command?: string
|
|
172
|
-
onStreamEvent?: (event: StreamEvent) => void;
|
|
140
|
+
onError?: (error: string, command?: string) => void;
|
|
173
141
|
}
|
|
174
142
|
|
|
175
143
|
export class HttpClient {
|
|
176
144
|
private baseUrl: string;
|
|
177
145
|
private options: HttpClientOptions;
|
|
178
|
-
private sessionId: string | null = null;
|
|
179
146
|
|
|
180
147
|
constructor(options: HttpClientOptions = {}) {
|
|
181
148
|
this.options = {
|
|
@@ -184,130 +151,93 @@ export class HttpClient {
|
|
|
184
151
|
this.baseUrl = this.options.baseUrl!;
|
|
185
152
|
}
|
|
186
153
|
|
|
187
|
-
|
|
154
|
+
protected async doFetch(
|
|
188
155
|
path: string,
|
|
189
156
|
options?: RequestInit
|
|
190
157
|
): Promise<Response> {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
// Public methods to set event handlers
|
|
197
|
-
setOnOutput(
|
|
198
|
-
handler: (
|
|
199
|
-
stream: "stdout" | "stderr",
|
|
200
|
-
data: string,
|
|
201
|
-
command: string
|
|
202
|
-
) => void
|
|
203
|
-
): void {
|
|
204
|
-
this.options.onOutput = handler;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
setOnCommandComplete(
|
|
208
|
-
handler: (
|
|
209
|
-
success: boolean,
|
|
210
|
-
exitCode: number,
|
|
211
|
-
stdout: string,
|
|
212
|
-
stderr: string,
|
|
213
|
-
command: string,
|
|
214
|
-
args: string[]
|
|
215
|
-
) => void
|
|
216
|
-
): void {
|
|
217
|
-
this.options.onCommandComplete = handler;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
setOnStreamEvent(handler: (event: StreamEvent) => void): void {
|
|
221
|
-
this.options.onStreamEvent = handler;
|
|
222
|
-
}
|
|
158
|
+
const url = this.options.stub
|
|
159
|
+
? `http://localhost:${this.options.port}${path}`
|
|
160
|
+
: `${this.baseUrl}${path}`;
|
|
161
|
+
const method = options?.method || "GET";
|
|
223
162
|
|
|
224
|
-
|
|
225
|
-
getOnOutput():
|
|
226
|
-
| ((stream: "stdout" | "stderr", data: string, command: string) => void)
|
|
227
|
-
| undefined {
|
|
228
|
-
return this.options.onOutput;
|
|
229
|
-
}
|
|
163
|
+
console.log(`[HTTP Client] Making ${method} request to ${url}`);
|
|
230
164
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
success: boolean,
|
|
234
|
-
exitCode: number,
|
|
235
|
-
stdout: string,
|
|
236
|
-
stderr: string,
|
|
237
|
-
command: string,
|
|
238
|
-
args: string[]
|
|
239
|
-
) => void)
|
|
240
|
-
| undefined {
|
|
241
|
-
return this.options.onCommandComplete;
|
|
242
|
-
}
|
|
165
|
+
try {
|
|
166
|
+
let response: Response;
|
|
243
167
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
168
|
+
if (this.options.stub) {
|
|
169
|
+
response = await this.options.stub.containerFetch(
|
|
170
|
+
url,
|
|
171
|
+
options,
|
|
172
|
+
this.options.port
|
|
173
|
+
);
|
|
174
|
+
} else {
|
|
175
|
+
response = await fetch(url, options);
|
|
176
|
+
}
|
|
247
177
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
headers: {
|
|
252
|
-
"Content-Type": "application/json",
|
|
253
|
-
},
|
|
254
|
-
method: "POST",
|
|
255
|
-
});
|
|
178
|
+
console.log(
|
|
179
|
+
`[HTTP Client] Response: ${response.status} ${response.statusText}`
|
|
180
|
+
);
|
|
256
181
|
|
|
257
182
|
if (!response.ok) {
|
|
258
|
-
|
|
183
|
+
console.error(
|
|
184
|
+
`[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`
|
|
185
|
+
);
|
|
259
186
|
}
|
|
260
187
|
|
|
261
|
-
|
|
262
|
-
this.sessionId = data.sessionId;
|
|
263
|
-
console.log(`[HTTP Client] Created session: ${this.sessionId}`);
|
|
264
|
-
return this.sessionId;
|
|
188
|
+
return response;
|
|
265
189
|
} catch (error) {
|
|
266
|
-
console.error(
|
|
190
|
+
console.error(`[HTTP Client] Request error: ${method} ${url}`, error);
|
|
267
191
|
throw error;
|
|
268
192
|
}
|
|
269
193
|
}
|
|
270
194
|
|
|
271
|
-
async
|
|
195
|
+
async createSession(options: {
|
|
196
|
+
id: string;
|
|
197
|
+
env?: Record<string, string>;
|
|
198
|
+
cwd?: string;
|
|
199
|
+
isolation?: boolean;
|
|
200
|
+
}): Promise<{ success: boolean; id: string; message: string }> {
|
|
272
201
|
try {
|
|
273
|
-
const response = await this.doFetch(`/api/session/
|
|
202
|
+
const response = await this.doFetch(`/api/session/create`, {
|
|
203
|
+
method: "POST",
|
|
274
204
|
headers: {
|
|
275
205
|
"Content-Type": "application/json",
|
|
276
206
|
},
|
|
277
|
-
|
|
207
|
+
body: JSON.stringify(options),
|
|
278
208
|
});
|
|
279
209
|
|
|
280
210
|
if (!response.ok) {
|
|
281
|
-
|
|
211
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
212
|
+
error?: string;
|
|
213
|
+
};
|
|
214
|
+
throw new Error(
|
|
215
|
+
errorData.error || `Failed to create session: ${response.status}`
|
|
216
|
+
);
|
|
282
217
|
}
|
|
283
218
|
|
|
284
|
-
const data
|
|
285
|
-
console.log(`[HTTP Client]
|
|
219
|
+
const data = await response.json() as { success: boolean; id: string; message: string };
|
|
220
|
+
console.log(`[HTTP Client] Session created: ${options.id}`);
|
|
286
221
|
return data;
|
|
287
222
|
} catch (error) {
|
|
288
|
-
console.error("[HTTP Client] Error
|
|
223
|
+
console.error("[HTTP Client] Error creating session:", error);
|
|
289
224
|
throw error;
|
|
290
225
|
}
|
|
291
226
|
}
|
|
292
227
|
|
|
293
|
-
async
|
|
228
|
+
async exec(
|
|
229
|
+
sessionId: string,
|
|
294
230
|
command: string,
|
|
295
|
-
|
|
296
|
-
sessionId?: string
|
|
231
|
+
options?: Pick<BaseExecOptions, "cwd" | "env">
|
|
297
232
|
): Promise<ExecuteResponse> {
|
|
298
233
|
try {
|
|
299
|
-
|
|
300
|
-
|
|
234
|
+
// Always use session-specific endpoint
|
|
301
235
|
const response = await this.doFetch(`/api/execute`, {
|
|
302
|
-
|
|
303
|
-
args,
|
|
304
|
-
command,
|
|
305
|
-
sessionId: targetSessionId,
|
|
306
|
-
}),
|
|
236
|
+
method: "POST",
|
|
307
237
|
headers: {
|
|
308
238
|
"Content-Type": "application/json",
|
|
309
239
|
},
|
|
310
|
-
|
|
240
|
+
body: JSON.stringify({ id: sessionId, command }),
|
|
311
241
|
});
|
|
312
242
|
|
|
313
243
|
if (!response.ok) {
|
|
@@ -315,55 +245,57 @@ export class HttpClient {
|
|
|
315
245
|
error?: string;
|
|
316
246
|
};
|
|
317
247
|
throw new Error(
|
|
318
|
-
errorData.error || `
|
|
248
|
+
errorData.error || `Failed to execute in session: ${response.status}`
|
|
319
249
|
);
|
|
320
250
|
}
|
|
321
251
|
|
|
322
|
-
const data
|
|
252
|
+
const data = await response.json() as { stdout: string; stderr: string; exitCode: number; success: boolean };
|
|
323
253
|
console.log(
|
|
324
|
-
`[HTTP Client] Command executed
|
|
254
|
+
`[HTTP Client] Command executed in session ${sessionId}: ${command}`
|
|
325
255
|
);
|
|
256
|
+
|
|
257
|
+
// Convert to ExecuteResponse format for consistency
|
|
258
|
+
const executeResponse: ExecuteResponse = {
|
|
259
|
+
...data,
|
|
260
|
+
command,
|
|
261
|
+
timestamp: new Date().toISOString()
|
|
262
|
+
};
|
|
326
263
|
|
|
327
264
|
// Call the callback if provided
|
|
328
265
|
this.options.onCommandComplete?.(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
data.args
|
|
266
|
+
executeResponse.success,
|
|
267
|
+
executeResponse.exitCode,
|
|
268
|
+
executeResponse.stdout,
|
|
269
|
+
executeResponse.stderr,
|
|
270
|
+
executeResponse.command
|
|
335
271
|
);
|
|
336
272
|
|
|
337
|
-
return
|
|
273
|
+
return executeResponse;
|
|
338
274
|
} catch (error) {
|
|
339
|
-
console.error("[HTTP Client] Error executing
|
|
275
|
+
console.error("[HTTP Client] Error executing in session:", error);
|
|
340
276
|
this.options.onError?.(
|
|
341
277
|
error instanceof Error ? error.message : "Unknown error",
|
|
342
|
-
command
|
|
343
|
-
args
|
|
278
|
+
command
|
|
344
279
|
);
|
|
345
280
|
throw error;
|
|
346
281
|
}
|
|
347
282
|
}
|
|
348
283
|
|
|
349
|
-
async
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
): Promise<void> {
|
|
284
|
+
async execStream(
|
|
285
|
+
sessionId: string,
|
|
286
|
+
command: string
|
|
287
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
354
288
|
try {
|
|
355
|
-
|
|
356
|
-
|
|
289
|
+
// Always use session-specific streaming endpoint
|
|
357
290
|
const response = await this.doFetch(`/api/execute/stream`, {
|
|
358
|
-
|
|
359
|
-
args,
|
|
360
|
-
command,
|
|
361
|
-
sessionId: targetSessionId,
|
|
362
|
-
}),
|
|
291
|
+
method: "POST",
|
|
363
292
|
headers: {
|
|
364
293
|
"Content-Type": "application/json",
|
|
365
294
|
},
|
|
366
|
-
|
|
295
|
+
body: JSON.stringify({
|
|
296
|
+
id: sessionId,
|
|
297
|
+
command
|
|
298
|
+
}),
|
|
367
299
|
});
|
|
368
300
|
|
|
369
301
|
if (!response.ok) {
|
|
@@ -371,123 +303,38 @@ export class HttpClient {
|
|
|
371
303
|
error?: string;
|
|
372
304
|
};
|
|
373
305
|
throw new Error(
|
|
374
|
-
errorData.error || `
|
|
306
|
+
errorData.error || `Failed to stream execute in session: ${response.status}`
|
|
375
307
|
);
|
|
376
308
|
}
|
|
377
309
|
|
|
378
310
|
if (!response.body) {
|
|
379
|
-
throw new Error("No response body for streaming
|
|
311
|
+
throw new Error("No response body for streaming execution");
|
|
380
312
|
}
|
|
381
313
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
try {
|
|
386
|
-
while (true) {
|
|
387
|
-
const { done, value } = await reader.read();
|
|
388
|
-
|
|
389
|
-
if (done) {
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
394
|
-
const lines = chunk.split("\n");
|
|
395
|
-
|
|
396
|
-
for (const line of lines) {
|
|
397
|
-
if (line.startsWith("data: ")) {
|
|
398
|
-
try {
|
|
399
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
400
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
401
|
-
|
|
402
|
-
console.log(`[HTTP Client] Stream event: ${event.type}`);
|
|
403
|
-
this.options.onStreamEvent?.(event);
|
|
404
|
-
|
|
405
|
-
switch (event.type) {
|
|
406
|
-
case "command_start":
|
|
407
|
-
console.log(
|
|
408
|
-
`[HTTP Client] Command started: ${
|
|
409
|
-
event.command
|
|
410
|
-
} ${event.args?.join(" ")}`
|
|
411
|
-
);
|
|
412
|
-
this.options.onCommandStart?.(
|
|
413
|
-
event.command!,
|
|
414
|
-
event.args || []
|
|
415
|
-
);
|
|
416
|
-
break;
|
|
417
|
-
|
|
418
|
-
case "output":
|
|
419
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
420
|
-
this.options.onOutput?.(
|
|
421
|
-
event.stream!,
|
|
422
|
-
event.data!,
|
|
423
|
-
event.command!
|
|
424
|
-
);
|
|
425
|
-
break;
|
|
426
|
-
|
|
427
|
-
case "command_complete":
|
|
428
|
-
console.log(
|
|
429
|
-
`[HTTP Client] Command completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
430
|
-
);
|
|
431
|
-
this.options.onCommandComplete?.(
|
|
432
|
-
event.success!,
|
|
433
|
-
event.exitCode!,
|
|
434
|
-
event.stdout!,
|
|
435
|
-
event.stderr!,
|
|
436
|
-
event.command!,
|
|
437
|
-
event.args || []
|
|
438
|
-
);
|
|
439
|
-
break;
|
|
440
|
-
|
|
441
|
-
case "error":
|
|
442
|
-
console.error(
|
|
443
|
-
`[HTTP Client] Command error: ${event.error}`
|
|
444
|
-
);
|
|
445
|
-
this.options.onError?.(
|
|
446
|
-
event.error!,
|
|
447
|
-
event.command,
|
|
448
|
-
event.args
|
|
449
|
-
);
|
|
450
|
-
break;
|
|
451
|
-
}
|
|
452
|
-
} catch (parseError) {
|
|
453
|
-
console.warn(
|
|
454
|
-
"[HTTP Client] Failed to parse stream event:",
|
|
455
|
-
parseError
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
} finally {
|
|
462
|
-
reader.releaseLock();
|
|
463
|
-
}
|
|
464
|
-
} catch (error) {
|
|
465
|
-
console.error("[HTTP Client] Error in streaming execution:", error);
|
|
466
|
-
this.options.onError?.(
|
|
467
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
468
|
-
command,
|
|
469
|
-
args
|
|
314
|
+
console.log(
|
|
315
|
+
`[HTTP Client] Started streaming command in session ${sessionId}: ${command}`
|
|
470
316
|
);
|
|
317
|
+
return response.body;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error("[HTTP Client] Error streaming execute in session:", error);
|
|
471
320
|
throw error;
|
|
472
321
|
}
|
|
473
322
|
}
|
|
474
323
|
|
|
475
324
|
async gitCheckout(
|
|
476
325
|
repoUrl: string,
|
|
326
|
+
sessionId: string,
|
|
477
327
|
branch: string = "main",
|
|
478
|
-
targetDir?: string
|
|
479
|
-
sessionId?: string
|
|
328
|
+
targetDir?: string
|
|
480
329
|
): Promise<GitCheckoutResponse> {
|
|
481
330
|
try {
|
|
482
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
483
|
-
|
|
484
331
|
const response = await this.doFetch(`/api/git/checkout`, {
|
|
485
332
|
body: JSON.stringify({
|
|
486
333
|
branch,
|
|
487
334
|
repoUrl,
|
|
488
|
-
sessionId: targetSessionId,
|
|
489
335
|
targetDir,
|
|
490
|
-
|
|
336
|
+
sessionId,
|
|
337
|
+
} as GitCheckoutRequest),
|
|
491
338
|
headers: {
|
|
492
339
|
"Content-Type": "application/json",
|
|
493
340
|
},
|
|
@@ -515,22 +362,18 @@ export class HttpClient {
|
|
|
515
362
|
}
|
|
516
363
|
}
|
|
517
364
|
|
|
518
|
-
async
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
): Promise<void> {
|
|
365
|
+
async mkdir(
|
|
366
|
+
path: string,
|
|
367
|
+
recursive: boolean = false,
|
|
368
|
+
sessionId: string
|
|
369
|
+
): Promise<MkdirResponse> {
|
|
524
370
|
try {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
const response = await this.doFetch(`/api/git/checkout/stream`, {
|
|
371
|
+
const response = await this.doFetch(`/api/mkdir`, {
|
|
528
372
|
body: JSON.stringify({
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
sessionId
|
|
532
|
-
|
|
533
|
-
}),
|
|
373
|
+
path,
|
|
374
|
+
recursive,
|
|
375
|
+
sessionId,
|
|
376
|
+
} as MkdirRequest),
|
|
534
377
|
headers: {
|
|
535
378
|
"Content-Type": "application/json",
|
|
536
379
|
},
|
|
@@ -546,119 +389,32 @@ export class HttpClient {
|
|
|
546
389
|
);
|
|
547
390
|
}
|
|
548
391
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
392
|
+
const data: MkdirResponse = await response.json();
|
|
393
|
+
console.log(
|
|
394
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
395
|
+
);
|
|
552
396
|
|
|
553
|
-
|
|
554
|
-
const decoder = new TextDecoder();
|
|
555
|
-
|
|
556
|
-
try {
|
|
557
|
-
while (true) {
|
|
558
|
-
const { done, value } = await reader.read();
|
|
559
|
-
|
|
560
|
-
if (done) {
|
|
561
|
-
break;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
565
|
-
const lines = chunk.split("\n");
|
|
566
|
-
|
|
567
|
-
for (const line of lines) {
|
|
568
|
-
if (line.startsWith("data: ")) {
|
|
569
|
-
try {
|
|
570
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
571
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
572
|
-
|
|
573
|
-
console.log(
|
|
574
|
-
`[HTTP Client] Git checkout stream event: ${event.type}`
|
|
575
|
-
);
|
|
576
|
-
this.options.onStreamEvent?.(event);
|
|
577
|
-
|
|
578
|
-
switch (event.type) {
|
|
579
|
-
case "command_start":
|
|
580
|
-
console.log(
|
|
581
|
-
`[HTTP Client] Git checkout started: ${
|
|
582
|
-
event.command
|
|
583
|
-
} ${event.args?.join(" ")}`
|
|
584
|
-
);
|
|
585
|
-
this.options.onCommandStart?.(
|
|
586
|
-
event.command!,
|
|
587
|
-
event.args || []
|
|
588
|
-
);
|
|
589
|
-
break;
|
|
590
|
-
|
|
591
|
-
case "output":
|
|
592
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
593
|
-
this.options.onOutput?.(
|
|
594
|
-
event.stream!,
|
|
595
|
-
event.data!,
|
|
596
|
-
event.command!
|
|
597
|
-
);
|
|
598
|
-
break;
|
|
599
|
-
|
|
600
|
-
case "command_complete":
|
|
601
|
-
console.log(
|
|
602
|
-
`[HTTP Client] Git checkout completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
603
|
-
);
|
|
604
|
-
this.options.onCommandComplete?.(
|
|
605
|
-
event.success!,
|
|
606
|
-
event.exitCode!,
|
|
607
|
-
event.stdout!,
|
|
608
|
-
event.stderr!,
|
|
609
|
-
event.command!,
|
|
610
|
-
event.args || []
|
|
611
|
-
);
|
|
612
|
-
break;
|
|
613
|
-
|
|
614
|
-
case "error":
|
|
615
|
-
console.error(
|
|
616
|
-
`[HTTP Client] Git checkout error: ${event.error}`
|
|
617
|
-
);
|
|
618
|
-
this.options.onError?.(
|
|
619
|
-
event.error!,
|
|
620
|
-
event.command,
|
|
621
|
-
event.args
|
|
622
|
-
);
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
} catch (parseError) {
|
|
626
|
-
console.warn(
|
|
627
|
-
"[HTTP Client] Failed to parse git checkout stream event:",
|
|
628
|
-
parseError
|
|
629
|
-
);
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
} finally {
|
|
635
|
-
reader.releaseLock();
|
|
636
|
-
}
|
|
397
|
+
return data;
|
|
637
398
|
} catch (error) {
|
|
638
|
-
console.error("[HTTP Client] Error
|
|
639
|
-
this.options.onError?.(
|
|
640
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
641
|
-
"git clone",
|
|
642
|
-
[branch, repoUrl, targetDir || ""]
|
|
643
|
-
);
|
|
399
|
+
console.error("[HTTP Client] Error creating directory:", error);
|
|
644
400
|
throw error;
|
|
645
401
|
}
|
|
646
402
|
}
|
|
647
403
|
|
|
648
|
-
async
|
|
404
|
+
async writeFile(
|
|
649
405
|
path: string,
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
406
|
+
content: string,
|
|
407
|
+
encoding: string = "utf-8",
|
|
408
|
+
sessionId: string
|
|
409
|
+
): Promise<WriteFileResponse> {
|
|
653
410
|
try {
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
const response = await this.doFetch(`/api/mkdir`, {
|
|
411
|
+
const response = await this.doFetch(`/api/write`, {
|
|
657
412
|
body: JSON.stringify({
|
|
413
|
+
content,
|
|
414
|
+
encoding,
|
|
658
415
|
path,
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}),
|
|
416
|
+
sessionId,
|
|
417
|
+
} as WriteFileRequest),
|
|
662
418
|
headers: {
|
|
663
419
|
"Content-Type": "application/json",
|
|
664
420
|
},
|
|
@@ -674,32 +430,30 @@ export class HttpClient {
|
|
|
674
430
|
);
|
|
675
431
|
}
|
|
676
432
|
|
|
677
|
-
const data:
|
|
433
|
+
const data: WriteFileResponse = await response.json();
|
|
678
434
|
console.log(
|
|
679
|
-
`[HTTP Client]
|
|
435
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
680
436
|
);
|
|
681
437
|
|
|
682
438
|
return data;
|
|
683
439
|
} catch (error) {
|
|
684
|
-
console.error("[HTTP Client] Error
|
|
440
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
685
441
|
throw error;
|
|
686
442
|
}
|
|
687
443
|
}
|
|
688
444
|
|
|
689
|
-
async
|
|
445
|
+
async readFile(
|
|
690
446
|
path: string,
|
|
691
|
-
|
|
692
|
-
sessionId
|
|
693
|
-
): Promise<
|
|
447
|
+
encoding: string = "utf-8",
|
|
448
|
+
sessionId: string
|
|
449
|
+
): Promise<ReadFileResponse> {
|
|
694
450
|
try {
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
const response = await this.doFetch(`/api/mkdir/stream`, {
|
|
451
|
+
const response = await this.doFetch(`/api/read`, {
|
|
698
452
|
body: JSON.stringify({
|
|
453
|
+
encoding,
|
|
699
454
|
path,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}),
|
|
455
|
+
sessionId,
|
|
456
|
+
} as ReadFileRequest),
|
|
703
457
|
headers: {
|
|
704
458
|
"Content-Type": "application/json",
|
|
705
459
|
},
|
|
@@ -715,117 +469,28 @@ export class HttpClient {
|
|
|
715
469
|
);
|
|
716
470
|
}
|
|
717
471
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
472
|
+
const data: ReadFileResponse = await response.json();
|
|
473
|
+
console.log(
|
|
474
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
475
|
+
);
|
|
721
476
|
|
|
722
|
-
|
|
723
|
-
const decoder = new TextDecoder();
|
|
724
|
-
|
|
725
|
-
try {
|
|
726
|
-
while (true) {
|
|
727
|
-
const { done, value } = await reader.read();
|
|
728
|
-
|
|
729
|
-
if (done) {
|
|
730
|
-
break;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
734
|
-
const lines = chunk.split("\n");
|
|
735
|
-
|
|
736
|
-
for (const line of lines) {
|
|
737
|
-
if (line.startsWith("data: ")) {
|
|
738
|
-
try {
|
|
739
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
740
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
741
|
-
|
|
742
|
-
console.log(`[HTTP Client] Mkdir stream event: ${event.type}`);
|
|
743
|
-
this.options.onStreamEvent?.(event);
|
|
744
|
-
|
|
745
|
-
switch (event.type) {
|
|
746
|
-
case "command_start":
|
|
747
|
-
console.log(
|
|
748
|
-
`[HTTP Client] Mkdir started: ${
|
|
749
|
-
event.command
|
|
750
|
-
} ${event.args?.join(" ")}`
|
|
751
|
-
);
|
|
752
|
-
this.options.onCommandStart?.(
|
|
753
|
-
event.command!,
|
|
754
|
-
event.args || []
|
|
755
|
-
);
|
|
756
|
-
break;
|
|
757
|
-
|
|
758
|
-
case "output":
|
|
759
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
760
|
-
this.options.onOutput?.(
|
|
761
|
-
event.stream!,
|
|
762
|
-
event.data!,
|
|
763
|
-
event.command!
|
|
764
|
-
);
|
|
765
|
-
break;
|
|
766
|
-
|
|
767
|
-
case "command_complete":
|
|
768
|
-
console.log(
|
|
769
|
-
`[HTTP Client] Mkdir completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
770
|
-
);
|
|
771
|
-
this.options.onCommandComplete?.(
|
|
772
|
-
event.success!,
|
|
773
|
-
event.exitCode!,
|
|
774
|
-
event.stdout!,
|
|
775
|
-
event.stderr!,
|
|
776
|
-
event.command!,
|
|
777
|
-
event.args || []
|
|
778
|
-
);
|
|
779
|
-
break;
|
|
780
|
-
|
|
781
|
-
case "error":
|
|
782
|
-
console.error(`[HTTP Client] Mkdir error: ${event.error}`);
|
|
783
|
-
this.options.onError?.(
|
|
784
|
-
event.error!,
|
|
785
|
-
event.command,
|
|
786
|
-
event.args
|
|
787
|
-
);
|
|
788
|
-
break;
|
|
789
|
-
}
|
|
790
|
-
} catch (parseError) {
|
|
791
|
-
console.warn(
|
|
792
|
-
"[HTTP Client] Failed to parse mkdir stream event:",
|
|
793
|
-
parseError
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
} finally {
|
|
800
|
-
reader.releaseLock();
|
|
801
|
-
}
|
|
477
|
+
return data;
|
|
802
478
|
} catch (error) {
|
|
803
|
-
console.error("[HTTP Client] Error
|
|
804
|
-
this.options.onError?.(
|
|
805
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
806
|
-
"mkdir",
|
|
807
|
-
recursive ? ["-p", path] : [path]
|
|
808
|
-
);
|
|
479
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
809
480
|
throw error;
|
|
810
481
|
}
|
|
811
482
|
}
|
|
812
483
|
|
|
813
|
-
async
|
|
484
|
+
async deleteFile(
|
|
814
485
|
path: string,
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
sessionId?: string
|
|
818
|
-
): Promise<WriteFileResponse> {
|
|
486
|
+
sessionId: string
|
|
487
|
+
): Promise<DeleteFileResponse> {
|
|
819
488
|
try {
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
const response = await this.doFetch(`/api/write`, {
|
|
489
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
823
490
|
body: JSON.stringify({
|
|
824
|
-
content,
|
|
825
|
-
encoding,
|
|
826
491
|
path,
|
|
827
|
-
sessionId
|
|
828
|
-
}),
|
|
492
|
+
sessionId,
|
|
493
|
+
} as DeleteFileRequest),
|
|
829
494
|
headers: {
|
|
830
495
|
"Content-Type": "application/json",
|
|
831
496
|
},
|
|
@@ -841,34 +506,30 @@ export class HttpClient {
|
|
|
841
506
|
);
|
|
842
507
|
}
|
|
843
508
|
|
|
844
|
-
const data:
|
|
509
|
+
const data: DeleteFileResponse = await response.json();
|
|
845
510
|
console.log(
|
|
846
|
-
`[HTTP Client] File
|
|
511
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
847
512
|
);
|
|
848
513
|
|
|
849
514
|
return data;
|
|
850
515
|
} catch (error) {
|
|
851
|
-
console.error("[HTTP Client] Error
|
|
516
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
852
517
|
throw error;
|
|
853
518
|
}
|
|
854
519
|
}
|
|
855
520
|
|
|
856
|
-
async
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
): Promise<void> {
|
|
521
|
+
async renameFile(
|
|
522
|
+
oldPath: string,
|
|
523
|
+
newPath: string,
|
|
524
|
+
sessionId: string
|
|
525
|
+
): Promise<RenameFileResponse> {
|
|
862
526
|
try {
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
const response = await this.doFetch(`/api/write/stream`, {
|
|
527
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
866
528
|
body: JSON.stringify({
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
}),
|
|
529
|
+
newPath,
|
|
530
|
+
oldPath,
|
|
531
|
+
sessionId,
|
|
532
|
+
} as RenameFileRequest),
|
|
872
533
|
headers: {
|
|
873
534
|
"Content-Type": "application/json",
|
|
874
535
|
},
|
|
@@ -884,112 +545,30 @@ export class HttpClient {
|
|
|
884
545
|
);
|
|
885
546
|
}
|
|
886
547
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
548
|
+
const data: RenameFileResponse = await response.json();
|
|
549
|
+
console.log(
|
|
550
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
551
|
+
);
|
|
890
552
|
|
|
891
|
-
|
|
892
|
-
const decoder = new TextDecoder();
|
|
893
|
-
|
|
894
|
-
try {
|
|
895
|
-
while (true) {
|
|
896
|
-
const { done, value } = await reader.read();
|
|
897
|
-
|
|
898
|
-
if (done) {
|
|
899
|
-
break;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
903
|
-
const lines = chunk.split("\n");
|
|
904
|
-
|
|
905
|
-
for (const line of lines) {
|
|
906
|
-
if (line.startsWith("data: ")) {
|
|
907
|
-
try {
|
|
908
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
909
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
910
|
-
|
|
911
|
-
console.log(
|
|
912
|
-
`[HTTP Client] Write file stream event: ${event.type}`
|
|
913
|
-
);
|
|
914
|
-
this.options.onStreamEvent?.(event);
|
|
915
|
-
|
|
916
|
-
switch (event.type) {
|
|
917
|
-
case "command_start":
|
|
918
|
-
console.log(
|
|
919
|
-
`[HTTP Client] Write file started: ${event.path}`
|
|
920
|
-
);
|
|
921
|
-
this.options.onCommandStart?.("write", [
|
|
922
|
-
path,
|
|
923
|
-
content,
|
|
924
|
-
encoding,
|
|
925
|
-
]);
|
|
926
|
-
break;
|
|
927
|
-
|
|
928
|
-
case "output":
|
|
929
|
-
console.log(`[output] ${event.message}`);
|
|
930
|
-
this.options.onOutput?.("stdout", event.message!, "write");
|
|
931
|
-
break;
|
|
932
|
-
|
|
933
|
-
case "command_complete":
|
|
934
|
-
console.log(
|
|
935
|
-
`[HTTP Client] Write file completed: ${event.path}, Success: ${event.success}`
|
|
936
|
-
);
|
|
937
|
-
this.options.onCommandComplete?.(
|
|
938
|
-
event.success!,
|
|
939
|
-
0,
|
|
940
|
-
"",
|
|
941
|
-
"",
|
|
942
|
-
"write",
|
|
943
|
-
[path, content, encoding]
|
|
944
|
-
);
|
|
945
|
-
break;
|
|
946
|
-
|
|
947
|
-
case "error":
|
|
948
|
-
console.error(
|
|
949
|
-
`[HTTP Client] Write file error: ${event.error}`
|
|
950
|
-
);
|
|
951
|
-
this.options.onError?.(event.error!, "write", [
|
|
952
|
-
path,
|
|
953
|
-
content,
|
|
954
|
-
encoding,
|
|
955
|
-
]);
|
|
956
|
-
break;
|
|
957
|
-
}
|
|
958
|
-
} catch (parseError) {
|
|
959
|
-
console.warn(
|
|
960
|
-
"[HTTP Client] Failed to parse write file stream event:",
|
|
961
|
-
parseError
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
} finally {
|
|
968
|
-
reader.releaseLock();
|
|
969
|
-
}
|
|
553
|
+
return data;
|
|
970
554
|
} catch (error) {
|
|
971
|
-
console.error("[HTTP Client] Error
|
|
972
|
-
this.options.onError?.(
|
|
973
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
974
|
-
"write",
|
|
975
|
-
[path, content, encoding]
|
|
976
|
-
);
|
|
555
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
977
556
|
throw error;
|
|
978
557
|
}
|
|
979
558
|
}
|
|
980
559
|
|
|
981
|
-
async
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
560
|
+
async moveFile(
|
|
561
|
+
sourcePath: string,
|
|
562
|
+
destinationPath: string,
|
|
563
|
+
sessionId: string
|
|
564
|
+
): Promise<MoveFileResponse> {
|
|
985
565
|
try {
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
566
|
+
const response = await this.doFetch(`/api/move`, {
|
|
989
567
|
body: JSON.stringify({
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
568
|
+
destinationPath,
|
|
569
|
+
sourcePath,
|
|
570
|
+
sessionId,
|
|
571
|
+
} as MoveFileRequest),
|
|
993
572
|
headers: {
|
|
994
573
|
"Content-Type": "application/json",
|
|
995
574
|
},
|
|
@@ -1005,27 +584,33 @@ export class HttpClient {
|
|
|
1005
584
|
);
|
|
1006
585
|
}
|
|
1007
586
|
|
|
1008
|
-
const data:
|
|
587
|
+
const data: MoveFileResponse = await response.json();
|
|
1009
588
|
console.log(
|
|
1010
|
-
`[HTTP Client] File
|
|
589
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1011
590
|
);
|
|
1012
591
|
|
|
1013
592
|
return data;
|
|
1014
593
|
} catch (error) {
|
|
1015
|
-
console.error("[HTTP Client] Error
|
|
594
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1016
595
|
throw error;
|
|
1017
596
|
}
|
|
1018
597
|
}
|
|
1019
598
|
|
|
1020
|
-
async
|
|
599
|
+
async listFiles(
|
|
600
|
+
path: string,
|
|
601
|
+
sessionId: string,
|
|
602
|
+
options?: {
|
|
603
|
+
recursive?: boolean;
|
|
604
|
+
includeHidden?: boolean;
|
|
605
|
+
}
|
|
606
|
+
): Promise<ListFilesResponse> {
|
|
1021
607
|
try {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
608
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
1025
609
|
body: JSON.stringify({
|
|
1026
610
|
path,
|
|
1027
|
-
|
|
1028
|
-
|
|
611
|
+
options,
|
|
612
|
+
sessionId,
|
|
613
|
+
} as ListFilesRequest),
|
|
1029
614
|
headers: {
|
|
1030
615
|
"Content-Type": "application/json",
|
|
1031
616
|
},
|
|
@@ -1041,100 +626,24 @@ export class HttpClient {
|
|
|
1041
626
|
);
|
|
1042
627
|
}
|
|
1043
628
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
629
|
+
const data: ListFilesResponse = await response.json();
|
|
630
|
+
console.log(
|
|
631
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
632
|
+
);
|
|
1047
633
|
|
|
1048
|
-
|
|
1049
|
-
const decoder = new TextDecoder();
|
|
1050
|
-
|
|
1051
|
-
try {
|
|
1052
|
-
while (true) {
|
|
1053
|
-
const { done, value } = await reader.read();
|
|
1054
|
-
|
|
1055
|
-
if (done) {
|
|
1056
|
-
break;
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1060
|
-
const lines = chunk.split("\n");
|
|
1061
|
-
|
|
1062
|
-
for (const line of lines) {
|
|
1063
|
-
if (line.startsWith("data: ")) {
|
|
1064
|
-
try {
|
|
1065
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1066
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1067
|
-
|
|
1068
|
-
console.log(
|
|
1069
|
-
`[HTTP Client] Delete file stream event: ${event.type}`
|
|
1070
|
-
);
|
|
1071
|
-
this.options.onStreamEvent?.(event);
|
|
1072
|
-
|
|
1073
|
-
switch (event.type) {
|
|
1074
|
-
case "command_start":
|
|
1075
|
-
console.log(
|
|
1076
|
-
`[HTTP Client] Delete file started: ${event.path}`
|
|
1077
|
-
);
|
|
1078
|
-
this.options.onCommandStart?.("delete", [path]);
|
|
1079
|
-
break;
|
|
1080
|
-
|
|
1081
|
-
case "command_complete":
|
|
1082
|
-
console.log(
|
|
1083
|
-
`[HTTP Client] Delete file completed: ${event.path}, Success: ${event.success}`
|
|
1084
|
-
);
|
|
1085
|
-
this.options.onCommandComplete?.(
|
|
1086
|
-
event.success!,
|
|
1087
|
-
0,
|
|
1088
|
-
"",
|
|
1089
|
-
"",
|
|
1090
|
-
"delete",
|
|
1091
|
-
[path]
|
|
1092
|
-
);
|
|
1093
|
-
break;
|
|
1094
|
-
|
|
1095
|
-
case "error":
|
|
1096
|
-
console.error(
|
|
1097
|
-
`[HTTP Client] Delete file error: ${event.error}`
|
|
1098
|
-
);
|
|
1099
|
-
this.options.onError?.(event.error!, "delete", [path]);
|
|
1100
|
-
break;
|
|
1101
|
-
}
|
|
1102
|
-
} catch (parseError) {
|
|
1103
|
-
console.warn(
|
|
1104
|
-
"[HTTP Client] Failed to parse delete file stream event:",
|
|
1105
|
-
parseError
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
} finally {
|
|
1112
|
-
reader.releaseLock();
|
|
1113
|
-
}
|
|
634
|
+
return data;
|
|
1114
635
|
} catch (error) {
|
|
1115
|
-
console.error("[HTTP Client] Error
|
|
1116
|
-
this.options.onError?.(
|
|
1117
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1118
|
-
"delete",
|
|
1119
|
-
[path]
|
|
1120
|
-
);
|
|
636
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
1121
637
|
throw error;
|
|
1122
638
|
}
|
|
1123
639
|
}
|
|
1124
640
|
|
|
1125
|
-
async
|
|
1126
|
-
oldPath: string,
|
|
1127
|
-
newPath: string,
|
|
1128
|
-
sessionId?: string
|
|
1129
|
-
): Promise<RenameFileResponse> {
|
|
641
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1130
642
|
try {
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
const response = await this.doFetch(`/api/rename`, {
|
|
643
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1134
644
|
body: JSON.stringify({
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
sessionId: targetSessionId,
|
|
645
|
+
port,
|
|
646
|
+
name,
|
|
1138
647
|
}),
|
|
1139
648
|
headers: {
|
|
1140
649
|
"Content-Type": "application/json",
|
|
@@ -1146,41 +655,36 @@ export class HttpClient {
|
|
|
1146
655
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1147
656
|
error?: string;
|
|
1148
657
|
};
|
|
658
|
+
console.log(errorData);
|
|
1149
659
|
throw new Error(
|
|
1150
660
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1151
661
|
);
|
|
1152
662
|
}
|
|
1153
663
|
|
|
1154
|
-
const data:
|
|
664
|
+
const data: ExposePortResponse = await response.json();
|
|
1155
665
|
console.log(
|
|
1156
|
-
`[HTTP Client]
|
|
666
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
667
|
+
name ? ` (${name})` : ""
|
|
668
|
+
}, Success: ${data.success}`
|
|
1157
669
|
);
|
|
1158
670
|
|
|
1159
671
|
return data;
|
|
1160
672
|
} catch (error) {
|
|
1161
|
-
console.error("[HTTP Client] Error
|
|
673
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1162
674
|
throw error;
|
|
1163
675
|
}
|
|
1164
676
|
}
|
|
1165
677
|
|
|
1166
|
-
async
|
|
1167
|
-
oldPath: string,
|
|
1168
|
-
newPath: string,
|
|
1169
|
-
sessionId?: string
|
|
1170
|
-
): Promise<void> {
|
|
678
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1171
679
|
try {
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
const response = await this.doFetch(`/api/rename/stream`, {
|
|
680
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1175
681
|
body: JSON.stringify({
|
|
1176
|
-
|
|
1177
|
-
oldPath,
|
|
1178
|
-
sessionId: targetSessionId,
|
|
682
|
+
port,
|
|
1179
683
|
}),
|
|
1180
684
|
headers: {
|
|
1181
685
|
"Content-Type": "application/json",
|
|
1182
686
|
},
|
|
1183
|
-
method: "
|
|
687
|
+
method: "DELETE",
|
|
1184
688
|
});
|
|
1185
689
|
|
|
1186
690
|
if (!response.ok) {
|
|
@@ -1192,108 +696,25 @@ export class HttpClient {
|
|
|
1192
696
|
);
|
|
1193
697
|
}
|
|
1194
698
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
699
|
+
const data: UnexposePortResponse = await response.json();
|
|
700
|
+
console.log(
|
|
701
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
702
|
+
);
|
|
1198
703
|
|
|
1199
|
-
|
|
1200
|
-
const decoder = new TextDecoder();
|
|
1201
|
-
|
|
1202
|
-
try {
|
|
1203
|
-
while (true) {
|
|
1204
|
-
const { done, value } = await reader.read();
|
|
1205
|
-
|
|
1206
|
-
if (done) {
|
|
1207
|
-
break;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1211
|
-
const lines = chunk.split("\n");
|
|
1212
|
-
|
|
1213
|
-
for (const line of lines) {
|
|
1214
|
-
if (line.startsWith("data: ")) {
|
|
1215
|
-
try {
|
|
1216
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1217
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1218
|
-
|
|
1219
|
-
console.log(
|
|
1220
|
-
`[HTTP Client] Rename file stream event: ${event.type}`
|
|
1221
|
-
);
|
|
1222
|
-
this.options.onStreamEvent?.(event);
|
|
1223
|
-
|
|
1224
|
-
switch (event.type) {
|
|
1225
|
-
case "command_start":
|
|
1226
|
-
console.log(
|
|
1227
|
-
`[HTTP Client] Rename file started: ${event.oldPath} -> ${event.newPath}`
|
|
1228
|
-
);
|
|
1229
|
-
this.options.onCommandStart?.("rename", [oldPath, newPath]);
|
|
1230
|
-
break;
|
|
1231
|
-
|
|
1232
|
-
case "command_complete":
|
|
1233
|
-
console.log(
|
|
1234
|
-
`[HTTP Client] Rename file completed: ${event.oldPath} -> ${event.newPath}, Success: ${event.success}`
|
|
1235
|
-
);
|
|
1236
|
-
this.options.onCommandComplete?.(
|
|
1237
|
-
event.success!,
|
|
1238
|
-
0,
|
|
1239
|
-
"",
|
|
1240
|
-
"",
|
|
1241
|
-
"rename",
|
|
1242
|
-
[oldPath, newPath]
|
|
1243
|
-
);
|
|
1244
|
-
break;
|
|
1245
|
-
|
|
1246
|
-
case "error":
|
|
1247
|
-
console.error(
|
|
1248
|
-
`[HTTP Client] Rename file error: ${event.error}`
|
|
1249
|
-
);
|
|
1250
|
-
this.options.onError?.(event.error!, "rename", [
|
|
1251
|
-
oldPath,
|
|
1252
|
-
newPath,
|
|
1253
|
-
]);
|
|
1254
|
-
break;
|
|
1255
|
-
}
|
|
1256
|
-
} catch (parseError) {
|
|
1257
|
-
console.warn(
|
|
1258
|
-
"[HTTP Client] Failed to parse rename file stream event:",
|
|
1259
|
-
parseError
|
|
1260
|
-
);
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
} finally {
|
|
1266
|
-
reader.releaseLock();
|
|
1267
|
-
}
|
|
704
|
+
return data;
|
|
1268
705
|
} catch (error) {
|
|
1269
|
-
console.error("[HTTP Client] Error
|
|
1270
|
-
this.options.onError?.(
|
|
1271
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1272
|
-
"rename",
|
|
1273
|
-
[oldPath, newPath]
|
|
1274
|
-
);
|
|
706
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
1275
707
|
throw error;
|
|
1276
708
|
}
|
|
1277
709
|
}
|
|
1278
710
|
|
|
1279
|
-
async
|
|
1280
|
-
sourcePath: string,
|
|
1281
|
-
destinationPath: string,
|
|
1282
|
-
sessionId?: string
|
|
1283
|
-
): Promise<MoveFileResponse> {
|
|
711
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
1284
712
|
try {
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
const response = await this.doFetch(`/api/move`, {
|
|
1288
|
-
body: JSON.stringify({
|
|
1289
|
-
destinationPath,
|
|
1290
|
-
sessionId: targetSessionId,
|
|
1291
|
-
sourcePath,
|
|
1292
|
-
}),
|
|
713
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
1293
714
|
headers: {
|
|
1294
715
|
"Content-Type": "application/json",
|
|
1295
716
|
},
|
|
1296
|
-
method: "
|
|
717
|
+
method: "GET",
|
|
1297
718
|
});
|
|
1298
719
|
|
|
1299
720
|
if (!response.ok) {
|
|
@@ -1305,32 +726,59 @@ export class HttpClient {
|
|
|
1305
726
|
);
|
|
1306
727
|
}
|
|
1307
728
|
|
|
1308
|
-
const data:
|
|
1309
|
-
console.log(
|
|
1310
|
-
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`
|
|
1311
|
-
);
|
|
729
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
730
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
1312
731
|
|
|
1313
732
|
return data;
|
|
1314
733
|
} catch (error) {
|
|
1315
|
-
console.error("[HTTP Client] Error
|
|
734
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
1316
735
|
throw error;
|
|
1317
736
|
}
|
|
1318
737
|
}
|
|
1319
738
|
|
|
1320
|
-
async
|
|
1321
|
-
sourcePath: string,
|
|
1322
|
-
destinationPath: string,
|
|
1323
|
-
sessionId?: string
|
|
1324
|
-
): Promise<void> {
|
|
739
|
+
async ping(): Promise<string> {
|
|
1325
740
|
try {
|
|
1326
|
-
const
|
|
741
|
+
const response = await this.doFetch(`/api/ping`, {
|
|
742
|
+
headers: {
|
|
743
|
+
"Content-Type": "application/json",
|
|
744
|
+
},
|
|
745
|
+
method: "GET",
|
|
746
|
+
});
|
|
1327
747
|
|
|
1328
|
-
|
|
748
|
+
if (!response.ok) {
|
|
749
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const data: PingResponse = await response.json();
|
|
753
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
754
|
+
return data.timestamp;
|
|
755
|
+
} catch (error) {
|
|
756
|
+
console.error("[HTTP Client] Error pinging server:", error);
|
|
757
|
+
throw error;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
// Process management methods
|
|
763
|
+
async startProcess(
|
|
764
|
+
command: string,
|
|
765
|
+
sessionId: string,
|
|
766
|
+
options?: {
|
|
767
|
+
processId?: string;
|
|
768
|
+
timeout?: number;
|
|
769
|
+
env?: Record<string, string>;
|
|
770
|
+
cwd?: string;
|
|
771
|
+
encoding?: string;
|
|
772
|
+
autoCleanup?: boolean;
|
|
773
|
+
}
|
|
774
|
+
): Promise<StartProcessResponse> {
|
|
775
|
+
try {
|
|
776
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1329
777
|
body: JSON.stringify({
|
|
1330
|
-
|
|
1331
|
-
sessionId
|
|
1332
|
-
|
|
1333
|
-
}),
|
|
778
|
+
command,
|
|
779
|
+
sessionId,
|
|
780
|
+
options,
|
|
781
|
+
} as StartProcessRequest),
|
|
1334
782
|
headers: {
|
|
1335
783
|
"Content-Type": "application/json",
|
|
1336
784
|
},
|
|
@@ -1346,96 +794,24 @@ export class HttpClient {
|
|
|
1346
794
|
);
|
|
1347
795
|
}
|
|
1348
796
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
797
|
+
const data: StartProcessResponse = await response.json();
|
|
798
|
+
console.log(
|
|
799
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
800
|
+
);
|
|
1352
801
|
|
|
1353
|
-
|
|
1354
|
-
const decoder = new TextDecoder();
|
|
1355
|
-
|
|
1356
|
-
try {
|
|
1357
|
-
while (true) {
|
|
1358
|
-
const { done, value } = await reader.read();
|
|
1359
|
-
|
|
1360
|
-
if (done) {
|
|
1361
|
-
break;
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
1365
|
-
const lines = chunk.split("\n");
|
|
1366
|
-
|
|
1367
|
-
for (const line of lines) {
|
|
1368
|
-
if (line.startsWith("data: ")) {
|
|
1369
|
-
try {
|
|
1370
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
1371
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
1372
|
-
|
|
1373
|
-
console.log(
|
|
1374
|
-
`[HTTP Client] Move file stream event: ${event.type}`
|
|
1375
|
-
);
|
|
1376
|
-
this.options.onStreamEvent?.(event);
|
|
1377
|
-
|
|
1378
|
-
switch (event.type) {
|
|
1379
|
-
case "command_start":
|
|
1380
|
-
console.log(
|
|
1381
|
-
`[HTTP Client] Move file started: ${event.sourcePath} -> ${event.destinationPath}`
|
|
1382
|
-
);
|
|
1383
|
-
this.options.onCommandStart?.("move", [
|
|
1384
|
-
sourcePath,
|
|
1385
|
-
destinationPath,
|
|
1386
|
-
]);
|
|
1387
|
-
break;
|
|
1388
|
-
|
|
1389
|
-
case "command_complete":
|
|
1390
|
-
console.log(
|
|
1391
|
-
`[HTTP Client] Move file completed: ${event.sourcePath} -> ${event.destinationPath}, Success: ${event.success}`
|
|
1392
|
-
);
|
|
1393
|
-
this.options.onCommandComplete?.(
|
|
1394
|
-
event.success!,
|
|
1395
|
-
0,
|
|
1396
|
-
"",
|
|
1397
|
-
"",
|
|
1398
|
-
"move",
|
|
1399
|
-
[sourcePath, destinationPath]
|
|
1400
|
-
);
|
|
1401
|
-
break;
|
|
1402
|
-
|
|
1403
|
-
case "error":
|
|
1404
|
-
console.error(
|
|
1405
|
-
`[HTTP Client] Move file error: ${event.error}`
|
|
1406
|
-
);
|
|
1407
|
-
this.options.onError?.(event.error!, "move", [
|
|
1408
|
-
sourcePath,
|
|
1409
|
-
destinationPath,
|
|
1410
|
-
]);
|
|
1411
|
-
break;
|
|
1412
|
-
}
|
|
1413
|
-
} catch (parseError) {
|
|
1414
|
-
console.warn(
|
|
1415
|
-
"[HTTP Client] Failed to parse move file stream event:",
|
|
1416
|
-
parseError
|
|
1417
|
-
);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
} finally {
|
|
1423
|
-
reader.releaseLock();
|
|
1424
|
-
}
|
|
802
|
+
return data;
|
|
1425
803
|
} catch (error) {
|
|
1426
|
-
console.error("[HTTP Client] Error
|
|
1427
|
-
this.options.onError?.(
|
|
1428
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
1429
|
-
"move",
|
|
1430
|
-
[sourcePath, destinationPath]
|
|
1431
|
-
);
|
|
804
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1432
805
|
throw error;
|
|
1433
806
|
}
|
|
1434
807
|
}
|
|
1435
808
|
|
|
1436
|
-
async
|
|
809
|
+
async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
|
|
1437
810
|
try {
|
|
1438
|
-
const
|
|
811
|
+
const url = sessionId
|
|
812
|
+
? `/api/process/list?session=${encodeURIComponent(sessionId)}`
|
|
813
|
+
: "/api/process/list";
|
|
814
|
+
const response = await this.doFetch(url, {
|
|
1439
815
|
headers: {
|
|
1440
816
|
"Content-Type": "application/json",
|
|
1441
817
|
},
|
|
@@ -1443,21 +819,27 @@ export class HttpClient {
|
|
|
1443
819
|
});
|
|
1444
820
|
|
|
1445
821
|
if (!response.ok) {
|
|
1446
|
-
|
|
822
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
823
|
+
error?: string;
|
|
824
|
+
};
|
|
825
|
+
throw new Error(
|
|
826
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
827
|
+
);
|
|
1447
828
|
}
|
|
1448
829
|
|
|
1449
|
-
const data:
|
|
1450
|
-
console.log(`[HTTP Client]
|
|
1451
|
-
|
|
830
|
+
const data: ListProcessesResponse = await response.json();
|
|
831
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
832
|
+
|
|
833
|
+
return data;
|
|
1452
834
|
} catch (error) {
|
|
1453
|
-
console.error("[HTTP Client] Error
|
|
835
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1454
836
|
throw error;
|
|
1455
837
|
}
|
|
1456
838
|
}
|
|
1457
839
|
|
|
1458
|
-
async
|
|
840
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1459
841
|
try {
|
|
1460
|
-
const response = await
|
|
842
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1461
843
|
headers: {
|
|
1462
844
|
"Content-Type": "application/json",
|
|
1463
845
|
},
|
|
@@ -1465,260 +847,163 @@ export class HttpClient {
|
|
|
1465
847
|
});
|
|
1466
848
|
|
|
1467
849
|
if (!response.ok) {
|
|
1468
|
-
|
|
850
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
851
|
+
error?: string;
|
|
852
|
+
};
|
|
853
|
+
throw new Error(
|
|
854
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
855
|
+
);
|
|
1469
856
|
}
|
|
1470
857
|
|
|
1471
|
-
const data:
|
|
858
|
+
const data: GetProcessResponse = await response.json();
|
|
1472
859
|
console.log(
|
|
1473
|
-
`[HTTP Client]
|
|
860
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
861
|
+
data.process?.status || "not found"
|
|
862
|
+
}`
|
|
1474
863
|
);
|
|
1475
|
-
|
|
864
|
+
|
|
865
|
+
return data;
|
|
1476
866
|
} catch (error) {
|
|
1477
|
-
console.error("[HTTP Client] Error getting
|
|
867
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1478
868
|
throw error;
|
|
1479
869
|
}
|
|
1480
870
|
}
|
|
1481
871
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
872
|
+
async killProcess(
|
|
873
|
+
processId: string
|
|
874
|
+
): Promise<{ success: boolean; message: string }> {
|
|
875
|
+
try {
|
|
876
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
877
|
+
headers: {
|
|
878
|
+
"Content-Type": "application/json",
|
|
879
|
+
},
|
|
880
|
+
method: "DELETE",
|
|
881
|
+
});
|
|
1489
882
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
}
|
|
883
|
+
if (!response.ok) {
|
|
884
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
885
|
+
error?: string;
|
|
886
|
+
};
|
|
887
|
+
throw new Error(
|
|
888
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
889
|
+
);
|
|
890
|
+
}
|
|
1494
891
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
}
|
|
892
|
+
const data = (await response.json()) as {
|
|
893
|
+
success: boolean;
|
|
894
|
+
message: string;
|
|
895
|
+
};
|
|
896
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1499
897
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
): Promise<ExecuteResponse> {
|
|
1506
|
-
const client = createClient(options);
|
|
1507
|
-
await client.createSession();
|
|
1508
|
-
|
|
1509
|
-
try {
|
|
1510
|
-
return await client.execute(command, args);
|
|
1511
|
-
} finally {
|
|
1512
|
-
client.clearSession();
|
|
898
|
+
return data;
|
|
899
|
+
} catch (error) {
|
|
900
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
901
|
+
throw error;
|
|
902
|
+
}
|
|
1513
903
|
}
|
|
1514
|
-
}
|
|
1515
904
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
}
|
|
905
|
+
async killAllProcesses(sessionId?: string): Promise<{
|
|
906
|
+
success: boolean;
|
|
907
|
+
killedCount: number;
|
|
908
|
+
message: string;
|
|
909
|
+
}> {
|
|
910
|
+
try {
|
|
911
|
+
const url = sessionId
|
|
912
|
+
? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
|
|
913
|
+
: "/api/process/kill-all";
|
|
914
|
+
const response = await this.doFetch(url, {
|
|
915
|
+
headers: {
|
|
916
|
+
"Content-Type": "application/json",
|
|
917
|
+
},
|
|
918
|
+
method: "DELETE",
|
|
919
|
+
});
|
|
1531
920
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
)
|
|
1539
|
-
|
|
1540
|
-
await client.createSession();
|
|
1541
|
-
|
|
1542
|
-
try {
|
|
1543
|
-
return await client.gitCheckout(repoUrl, branch, targetDir);
|
|
1544
|
-
} finally {
|
|
1545
|
-
client.clearSession();
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
921
|
+
if (!response.ok) {
|
|
922
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
923
|
+
error?: string;
|
|
924
|
+
};
|
|
925
|
+
throw new Error(
|
|
926
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
927
|
+
);
|
|
928
|
+
}
|
|
1548
929
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
const client = createClient(options);
|
|
1556
|
-
await client.createSession();
|
|
1557
|
-
|
|
1558
|
-
try {
|
|
1559
|
-
return await client.mkdir(path, recursive);
|
|
1560
|
-
} finally {
|
|
1561
|
-
client.clearSession();
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
930
|
+
const data = (await response.json()) as {
|
|
931
|
+
success: boolean;
|
|
932
|
+
killedCount: number;
|
|
933
|
+
message: string;
|
|
934
|
+
};
|
|
935
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
1564
936
|
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
options?: HttpClientOptions
|
|
1571
|
-
): Promise<void> {
|
|
1572
|
-
const client = createClient(options);
|
|
1573
|
-
await client.createSession();
|
|
1574
|
-
|
|
1575
|
-
try {
|
|
1576
|
-
await client.gitCheckoutStream(repoUrl, branch, targetDir);
|
|
1577
|
-
} finally {
|
|
1578
|
-
client.clearSession();
|
|
937
|
+
return data;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
940
|
+
throw error;
|
|
941
|
+
}
|
|
1579
942
|
}
|
|
1580
|
-
}
|
|
1581
943
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
try {
|
|
1592
|
-
await client.mkdirStream(path, recursive);
|
|
1593
|
-
} finally {
|
|
1594
|
-
client.clearSession();
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
944
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
945
|
+
try {
|
|
946
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
947
|
+
headers: {
|
|
948
|
+
"Content-Type": "application/json",
|
|
949
|
+
},
|
|
950
|
+
method: "GET",
|
|
951
|
+
});
|
|
1597
952
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
)
|
|
1605
|
-
|
|
1606
|
-
await client.createSession();
|
|
1607
|
-
|
|
1608
|
-
try {
|
|
1609
|
-
return await client.writeFile(path, content, encoding);
|
|
1610
|
-
} finally {
|
|
1611
|
-
client.clearSession();
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
953
|
+
if (!response.ok) {
|
|
954
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
955
|
+
error?: string;
|
|
956
|
+
};
|
|
957
|
+
throw new Error(
|
|
958
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
959
|
+
);
|
|
960
|
+
}
|
|
1614
961
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
path: string,
|
|
1618
|
-
content: string,
|
|
1619
|
-
encoding: string = "utf-8",
|
|
1620
|
-
options?: HttpClientOptions
|
|
1621
|
-
): Promise<void> {
|
|
1622
|
-
const client = createClient(options);
|
|
1623
|
-
await client.createSession();
|
|
1624
|
-
|
|
1625
|
-
try {
|
|
1626
|
-
await client.writeFileStream(path, content, encoding);
|
|
1627
|
-
} finally {
|
|
1628
|
-
client.clearSession();
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
962
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
963
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
1631
964
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
const client = createClient(options);
|
|
1638
|
-
await client.createSession();
|
|
1639
|
-
|
|
1640
|
-
try {
|
|
1641
|
-
return await client.deleteFile(path);
|
|
1642
|
-
} finally {
|
|
1643
|
-
client.clearSession();
|
|
965
|
+
return data;
|
|
966
|
+
} catch (error) {
|
|
967
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
968
|
+
throw error;
|
|
969
|
+
}
|
|
1644
970
|
}
|
|
1645
|
-
}
|
|
1646
971
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
}
|
|
972
|
+
async streamProcessLogs(
|
|
973
|
+
processId: string,
|
|
974
|
+
options?: { signal?: AbortSignal }
|
|
975
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
976
|
+
try {
|
|
977
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
978
|
+
headers: {
|
|
979
|
+
Accept: "text/event-stream",
|
|
980
|
+
"Cache-Control": "no-cache",
|
|
981
|
+
},
|
|
982
|
+
method: "GET",
|
|
983
|
+
signal: options?.signal,
|
|
984
|
+
});
|
|
1661
985
|
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
try {
|
|
1672
|
-
return await client.renameFile(oldPath, newPath);
|
|
1673
|
-
} finally {
|
|
1674
|
-
client.clearSession();
|
|
1675
|
-
}
|
|
1676
|
-
}
|
|
986
|
+
if (!response.ok) {
|
|
987
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
988
|
+
error?: string;
|
|
989
|
+
};
|
|
990
|
+
throw new Error(
|
|
991
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
1677
994
|
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
newPath: string,
|
|
1682
|
-
options?: HttpClientOptions
|
|
1683
|
-
): Promise<void> {
|
|
1684
|
-
const client = createClient(options);
|
|
1685
|
-
await client.createSession();
|
|
1686
|
-
|
|
1687
|
-
try {
|
|
1688
|
-
await client.renameFileStream(oldPath, newPath);
|
|
1689
|
-
} finally {
|
|
1690
|
-
client.clearSession();
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
995
|
+
if (!response.body) {
|
|
996
|
+
throw new Error("No response body for streaming request");
|
|
997
|
+
}
|
|
1693
998
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
destinationPath: string,
|
|
1698
|
-
options?: HttpClientOptions
|
|
1699
|
-
): Promise<MoveFileResponse> {
|
|
1700
|
-
const client = createClient(options);
|
|
1701
|
-
await client.createSession();
|
|
1702
|
-
|
|
1703
|
-
try {
|
|
1704
|
-
return await client.moveFile(sourcePath, destinationPath);
|
|
1705
|
-
} finally {
|
|
1706
|
-
client.clearSession();
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
999
|
+
console.log(
|
|
1000
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1001
|
+
);
|
|
1709
1002
|
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
): Promise<void> {
|
|
1716
|
-
const client = createClient(options);
|
|
1717
|
-
await client.createSession();
|
|
1718
|
-
|
|
1719
|
-
try {
|
|
1720
|
-
await client.moveFileStream(sourcePath, destinationPath);
|
|
1721
|
-
} finally {
|
|
1722
|
-
client.clearSession();
|
|
1003
|
+
return response.body;
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1006
|
+
throw error;
|
|
1007
|
+
}
|
|
1723
1008
|
}
|
|
1724
1009
|
}
|