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