@cloudflare/sandbox 0.0.0-db09b4d → 0.0.0-dcf36ef
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 +235 -0
- package/Dockerfile +103 -10
- package/README.md +859 -22
- package/container_src/bun.lock +76 -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 +457 -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 +447 -2466
- package/container_src/interpreter-service.ts +276 -0
- package/container_src/isolation.ts +1213 -0
- package/container_src/mime-processor.ts +255 -0
- package/container_src/package.json +9 -0
- package/container_src/runtime/executors/javascript/node_executor.ts +123 -0
- package/container_src/runtime/executors/python/ipython_executor.py +338 -0
- package/container_src/runtime/executors/typescript/ts_executor.ts +138 -0
- package/container_src/runtime/process-pool.ts +464 -0
- package/container_src/shell-escape.ts +42 -0
- package/container_src/startup.sh +11 -0
- package/container_src/types.ts +131 -0
- package/package.json +6 -8
- package/src/client.ts +510 -1186
- package/src/errors.ts +219 -0
- package/src/file-stream.ts +162 -0
- package/src/index.ts +78 -76
- package/src/interpreter-client.ts +352 -0
- package/src/interpreter-types.ts +390 -0
- package/src/interpreter.ts +150 -0
- package/src/request-handler.ts +144 -0
- package/src/sandbox.ts +756 -0
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +571 -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;
|
|
118
74
|
}
|
|
119
75
|
|
|
120
|
-
|
|
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;
|
|
91
|
+
}
|
|
92
|
+
|
|
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;
|
|
109
|
+
timestamp: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface GetExposedPortsResponse {
|
|
113
|
+
ports: ExposedPort[];
|
|
114
|
+
count: number;
|
|
130
115
|
timestamp: string;
|
|
131
116
|
}
|
|
132
117
|
|
|
133
|
-
interface
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
args?: string[];
|
|
137
|
-
stream?: "stdout" | "stderr";
|
|
138
|
-
data?: string;
|
|
139
|
-
message?: string;
|
|
140
|
-
path?: string;
|
|
141
|
-
oldPath?: string;
|
|
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;
|
|
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,150 +362,18 @@ export class HttpClient {
|
|
|
515
362
|
}
|
|
516
363
|
}
|
|
517
364
|
|
|
518
|
-
async gitCheckoutStream(
|
|
519
|
-
repoUrl: string,
|
|
520
|
-
branch: string = "main",
|
|
521
|
-
targetDir?: string,
|
|
522
|
-
sessionId?: string
|
|
523
|
-
): Promise<void> {
|
|
524
|
-
try {
|
|
525
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
526
|
-
|
|
527
|
-
const response = await this.doFetch(`/api/git/checkout/stream`, {
|
|
528
|
-
body: JSON.stringify({
|
|
529
|
-
branch,
|
|
530
|
-
repoUrl,
|
|
531
|
-
sessionId: targetSessionId,
|
|
532
|
-
targetDir,
|
|
533
|
-
}),
|
|
534
|
-
headers: {
|
|
535
|
-
"Content-Type": "application/json",
|
|
536
|
-
},
|
|
537
|
-
method: "POST",
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
if (!response.ok) {
|
|
541
|
-
const errorData = (await response.json().catch(() => ({}))) as {
|
|
542
|
-
error?: string;
|
|
543
|
-
};
|
|
544
|
-
throw new Error(
|
|
545
|
-
errorData.error || `HTTP error! status: ${response.status}`
|
|
546
|
-
);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
if (!response.body) {
|
|
550
|
-
throw new Error("No response body for streaming request");
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
const reader = response.body.getReader();
|
|
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
|
-
}
|
|
637
|
-
} catch (error) {
|
|
638
|
-
console.error("[HTTP Client] Error in streaming git checkout:", error);
|
|
639
|
-
this.options.onError?.(
|
|
640
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
641
|
-
"git clone",
|
|
642
|
-
[branch, repoUrl, targetDir || ""]
|
|
643
|
-
);
|
|
644
|
-
throw error;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
365
|
async mkdir(
|
|
649
366
|
path: string,
|
|
650
367
|
recursive: boolean = false,
|
|
651
|
-
sessionId
|
|
368
|
+
sessionId: string
|
|
652
369
|
): Promise<MkdirResponse> {
|
|
653
370
|
try {
|
|
654
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
655
|
-
|
|
656
371
|
const response = await this.doFetch(`/api/mkdir`, {
|
|
657
372
|
body: JSON.stringify({
|
|
658
373
|
path,
|
|
659
374
|
recursive,
|
|
660
|
-
sessionId
|
|
661
|
-
}),
|
|
375
|
+
sessionId,
|
|
376
|
+
} as MkdirRequest),
|
|
662
377
|
headers: {
|
|
663
378
|
"Content-Type": "application/json",
|
|
664
379
|
},
|
|
@@ -676,7 +391,7 @@ export class HttpClient {
|
|
|
676
391
|
|
|
677
392
|
const data: MkdirResponse = await response.json();
|
|
678
393
|
console.log(
|
|
679
|
-
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`
|
|
394
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
680
395
|
);
|
|
681
396
|
|
|
682
397
|
return data;
|
|
@@ -686,20 +401,20 @@ export class HttpClient {
|
|
|
686
401
|
}
|
|
687
402
|
}
|
|
688
403
|
|
|
689
|
-
async
|
|
404
|
+
async writeFile(
|
|
690
405
|
path: string,
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
406
|
+
content: string,
|
|
407
|
+
encoding: string = "utf-8",
|
|
408
|
+
sessionId: string
|
|
409
|
+
): Promise<WriteFileResponse> {
|
|
694
410
|
try {
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
const response = await this.doFetch(`/api/mkdir/stream`, {
|
|
411
|
+
const response = await this.doFetch(`/api/write`, {
|
|
698
412
|
body: JSON.stringify({
|
|
413
|
+
content,
|
|
414
|
+
encoding,
|
|
699
415
|
path,
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}),
|
|
416
|
+
sessionId,
|
|
417
|
+
} as WriteFileRequest),
|
|
703
418
|
headers: {
|
|
704
419
|
"Content-Type": "application/json",
|
|
705
420
|
},
|
|
@@ -715,117 +430,30 @@ export class HttpClient {
|
|
|
715
430
|
);
|
|
716
431
|
}
|
|
717
432
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
433
|
+
const data: WriteFileResponse = await response.json();
|
|
434
|
+
console.log(
|
|
435
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
436
|
+
);
|
|
721
437
|
|
|
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
|
-
}
|
|
438
|
+
return data;
|
|
802
439
|
} 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
|
-
);
|
|
440
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
809
441
|
throw error;
|
|
810
442
|
}
|
|
811
443
|
}
|
|
812
444
|
|
|
813
|
-
async
|
|
445
|
+
async readFile(
|
|
814
446
|
path: string,
|
|
815
|
-
content: string,
|
|
816
447
|
encoding: string = "utf-8",
|
|
817
|
-
sessionId
|
|
818
|
-
): Promise<
|
|
448
|
+
sessionId: string
|
|
449
|
+
): Promise<ReadFileResponse> {
|
|
819
450
|
try {
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
const response = await this.doFetch(`/api/write`, {
|
|
451
|
+
const response = await this.doFetch(`/api/read`, {
|
|
823
452
|
body: JSON.stringify({
|
|
824
|
-
content,
|
|
825
453
|
encoding,
|
|
826
454
|
path,
|
|
827
|
-
sessionId
|
|
828
|
-
}),
|
|
455
|
+
sessionId,
|
|
456
|
+
} as ReadFileRequest),
|
|
829
457
|
headers: {
|
|
830
458
|
"Content-Type": "application/json",
|
|
831
459
|
},
|
|
@@ -841,38 +469,32 @@ export class HttpClient {
|
|
|
841
469
|
);
|
|
842
470
|
}
|
|
843
471
|
|
|
844
|
-
const data:
|
|
472
|
+
const data: ReadFileResponse = await response.json();
|
|
845
473
|
console.log(
|
|
846
|
-
`[HTTP Client] File
|
|
474
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
847
475
|
);
|
|
848
476
|
|
|
849
477
|
return data;
|
|
850
478
|
} catch (error) {
|
|
851
|
-
console.error("[HTTP Client] Error
|
|
479
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
852
480
|
throw error;
|
|
853
481
|
}
|
|
854
482
|
}
|
|
855
483
|
|
|
856
|
-
async
|
|
484
|
+
async readFileStream(
|
|
857
485
|
path: string,
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
sessionId?: string
|
|
861
|
-
): Promise<void> {
|
|
486
|
+
sessionId: string
|
|
487
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
862
488
|
try {
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
const response = await this.doFetch(`/api/write/stream`, {
|
|
866
|
-
body: JSON.stringify({
|
|
867
|
-
content,
|
|
868
|
-
encoding,
|
|
869
|
-
path,
|
|
870
|
-
sessionId: targetSessionId,
|
|
871
|
-
}),
|
|
489
|
+
const response = await this.doFetch(`/api/read/stream`, {
|
|
490
|
+
method: "POST",
|
|
872
491
|
headers: {
|
|
873
492
|
"Content-Type": "application/json",
|
|
874
493
|
},
|
|
875
|
-
|
|
494
|
+
body: JSON.stringify({
|
|
495
|
+
path,
|
|
496
|
+
sessionId,
|
|
497
|
+
} as ReadFileRequest),
|
|
876
498
|
});
|
|
877
499
|
|
|
878
500
|
if (!response.ok) {
|
|
@@ -885,111 +507,29 @@ export class HttpClient {
|
|
|
885
507
|
}
|
|
886
508
|
|
|
887
509
|
if (!response.body) {
|
|
888
|
-
throw new Error("No response body for streaming
|
|
510
|
+
throw new Error("No response body for file streaming");
|
|
889
511
|
}
|
|
890
512
|
|
|
891
|
-
|
|
892
|
-
|
|
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
|
-
}
|
|
970
|
-
} catch (error) {
|
|
971
|
-
console.error("[HTTP Client] Error in streaming write file:", error);
|
|
972
|
-
this.options.onError?.(
|
|
973
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
974
|
-
"write",
|
|
975
|
-
[path, content, encoding]
|
|
513
|
+
console.log(
|
|
514
|
+
`[HTTP Client] Started streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
976
515
|
);
|
|
516
|
+
return response.body;
|
|
517
|
+
} catch (error) {
|
|
518
|
+
console.error("[HTTP Client] Error streaming file:", error);
|
|
977
519
|
throw error;
|
|
978
520
|
}
|
|
979
521
|
}
|
|
980
522
|
|
|
981
523
|
async deleteFile(
|
|
982
524
|
path: string,
|
|
983
|
-
sessionId
|
|
525
|
+
sessionId: string
|
|
984
526
|
): Promise<DeleteFileResponse> {
|
|
985
527
|
try {
|
|
986
|
-
const targetSessionId = sessionId || this.sessionId;
|
|
987
|
-
|
|
988
528
|
const response = await this.doFetch(`/api/delete`, {
|
|
989
529
|
body: JSON.stringify({
|
|
990
530
|
path,
|
|
991
|
-
sessionId
|
|
992
|
-
}),
|
|
531
|
+
sessionId,
|
|
532
|
+
} as DeleteFileRequest),
|
|
993
533
|
headers: {
|
|
994
534
|
"Content-Type": "application/json",
|
|
995
535
|
},
|
|
@@ -1007,7 +547,7 @@ export class HttpClient {
|
|
|
1007
547
|
|
|
1008
548
|
const data: DeleteFileResponse = await response.json();
|
|
1009
549
|
console.log(
|
|
1010
|
-
`[HTTP Client] File deleted: ${path}, Success: ${data.success}`
|
|
550
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1011
551
|
);
|
|
1012
552
|
|
|
1013
553
|
return data;
|
|
@@ -1017,15 +557,18 @@ export class HttpClient {
|
|
|
1017
557
|
}
|
|
1018
558
|
}
|
|
1019
559
|
|
|
1020
|
-
async
|
|
560
|
+
async renameFile(
|
|
561
|
+
oldPath: string,
|
|
562
|
+
newPath: string,
|
|
563
|
+
sessionId: string
|
|
564
|
+
): Promise<RenameFileResponse> {
|
|
1021
565
|
try {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
566
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
1025
567
|
body: JSON.stringify({
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
568
|
+
newPath,
|
|
569
|
+
oldPath,
|
|
570
|
+
sessionId,
|
|
571
|
+
} as RenameFileRequest),
|
|
1029
572
|
headers: {
|
|
1030
573
|
"Content-Type": "application/json",
|
|
1031
574
|
},
|
|
@@ -1041,101 +584,30 @@ export class HttpClient {
|
|
|
1041
584
|
);
|
|
1042
585
|
}
|
|
1043
586
|
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
587
|
+
const data: RenameFileResponse = await response.json();
|
|
588
|
+
console.log(
|
|
589
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
590
|
+
);
|
|
1047
591
|
|
|
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
|
-
}
|
|
592
|
+
return data;
|
|
1114
593
|
} 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
|
-
);
|
|
594
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
1121
595
|
throw error;
|
|
1122
596
|
}
|
|
1123
597
|
}
|
|
1124
598
|
|
|
1125
|
-
async
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
sessionId
|
|
1129
|
-
): Promise<
|
|
599
|
+
async moveFile(
|
|
600
|
+
sourcePath: string,
|
|
601
|
+
destinationPath: string,
|
|
602
|
+
sessionId: string
|
|
603
|
+
): Promise<MoveFileResponse> {
|
|
1130
604
|
try {
|
|
1131
|
-
const
|
|
1132
|
-
|
|
1133
|
-
const response = await this.doFetch(`/api/rename`, {
|
|
605
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1134
606
|
body: JSON.stringify({
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
sessionId
|
|
1138
|
-
}),
|
|
607
|
+
destinationPath,
|
|
608
|
+
sourcePath,
|
|
609
|
+
sessionId,
|
|
610
|
+
} as MoveFileRequest),
|
|
1139
611
|
headers: {
|
|
1140
612
|
"Content-Type": "application/json",
|
|
1141
613
|
},
|
|
@@ -1151,32 +623,33 @@ export class HttpClient {
|
|
|
1151
623
|
);
|
|
1152
624
|
}
|
|
1153
625
|
|
|
1154
|
-
const data:
|
|
626
|
+
const data: MoveFileResponse = await response.json();
|
|
1155
627
|
console.log(
|
|
1156
|
-
`[HTTP Client] File
|
|
628
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1157
629
|
);
|
|
1158
630
|
|
|
1159
631
|
return data;
|
|
1160
632
|
} catch (error) {
|
|
1161
|
-
console.error("[HTTP Client] Error
|
|
633
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1162
634
|
throw error;
|
|
1163
635
|
}
|
|
1164
636
|
}
|
|
1165
637
|
|
|
1166
|
-
async
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
638
|
+
async listFiles(
|
|
639
|
+
path: string,
|
|
640
|
+
sessionId: string,
|
|
641
|
+
options?: {
|
|
642
|
+
recursive?: boolean;
|
|
643
|
+
includeHidden?: boolean;
|
|
644
|
+
}
|
|
645
|
+
): Promise<ListFilesResponse> {
|
|
1171
646
|
try {
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
const response = await this.doFetch(`/api/rename/stream`, {
|
|
647
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
1175
648
|
body: JSON.stringify({
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
sessionId
|
|
1179
|
-
}),
|
|
649
|
+
path,
|
|
650
|
+
options,
|
|
651
|
+
sessionId,
|
|
652
|
+
} as ListFilesRequest),
|
|
1180
653
|
headers: {
|
|
1181
654
|
"Content-Type": "application/json",
|
|
1182
655
|
},
|
|
@@ -1192,103 +665,24 @@ export class HttpClient {
|
|
|
1192
665
|
);
|
|
1193
666
|
}
|
|
1194
667
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
668
|
+
const data: ListFilesResponse = await response.json();
|
|
669
|
+
console.log(
|
|
670
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
671
|
+
);
|
|
1198
672
|
|
|
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
|
-
}
|
|
673
|
+
return data;
|
|
1268
674
|
} 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
|
-
);
|
|
675
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
1275
676
|
throw error;
|
|
1276
677
|
}
|
|
1277
678
|
}
|
|
1278
679
|
|
|
1279
|
-
async
|
|
1280
|
-
sourcePath: string,
|
|
1281
|
-
destinationPath: string,
|
|
1282
|
-
sessionId?: string
|
|
1283
|
-
): Promise<MoveFileResponse> {
|
|
680
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1284
681
|
try {
|
|
1285
|
-
const
|
|
1286
|
-
|
|
1287
|
-
const response = await this.doFetch(`/api/move`, {
|
|
682
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1288
683
|
body: JSON.stringify({
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
sourcePath,
|
|
684
|
+
port,
|
|
685
|
+
name,
|
|
1292
686
|
}),
|
|
1293
687
|
headers: {
|
|
1294
688
|
"Content-Type": "application/json",
|
|
@@ -1300,41 +694,36 @@ export class HttpClient {
|
|
|
1300
694
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1301
695
|
error?: string;
|
|
1302
696
|
};
|
|
697
|
+
console.log(errorData);
|
|
1303
698
|
throw new Error(
|
|
1304
699
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1305
700
|
);
|
|
1306
701
|
}
|
|
1307
702
|
|
|
1308
|
-
const data:
|
|
703
|
+
const data: ExposePortResponse = await response.json();
|
|
1309
704
|
console.log(
|
|
1310
|
-
`[HTTP Client]
|
|
705
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
706
|
+
name ? ` (${name})` : ""
|
|
707
|
+
}, Success: ${data.success}`
|
|
1311
708
|
);
|
|
1312
709
|
|
|
1313
710
|
return data;
|
|
1314
711
|
} catch (error) {
|
|
1315
|
-
console.error("[HTTP Client] Error
|
|
712
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1316
713
|
throw error;
|
|
1317
714
|
}
|
|
1318
715
|
}
|
|
1319
716
|
|
|
1320
|
-
async
|
|
1321
|
-
sourcePath: string,
|
|
1322
|
-
destinationPath: string,
|
|
1323
|
-
sessionId?: string
|
|
1324
|
-
): Promise<void> {
|
|
717
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1325
718
|
try {
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
const response = await this.doFetch(`/api/move/stream`, {
|
|
719
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1329
720
|
body: JSON.stringify({
|
|
1330
|
-
|
|
1331
|
-
sessionId: targetSessionId,
|
|
1332
|
-
sourcePath,
|
|
721
|
+
port,
|
|
1333
722
|
}),
|
|
1334
723
|
headers: {
|
|
1335
724
|
"Content-Type": "application/json",
|
|
1336
725
|
},
|
|
1337
|
-
method: "
|
|
726
|
+
method: "DELETE",
|
|
1338
727
|
});
|
|
1339
728
|
|
|
1340
729
|
if (!response.ok) {
|
|
@@ -1346,89 +735,42 @@ export class HttpClient {
|
|
|
1346
735
|
);
|
|
1347
736
|
}
|
|
1348
737
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
738
|
+
const data: UnexposePortResponse = await response.json();
|
|
739
|
+
console.log(
|
|
740
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
741
|
+
);
|
|
1352
742
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
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();
|
|
743
|
+
return data;
|
|
744
|
+
} catch (error) {
|
|
745
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
746
|
+
throw error;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
751
|
+
try {
|
|
752
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
753
|
+
headers: {
|
|
754
|
+
"Content-Type": "application/json",
|
|
755
|
+
},
|
|
756
|
+
method: "GET",
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
if (!response.ok) {
|
|
760
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
761
|
+
error?: string;
|
|
762
|
+
};
|
|
763
|
+
throw new Error(
|
|
764
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
765
|
+
);
|
|
1424
766
|
}
|
|
767
|
+
|
|
768
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
769
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
770
|
+
|
|
771
|
+
return data;
|
|
1425
772
|
} 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
|
-
);
|
|
773
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
1432
774
|
throw error;
|
|
1433
775
|
}
|
|
1434
776
|
}
|
|
@@ -1455,270 +797,252 @@ export class HttpClient {
|
|
|
1455
797
|
}
|
|
1456
798
|
}
|
|
1457
799
|
|
|
1458
|
-
|
|
800
|
+
|
|
801
|
+
// Process management methods
|
|
802
|
+
async startProcess(
|
|
803
|
+
command: string,
|
|
804
|
+
sessionId: string,
|
|
805
|
+
options?: {
|
|
806
|
+
processId?: string;
|
|
807
|
+
timeout?: number;
|
|
808
|
+
env?: Record<string, string>;
|
|
809
|
+
cwd?: string;
|
|
810
|
+
encoding?: string;
|
|
811
|
+
autoCleanup?: boolean;
|
|
812
|
+
}
|
|
813
|
+
): Promise<StartProcessResponse> {
|
|
1459
814
|
try {
|
|
1460
|
-
const response = await
|
|
815
|
+
const response = await this.doFetch("/api/process/start", {
|
|
816
|
+
body: JSON.stringify({
|
|
817
|
+
command,
|
|
818
|
+
sessionId,
|
|
819
|
+
options,
|
|
820
|
+
} as StartProcessRequest),
|
|
1461
821
|
headers: {
|
|
1462
822
|
"Content-Type": "application/json",
|
|
1463
823
|
},
|
|
1464
|
-
method: "
|
|
824
|
+
method: "POST",
|
|
1465
825
|
});
|
|
1466
826
|
|
|
1467
827
|
if (!response.ok) {
|
|
1468
|
-
|
|
828
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
829
|
+
error?: string;
|
|
830
|
+
};
|
|
831
|
+
throw new Error(
|
|
832
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
833
|
+
);
|
|
1469
834
|
}
|
|
1470
835
|
|
|
1471
|
-
const data:
|
|
836
|
+
const data: StartProcessResponse = await response.json();
|
|
1472
837
|
console.log(
|
|
1473
|
-
`[HTTP Client]
|
|
838
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
1474
839
|
);
|
|
1475
|
-
|
|
840
|
+
|
|
841
|
+
return data;
|
|
1476
842
|
} catch (error) {
|
|
1477
|
-
console.error("[HTTP Client] Error
|
|
843
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1478
844
|
throw error;
|
|
1479
845
|
}
|
|
1480
846
|
}
|
|
1481
847
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
848
|
+
async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
|
|
849
|
+
try {
|
|
850
|
+
const url = sessionId
|
|
851
|
+
? `/api/process/list?session=${encodeURIComponent(sessionId)}`
|
|
852
|
+
: "/api/process/list";
|
|
853
|
+
const response = await this.doFetch(url, {
|
|
854
|
+
headers: {
|
|
855
|
+
"Content-Type": "application/json",
|
|
856
|
+
},
|
|
857
|
+
method: "GET",
|
|
858
|
+
});
|
|
1485
859
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
860
|
+
if (!response.ok) {
|
|
861
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
862
|
+
error?: string;
|
|
863
|
+
};
|
|
864
|
+
throw new Error(
|
|
865
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
866
|
+
);
|
|
867
|
+
}
|
|
1489
868
|
|
|
1490
|
-
|
|
1491
|
-
|
|
869
|
+
const data: ListProcessesResponse = await response.json();
|
|
870
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
871
|
+
|
|
872
|
+
return data;
|
|
873
|
+
} catch (error) {
|
|
874
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
875
|
+
throw error;
|
|
876
|
+
}
|
|
1492
877
|
}
|
|
1493
|
-
}
|
|
1494
878
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
879
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
880
|
+
try {
|
|
881
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
882
|
+
headers: {
|
|
883
|
+
"Content-Type": "application/json",
|
|
884
|
+
},
|
|
885
|
+
method: "GET",
|
|
886
|
+
});
|
|
1499
887
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
try {
|
|
1510
|
-
return await client.execute(command, args);
|
|
1511
|
-
} finally {
|
|
1512
|
-
client.clearSession();
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
888
|
+
if (!response.ok) {
|
|
889
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
890
|
+
error?: string;
|
|
891
|
+
};
|
|
892
|
+
throw new Error(
|
|
893
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
894
|
+
);
|
|
895
|
+
}
|
|
1515
896
|
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
)
|
|
1522
|
-
const client = createClient(options);
|
|
1523
|
-
await client.createSession();
|
|
1524
|
-
|
|
1525
|
-
try {
|
|
1526
|
-
await client.executeStream(command, args);
|
|
1527
|
-
} finally {
|
|
1528
|
-
client.clearSession();
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
897
|
+
const data: GetProcessResponse = await response.json();
|
|
898
|
+
console.log(
|
|
899
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
900
|
+
data.process?.status || "not found"
|
|
901
|
+
}`
|
|
902
|
+
);
|
|
1531
903
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
options?: HttpClientOptions
|
|
1538
|
-
): Promise<GitCheckoutResponse> {
|
|
1539
|
-
const client = createClient(options);
|
|
1540
|
-
await client.createSession();
|
|
1541
|
-
|
|
1542
|
-
try {
|
|
1543
|
-
return await client.gitCheckout(repoUrl, branch, targetDir);
|
|
1544
|
-
} finally {
|
|
1545
|
-
client.clearSession();
|
|
904
|
+
return data;
|
|
905
|
+
} catch (error) {
|
|
906
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
907
|
+
throw error;
|
|
908
|
+
}
|
|
1546
909
|
}
|
|
1547
|
-
}
|
|
1548
910
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
return await client.mkdir(path, recursive);
|
|
1560
|
-
} finally {
|
|
1561
|
-
client.clearSession();
|
|
1562
|
-
}
|
|
1563
|
-
}
|
|
911
|
+
async killProcess(
|
|
912
|
+
processId: string
|
|
913
|
+
): Promise<{ success: boolean; message: string }> {
|
|
914
|
+
try {
|
|
915
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
916
|
+
headers: {
|
|
917
|
+
"Content-Type": "application/json",
|
|
918
|
+
},
|
|
919
|
+
method: "DELETE",
|
|
920
|
+
});
|
|
1564
921
|
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
)
|
|
1572
|
-
|
|
1573
|
-
await client.createSession();
|
|
1574
|
-
|
|
1575
|
-
try {
|
|
1576
|
-
await client.gitCheckoutStream(repoUrl, branch, targetDir);
|
|
1577
|
-
} finally {
|
|
1578
|
-
client.clearSession();
|
|
1579
|
-
}
|
|
1580
|
-
}
|
|
922
|
+
if (!response.ok) {
|
|
923
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
924
|
+
error?: string;
|
|
925
|
+
};
|
|
926
|
+
throw new Error(
|
|
927
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
928
|
+
);
|
|
929
|
+
}
|
|
1581
930
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
): Promise<void> {
|
|
1588
|
-
const client = createClient(options);
|
|
1589
|
-
await client.createSession();
|
|
1590
|
-
|
|
1591
|
-
try {
|
|
1592
|
-
await client.mkdirStream(path, recursive);
|
|
1593
|
-
} finally {
|
|
1594
|
-
client.clearSession();
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
931
|
+
const data = (await response.json()) as {
|
|
932
|
+
success: boolean;
|
|
933
|
+
message: string;
|
|
934
|
+
};
|
|
935
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1597
936
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
options?: HttpClientOptions
|
|
1604
|
-
): Promise<WriteFileResponse> {
|
|
1605
|
-
const client = createClient(options);
|
|
1606
|
-
await client.createSession();
|
|
1607
|
-
|
|
1608
|
-
try {
|
|
1609
|
-
return await client.writeFile(path, content, encoding);
|
|
1610
|
-
} finally {
|
|
1611
|
-
client.clearSession();
|
|
937
|
+
return data;
|
|
938
|
+
} catch (error) {
|
|
939
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
940
|
+
throw error;
|
|
941
|
+
}
|
|
1612
942
|
}
|
|
1613
|
-
}
|
|
1614
943
|
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
}
|
|
944
|
+
async killAllProcesses(sessionId?: string): Promise<{
|
|
945
|
+
success: boolean;
|
|
946
|
+
killedCount: number;
|
|
947
|
+
message: string;
|
|
948
|
+
}> {
|
|
949
|
+
try {
|
|
950
|
+
const url = sessionId
|
|
951
|
+
? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
|
|
952
|
+
: "/api/process/kill-all";
|
|
953
|
+
const response = await this.doFetch(url, {
|
|
954
|
+
headers: {
|
|
955
|
+
"Content-Type": "application/json",
|
|
956
|
+
},
|
|
957
|
+
method: "DELETE",
|
|
958
|
+
});
|
|
1631
959
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
try {
|
|
1641
|
-
return await client.deleteFile(path);
|
|
1642
|
-
} finally {
|
|
1643
|
-
client.clearSession();
|
|
1644
|
-
}
|
|
1645
|
-
}
|
|
960
|
+
if (!response.ok) {
|
|
961
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
962
|
+
error?: string;
|
|
963
|
+
};
|
|
964
|
+
throw new Error(
|
|
965
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
966
|
+
);
|
|
967
|
+
}
|
|
1646
968
|
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
await client.createSession();
|
|
1654
|
-
|
|
1655
|
-
try {
|
|
1656
|
-
await client.deleteFileStream(path);
|
|
1657
|
-
} finally {
|
|
1658
|
-
client.clearSession();
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
969
|
+
const data = (await response.json()) as {
|
|
970
|
+
success: boolean;
|
|
971
|
+
killedCount: number;
|
|
972
|
+
message: string;
|
|
973
|
+
};
|
|
974
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
1661
975
|
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
): Promise<RenameFileResponse> {
|
|
1668
|
-
const client = createClient(options);
|
|
1669
|
-
await client.createSession();
|
|
1670
|
-
|
|
1671
|
-
try {
|
|
1672
|
-
return await client.renameFile(oldPath, newPath);
|
|
1673
|
-
} finally {
|
|
1674
|
-
client.clearSession();
|
|
976
|
+
return data;
|
|
977
|
+
} catch (error) {
|
|
978
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
1675
981
|
}
|
|
1676
|
-
}
|
|
1677
982
|
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
983
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
984
|
+
try {
|
|
985
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
986
|
+
headers: {
|
|
987
|
+
"Content-Type": "application/json",
|
|
988
|
+
},
|
|
989
|
+
method: "GET",
|
|
990
|
+
});
|
|
991
|
+
|
|
992
|
+
if (!response.ok) {
|
|
993
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
994
|
+
error?: string;
|
|
995
|
+
};
|
|
996
|
+
throw new Error(
|
|
997
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
1002
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
1693
1003
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
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();
|
|
1004
|
+
return data;
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1707
1009
|
}
|
|
1708
|
-
}
|
|
1709
1010
|
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1011
|
+
async streamProcessLogs(
|
|
1012
|
+
processId: string,
|
|
1013
|
+
options?: { signal?: AbortSignal }
|
|
1014
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
1015
|
+
try {
|
|
1016
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
1017
|
+
headers: {
|
|
1018
|
+
Accept: "text/event-stream",
|
|
1019
|
+
"Cache-Control": "no-cache",
|
|
1020
|
+
},
|
|
1021
|
+
method: "GET",
|
|
1022
|
+
signal: options?.signal,
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
if (!response.ok) {
|
|
1026
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1027
|
+
error?: string;
|
|
1028
|
+
};
|
|
1029
|
+
throw new Error(
|
|
1030
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (!response.body) {
|
|
1035
|
+
throw new Error("No response body for streaming request");
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
console.log(
|
|
1039
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1040
|
+
);
|
|
1041
|
+
|
|
1042
|
+
return response.body;
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1045
|
+
throw error;
|
|
1046
|
+
}
|
|
1723
1047
|
}
|
|
1724
1048
|
}
|