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