@cloudflare/sandbox 0.3.6 → 0.4.1
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/.turbo/turbo-build.log +44 -0
- package/CHANGELOG.md +6 -8
- package/Dockerfile +88 -18
- package/README.md +89 -824
- package/dist/{chunk-JTKON2SH.js → chunk-BCJ7SF3Q.js} +9 -5
- package/dist/chunk-BCJ7SF3Q.js.map +1 -0
- package/dist/chunk-BFVUNTP4.js +104 -0
- package/dist/chunk-BFVUNTP4.js.map +1 -0
- package/dist/{chunk-NNGBXDMY.js → chunk-EKSWCBCA.js} +3 -6
- package/dist/chunk-EKSWCBCA.js.map +1 -0
- package/dist/chunk-HGF554LH.js +2236 -0
- package/dist/chunk-HGF554LH.js.map +1 -0
- package/dist/{chunk-6UAWTJ5S.js → chunk-Z532A7QC.js} +13 -20
- package/dist/{chunk-6UAWTJ5S.js.map → chunk-Z532A7QC.js.map} +1 -1
- package/dist/file-stream.d.ts +16 -38
- package/dist/file-stream.js +1 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +35 -39
- package/dist/index.js.map +1 -1
- package/dist/interpreter.d.ts +3 -3
- package/dist/interpreter.js +2 -2
- package/dist/request-handler.d.ts +4 -3
- package/dist/request-handler.js +4 -7
- package/dist/sandbox-D9K2ypln.d.ts +583 -0
- package/dist/sandbox.d.ts +3 -3
- package/dist/sandbox.js +4 -7
- package/dist/security.d.ts +4 -3
- package/dist/security.js +3 -3
- package/dist/sse-parser.js +1 -1
- package/package.json +11 -5
- package/src/clients/base-client.ts +280 -0
- package/src/clients/command-client.ts +115 -0
- package/src/clients/file-client.ts +269 -0
- package/src/clients/git-client.ts +92 -0
- package/src/clients/index.ts +63 -0
- package/src/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
- package/src/clients/port-client.ts +105 -0
- package/src/clients/process-client.ts +177 -0
- package/src/clients/sandbox-client.ts +41 -0
- package/src/clients/types.ts +84 -0
- package/src/clients/utility-client.ts +94 -0
- package/src/errors/adapter.ts +180 -0
- package/src/errors/classes.ts +469 -0
- package/src/errors/index.ts +105 -0
- package/src/file-stream.ts +119 -117
- package/src/index.ts +81 -69
- package/src/interpreter.ts +17 -8
- package/src/request-handler.ts +69 -43
- package/src/sandbox.ts +694 -533
- package/src/security.ts +14 -23
- package/src/sse-parser.ts +4 -8
- package/startup.sh +3 -0
- package/tests/base-client.test.ts +328 -0
- package/tests/command-client.test.ts +407 -0
- package/tests/file-client.test.ts +643 -0
- package/tests/file-stream.test.ts +306 -0
- package/tests/git-client.test.ts +328 -0
- package/tests/port-client.test.ts +301 -0
- package/tests/process-client.test.ts +658 -0
- package/tests/sandbox.test.ts +465 -0
- package/tests/sse-parser.test.ts +290 -0
- package/tests/utility-client.test.ts +266 -0
- package/tests/wrangler.jsonc +35 -0
- package/tsconfig.json +9 -1
- package/vitest.config.ts +31 -0
- package/container_src/bun.lock +0 -76
- package/container_src/circuit-breaker.ts +0 -121
- package/container_src/control-process.ts +0 -784
- package/container_src/handler/exec.ts +0 -185
- package/container_src/handler/file.ts +0 -457
- package/container_src/handler/git.ts +0 -130
- package/container_src/handler/ports.ts +0 -314
- package/container_src/handler/process.ts +0 -568
- package/container_src/handler/session.ts +0 -92
- package/container_src/index.ts +0 -601
- package/container_src/interpreter-service.ts +0 -276
- package/container_src/isolation.ts +0 -1213
- package/container_src/mime-processor.ts +0 -255
- package/container_src/package.json +0 -18
- package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
- package/container_src/runtime/executors/python/ipython_executor.py +0 -338
- package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
- package/container_src/runtime/process-pool.ts +0 -464
- package/container_src/shell-escape.ts +0 -42
- package/container_src/startup.sh +0 -11
- package/container_src/types.ts +0 -131
- package/dist/chunk-32UDXUPC.js +0 -671
- package/dist/chunk-32UDXUPC.js.map +0 -1
- package/dist/chunk-5DILEXGY.js +0 -85
- package/dist/chunk-5DILEXGY.js.map +0 -1
- package/dist/chunk-D3U63BZP.js +0 -240
- package/dist/chunk-D3U63BZP.js.map +0 -1
- package/dist/chunk-FXYPFGOZ.js +0 -129
- package/dist/chunk-FXYPFGOZ.js.map +0 -1
- package/dist/chunk-JTKON2SH.js.map +0 -1
- package/dist/chunk-NNGBXDMY.js.map +0 -1
- package/dist/chunk-SQLJNZ3K.js +0 -674
- package/dist/chunk-SQLJNZ3K.js.map +0 -1
- package/dist/chunk-W7TVRPBG.js +0 -108
- package/dist/chunk-W7TVRPBG.js.map +0 -1
- package/dist/client-B3RUab0s.d.ts +0 -225
- package/dist/client.d.ts +0 -4
- package/dist/client.js +0 -7
- package/dist/client.js.map +0 -1
- package/dist/errors.d.ts +0 -95
- package/dist/errors.js +0 -27
- package/dist/errors.js.map +0 -1
- package/dist/interpreter-client.d.ts +0 -4
- package/dist/interpreter-client.js +0 -9
- package/dist/interpreter-client.js.map +0 -1
- package/dist/interpreter-types.d.ts +0 -259
- package/dist/interpreter-types.js +0 -9
- package/dist/interpreter-types.js.map +0 -1
- package/dist/types.d.ts +0 -453
- package/dist/types.js +0 -45
- package/dist/types.js.map +0 -1
- package/src/client.ts +0 -1048
- package/src/errors.ts +0 -219
- package/src/interpreter-types.ts +0 -390
- package/src/types.ts +0 -571
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import type { SessionManager } from "../isolation";
|
|
2
|
-
import type { SessionExecRequest } from "../types";
|
|
3
|
-
|
|
4
|
-
export async function handleExecuteRequest(
|
|
5
|
-
req: Request,
|
|
6
|
-
corsHeaders: Record<string, string>,
|
|
7
|
-
sessionManager: SessionManager
|
|
8
|
-
) {
|
|
9
|
-
try {
|
|
10
|
-
const body = (await req.json()) as SessionExecRequest;
|
|
11
|
-
const { id, command } = body;
|
|
12
|
-
|
|
13
|
-
console.log(
|
|
14
|
-
`[Container] Session exec request for '${id}': ${command}`
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
if (!id || !command) {
|
|
18
|
-
return new Response(
|
|
19
|
-
JSON.stringify({
|
|
20
|
-
error: "Session ID and command are required",
|
|
21
|
-
}),
|
|
22
|
-
{
|
|
23
|
-
status: 400,
|
|
24
|
-
headers: {
|
|
25
|
-
"Content-Type": "application/json",
|
|
26
|
-
...corsHeaders,
|
|
27
|
-
},
|
|
28
|
-
}
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const session = sessionManager.getSession(id);
|
|
33
|
-
if (!session) {
|
|
34
|
-
console.error(`[Container] Session '${id}' not found!`);
|
|
35
|
-
const availableSessions = sessionManager.listSessions();
|
|
36
|
-
console.log(
|
|
37
|
-
`[Container] Available sessions: ${
|
|
38
|
-
availableSessions.join(", ") || "none"
|
|
39
|
-
}`
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return new Response(
|
|
43
|
-
JSON.stringify({
|
|
44
|
-
error: `Session '${id}' not found`,
|
|
45
|
-
availableSessions,
|
|
46
|
-
}),
|
|
47
|
-
{
|
|
48
|
-
status: 404,
|
|
49
|
-
headers: {
|
|
50
|
-
"Content-Type": "application/json",
|
|
51
|
-
...corsHeaders,
|
|
52
|
-
},
|
|
53
|
-
}
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const result = await session.exec(command);
|
|
58
|
-
|
|
59
|
-
return new Response(JSON.stringify(result), {
|
|
60
|
-
headers: { "Content-Type": "application/json", ...corsHeaders },
|
|
61
|
-
});
|
|
62
|
-
} catch (error) {
|
|
63
|
-
console.error("[Container] Session exec failed:", error);
|
|
64
|
-
return new Response(
|
|
65
|
-
JSON.stringify({
|
|
66
|
-
error: "Command execution failed",
|
|
67
|
-
message:
|
|
68
|
-
error instanceof Error ? error.message : String(error),
|
|
69
|
-
}),
|
|
70
|
-
{
|
|
71
|
-
status: 500,
|
|
72
|
-
headers: {
|
|
73
|
-
"Content-Type": "application/json",
|
|
74
|
-
...corsHeaders,
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export async function handleStreamingExecuteRequest(
|
|
82
|
-
req: Request,
|
|
83
|
-
sessionManager: SessionManager,
|
|
84
|
-
corsHeaders: Record<string, string>
|
|
85
|
-
) {
|
|
86
|
-
try {
|
|
87
|
-
const body = (await req.json()) as SessionExecRequest;
|
|
88
|
-
const { id, command } = body;
|
|
89
|
-
|
|
90
|
-
console.log(
|
|
91
|
-
`[Container] Session streaming exec request for '${id}': ${command}`
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
if (!id || !command) {
|
|
95
|
-
return new Response(
|
|
96
|
-
JSON.stringify({
|
|
97
|
-
error: "Session ID and command are required",
|
|
98
|
-
}),
|
|
99
|
-
{
|
|
100
|
-
status: 400,
|
|
101
|
-
headers: {
|
|
102
|
-
"Content-Type": "application/json",
|
|
103
|
-
...corsHeaders,
|
|
104
|
-
},
|
|
105
|
-
}
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const session = sessionManager.getSession(id);
|
|
110
|
-
if (!session) {
|
|
111
|
-
console.error(`[Container] Session '${id}' not found!`);
|
|
112
|
-
const availableSessions = sessionManager.listSessions();
|
|
113
|
-
|
|
114
|
-
return new Response(
|
|
115
|
-
JSON.stringify({
|
|
116
|
-
error: `Session '${id}' not found`,
|
|
117
|
-
availableSessions,
|
|
118
|
-
}),
|
|
119
|
-
{
|
|
120
|
-
status: 404,
|
|
121
|
-
headers: {
|
|
122
|
-
"Content-Type": "application/json",
|
|
123
|
-
...corsHeaders,
|
|
124
|
-
},
|
|
125
|
-
}
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Create a streaming response using the actual streaming method
|
|
130
|
-
const stream = new ReadableStream({
|
|
131
|
-
async start(controller) {
|
|
132
|
-
try {
|
|
133
|
-
// Use the streaming generator method
|
|
134
|
-
for await (const event of session.execStream(command)) {
|
|
135
|
-
// Forward each event as SSE
|
|
136
|
-
controller.enqueue(
|
|
137
|
-
new TextEncoder().encode(
|
|
138
|
-
`data: ${JSON.stringify(event)}\n\n`
|
|
139
|
-
)
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
controller.close();
|
|
143
|
-
} catch (error) {
|
|
144
|
-
controller.enqueue(
|
|
145
|
-
new TextEncoder().encode(
|
|
146
|
-
`data: ${JSON.stringify({
|
|
147
|
-
type: "error",
|
|
148
|
-
message:
|
|
149
|
-
error instanceof Error
|
|
150
|
-
? error.message
|
|
151
|
-
: String(error),
|
|
152
|
-
})}\n\n`
|
|
153
|
-
)
|
|
154
|
-
);
|
|
155
|
-
controller.close();
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
return new Response(stream, {
|
|
161
|
-
headers: {
|
|
162
|
-
"Content-Type": "text/event-stream",
|
|
163
|
-
"Cache-Control": "no-cache",
|
|
164
|
-
Connection: "keep-alive",
|
|
165
|
-
...corsHeaders,
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error("[Container] Session stream exec failed:", error);
|
|
170
|
-
return new Response(
|
|
171
|
-
JSON.stringify({
|
|
172
|
-
error: "Stream execution failed",
|
|
173
|
-
message:
|
|
174
|
-
error instanceof Error ? error.message : String(error),
|
|
175
|
-
}),
|
|
176
|
-
{
|
|
177
|
-
status: 500,
|
|
178
|
-
headers: {
|
|
179
|
-
"Content-Type": "application/json",
|
|
180
|
-
...corsHeaders,
|
|
181
|
-
},
|
|
182
|
-
}
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
import type { SessionManager } from "../isolation";
|
|
2
|
-
import type {
|
|
3
|
-
DeleteFileRequest,
|
|
4
|
-
ListFilesRequest,
|
|
5
|
-
MkdirRequest,
|
|
6
|
-
MoveFileRequest,
|
|
7
|
-
ReadFileRequest,
|
|
8
|
-
RenameFileRequest,
|
|
9
|
-
WriteFileRequest
|
|
10
|
-
} from "../types";
|
|
11
|
-
|
|
12
|
-
// Common path validation patterns
|
|
13
|
-
const DANGEROUS_PATH_PATTERNS = [
|
|
14
|
-
/^\/$/, // Root directory
|
|
15
|
-
/^\/etc/, // System directories
|
|
16
|
-
/^\/var/, // System directories
|
|
17
|
-
/^\/usr/, // System directories
|
|
18
|
-
/^\/bin/, // System directories
|
|
19
|
-
/^\/sbin/, // System directories
|
|
20
|
-
/^\/boot/, // System directories
|
|
21
|
-
/^\/dev/, // System directories
|
|
22
|
-
/^\/proc/, // System directories
|
|
23
|
-
/^\/sys/, // System directories
|
|
24
|
-
/^\/tmp\/\.\./, // Path traversal attempts
|
|
25
|
-
/\.\./, // Path traversal attempts
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// Path validation utility
|
|
29
|
-
function validatePath(...paths: string[]): string | null {
|
|
30
|
-
for (const path of paths) {
|
|
31
|
-
if (!path || typeof path !== "string") {
|
|
32
|
-
return "Path is required and must be a string";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (DANGEROUS_PATH_PATTERNS.some((pattern) => pattern.test(path))) {
|
|
36
|
-
return "Dangerous path not allowed";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Common error response utility
|
|
43
|
-
function createPathErrorResponse(
|
|
44
|
-
error: string,
|
|
45
|
-
corsHeaders: Record<string, string>
|
|
46
|
-
): Response {
|
|
47
|
-
return new Response(
|
|
48
|
-
JSON.stringify({ error }),
|
|
49
|
-
{
|
|
50
|
-
headers: {
|
|
51
|
-
"Content-Type": "application/json",
|
|
52
|
-
...corsHeaders,
|
|
53
|
-
},
|
|
54
|
-
status: 400,
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Common server error response utility
|
|
60
|
-
function createServerErrorResponse(
|
|
61
|
-
operation: string,
|
|
62
|
-
error: unknown,
|
|
63
|
-
corsHeaders: Record<string, string>
|
|
64
|
-
): Response {
|
|
65
|
-
console.error(`[Server] Error in ${operation}:`, error);
|
|
66
|
-
return new Response(
|
|
67
|
-
JSON.stringify({
|
|
68
|
-
error: `Failed to ${operation.replace('handle', '').replace('Request', '').toLowerCase().replace('file', ' file')}`,
|
|
69
|
-
message: error instanceof Error ? error.message : "Unknown error",
|
|
70
|
-
}),
|
|
71
|
-
{
|
|
72
|
-
headers: {
|
|
73
|
-
"Content-Type": "application/json",
|
|
74
|
-
...corsHeaders,
|
|
75
|
-
},
|
|
76
|
-
status: 500,
|
|
77
|
-
}
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export async function handleMkdirRequest(
|
|
82
|
-
req: Request,
|
|
83
|
-
corsHeaders: Record<string, string>,
|
|
84
|
-
sessionManager: SessionManager
|
|
85
|
-
): Promise<Response> {
|
|
86
|
-
try {
|
|
87
|
-
const body = (await req.json()) as MkdirRequest;
|
|
88
|
-
const { path, recursive = false, sessionId } = body;
|
|
89
|
-
|
|
90
|
-
// Validate path
|
|
91
|
-
const pathError = validatePath(path);
|
|
92
|
-
if (pathError) {
|
|
93
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
console.log(`[Server] Creating directory: ${path} (recursive: ${recursive})${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
97
|
-
|
|
98
|
-
// Use specific session if provided, otherwise default session
|
|
99
|
-
const result = sessionId
|
|
100
|
-
? await sessionManager.getSession(sessionId)?.mkdirOperation(path, recursive)
|
|
101
|
-
: await sessionManager.mkdir(path, recursive);
|
|
102
|
-
|
|
103
|
-
if (!result) {
|
|
104
|
-
return createServerErrorResponse("handleMkdirRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return new Response(
|
|
108
|
-
JSON.stringify({
|
|
109
|
-
exitCode: result.exitCode,
|
|
110
|
-
path,
|
|
111
|
-
recursive,
|
|
112
|
-
stderr: "",
|
|
113
|
-
stdout: "",
|
|
114
|
-
success: result.success,
|
|
115
|
-
timestamp: new Date().toISOString(),
|
|
116
|
-
}),
|
|
117
|
-
{
|
|
118
|
-
headers: {
|
|
119
|
-
"Content-Type": "application/json",
|
|
120
|
-
...corsHeaders,
|
|
121
|
-
},
|
|
122
|
-
}
|
|
123
|
-
);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
return createServerErrorResponse("handleMkdirRequest", error, corsHeaders);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export async function handleWriteFileRequest(
|
|
130
|
-
req: Request,
|
|
131
|
-
corsHeaders: Record<string, string>,
|
|
132
|
-
sessionManager: SessionManager
|
|
133
|
-
): Promise<Response> {
|
|
134
|
-
try {
|
|
135
|
-
const body = (await req.json()) as WriteFileRequest;
|
|
136
|
-
const { path, content, encoding = "utf-8", sessionId } = body;
|
|
137
|
-
|
|
138
|
-
// Validate path
|
|
139
|
-
const pathError = validatePath(path);
|
|
140
|
-
if (pathError) {
|
|
141
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log(`[Server] Writing file: ${path} (content length: ${content.length})${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
145
|
-
|
|
146
|
-
// Use specific session if provided, otherwise default session
|
|
147
|
-
const result = sessionId
|
|
148
|
-
? await sessionManager.getSession(sessionId)?.writeFileOperation(path, content, encoding)
|
|
149
|
-
: await sessionManager.writeFile(path, content, encoding);
|
|
150
|
-
|
|
151
|
-
if (!result) {
|
|
152
|
-
return createServerErrorResponse("handleWriteFileRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return new Response(
|
|
156
|
-
JSON.stringify({
|
|
157
|
-
exitCode: result.exitCode,
|
|
158
|
-
path,
|
|
159
|
-
success: result.success,
|
|
160
|
-
timestamp: new Date().toISOString(),
|
|
161
|
-
}),
|
|
162
|
-
{
|
|
163
|
-
headers: {
|
|
164
|
-
"Content-Type": "application/json",
|
|
165
|
-
...corsHeaders,
|
|
166
|
-
},
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
} catch (error) {
|
|
170
|
-
return createServerErrorResponse("handleWriteFileRequest", error, corsHeaders);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export async function handleReadFileRequest(
|
|
175
|
-
req: Request,
|
|
176
|
-
corsHeaders: Record<string, string>,
|
|
177
|
-
sessionManager: SessionManager
|
|
178
|
-
): Promise<Response> {
|
|
179
|
-
try {
|
|
180
|
-
const body = (await req.json()) as ReadFileRequest;
|
|
181
|
-
const { path, encoding = "utf-8", sessionId } = body;
|
|
182
|
-
|
|
183
|
-
// Validate path
|
|
184
|
-
const pathError = validatePath(path);
|
|
185
|
-
if (pathError) {
|
|
186
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
console.log(`[Server] Reading file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
190
|
-
|
|
191
|
-
// Use specific session if provided, otherwise default session
|
|
192
|
-
const result = sessionId
|
|
193
|
-
? await sessionManager.getSession(sessionId)?.readFileOperation(path, encoding)
|
|
194
|
-
: await sessionManager.readFile(path, encoding);
|
|
195
|
-
|
|
196
|
-
if (!result) {
|
|
197
|
-
return createServerErrorResponse("handleReadFileRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return new Response(
|
|
201
|
-
JSON.stringify({
|
|
202
|
-
content: result.content,
|
|
203
|
-
exitCode: result.exitCode,
|
|
204
|
-
path,
|
|
205
|
-
success: result.success,
|
|
206
|
-
timestamp: new Date().toISOString(),
|
|
207
|
-
// New metadata fields for binary file support
|
|
208
|
-
encoding: result.encoding,
|
|
209
|
-
isBinary: result.isBinary,
|
|
210
|
-
mimeType: result.mimeType,
|
|
211
|
-
size: result.size,
|
|
212
|
-
}),
|
|
213
|
-
{
|
|
214
|
-
headers: {
|
|
215
|
-
"Content-Type": "application/json",
|
|
216
|
-
...corsHeaders,
|
|
217
|
-
},
|
|
218
|
-
}
|
|
219
|
-
);
|
|
220
|
-
} catch (error) {
|
|
221
|
-
return createServerErrorResponse("handleReadFileRequest", error, corsHeaders);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export async function handleDeleteFileRequest(
|
|
226
|
-
req: Request,
|
|
227
|
-
corsHeaders: Record<string, string>,
|
|
228
|
-
sessionManager: SessionManager
|
|
229
|
-
): Promise<Response> {
|
|
230
|
-
try {
|
|
231
|
-
const body = (await req.json()) as DeleteFileRequest;
|
|
232
|
-
const { path, sessionId } = body;
|
|
233
|
-
|
|
234
|
-
// Validate path
|
|
235
|
-
const pathError = validatePath(path);
|
|
236
|
-
if (pathError) {
|
|
237
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
console.log(`[Server] Deleting file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
241
|
-
|
|
242
|
-
// Use specific session if provided, otherwise default session
|
|
243
|
-
const result = sessionId
|
|
244
|
-
? await sessionManager.getSession(sessionId)?.deleteFileOperation(path)
|
|
245
|
-
: await sessionManager.deleteFile(path);
|
|
246
|
-
|
|
247
|
-
if (!result) {
|
|
248
|
-
return createServerErrorResponse("handleDeleteFileRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return new Response(
|
|
252
|
-
JSON.stringify({
|
|
253
|
-
exitCode: result.exitCode,
|
|
254
|
-
path,
|
|
255
|
-
success: result.success,
|
|
256
|
-
timestamp: new Date().toISOString(),
|
|
257
|
-
}),
|
|
258
|
-
{
|
|
259
|
-
headers: {
|
|
260
|
-
"Content-Type": "application/json",
|
|
261
|
-
...corsHeaders,
|
|
262
|
-
},
|
|
263
|
-
}
|
|
264
|
-
);
|
|
265
|
-
} catch (error) {
|
|
266
|
-
return createServerErrorResponse("handleDeleteFileRequest", error, corsHeaders);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export async function handleRenameFileRequest(
|
|
271
|
-
req: Request,
|
|
272
|
-
corsHeaders: Record<string, string>,
|
|
273
|
-
sessionManager: SessionManager
|
|
274
|
-
): Promise<Response> {
|
|
275
|
-
try {
|
|
276
|
-
const body = (await req.json()) as RenameFileRequest;
|
|
277
|
-
const { oldPath, newPath, sessionId } = body;
|
|
278
|
-
|
|
279
|
-
// Validate paths
|
|
280
|
-
const pathError = validatePath(oldPath, newPath);
|
|
281
|
-
if (pathError) {
|
|
282
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
console.log(`[Server] Renaming file: ${oldPath} -> ${newPath}${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
286
|
-
|
|
287
|
-
// Use specific session if provided, otherwise default session
|
|
288
|
-
const result = sessionId
|
|
289
|
-
? await sessionManager.getSession(sessionId)?.renameFileOperation(oldPath, newPath)
|
|
290
|
-
: await sessionManager.renameFile(oldPath, newPath);
|
|
291
|
-
|
|
292
|
-
if (!result) {
|
|
293
|
-
return createServerErrorResponse("handleRenameFileRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return new Response(
|
|
297
|
-
JSON.stringify({
|
|
298
|
-
exitCode: result.exitCode,
|
|
299
|
-
newPath,
|
|
300
|
-
oldPath,
|
|
301
|
-
success: result.success,
|
|
302
|
-
timestamp: new Date().toISOString(),
|
|
303
|
-
}),
|
|
304
|
-
{
|
|
305
|
-
headers: {
|
|
306
|
-
"Content-Type": "application/json",
|
|
307
|
-
...corsHeaders,
|
|
308
|
-
},
|
|
309
|
-
}
|
|
310
|
-
);
|
|
311
|
-
} catch (error) {
|
|
312
|
-
return createServerErrorResponse("handleRenameFileRequest", error, corsHeaders);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
export async function handleMoveFileRequest(
|
|
317
|
-
req: Request,
|
|
318
|
-
corsHeaders: Record<string, string>,
|
|
319
|
-
sessionManager: SessionManager
|
|
320
|
-
): Promise<Response> {
|
|
321
|
-
try {
|
|
322
|
-
const body = (await req.json()) as MoveFileRequest;
|
|
323
|
-
const { sourcePath, destinationPath, sessionId } = body;
|
|
324
|
-
|
|
325
|
-
// Validate paths
|
|
326
|
-
const pathError = validatePath(sourcePath, destinationPath);
|
|
327
|
-
if (pathError) {
|
|
328
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
console.log(`[Server] Moving file: ${sourcePath} -> ${destinationPath}${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
332
|
-
|
|
333
|
-
// Use specific session if provided, otherwise default session
|
|
334
|
-
const result = sessionId
|
|
335
|
-
? await sessionManager.getSession(sessionId)?.moveFileOperation(sourcePath, destinationPath)
|
|
336
|
-
: await sessionManager.moveFile(sourcePath, destinationPath);
|
|
337
|
-
|
|
338
|
-
if (!result) {
|
|
339
|
-
return createServerErrorResponse("handleMoveFileRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
return new Response(
|
|
343
|
-
JSON.stringify({
|
|
344
|
-
destinationPath,
|
|
345
|
-
exitCode: result.exitCode,
|
|
346
|
-
sourcePath,
|
|
347
|
-
success: result.success,
|
|
348
|
-
timestamp: new Date().toISOString(),
|
|
349
|
-
}),
|
|
350
|
-
{
|
|
351
|
-
headers: {
|
|
352
|
-
"Content-Type": "application/json",
|
|
353
|
-
...corsHeaders,
|
|
354
|
-
},
|
|
355
|
-
}
|
|
356
|
-
);
|
|
357
|
-
} catch (error) {
|
|
358
|
-
return createServerErrorResponse("handleMoveFileRequest", error, corsHeaders);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export async function handleListFilesRequest(
|
|
363
|
-
req: Request,
|
|
364
|
-
corsHeaders: Record<string, string>,
|
|
365
|
-
sessionManager: SessionManager
|
|
366
|
-
): Promise<Response> {
|
|
367
|
-
try {
|
|
368
|
-
const body = (await req.json()) as ListFilesRequest;
|
|
369
|
-
const { path, options, sessionId } = body;
|
|
370
|
-
|
|
371
|
-
// Validate path (note: listFiles allows root directory listing)
|
|
372
|
-
const pathError = validatePath(path);
|
|
373
|
-
if (pathError && pathError !== "Dangerous path not allowed") {
|
|
374
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// For listFiles, we allow root directory but still check other dangerous patterns
|
|
378
|
-
if (path !== "/" && DANGEROUS_PATH_PATTERNS.slice(1).some((pattern) => pattern.test(path))) {
|
|
379
|
-
return createPathErrorResponse("Dangerous path not allowed", corsHeaders);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
console.log(`[Server] Listing files in: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
383
|
-
|
|
384
|
-
// Use specific session if provided, otherwise default session
|
|
385
|
-
const result = sessionId
|
|
386
|
-
? await sessionManager.getSession(sessionId)?.listFilesOperation(path, options)
|
|
387
|
-
: await sessionManager.listFiles(path, options);
|
|
388
|
-
|
|
389
|
-
if (!result) {
|
|
390
|
-
return createServerErrorResponse("handleListFilesRequest", new Error(`Session '${sessionId}' not found`), corsHeaders);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return new Response(
|
|
394
|
-
JSON.stringify({
|
|
395
|
-
exitCode: result.exitCode,
|
|
396
|
-
files: result.files,
|
|
397
|
-
path,
|
|
398
|
-
success: result.success,
|
|
399
|
-
timestamp: new Date().toISOString(),
|
|
400
|
-
}),
|
|
401
|
-
{
|
|
402
|
-
headers: {
|
|
403
|
-
"Content-Type": "application/json",
|
|
404
|
-
...corsHeaders,
|
|
405
|
-
},
|
|
406
|
-
}
|
|
407
|
-
);
|
|
408
|
-
} catch (error) {
|
|
409
|
-
return createServerErrorResponse("handleListFilesRequest", error, corsHeaders);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export async function handleReadFileStreamRequest(
|
|
414
|
-
req: Request,
|
|
415
|
-
corsHeaders: Record<string, string>,
|
|
416
|
-
sessionManager: SessionManager
|
|
417
|
-
): Promise<Response> {
|
|
418
|
-
try {
|
|
419
|
-
const body = (await req.json()) as ReadFileRequest;
|
|
420
|
-
const { path, sessionId } = body;
|
|
421
|
-
|
|
422
|
-
// Validate path
|
|
423
|
-
const pathError = validatePath(path);
|
|
424
|
-
if (pathError) {
|
|
425
|
-
return createPathErrorResponse(pathError, corsHeaders);
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
console.log(`[Server] Streaming file: ${path}${sessionId ? ` in session: ${sessionId}` : ''}`);
|
|
429
|
-
|
|
430
|
-
// Get the appropriate session
|
|
431
|
-
const session = sessionId
|
|
432
|
-
? sessionManager.getSession(sessionId)
|
|
433
|
-
: await sessionManager.getOrCreateDefaultSession();
|
|
434
|
-
|
|
435
|
-
if (!session) {
|
|
436
|
-
return createServerErrorResponse(
|
|
437
|
-
"handleReadFileStreamRequest",
|
|
438
|
-
new Error(`Session '${sessionId}' not found`),
|
|
439
|
-
corsHeaders
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Create SSE stream
|
|
444
|
-
const stream = await session.readFileStreamOperation(path);
|
|
445
|
-
|
|
446
|
-
return new Response(stream, {
|
|
447
|
-
headers: {
|
|
448
|
-
"Content-Type": "text/event-stream",
|
|
449
|
-
"Cache-Control": "no-cache",
|
|
450
|
-
"Connection": "keep-alive",
|
|
451
|
-
...corsHeaders,
|
|
452
|
-
},
|
|
453
|
-
});
|
|
454
|
-
} catch (error) {
|
|
455
|
-
return createServerErrorResponse("handleReadFileStreamRequest", error, corsHeaders);
|
|
456
|
-
}
|
|
457
|
-
}
|