@cloudflare/sandbox 0.0.0-ee8c772 → 0.0.0-ef9e320
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 +193 -0
- package/Dockerfile +96 -10
- package/README.md +806 -23
- 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 +406 -0
- package/container_src/handler/git.ts +130 -0
- package/container_src/handler/ports.ts +314 -0
- package/container_src/handler/process.ts +568 -0
- package/container_src/handler/session.ts +92 -0
- package/container_src/index.ts +432 -2740
- package/container_src/interpreter-service.ts +276 -0
- package/container_src/isolation.ts +1038 -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 +442 -1362
- package/src/errors.ts +219 -0
- package/src/index.ts +72 -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 +747 -0
- package/src/security.ts +113 -0
- package/src/sse-parser.ts +147 -0
- package/src/types.ts +502 -0
- package/tsconfig.json +1 -1
- package/tests/client.example.ts +0 -308
- package/tests/connection-test.ts +0 -81
- package/tests/simple-test.ts +0 -81
- package/tests/test1.ts +0 -281
- package/tests/test2.ts +0 -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;
|
|
132
74
|
}
|
|
133
75
|
|
|
134
|
-
|
|
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 {
|
|
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;
|
|
144
109
|
timestamp: string;
|
|
145
110
|
}
|
|
146
111
|
|
|
147
|
-
interface
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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;
|
|
112
|
+
interface GetExposedPortsResponse {
|
|
113
|
+
ports: ExposedPort[];
|
|
114
|
+
count: number;
|
|
115
|
+
timestamp: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
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,22 +362,18 @@ export class HttpClient {
|
|
|
530
362
|
}
|
|
531
363
|
}
|
|
532
364
|
|
|
533
|
-
async
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
): Promise<void> {
|
|
365
|
+
async mkdir(
|
|
366
|
+
path: string,
|
|
367
|
+
recursive: boolean = false,
|
|
368
|
+
sessionId: string
|
|
369
|
+
): Promise<MkdirResponse> {
|
|
539
370
|
try {
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
const response = await this.doFetch(`/api/git/checkout/stream`, {
|
|
371
|
+
const response = await this.doFetch(`/api/mkdir`, {
|
|
543
372
|
body: JSON.stringify({
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
sessionId
|
|
547
|
-
|
|
548
|
-
}),
|
|
373
|
+
path,
|
|
374
|
+
recursive,
|
|
375
|
+
sessionId,
|
|
376
|
+
} as MkdirRequest),
|
|
549
377
|
headers: {
|
|
550
378
|
"Content-Type": "application/json",
|
|
551
379
|
},
|
|
@@ -561,119 +389,32 @@ export class HttpClient {
|
|
|
561
389
|
);
|
|
562
390
|
}
|
|
563
391
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
392
|
+
const data: MkdirResponse = await response.json();
|
|
393
|
+
console.log(
|
|
394
|
+
`[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
395
|
+
);
|
|
567
396
|
|
|
568
|
-
|
|
569
|
-
const decoder = new TextDecoder();
|
|
570
|
-
|
|
571
|
-
try {
|
|
572
|
-
while (true) {
|
|
573
|
-
const { done, value } = await reader.read();
|
|
574
|
-
|
|
575
|
-
if (done) {
|
|
576
|
-
break;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
580
|
-
const lines = chunk.split("\n");
|
|
581
|
-
|
|
582
|
-
for (const line of lines) {
|
|
583
|
-
if (line.startsWith("data: ")) {
|
|
584
|
-
try {
|
|
585
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
586
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
587
|
-
|
|
588
|
-
console.log(
|
|
589
|
-
`[HTTP Client] Git checkout stream event: ${event.type}`
|
|
590
|
-
);
|
|
591
|
-
this.options.onStreamEvent?.(event);
|
|
592
|
-
|
|
593
|
-
switch (event.type) {
|
|
594
|
-
case "command_start":
|
|
595
|
-
console.log(
|
|
596
|
-
`[HTTP Client] Git checkout started: ${
|
|
597
|
-
event.command
|
|
598
|
-
} ${event.args?.join(" ")}`
|
|
599
|
-
);
|
|
600
|
-
this.options.onCommandStart?.(
|
|
601
|
-
event.command!,
|
|
602
|
-
event.args || []
|
|
603
|
-
);
|
|
604
|
-
break;
|
|
605
|
-
|
|
606
|
-
case "output":
|
|
607
|
-
console.log(`[${event.stream}] ${event.data}`);
|
|
608
|
-
this.options.onOutput?.(
|
|
609
|
-
event.stream!,
|
|
610
|
-
event.data!,
|
|
611
|
-
event.command!
|
|
612
|
-
);
|
|
613
|
-
break;
|
|
614
|
-
|
|
615
|
-
case "command_complete":
|
|
616
|
-
console.log(
|
|
617
|
-
`[HTTP Client] Git checkout completed: ${event.command}, Success: ${event.success}, Exit code: ${event.exitCode}`
|
|
618
|
-
);
|
|
619
|
-
this.options.onCommandComplete?.(
|
|
620
|
-
event.success!,
|
|
621
|
-
event.exitCode!,
|
|
622
|
-
event.stdout!,
|
|
623
|
-
event.stderr!,
|
|
624
|
-
event.command!,
|
|
625
|
-
event.args || []
|
|
626
|
-
);
|
|
627
|
-
break;
|
|
628
|
-
|
|
629
|
-
case "error":
|
|
630
|
-
console.error(
|
|
631
|
-
`[HTTP Client] Git checkout error: ${event.error}`
|
|
632
|
-
);
|
|
633
|
-
this.options.onError?.(
|
|
634
|
-
event.error!,
|
|
635
|
-
event.command,
|
|
636
|
-
event.args
|
|
637
|
-
);
|
|
638
|
-
break;
|
|
639
|
-
}
|
|
640
|
-
} catch (parseError) {
|
|
641
|
-
console.warn(
|
|
642
|
-
"[HTTP Client] Failed to parse git checkout stream event:",
|
|
643
|
-
parseError
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
} finally {
|
|
650
|
-
reader.releaseLock();
|
|
651
|
-
}
|
|
397
|
+
return data;
|
|
652
398
|
} catch (error) {
|
|
653
|
-
console.error("[HTTP Client] Error
|
|
654
|
-
this.options.onError?.(
|
|
655
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
656
|
-
"git clone",
|
|
657
|
-
[branch, repoUrl, targetDir || ""]
|
|
658
|
-
);
|
|
399
|
+
console.error("[HTTP Client] Error creating directory:", error);
|
|
659
400
|
throw error;
|
|
660
401
|
}
|
|
661
402
|
}
|
|
662
403
|
|
|
663
|
-
async
|
|
404
|
+
async writeFile(
|
|
664
405
|
path: string,
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
406
|
+
content: string,
|
|
407
|
+
encoding: string = "utf-8",
|
|
408
|
+
sessionId: string
|
|
409
|
+
): Promise<WriteFileResponse> {
|
|
668
410
|
try {
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
const response = await this.doFetch(`/api/mkdir`, {
|
|
411
|
+
const response = await this.doFetch(`/api/write`, {
|
|
672
412
|
body: JSON.stringify({
|
|
413
|
+
content,
|
|
414
|
+
encoding,
|
|
673
415
|
path,
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}),
|
|
416
|
+
sessionId,
|
|
417
|
+
} as WriteFileRequest),
|
|
677
418
|
headers: {
|
|
678
419
|
"Content-Type": "application/json",
|
|
679
420
|
},
|
|
@@ -689,32 +430,30 @@ export class HttpClient {
|
|
|
689
430
|
);
|
|
690
431
|
}
|
|
691
432
|
|
|
692
|
-
const data:
|
|
433
|
+
const data: WriteFileResponse = await response.json();
|
|
693
434
|
console.log(
|
|
694
|
-
`[HTTP Client]
|
|
435
|
+
`[HTTP Client] File written: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
695
436
|
);
|
|
696
437
|
|
|
697
438
|
return data;
|
|
698
439
|
} catch (error) {
|
|
699
|
-
console.error("[HTTP Client] Error
|
|
440
|
+
console.error("[HTTP Client] Error writing file:", error);
|
|
700
441
|
throw error;
|
|
701
442
|
}
|
|
702
443
|
}
|
|
703
444
|
|
|
704
|
-
async
|
|
445
|
+
async readFile(
|
|
705
446
|
path: string,
|
|
706
|
-
|
|
707
|
-
sessionId
|
|
708
|
-
): Promise<
|
|
447
|
+
encoding: string = "utf-8",
|
|
448
|
+
sessionId: string
|
|
449
|
+
): Promise<ReadFileResponse> {
|
|
709
450
|
try {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
const response = await this.doFetch(`/api/mkdir/stream`, {
|
|
451
|
+
const response = await this.doFetch(`/api/read`, {
|
|
713
452
|
body: JSON.stringify({
|
|
453
|
+
encoding,
|
|
714
454
|
path,
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}),
|
|
455
|
+
sessionId,
|
|
456
|
+
} as ReadFileRequest),
|
|
718
457
|
headers: {
|
|
719
458
|
"Content-Type": "application/json",
|
|
720
459
|
},
|
|
@@ -730,117 +469,28 @@ export class HttpClient {
|
|
|
730
469
|
);
|
|
731
470
|
}
|
|
732
471
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
472
|
+
const data: ReadFileResponse = await response.json();
|
|
473
|
+
console.log(
|
|
474
|
+
`[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
475
|
+
);
|
|
736
476
|
|
|
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
|
-
}
|
|
477
|
+
return data;
|
|
817
478
|
} 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
|
-
);
|
|
479
|
+
console.error("[HTTP Client] Error reading file:", error);
|
|
824
480
|
throw error;
|
|
825
481
|
}
|
|
826
482
|
}
|
|
827
483
|
|
|
828
|
-
async
|
|
484
|
+
async deleteFile(
|
|
829
485
|
path: string,
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
sessionId?: string
|
|
833
|
-
): Promise<WriteFileResponse> {
|
|
486
|
+
sessionId: string
|
|
487
|
+
): Promise<DeleteFileResponse> {
|
|
834
488
|
try {
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
const response = await this.doFetch(`/api/write`, {
|
|
489
|
+
const response = await this.doFetch(`/api/delete`, {
|
|
838
490
|
body: JSON.stringify({
|
|
839
|
-
content,
|
|
840
|
-
encoding,
|
|
841
491
|
path,
|
|
842
|
-
sessionId
|
|
843
|
-
}),
|
|
492
|
+
sessionId,
|
|
493
|
+
} as DeleteFileRequest),
|
|
844
494
|
headers: {
|
|
845
495
|
"Content-Type": "application/json",
|
|
846
496
|
},
|
|
@@ -856,34 +506,30 @@ export class HttpClient {
|
|
|
856
506
|
);
|
|
857
507
|
}
|
|
858
508
|
|
|
859
|
-
const data:
|
|
509
|
+
const data: DeleteFileResponse = await response.json();
|
|
860
510
|
console.log(
|
|
861
|
-
`[HTTP Client] File
|
|
511
|
+
`[HTTP Client] File deleted: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
862
512
|
);
|
|
863
513
|
|
|
864
514
|
return data;
|
|
865
515
|
} catch (error) {
|
|
866
|
-
console.error("[HTTP Client] Error
|
|
516
|
+
console.error("[HTTP Client] Error deleting file:", error);
|
|
867
517
|
throw error;
|
|
868
518
|
}
|
|
869
519
|
}
|
|
870
520
|
|
|
871
|
-
async
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
): Promise<void> {
|
|
521
|
+
async renameFile(
|
|
522
|
+
oldPath: string,
|
|
523
|
+
newPath: string,
|
|
524
|
+
sessionId: string
|
|
525
|
+
): Promise<RenameFileResponse> {
|
|
877
526
|
try {
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
const response = await this.doFetch(`/api/write/stream`, {
|
|
527
|
+
const response = await this.doFetch(`/api/rename`, {
|
|
881
528
|
body: JSON.stringify({
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}),
|
|
529
|
+
newPath,
|
|
530
|
+
oldPath,
|
|
531
|
+
sessionId,
|
|
532
|
+
} as RenameFileRequest),
|
|
887
533
|
headers: {
|
|
888
534
|
"Content-Type": "application/json",
|
|
889
535
|
},
|
|
@@ -899,114 +545,30 @@ export class HttpClient {
|
|
|
899
545
|
);
|
|
900
546
|
}
|
|
901
547
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
548
|
+
const data: RenameFileResponse = await response.json();
|
|
549
|
+
console.log(
|
|
550
|
+
`[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
551
|
+
);
|
|
905
552
|
|
|
906
|
-
|
|
907
|
-
const decoder = new TextDecoder();
|
|
908
|
-
|
|
909
|
-
try {
|
|
910
|
-
while (true) {
|
|
911
|
-
const { done, value } = await reader.read();
|
|
912
|
-
|
|
913
|
-
if (done) {
|
|
914
|
-
break;
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
const chunk = decoder.decode(value, { stream: true });
|
|
918
|
-
const lines = chunk.split("\n");
|
|
919
|
-
|
|
920
|
-
for (const line of lines) {
|
|
921
|
-
if (line.startsWith("data: ")) {
|
|
922
|
-
try {
|
|
923
|
-
const eventData = line.slice(6); // Remove 'data: ' prefix
|
|
924
|
-
const event: StreamEvent = JSON.parse(eventData);
|
|
925
|
-
|
|
926
|
-
console.log(
|
|
927
|
-
`[HTTP Client] Write file stream event: ${event.type}`
|
|
928
|
-
);
|
|
929
|
-
this.options.onStreamEvent?.(event);
|
|
930
|
-
|
|
931
|
-
switch (event.type) {
|
|
932
|
-
case "command_start":
|
|
933
|
-
console.log(
|
|
934
|
-
`[HTTP Client] Write file started: ${event.path}`
|
|
935
|
-
);
|
|
936
|
-
this.options.onCommandStart?.("write", [
|
|
937
|
-
path,
|
|
938
|
-
content,
|
|
939
|
-
encoding,
|
|
940
|
-
]);
|
|
941
|
-
break;
|
|
942
|
-
|
|
943
|
-
case "output":
|
|
944
|
-
console.log(`[output] ${event.message}`);
|
|
945
|
-
this.options.onOutput?.("stdout", event.message!, "write");
|
|
946
|
-
break;
|
|
947
|
-
|
|
948
|
-
case "command_complete":
|
|
949
|
-
console.log(
|
|
950
|
-
`[HTTP Client] Write file completed: ${event.path}, Success: ${event.success}`
|
|
951
|
-
);
|
|
952
|
-
this.options.onCommandComplete?.(
|
|
953
|
-
event.success!,
|
|
954
|
-
0,
|
|
955
|
-
"",
|
|
956
|
-
"",
|
|
957
|
-
"write",
|
|
958
|
-
[path, content, encoding]
|
|
959
|
-
);
|
|
960
|
-
break;
|
|
961
|
-
|
|
962
|
-
case "error":
|
|
963
|
-
console.error(
|
|
964
|
-
`[HTTP Client] Write file error: ${event.error}`
|
|
965
|
-
);
|
|
966
|
-
this.options.onError?.(event.error!, "write", [
|
|
967
|
-
path,
|
|
968
|
-
content,
|
|
969
|
-
encoding,
|
|
970
|
-
]);
|
|
971
|
-
break;
|
|
972
|
-
}
|
|
973
|
-
} catch (parseError) {
|
|
974
|
-
console.warn(
|
|
975
|
-
"[HTTP Client] Failed to parse write file stream event:",
|
|
976
|
-
parseError
|
|
977
|
-
);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
} finally {
|
|
983
|
-
reader.releaseLock();
|
|
984
|
-
}
|
|
553
|
+
return data;
|
|
985
554
|
} catch (error) {
|
|
986
|
-
console.error("[HTTP Client] Error
|
|
987
|
-
this.options.onError?.(
|
|
988
|
-
error instanceof Error ? error.message : "Unknown error",
|
|
989
|
-
"write",
|
|
990
|
-
[path, content, encoding]
|
|
991
|
-
);
|
|
555
|
+
console.error("[HTTP Client] Error renaming file:", error);
|
|
992
556
|
throw error;
|
|
993
557
|
}
|
|
994
558
|
}
|
|
995
559
|
|
|
996
|
-
async
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
sessionId
|
|
1000
|
-
): Promise<
|
|
560
|
+
async moveFile(
|
|
561
|
+
sourcePath: string,
|
|
562
|
+
destinationPath: string,
|
|
563
|
+
sessionId: string
|
|
564
|
+
): Promise<MoveFileResponse> {
|
|
1001
565
|
try {
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
const response = await this.doFetch(`/api/read`, {
|
|
566
|
+
const response = await this.doFetch(`/api/move`, {
|
|
1005
567
|
body: JSON.stringify({
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
sessionId
|
|
1009
|
-
}),
|
|
568
|
+
destinationPath,
|
|
569
|
+
sourcePath,
|
|
570
|
+
sessionId,
|
|
571
|
+
} as MoveFileRequest),
|
|
1010
572
|
headers: {
|
|
1011
573
|
"Content-Type": "application/json",
|
|
1012
574
|
},
|
|
@@ -1022,32 +584,33 @@ export class HttpClient {
|
|
|
1022
584
|
);
|
|
1023
585
|
}
|
|
1024
586
|
|
|
1025
|
-
const data:
|
|
587
|
+
const data: MoveFileResponse = await response.json();
|
|
1026
588
|
console.log(
|
|
1027
|
-
`[HTTP Client] File
|
|
589
|
+
`[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
1028
590
|
);
|
|
1029
591
|
|
|
1030
592
|
return data;
|
|
1031
593
|
} catch (error) {
|
|
1032
|
-
console.error("[HTTP Client] Error
|
|
594
|
+
console.error("[HTTP Client] Error moving file:", error);
|
|
1033
595
|
throw error;
|
|
1034
596
|
}
|
|
1035
597
|
}
|
|
1036
598
|
|
|
1037
|
-
async
|
|
599
|
+
async listFiles(
|
|
1038
600
|
path: string,
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
601
|
+
sessionId: string,
|
|
602
|
+
options?: {
|
|
603
|
+
recursive?: boolean;
|
|
604
|
+
includeHidden?: boolean;
|
|
605
|
+
}
|
|
606
|
+
): Promise<ListFilesResponse> {
|
|
1042
607
|
try {
|
|
1043
|
-
const
|
|
1044
|
-
|
|
1045
|
-
const response = await this.doFetch(`/api/read/stream`, {
|
|
608
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
1046
609
|
body: JSON.stringify({
|
|
1047
|
-
encoding,
|
|
1048
610
|
path,
|
|
1049
|
-
|
|
1050
|
-
|
|
611
|
+
options,
|
|
612
|
+
sessionId,
|
|
613
|
+
} as ListFilesRequest),
|
|
1051
614
|
headers: {
|
|
1052
615
|
"Content-Type": "application/json",
|
|
1053
616
|
},
|
|
@@ -1063,105 +626,24 @@ export class HttpClient {
|
|
|
1063
626
|
);
|
|
1064
627
|
}
|
|
1065
628
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
629
|
+
const data: ListFilesResponse = await response.json();
|
|
630
|
+
console.log(
|
|
631
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}${sessionId ? ` in session: ${sessionId}` : ''}`
|
|
632
|
+
);
|
|
1069
633
|
|
|
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
|
-
}
|
|
634
|
+
return data;
|
|
1143
635
|
} 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
|
-
);
|
|
636
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
1150
637
|
throw error;
|
|
1151
638
|
}
|
|
1152
639
|
}
|
|
1153
640
|
|
|
1154
|
-
async
|
|
1155
|
-
path: string,
|
|
1156
|
-
sessionId?: string
|
|
1157
|
-
): Promise<DeleteFileResponse> {
|
|
641
|
+
async exposePort(port: number, name?: string): Promise<ExposePortResponse> {
|
|
1158
642
|
try {
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
const response = await this.doFetch(`/api/delete`, {
|
|
643
|
+
const response = await this.doFetch(`/api/expose-port`, {
|
|
1162
644
|
body: JSON.stringify({
|
|
1163
|
-
|
|
1164
|
-
|
|
645
|
+
port,
|
|
646
|
+
name,
|
|
1165
647
|
}),
|
|
1166
648
|
headers: {
|
|
1167
649
|
"Content-Type": "application/json",
|
|
@@ -1173,36 +655,36 @@ export class HttpClient {
|
|
|
1173
655
|
const errorData = (await response.json().catch(() => ({}))) as {
|
|
1174
656
|
error?: string;
|
|
1175
657
|
};
|
|
658
|
+
console.log(errorData);
|
|
1176
659
|
throw new Error(
|
|
1177
660
|
errorData.error || `HTTP error! status: ${response.status}`
|
|
1178
661
|
);
|
|
1179
662
|
}
|
|
1180
663
|
|
|
1181
|
-
const data:
|
|
664
|
+
const data: ExposePortResponse = await response.json();
|
|
1182
665
|
console.log(
|
|
1183
|
-
`[HTTP Client]
|
|
666
|
+
`[HTTP Client] Port exposed: ${port}${
|
|
667
|
+
name ? ` (${name})` : ""
|
|
668
|
+
}, Success: ${data.success}`
|
|
1184
669
|
);
|
|
1185
670
|
|
|
1186
671
|
return data;
|
|
1187
672
|
} catch (error) {
|
|
1188
|
-
console.error("[HTTP Client] Error
|
|
673
|
+
console.error("[HTTP Client] Error exposing port:", error);
|
|
1189
674
|
throw error;
|
|
1190
675
|
}
|
|
1191
676
|
}
|
|
1192
677
|
|
|
1193
|
-
async
|
|
678
|
+
async unexposePort(port: number): Promise<UnexposePortResponse> {
|
|
1194
679
|
try {
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
const response = await this.doFetch(`/api/delete/stream`, {
|
|
680
|
+
const response = await this.doFetch(`/api/unexpose-port`, {
|
|
1198
681
|
body: JSON.stringify({
|
|
1199
|
-
|
|
1200
|
-
sessionId: targetSessionId,
|
|
682
|
+
port,
|
|
1201
683
|
}),
|
|
1202
684
|
headers: {
|
|
1203
685
|
"Content-Type": "application/json",
|
|
1204
686
|
},
|
|
1205
|
-
method: "
|
|
687
|
+
method: "DELETE",
|
|
1206
688
|
});
|
|
1207
689
|
|
|
1208
690
|
if (!response.ok) {
|
|
@@ -1214,101 +696,89 @@ export class HttpClient {
|
|
|
1214
696
|
);
|
|
1215
697
|
}
|
|
1216
698
|
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
699
|
+
const data: UnexposePortResponse = await response.json();
|
|
700
|
+
console.log(
|
|
701
|
+
`[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`
|
|
702
|
+
);
|
|
1220
703
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
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();
|
|
704
|
+
return data;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
console.error("[HTTP Client] Error unexposing port:", error);
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
async getExposedPorts(): Promise<GetExposedPortsResponse> {
|
|
712
|
+
try {
|
|
713
|
+
const response = await this.doFetch(`/api/exposed-ports`, {
|
|
714
|
+
headers: {
|
|
715
|
+
"Content-Type": "application/json",
|
|
716
|
+
},
|
|
717
|
+
method: "GET",
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
if (!response.ok) {
|
|
721
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
722
|
+
error?: string;
|
|
723
|
+
};
|
|
724
|
+
throw new Error(
|
|
725
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
726
|
+
);
|
|
1286
727
|
}
|
|
728
|
+
|
|
729
|
+
const data: GetExposedPortsResponse = await response.json();
|
|
730
|
+
console.log(`[HTTP Client] Got ${data.count} exposed ports`);
|
|
731
|
+
|
|
732
|
+
return data;
|
|
1287
733
|
} 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
|
-
);
|
|
734
|
+
console.error("[HTTP Client] Error getting exposed ports:", error);
|
|
1294
735
|
throw error;
|
|
1295
736
|
}
|
|
1296
737
|
}
|
|
1297
738
|
|
|
1298
|
-
async
|
|
1299
|
-
oldPath: string,
|
|
1300
|
-
newPath: string,
|
|
1301
|
-
sessionId?: string
|
|
1302
|
-
): Promise<RenameFileResponse> {
|
|
739
|
+
async ping(): Promise<string> {
|
|
1303
740
|
try {
|
|
1304
|
-
const
|
|
741
|
+
const response = await this.doFetch(`/api/ping`, {
|
|
742
|
+
headers: {
|
|
743
|
+
"Content-Type": "application/json",
|
|
744
|
+
},
|
|
745
|
+
method: "GET",
|
|
746
|
+
});
|
|
1305
747
|
|
|
1306
|
-
|
|
748
|
+
if (!response.ok) {
|
|
749
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
const data: PingResponse = await response.json();
|
|
753
|
+
console.log(`[HTTP Client] Ping response: ${data.message}`);
|
|
754
|
+
return data.timestamp;
|
|
755
|
+
} catch (error) {
|
|
756
|
+
console.error("[HTTP Client] Error pinging server:", error);
|
|
757
|
+
throw error;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
// Process management methods
|
|
763
|
+
async startProcess(
|
|
764
|
+
command: string,
|
|
765
|
+
sessionId: string,
|
|
766
|
+
options?: {
|
|
767
|
+
processId?: string;
|
|
768
|
+
timeout?: number;
|
|
769
|
+
env?: Record<string, string>;
|
|
770
|
+
cwd?: string;
|
|
771
|
+
encoding?: string;
|
|
772
|
+
autoCleanup?: boolean;
|
|
773
|
+
}
|
|
774
|
+
): Promise<StartProcessResponse> {
|
|
775
|
+
try {
|
|
776
|
+
const response = await this.doFetch("/api/process/start", {
|
|
1307
777
|
body: JSON.stringify({
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}),
|
|
778
|
+
command,
|
|
779
|
+
sessionId,
|
|
780
|
+
options,
|
|
781
|
+
} as StartProcessRequest),
|
|
1312
782
|
headers: {
|
|
1313
783
|
"Content-Type": "application/json",
|
|
1314
784
|
},
|
|
@@ -1324,36 +794,28 @@ export class HttpClient {
|
|
|
1324
794
|
);
|
|
1325
795
|
}
|
|
1326
796
|
|
|
1327
|
-
const data:
|
|
797
|
+
const data: StartProcessResponse = await response.json();
|
|
1328
798
|
console.log(
|
|
1329
|
-
`[HTTP Client]
|
|
799
|
+
`[HTTP Client] Process started: ${command}, ID: ${data.process.id}`
|
|
1330
800
|
);
|
|
1331
801
|
|
|
1332
802
|
return data;
|
|
1333
803
|
} catch (error) {
|
|
1334
|
-
console.error("[HTTP Client] Error
|
|
804
|
+
console.error("[HTTP Client] Error starting process:", error);
|
|
1335
805
|
throw error;
|
|
1336
806
|
}
|
|
1337
807
|
}
|
|
1338
808
|
|
|
1339
|
-
async
|
|
1340
|
-
oldPath: string,
|
|
1341
|
-
newPath: string,
|
|
1342
|
-
sessionId?: string
|
|
1343
|
-
): Promise<void> {
|
|
809
|
+
async listProcesses(sessionId?: string): Promise<ListProcessesResponse> {
|
|
1344
810
|
try {
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
newPath,
|
|
1350
|
-
oldPath,
|
|
1351
|
-
sessionId: targetSessionId,
|
|
1352
|
-
}),
|
|
811
|
+
const url = sessionId
|
|
812
|
+
? `/api/process/list?session=${encodeURIComponent(sessionId)}`
|
|
813
|
+
: "/api/process/list";
|
|
814
|
+
const response = await this.doFetch(url, {
|
|
1353
815
|
headers: {
|
|
1354
816
|
"Content-Type": "application/json",
|
|
1355
817
|
},
|
|
1356
|
-
method: "
|
|
818
|
+
method: "GET",
|
|
1357
819
|
});
|
|
1358
820
|
|
|
1359
821
|
if (!response.ok) {
|
|
@@ -1365,108 +827,23 @@ export class HttpClient {
|
|
|
1365
827
|
);
|
|
1366
828
|
}
|
|
1367
829
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
}
|
|
830
|
+
const data: ListProcessesResponse = await response.json();
|
|
831
|
+
console.log(`[HTTP Client] Listed ${data.processes.length} processes`);
|
|
1371
832
|
|
|
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
|
-
}
|
|
833
|
+
return data;
|
|
1441
834
|
} 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
|
-
);
|
|
835
|
+
console.error("[HTTP Client] Error listing processes:", error);
|
|
1448
836
|
throw error;
|
|
1449
837
|
}
|
|
1450
838
|
}
|
|
1451
839
|
|
|
1452
|
-
async
|
|
1453
|
-
sourcePath: string,
|
|
1454
|
-
destinationPath: string,
|
|
1455
|
-
sessionId?: string
|
|
1456
|
-
): Promise<MoveFileResponse> {
|
|
840
|
+
async getProcess(processId: string): Promise<GetProcessResponse> {
|
|
1457
841
|
try {
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1460
|
-
const response = await this.doFetch(`/api/move`, {
|
|
1461
|
-
body: JSON.stringify({
|
|
1462
|
-
destinationPath,
|
|
1463
|
-
sessionId: targetSessionId,
|
|
1464
|
-
sourcePath,
|
|
1465
|
-
}),
|
|
842
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1466
843
|
headers: {
|
|
1467
844
|
"Content-Type": "application/json",
|
|
1468
845
|
},
|
|
1469
|
-
method: "
|
|
846
|
+
method: "GET",
|
|
1470
847
|
});
|
|
1471
848
|
|
|
1472
849
|
if (!response.ok) {
|
|
@@ -1478,36 +855,29 @@ export class HttpClient {
|
|
|
1478
855
|
);
|
|
1479
856
|
}
|
|
1480
857
|
|
|
1481
|
-
const data:
|
|
858
|
+
const data: GetProcessResponse = await response.json();
|
|
1482
859
|
console.log(
|
|
1483
|
-
`[HTTP Client]
|
|
860
|
+
`[HTTP Client] Got process ${processId}: ${
|
|
861
|
+
data.process?.status || "not found"
|
|
862
|
+
}`
|
|
1484
863
|
);
|
|
1485
864
|
|
|
1486
865
|
return data;
|
|
1487
866
|
} catch (error) {
|
|
1488
|
-
console.error("[HTTP Client] Error
|
|
867
|
+
console.error("[HTTP Client] Error getting process:", error);
|
|
1489
868
|
throw error;
|
|
1490
869
|
}
|
|
1491
870
|
}
|
|
1492
871
|
|
|
1493
|
-
async
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
sessionId?: string
|
|
1497
|
-
): Promise<void> {
|
|
872
|
+
async killProcess(
|
|
873
|
+
processId: string
|
|
874
|
+
): Promise<{ success: boolean; message: string }> {
|
|
1498
875
|
try {
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1501
|
-
const response = await this.doFetch(`/api/move/stream`, {
|
|
1502
|
-
body: JSON.stringify({
|
|
1503
|
-
destinationPath,
|
|
1504
|
-
sessionId: targetSessionId,
|
|
1505
|
-
sourcePath,
|
|
1506
|
-
}),
|
|
876
|
+
const response = await this.doFetch(`/api/process/${processId}`, {
|
|
1507
877
|
headers: {
|
|
1508
878
|
"Content-Type": "application/json",
|
|
1509
879
|
},
|
|
1510
|
-
method: "
|
|
880
|
+
method: "DELETE",
|
|
1511
881
|
});
|
|
1512
882
|
|
|
1513
883
|
if (!response.ok) {
|
|
@@ -1519,118 +889,61 @@ export class HttpClient {
|
|
|
1519
889
|
);
|
|
1520
890
|
}
|
|
1521
891
|
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
892
|
+
const data = (await response.json()) as {
|
|
893
|
+
success: boolean;
|
|
894
|
+
message: string;
|
|
895
|
+
};
|
|
896
|
+
console.log(`[HTTP Client] Killed process ${processId}`);
|
|
1525
897
|
|
|
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
|
-
}
|
|
898
|
+
return data;
|
|
1598
899
|
} 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
|
-
);
|
|
900
|
+
console.error("[HTTP Client] Error killing process:", error);
|
|
1605
901
|
throw error;
|
|
1606
902
|
}
|
|
1607
903
|
}
|
|
1608
904
|
|
|
1609
|
-
async
|
|
905
|
+
async killAllProcesses(sessionId?: string): Promise<{
|
|
906
|
+
success: boolean;
|
|
907
|
+
killedCount: number;
|
|
908
|
+
message: string;
|
|
909
|
+
}> {
|
|
1610
910
|
try {
|
|
1611
|
-
const
|
|
911
|
+
const url = sessionId
|
|
912
|
+
? `/api/process/kill-all?session=${encodeURIComponent(sessionId)}`
|
|
913
|
+
: "/api/process/kill-all";
|
|
914
|
+
const response = await this.doFetch(url, {
|
|
1612
915
|
headers: {
|
|
1613
916
|
"Content-Type": "application/json",
|
|
1614
917
|
},
|
|
1615
|
-
method: "
|
|
918
|
+
method: "DELETE",
|
|
1616
919
|
});
|
|
1617
920
|
|
|
1618
921
|
if (!response.ok) {
|
|
1619
|
-
|
|
922
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
923
|
+
error?: string;
|
|
924
|
+
};
|
|
925
|
+
throw new Error(
|
|
926
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
927
|
+
);
|
|
1620
928
|
}
|
|
1621
929
|
|
|
1622
|
-
const data
|
|
1623
|
-
|
|
1624
|
-
|
|
930
|
+
const data = (await response.json()) as {
|
|
931
|
+
success: boolean;
|
|
932
|
+
killedCount: number;
|
|
933
|
+
message: string;
|
|
934
|
+
};
|
|
935
|
+
console.log(`[HTTP Client] Killed ${data.killedCount} processes`);
|
|
936
|
+
|
|
937
|
+
return data;
|
|
1625
938
|
} catch (error) {
|
|
1626
|
-
console.error("[HTTP Client] Error
|
|
939
|
+
console.error("[HTTP Client] Error killing all processes:", error);
|
|
1627
940
|
throw error;
|
|
1628
941
|
}
|
|
1629
942
|
}
|
|
1630
943
|
|
|
1631
|
-
async
|
|
944
|
+
async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {
|
|
1632
945
|
try {
|
|
1633
|
-
const response = await
|
|
946
|
+
const response = await this.doFetch(`/api/process/${processId}/logs`, {
|
|
1634
947
|
headers: {
|
|
1635
948
|
"Content-Type": "application/json",
|
|
1636
949
|
},
|
|
@@ -1638,292 +951,59 @@ export class HttpClient {
|
|
|
1638
951
|
});
|
|
1639
952
|
|
|
1640
953
|
if (!response.ok) {
|
|
1641
|
-
|
|
954
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
955
|
+
error?: string;
|
|
956
|
+
};
|
|
957
|
+
throw new Error(
|
|
958
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
959
|
+
);
|
|
1642
960
|
}
|
|
1643
961
|
|
|
1644
|
-
const data:
|
|
1645
|
-
console.log(
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
return data.availableCommands;
|
|
962
|
+
const data: GetProcessLogsResponse = await response.json();
|
|
963
|
+
console.log(`[HTTP Client] Got logs for process ${processId}`);
|
|
964
|
+
|
|
965
|
+
return data;
|
|
1649
966
|
} catch (error) {
|
|
1650
|
-
console.error("[HTTP Client] Error getting
|
|
967
|
+
console.error("[HTTP Client] Error getting process logs:", error);
|
|
1651
968
|
throw error;
|
|
1652
969
|
}
|
|
1653
970
|
}
|
|
1654
971
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
// Example usage and utility functions
|
|
1669
|
-
export function createClient(options?: HttpClientOptions): HttpClient {
|
|
1670
|
-
return new HttpClient(options);
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
// Convenience function for quick command execution
|
|
1674
|
-
export async function quickExecute(
|
|
1675
|
-
command: string,
|
|
1676
|
-
args: string[] = [],
|
|
1677
|
-
options?: HttpClientOptions
|
|
1678
|
-
): Promise<ExecuteResponse> {
|
|
1679
|
-
const client = createClient(options);
|
|
1680
|
-
await client.createSession();
|
|
1681
|
-
|
|
1682
|
-
try {
|
|
1683
|
-
return await client.execute(command, args);
|
|
1684
|
-
} finally {
|
|
1685
|
-
client.clearSession();
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
|
|
1689
|
-
// Convenience function for quick streaming command execution
|
|
1690
|
-
export async function quickExecuteStream(
|
|
1691
|
-
command: string,
|
|
1692
|
-
args: string[] = [],
|
|
1693
|
-
options?: HttpClientOptions
|
|
1694
|
-
): Promise<void> {
|
|
1695
|
-
const client = createClient(options);
|
|
1696
|
-
await client.createSession();
|
|
1697
|
-
|
|
1698
|
-
try {
|
|
1699
|
-
await client.executeStream(command, args);
|
|
1700
|
-
} finally {
|
|
1701
|
-
client.clearSession();
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
// Convenience function for quick git checkout
|
|
1706
|
-
export async function quickGitCheckout(
|
|
1707
|
-
repoUrl: string,
|
|
1708
|
-
branch: string = "main",
|
|
1709
|
-
targetDir?: string,
|
|
1710
|
-
options?: HttpClientOptions
|
|
1711
|
-
): Promise<GitCheckoutResponse> {
|
|
1712
|
-
const client = createClient(options);
|
|
1713
|
-
await client.createSession();
|
|
1714
|
-
|
|
1715
|
-
try {
|
|
1716
|
-
return await client.gitCheckout(repoUrl, branch, targetDir);
|
|
1717
|
-
} finally {
|
|
1718
|
-
client.clearSession();
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
// Convenience function for quick directory creation
|
|
1723
|
-
export async function quickMkdir(
|
|
1724
|
-
path: string,
|
|
1725
|
-
recursive: boolean = false,
|
|
1726
|
-
options?: HttpClientOptions
|
|
1727
|
-
): Promise<MkdirResponse> {
|
|
1728
|
-
const client = createClient(options);
|
|
1729
|
-
await client.createSession();
|
|
1730
|
-
|
|
1731
|
-
try {
|
|
1732
|
-
return await client.mkdir(path, recursive);
|
|
1733
|
-
} finally {
|
|
1734
|
-
client.clearSession();
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// Convenience function for quick streaming git checkout
|
|
1739
|
-
export async function quickGitCheckoutStream(
|
|
1740
|
-
repoUrl: string,
|
|
1741
|
-
branch: string = "main",
|
|
1742
|
-
targetDir?: string,
|
|
1743
|
-
options?: HttpClientOptions
|
|
1744
|
-
): Promise<void> {
|
|
1745
|
-
const client = createClient(options);
|
|
1746
|
-
await client.createSession();
|
|
1747
|
-
|
|
1748
|
-
try {
|
|
1749
|
-
await client.gitCheckoutStream(repoUrl, branch, targetDir);
|
|
1750
|
-
} finally {
|
|
1751
|
-
client.clearSession();
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
// Convenience function for quick streaming directory creation
|
|
1756
|
-
export async function quickMkdirStream(
|
|
1757
|
-
path: string,
|
|
1758
|
-
recursive: boolean = false,
|
|
1759
|
-
options?: HttpClientOptions
|
|
1760
|
-
): Promise<void> {
|
|
1761
|
-
const client = createClient(options);
|
|
1762
|
-
await client.createSession();
|
|
1763
|
-
|
|
1764
|
-
try {
|
|
1765
|
-
await client.mkdirStream(path, recursive);
|
|
1766
|
-
} finally {
|
|
1767
|
-
client.clearSession();
|
|
1768
|
-
}
|
|
1769
|
-
}
|
|
1770
|
-
|
|
1771
|
-
// Convenience function for quick file writing
|
|
1772
|
-
export async function quickWriteFile(
|
|
1773
|
-
path: string,
|
|
1774
|
-
content: string,
|
|
1775
|
-
encoding: string = "utf-8",
|
|
1776
|
-
options?: HttpClientOptions
|
|
1777
|
-
): Promise<WriteFileResponse> {
|
|
1778
|
-
const client = createClient(options);
|
|
1779
|
-
await client.createSession();
|
|
1780
|
-
|
|
1781
|
-
try {
|
|
1782
|
-
return await client.writeFile(path, content, encoding);
|
|
1783
|
-
} finally {
|
|
1784
|
-
client.clearSession();
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
// Convenience function for quick streaming file writing
|
|
1789
|
-
export async function quickWriteFileStream(
|
|
1790
|
-
path: string,
|
|
1791
|
-
content: string,
|
|
1792
|
-
encoding: string = "utf-8",
|
|
1793
|
-
options?: HttpClientOptions
|
|
1794
|
-
): Promise<void> {
|
|
1795
|
-
const client = createClient(options);
|
|
1796
|
-
await client.createSession();
|
|
1797
|
-
|
|
1798
|
-
try {
|
|
1799
|
-
await client.writeFileStream(path, content, encoding);
|
|
1800
|
-
} finally {
|
|
1801
|
-
client.clearSession();
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
|
|
1805
|
-
// Convenience function for quick file reading
|
|
1806
|
-
export async function quickReadFile(
|
|
1807
|
-
path: string,
|
|
1808
|
-
encoding: string = "utf-8",
|
|
1809
|
-
options?: HttpClientOptions
|
|
1810
|
-
): Promise<ReadFileResponse> {
|
|
1811
|
-
const client = createClient(options);
|
|
1812
|
-
await client.createSession();
|
|
1813
|
-
|
|
1814
|
-
try {
|
|
1815
|
-
return await client.readFile(path, encoding);
|
|
1816
|
-
} finally {
|
|
1817
|
-
client.clearSession();
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
// Convenience function for quick streaming file reading
|
|
1822
|
-
export async function quickReadFileStream(
|
|
1823
|
-
path: string,
|
|
1824
|
-
encoding: string = "utf-8",
|
|
1825
|
-
options?: HttpClientOptions
|
|
1826
|
-
): Promise<void> {
|
|
1827
|
-
const client = createClient(options);
|
|
1828
|
-
await client.createSession();
|
|
1829
|
-
|
|
1830
|
-
try {
|
|
1831
|
-
await client.readFileStream(path, encoding);
|
|
1832
|
-
} finally {
|
|
1833
|
-
client.clearSession();
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
// Convenience function for quick file deletion
|
|
1838
|
-
export async function quickDeleteFile(
|
|
1839
|
-
path: string,
|
|
1840
|
-
options?: HttpClientOptions
|
|
1841
|
-
): Promise<DeleteFileResponse> {
|
|
1842
|
-
const client = createClient(options);
|
|
1843
|
-
await client.createSession();
|
|
1844
|
-
|
|
1845
|
-
try {
|
|
1846
|
-
return await client.deleteFile(path);
|
|
1847
|
-
} finally {
|
|
1848
|
-
client.clearSession();
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1851
|
-
|
|
1852
|
-
// Convenience function for quick streaming file deletion
|
|
1853
|
-
export async function quickDeleteFileStream(
|
|
1854
|
-
path: string,
|
|
1855
|
-
options?: HttpClientOptions
|
|
1856
|
-
): Promise<void> {
|
|
1857
|
-
const client = createClient(options);
|
|
1858
|
-
await client.createSession();
|
|
1859
|
-
|
|
1860
|
-
try {
|
|
1861
|
-
await client.deleteFileStream(path);
|
|
1862
|
-
} finally {
|
|
1863
|
-
client.clearSession();
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
972
|
+
async streamProcessLogs(
|
|
973
|
+
processId: string,
|
|
974
|
+
options?: { signal?: AbortSignal }
|
|
975
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
976
|
+
try {
|
|
977
|
+
const response = await this.doFetch(`/api/process/${processId}/stream`, {
|
|
978
|
+
headers: {
|
|
979
|
+
Accept: "text/event-stream",
|
|
980
|
+
"Cache-Control": "no-cache",
|
|
981
|
+
},
|
|
982
|
+
method: "GET",
|
|
983
|
+
signal: options?.signal,
|
|
984
|
+
});
|
|
1866
985
|
|
|
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
|
-
}
|
|
986
|
+
if (!response.ok) {
|
|
987
|
+
const errorData = (await response.json().catch(() => ({}))) as {
|
|
988
|
+
error?: string;
|
|
989
|
+
};
|
|
990
|
+
throw new Error(
|
|
991
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
1882
994
|
|
|
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
|
-
}
|
|
995
|
+
if (!response.body) {
|
|
996
|
+
throw new Error("No response body for streaming request");
|
|
997
|
+
}
|
|
1898
998
|
|
|
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
|
-
}
|
|
999
|
+
console.log(
|
|
1000
|
+
`[HTTP Client] Started streaming logs for process ${processId}`
|
|
1001
|
+
);
|
|
1914
1002
|
|
|
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();
|
|
1003
|
+
return response.body;
|
|
1004
|
+
} catch (error) {
|
|
1005
|
+
console.error("[HTTP Client] Error streaming process logs:", error);
|
|
1006
|
+
throw error;
|
|
1007
|
+
}
|
|
1928
1008
|
}
|
|
1929
1009
|
}
|