@cloudflare/sandbox 0.2.2 → 0.2.4
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 +12 -0
- package/Dockerfile +10 -6
- package/README.md +1 -1
- package/container_src/handler/file.ts +222 -2
- package/container_src/index.ts +7 -0
- package/container_src/jupyter-service.ts +15 -2
- package/container_src/jupyter_config.py +48 -0
- package/container_src/startup.sh +33 -32
- package/container_src/types.ts +9 -0
- package/dist/{chunk-CUHYLCMT.js → chunk-3CQ6THKA.js} +31 -1
- package/dist/chunk-3CQ6THKA.js.map +1 -0
- package/dist/{chunk-S5FFBU4Y.js → chunk-6EWSYSO7.js} +1 -1
- package/dist/{chunk-S5FFBU4Y.js.map → chunk-6EWSYSO7.js.map} +1 -1
- package/dist/{chunk-ZMPO44U4.js → chunk-CKIGERRS.js} +9 -8
- package/dist/chunk-CKIGERRS.js.map +1 -0
- package/dist/{chunk-VTKZL632.js → chunk-HHUDRGPY.js} +2 -2
- package/dist/{client-bzEV222a.d.ts → client-Ce40ujDF.d.ts} +10 -62
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/interpreter.d.ts +2 -2
- package/dist/jupyter-client.d.ts +2 -2
- package/dist/jupyter-client.js +2 -2
- package/dist/request-handler.d.ts +3 -3
- package/dist/request-handler.js +4 -4
- package/dist/sandbox.d.ts +2 -2
- package/dist/sandbox.js +4 -4
- package/dist/types.d.ts +118 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/src/client.ts +62 -59
- package/src/index.ts +15 -13
- package/src/sandbox.ts +15 -5
- package/src/types.ts +110 -0
- package/dist/chunk-CUHYLCMT.js.map +0 -1
- package/dist/chunk-ZMPO44U4.js.map +0 -1
- /package/dist/{chunk-VTKZL632.js.map → chunk-HHUDRGPY.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @cloudflare/sandbox
|
|
2
2
|
|
|
3
|
+
## 0.2.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#57](https://github.com/cloudflare/sandbox-sdk/pull/57) [`12bbd12`](https://github.com/cloudflare/sandbox-sdk/commit/12bbd1229c07ef8c1c0bf58a4235a27938155b08) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Add listFiles method
|
|
8
|
+
|
|
9
|
+
## 0.2.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#53](https://github.com/cloudflare/sandbox-sdk/pull/53) [`c87db11`](https://github.com/cloudflare/sandbox-sdk/commit/c87db117693a86cfb667bf09fb7720d6a6e0524d) Thanks [@ghostwriternr](https://github.com/ghostwriternr)! - Improve jupyterlab config to speed up startup
|
|
14
|
+
|
|
3
15
|
## 0.2.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/Dockerfile
CHANGED
|
@@ -37,6 +37,7 @@ RUN apt-get update && apt-get install -y \
|
|
|
37
37
|
ca-certificates \
|
|
38
38
|
gnupg \
|
|
39
39
|
lsb-release \
|
|
40
|
+
strace \
|
|
40
41
|
&& rm -rf /var/lib/apt/lists/*
|
|
41
42
|
|
|
42
43
|
# Set Python 3.11 as default python3
|
|
@@ -56,17 +57,20 @@ RUN apt-get update && apt-get install -y ca-certificates curl gnupg \
|
|
|
56
57
|
COPY --from=bun-source /usr/local/bin/bun /usr/local/bin/bun
|
|
57
58
|
COPY --from=bun-source /usr/local/bin/bunx /usr/local/bin/bunx
|
|
58
59
|
|
|
59
|
-
# Install Jupyter
|
|
60
|
+
# Install minimal Jupyter components
|
|
60
61
|
RUN pip3 install --no-cache-dir \
|
|
61
|
-
jupyter \
|
|
62
|
-
|
|
62
|
+
jupyter-server \
|
|
63
|
+
jupyter-client \
|
|
63
64
|
ipykernel \
|
|
64
|
-
|
|
65
|
+
orjson \
|
|
66
|
+
&& python3 -m ipykernel install --user --name python3
|
|
67
|
+
|
|
68
|
+
# Install scientific packages
|
|
69
|
+
RUN pip3 install --no-cache-dir \
|
|
65
70
|
matplotlib \
|
|
66
71
|
numpy \
|
|
67
72
|
pandas \
|
|
68
|
-
seaborn
|
|
69
|
-
&& python3 -m ipykernel install --user --name python3
|
|
73
|
+
seaborn
|
|
70
74
|
|
|
71
75
|
# Install JavaScript kernel (ijavascript) - using E2B's fork
|
|
72
76
|
RUN npm install -g --unsafe-perm git+https://github.com/e2b-dev/ijavascript.git \
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ npm install @cloudflare/sandbox
|
|
|
72
72
|
1. **Create a Dockerfile** (temporary requirement, will be removed in future releases):
|
|
73
73
|
|
|
74
74
|
```dockerfile
|
|
75
|
-
FROM docker.io/cloudflare/sandbox:0.2.
|
|
75
|
+
FROM docker.io/cloudflare/sandbox:0.2.4
|
|
76
76
|
|
|
77
77
|
# Expose the ports you want to expose
|
|
78
78
|
EXPOSE 3000
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
-
import { mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
|
-
import { dirname } from "node:path";
|
|
2
|
+
import { mkdir, readdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
4
|
import type {
|
|
5
5
|
DeleteFileRequest,
|
|
6
|
+
ListFilesRequest,
|
|
6
7
|
MkdirRequest,
|
|
7
8
|
MoveFileRequest,
|
|
8
9
|
ReadFileRequest,
|
|
@@ -842,3 +843,222 @@ export async function handleMoveFileRequest(
|
|
|
842
843
|
}
|
|
843
844
|
}
|
|
844
845
|
|
|
846
|
+
|
|
847
|
+
async function executeListFiles(
|
|
848
|
+
path: string,
|
|
849
|
+
options?: {
|
|
850
|
+
recursive?: boolean;
|
|
851
|
+
includeHidden?: boolean;
|
|
852
|
+
}
|
|
853
|
+
): Promise<{
|
|
854
|
+
success: boolean;
|
|
855
|
+
exitCode: number;
|
|
856
|
+
files: Array<{
|
|
857
|
+
name: string;
|
|
858
|
+
absolutePath: string;
|
|
859
|
+
relativePath: string;
|
|
860
|
+
type: 'file' | 'directory' | 'symlink' | 'other';
|
|
861
|
+
size: number;
|
|
862
|
+
modifiedAt: string;
|
|
863
|
+
mode: string;
|
|
864
|
+
permissions: {
|
|
865
|
+
readable: boolean;
|
|
866
|
+
writable: boolean;
|
|
867
|
+
executable: boolean;
|
|
868
|
+
};
|
|
869
|
+
}>;
|
|
870
|
+
}> {
|
|
871
|
+
try {
|
|
872
|
+
const basePath = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
873
|
+
const files: Array<{
|
|
874
|
+
name: string;
|
|
875
|
+
absolutePath: string;
|
|
876
|
+
relativePath: string;
|
|
877
|
+
type: 'file' | 'directory' | 'symlink' | 'other';
|
|
878
|
+
size: number;
|
|
879
|
+
modifiedAt: string;
|
|
880
|
+
mode: string;
|
|
881
|
+
permissions: {
|
|
882
|
+
readable: boolean;
|
|
883
|
+
writable: boolean;
|
|
884
|
+
executable: boolean;
|
|
885
|
+
};
|
|
886
|
+
}> = [];
|
|
887
|
+
|
|
888
|
+
// Helper function to convert numeric mode to string like "rwxr-xr-x"
|
|
889
|
+
function modeToString(mode: number): string {
|
|
890
|
+
const perms = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
|
|
891
|
+
const user = (mode >> 6) & 7;
|
|
892
|
+
const group = (mode >> 3) & 7;
|
|
893
|
+
const other = mode & 7;
|
|
894
|
+
return perms[user] + perms[group] + perms[other];
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// Helper function to extract permission booleans for current user
|
|
898
|
+
function getPermissions(mode: number): { readable: boolean; writable: boolean; executable: boolean } {
|
|
899
|
+
// Extract user permissions (owner permissions)
|
|
900
|
+
const userPerms = (mode >> 6) & 7;
|
|
901
|
+
return {
|
|
902
|
+
readable: (userPerms & 4) !== 0,
|
|
903
|
+
writable: (userPerms & 2) !== 0,
|
|
904
|
+
executable: (userPerms & 1) !== 0
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
async function scanDirectory(dirPath: string): Promise<void> {
|
|
909
|
+
const entries = await readdir(dirPath);
|
|
910
|
+
|
|
911
|
+
for (const entry of entries) {
|
|
912
|
+
// Skip hidden files unless includeHidden is true
|
|
913
|
+
if (!options?.includeHidden && entry.startsWith('.')) {
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const fullPath = join(dirPath, entry);
|
|
918
|
+
const stats = await stat(fullPath);
|
|
919
|
+
|
|
920
|
+
let type: 'file' | 'directory' | 'symlink' | 'other';
|
|
921
|
+
if (stats.isDirectory()) {
|
|
922
|
+
type = 'directory';
|
|
923
|
+
} else if (stats.isFile()) {
|
|
924
|
+
type = 'file';
|
|
925
|
+
} else if (stats.isSymbolicLink()) {
|
|
926
|
+
type = 'symlink';
|
|
927
|
+
} else {
|
|
928
|
+
type = 'other';
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Extract mode (permissions) - stats.mode is a number
|
|
932
|
+
const mode = stats.mode & 0o777; // Get only permission bits
|
|
933
|
+
|
|
934
|
+
// Calculate relative path from base directory
|
|
935
|
+
const relativePath = fullPath.startsWith(`${basePath}/`)
|
|
936
|
+
? fullPath.substring(basePath.length + 1)
|
|
937
|
+
: fullPath === basePath
|
|
938
|
+
? '.'
|
|
939
|
+
: entry;
|
|
940
|
+
|
|
941
|
+
const fileInfo = {
|
|
942
|
+
name: entry,
|
|
943
|
+
absolutePath: fullPath,
|
|
944
|
+
relativePath,
|
|
945
|
+
type,
|
|
946
|
+
size: stats.size,
|
|
947
|
+
modifiedAt: stats.mtime.toISOString(),
|
|
948
|
+
mode: modeToString(mode),
|
|
949
|
+
permissions: getPermissions(mode)
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
files.push(fileInfo);
|
|
953
|
+
|
|
954
|
+
// Recursively scan subdirectories if requested
|
|
955
|
+
if (options?.recursive && type === 'directory') {
|
|
956
|
+
await scanDirectory(fullPath);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
await scanDirectory(path);
|
|
962
|
+
|
|
963
|
+
console.log(`[Server] Listed ${files.length} files in: ${path}`);
|
|
964
|
+
return {
|
|
965
|
+
exitCode: 0,
|
|
966
|
+
files,
|
|
967
|
+
success: true,
|
|
968
|
+
};
|
|
969
|
+
} catch (error) {
|
|
970
|
+
console.error(`[Server] Error listing files in: ${path}`, error);
|
|
971
|
+
throw error;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
export async function handleListFilesRequest(
|
|
976
|
+
req: Request,
|
|
977
|
+
corsHeaders: Record<string, string>
|
|
978
|
+
): Promise<Response> {
|
|
979
|
+
try {
|
|
980
|
+
const body = (await req.json()) as ListFilesRequest;
|
|
981
|
+
const { path, options } = body;
|
|
982
|
+
|
|
983
|
+
if (!path || typeof path !== "string") {
|
|
984
|
+
return new Response(
|
|
985
|
+
JSON.stringify({
|
|
986
|
+
error: "Path is required and must be a string",
|
|
987
|
+
}),
|
|
988
|
+
{
|
|
989
|
+
headers: {
|
|
990
|
+
"Content-Type": "application/json",
|
|
991
|
+
...corsHeaders,
|
|
992
|
+
},
|
|
993
|
+
status: 400,
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// Basic safety check - prevent dangerous paths
|
|
999
|
+
const dangerousPatterns = [
|
|
1000
|
+
/^\/etc/, // System directories
|
|
1001
|
+
/^\/var/, // System directories
|
|
1002
|
+
/^\/usr/, // System directories
|
|
1003
|
+
/^\/bin/, // System directories
|
|
1004
|
+
/^\/sbin/, // System directories
|
|
1005
|
+
/^\/boot/, // System directories
|
|
1006
|
+
/^\/dev/, // System directories
|
|
1007
|
+
/^\/proc/, // System directories
|
|
1008
|
+
/^\/sys/, // System directories
|
|
1009
|
+
/^\/tmp\/\.\./, // Path traversal attempts
|
|
1010
|
+
/\.\./, // Path traversal attempts
|
|
1011
|
+
];
|
|
1012
|
+
|
|
1013
|
+
if (dangerousPatterns.some((pattern) => pattern.test(path))) {
|
|
1014
|
+
return new Response(
|
|
1015
|
+
JSON.stringify({
|
|
1016
|
+
error: "Dangerous path not allowed",
|
|
1017
|
+
}),
|
|
1018
|
+
{
|
|
1019
|
+
headers: {
|
|
1020
|
+
"Content-Type": "application/json",
|
|
1021
|
+
...corsHeaders,
|
|
1022
|
+
},
|
|
1023
|
+
status: 400,
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
console.log(`[Server] Listing files in: ${path}`);
|
|
1029
|
+
|
|
1030
|
+
const result = await executeListFiles(path, options);
|
|
1031
|
+
|
|
1032
|
+
return new Response(
|
|
1033
|
+
JSON.stringify({
|
|
1034
|
+
exitCode: result.exitCode,
|
|
1035
|
+
files: result.files,
|
|
1036
|
+
path,
|
|
1037
|
+
success: result.success,
|
|
1038
|
+
timestamp: new Date().toISOString(),
|
|
1039
|
+
}),
|
|
1040
|
+
{
|
|
1041
|
+
headers: {
|
|
1042
|
+
"Content-Type": "application/json",
|
|
1043
|
+
...corsHeaders,
|
|
1044
|
+
},
|
|
1045
|
+
}
|
|
1046
|
+
);
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
console.error("[Server] Error in handleListFilesRequest:", error);
|
|
1049
|
+
return new Response(
|
|
1050
|
+
JSON.stringify({
|
|
1051
|
+
error: "Failed to list files",
|
|
1052
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
1053
|
+
}),
|
|
1054
|
+
{
|
|
1055
|
+
headers: {
|
|
1056
|
+
"Content-Type": "application/json",
|
|
1057
|
+
...corsHeaders,
|
|
1058
|
+
},
|
|
1059
|
+
status: 500,
|
|
1060
|
+
}
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
package/container_src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "./handler/exec";
|
|
7
7
|
import {
|
|
8
8
|
handleDeleteFileRequest,
|
|
9
|
+
handleListFilesRequest,
|
|
9
10
|
handleMkdirRequest,
|
|
10
11
|
handleMoveFileRequest,
|
|
11
12
|
handleReadFileRequest,
|
|
@@ -279,6 +280,12 @@ const server = serve({
|
|
|
279
280
|
}
|
|
280
281
|
break;
|
|
281
282
|
|
|
283
|
+
case "/api/list-files":
|
|
284
|
+
if (req.method === "POST") {
|
|
285
|
+
return handleListFilesRequest(req, corsHeaders);
|
|
286
|
+
}
|
|
287
|
+
break;
|
|
288
|
+
|
|
282
289
|
case "/api/expose-port":
|
|
283
290
|
if (req.method === "POST") {
|
|
284
291
|
return handleExposePortRequest(exposedPorts, req, corsHeaders);
|
|
@@ -133,16 +133,29 @@ export class JupyterService {
|
|
|
133
133
|
* Pre-warm context pools for better performance
|
|
134
134
|
*/
|
|
135
135
|
private async warmContextPools() {
|
|
136
|
+
// Delay pre-warming to avoid startup rush that triggers Bun WebSocket bug
|
|
137
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
138
|
+
|
|
136
139
|
try {
|
|
137
140
|
console.log(
|
|
138
141
|
"[JupyterService] Pre-warming context pools for better performance"
|
|
139
142
|
);
|
|
140
143
|
|
|
141
|
-
//
|
|
144
|
+
// Only pre-warm Python contexts to avoid Bun WebSocket validation errors
|
|
145
|
+
// JavaScript contexts will be created on-demand
|
|
142
146
|
await this.jupyterServer.enablePoolWarming("python", 1);
|
|
143
|
-
|
|
147
|
+
|
|
148
|
+
// Commenting out JavaScript pre-warming due to kernel message validation errors
|
|
149
|
+
// The errors appear to be related to Bun's handling of WebSocket messages
|
|
150
|
+
// JavaScript contexts still work fine when created on-demand
|
|
151
|
+
//
|
|
152
|
+
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
153
|
+
// await this.jupyterServer.enablePoolWarming("javascript", 1);
|
|
144
154
|
} catch (error) {
|
|
145
155
|
console.error("[JupyterService] Error pre-warming context pools:", error);
|
|
156
|
+
console.error(
|
|
157
|
+
"[JupyterService] Pre-warming failed but service continues"
|
|
158
|
+
);
|
|
146
159
|
}
|
|
147
160
|
}
|
|
148
161
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Minimal Jupyter configuration focused on kernel-only usage
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
c = get_config() # noqa
|
|
6
|
+
|
|
7
|
+
# Disable all authentication - we handle security at container level
|
|
8
|
+
c.ServerApp.token = ''
|
|
9
|
+
c.ServerApp.password = ''
|
|
10
|
+
c.IdentityProvider.token = ''
|
|
11
|
+
c.ServerApp.allow_origin = '*'
|
|
12
|
+
c.ServerApp.allow_remote_access = True
|
|
13
|
+
c.ServerApp.disable_check_xsrf = True
|
|
14
|
+
c.ServerApp.allow_root = True
|
|
15
|
+
c.ServerApp.allow_credentials = True
|
|
16
|
+
|
|
17
|
+
# Also set NotebookApp settings for compatibility
|
|
18
|
+
c.NotebookApp.token = ''
|
|
19
|
+
c.NotebookApp.password = ''
|
|
20
|
+
c.NotebookApp.allow_origin = '*'
|
|
21
|
+
c.NotebookApp.allow_remote_access = True
|
|
22
|
+
c.NotebookApp.disable_check_xsrf = True
|
|
23
|
+
c.NotebookApp.allow_credentials = True
|
|
24
|
+
|
|
25
|
+
# Performance settings
|
|
26
|
+
c.ServerApp.iopub_data_rate_limit = 1000000000 # E2B uses 1GB/s
|
|
27
|
+
|
|
28
|
+
# Minimal logging
|
|
29
|
+
c.Application.log_level = 'ERROR'
|
|
30
|
+
|
|
31
|
+
# Disable browser
|
|
32
|
+
c.ServerApp.open_browser = False
|
|
33
|
+
|
|
34
|
+
# Optimize for container environment
|
|
35
|
+
c.ServerApp.ip = '0.0.0.0'
|
|
36
|
+
c.ServerApp.port = 8888
|
|
37
|
+
|
|
38
|
+
# Kernel optimizations
|
|
39
|
+
c.KernelManager.shutdown_wait_time = 0.0
|
|
40
|
+
c.MappingKernelManager.cull_idle_timeout = 0
|
|
41
|
+
c.MappingKernelManager.cull_interval = 0
|
|
42
|
+
|
|
43
|
+
# Disable terminals
|
|
44
|
+
c.ServerApp.terminals_enabled = False
|
|
45
|
+
|
|
46
|
+
# Disable all extensions to speed up startup
|
|
47
|
+
c.ServerApp.jpserver_extensions = {}
|
|
48
|
+
c.ServerApp.nbserver_extensions = {}
|
package/container_src/startup.sh
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Function to check if Jupyter is ready
|
|
4
4
|
check_jupyter_ready() {
|
|
5
|
-
|
|
5
|
+
# Check if API is responsive and kernelspecs are available
|
|
6
|
+
curl -s http://localhost:8888/api/kernelspecs > /dev/null 2>&1
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
# Function to notify Bun server that Jupyter is ready
|
|
@@ -12,19 +13,10 @@ notify_jupyter_ready() {
|
|
|
12
13
|
echo "[Startup] Jupyter is ready, notified Bun server"
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
# Start Jupyter
|
|
16
|
+
# Start Jupyter server in background
|
|
16
17
|
echo "[Startup] Starting Jupyter server..."
|
|
17
|
-
jupyter
|
|
18
|
-
--
|
|
19
|
-
--port=8888 \
|
|
20
|
-
--no-browser \
|
|
21
|
-
--allow-root \
|
|
22
|
-
--NotebookApp.token='' \
|
|
23
|
-
--NotebookApp.password='' \
|
|
24
|
-
--NotebookApp.allow_origin='*' \
|
|
25
|
-
--NotebookApp.disable_check_xsrf=True \
|
|
26
|
-
--NotebookApp.allow_remote_access=True \
|
|
27
|
-
--NotebookApp.allow_credentials=True \
|
|
18
|
+
jupyter server \
|
|
19
|
+
--config=/container-server/jupyter_config.py \
|
|
28
20
|
> /tmp/jupyter.log 2>&1 &
|
|
29
21
|
|
|
30
22
|
JUPYTER_PID=$!
|
|
@@ -37,18 +29,21 @@ BUN_PID=$!
|
|
|
37
29
|
# Monitor Jupyter readiness in background
|
|
38
30
|
(
|
|
39
31
|
echo "[Startup] Monitoring Jupyter readiness in background..."
|
|
40
|
-
MAX_ATTEMPTS=
|
|
32
|
+
MAX_ATTEMPTS=60
|
|
41
33
|
ATTEMPT=0
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
|
|
35
|
+
# Track start time for reporting
|
|
36
|
+
START_TIME=$(date +%s.%N)
|
|
37
|
+
|
|
45
38
|
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
|
46
39
|
if check_jupyter_ready; then
|
|
47
40
|
notify_jupyter_ready
|
|
48
|
-
|
|
41
|
+
END_TIME=$(date +%s.%N)
|
|
42
|
+
ELAPSED=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}")
|
|
43
|
+
echo "[Startup] Jupyter server is ready after $ELAPSED seconds ($ATTEMPT attempts)"
|
|
49
44
|
break
|
|
50
45
|
fi
|
|
51
|
-
|
|
46
|
+
|
|
52
47
|
# Check if Jupyter process is still running
|
|
53
48
|
if ! kill -0 $JUPYTER_PID 2>/dev/null; then
|
|
54
49
|
echo "[Startup] WARNING: Jupyter process died. Check /tmp/jupyter.log for details"
|
|
@@ -56,21 +51,27 @@ BUN_PID=$!
|
|
|
56
51
|
# Don't exit - let Bun server continue running in degraded mode
|
|
57
52
|
break
|
|
58
53
|
fi
|
|
59
|
-
|
|
54
|
+
|
|
60
55
|
ATTEMPT=$((ATTEMPT + 1))
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
|
|
57
|
+
# Start with faster checks
|
|
58
|
+
if [ $ATTEMPT -eq 1 ]; then
|
|
59
|
+
DELAY=0.5 # Start at 0.5s
|
|
60
|
+
else
|
|
61
|
+
# Exponential backoff with 1.3x multiplier (less aggressive than 1.5x)
|
|
62
|
+
DELAY=$(awk "BEGIN {printf \"%.2f\", $DELAY * 1.3}")
|
|
63
|
+
# Cap at 2s max (instead of 5s)
|
|
64
|
+
if [ $(awk "BEGIN {print ($DELAY > 2)}") -eq 1 ]; then
|
|
65
|
+
DELAY=2
|
|
66
|
+
fi
|
|
71
67
|
fi
|
|
68
|
+
|
|
69
|
+
# Log with current delay for transparency
|
|
70
|
+
echo "[Startup] Jupyter not ready yet (attempt $ATTEMPT/$MAX_ATTEMPTS, next check in ${DELAY}s)"
|
|
71
|
+
|
|
72
|
+
sleep $DELAY
|
|
72
73
|
done
|
|
73
|
-
|
|
74
|
+
|
|
74
75
|
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
|
|
75
76
|
echo "[Startup] WARNING: Jupyter failed to become ready within attempts"
|
|
76
77
|
echo "[Startup] Jupyter logs:"
|
|
@@ -80,4 +81,4 @@ BUN_PID=$!
|
|
|
80
81
|
) &
|
|
81
82
|
|
|
82
83
|
# Wait for Bun server (main process)
|
|
83
|
-
wait $BUN_PID
|
|
84
|
+
wait $BUN_PID
|
package/container_src/types.ts
CHANGED
|
@@ -92,6 +92,15 @@ export interface MoveFileRequest {
|
|
|
92
92
|
sessionId?: string;
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
export interface ListFilesRequest {
|
|
96
|
+
path: string;
|
|
97
|
+
options?: {
|
|
98
|
+
recursive?: boolean;
|
|
99
|
+
includeHidden?: boolean;
|
|
100
|
+
};
|
|
101
|
+
sessionId?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
export interface ExposePortRequest {
|
|
96
105
|
port: number;
|
|
97
106
|
name?: string;
|
|
@@ -322,6 +322,36 @@ var HttpClient = class {
|
|
|
322
322
|
throw error;
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
+
async listFiles(path, options, sessionId) {
|
|
326
|
+
try {
|
|
327
|
+
const targetSessionId = sessionId || this.sessionId;
|
|
328
|
+
const response = await this.doFetch(`/api/list-files`, {
|
|
329
|
+
body: JSON.stringify({
|
|
330
|
+
path,
|
|
331
|
+
options,
|
|
332
|
+
sessionId: targetSessionId
|
|
333
|
+
}),
|
|
334
|
+
headers: {
|
|
335
|
+
"Content-Type": "application/json"
|
|
336
|
+
},
|
|
337
|
+
method: "POST"
|
|
338
|
+
});
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
const errorData = await response.json().catch(() => ({}));
|
|
341
|
+
throw new Error(
|
|
342
|
+
errorData.error || `HTTP error! status: ${response.status}`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const data = await response.json();
|
|
346
|
+
console.log(
|
|
347
|
+
`[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}`
|
|
348
|
+
);
|
|
349
|
+
return data;
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error("[HTTP Client] Error listing files:", error);
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
325
355
|
async exposePort(port, name) {
|
|
326
356
|
try {
|
|
327
357
|
const response = await this.doFetch(`/api/expose-port`, {
|
|
@@ -626,4 +656,4 @@ var HttpClient = class {
|
|
|
626
656
|
export {
|
|
627
657
|
HttpClient
|
|
628
658
|
};
|
|
629
|
-
//# sourceMappingURL=chunk-
|
|
659
|
+
//# sourceMappingURL=chunk-3CQ6THKA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type { ExecuteRequest } from \"../container_src/types\";\nimport type { Sandbox } from \"./index\";\nimport type {\n BaseExecOptions,\n DeleteFileResponse,\n ExecuteResponse,\n GetProcessLogsResponse,\n GetProcessResponse,\n GitCheckoutResponse,\n ListFilesResponse,\n ListProcessesResponse,\n MkdirResponse,\n MoveFileResponse,\n ReadFileResponse,\n RenameFileResponse,\n StartProcessRequest,\n StartProcessResponse,\n WriteFileResponse,\n} from \"./types\";\n\n\ninterface CommandsResponse {\n availableCommands: string[];\n timestamp: string;\n}\n\ninterface GitCheckoutRequest {\n repoUrl: string;\n branch?: string;\n targetDir?: string;\n sessionId?: string;\n}\n\n\ninterface MkdirRequest {\n path: string;\n recursive?: boolean;\n sessionId?: string;\n}\n\n\ninterface WriteFileRequest {\n path: string;\n content: string;\n encoding?: string;\n sessionId?: string;\n}\n\n\ninterface ReadFileRequest {\n path: string;\n encoding?: string;\n sessionId?: string;\n}\n\n\ninterface DeleteFileRequest {\n path: string;\n sessionId?: string;\n}\n\n\ninterface RenameFileRequest {\n oldPath: string;\n newPath: string;\n sessionId?: string;\n}\n\n\ninterface MoveFileRequest {\n sourcePath: string;\n destinationPath: string;\n sessionId?: string;\n}\n\n\ninterface ListFilesRequest {\n path: string;\n options?: {\n recursive?: boolean;\n includeHidden?: boolean;\n };\n sessionId?: string;\n}\n\n\ninterface PreviewInfo {\n url: string;\n port: number;\n name?: string;\n}\n\ninterface ExposedPort extends PreviewInfo {\n exposedAt: string;\n timestamp: string;\n}\n\ninterface ExposePortResponse {\n success: boolean;\n port: number;\n name?: string;\n exposedAt: string;\n timestamp: string;\n}\n\ninterface UnexposePortResponse {\n success: boolean;\n port: number;\n timestamp: string;\n}\n\ninterface GetExposedPortsResponse {\n ports: ExposedPort[];\n count: number;\n timestamp: string;\n}\n\ninterface PingResponse {\n message: string;\n timestamp: string;\n}\n\ninterface HttpClientOptions {\n stub?: Sandbox;\n baseUrl?: string;\n port?: number;\n onCommandStart?: (command: string) => void;\n onOutput?: (\n stream: \"stdout\" | \"stderr\",\n data: string,\n command: string\n ) => void;\n onCommandComplete?: (\n success: boolean,\n exitCode: number,\n stdout: string,\n stderr: string,\n command: string\n ) => void;\n onError?: (error: string, command?: string) => void;\n}\n\nexport class HttpClient {\n private baseUrl: string;\n private options: HttpClientOptions;\n private sessionId: string | null = null;\n\n constructor(options: HttpClientOptions = {}) {\n this.options = {\n ...options,\n };\n this.baseUrl = this.options.baseUrl!;\n }\n\n protected async doFetch(\n path: string,\n options?: RequestInit\n ): Promise<Response> {\n const url = this.options.stub\n ? `http://localhost:${this.options.port}${path}`\n : `${this.baseUrl}${path}`;\n const method = options?.method || \"GET\";\n\n console.log(`[HTTP Client] Making ${method} request to ${url}`);\n\n try {\n let response: Response;\n\n if (this.options.stub) {\n response = await this.options.stub.containerFetch(\n url,\n options,\n this.options.port\n );\n } else {\n response = await fetch(url, options);\n }\n\n console.log(\n `[HTTP Client] Response: ${response.status} ${response.statusText}`\n );\n\n if (!response.ok) {\n console.error(\n `[HTTP Client] Request failed: ${method} ${url} - ${response.status} ${response.statusText}`\n );\n }\n\n return response;\n } catch (error) {\n console.error(`[HTTP Client] Request error: ${method} ${url}`, error);\n throw error;\n }\n }\n\n async execute(\n command: string,\n options: Pick<BaseExecOptions, \"sessionId\" | \"cwd\" | \"env\">\n ): Promise<ExecuteResponse> {\n try {\n const targetSessionId = options.sessionId || this.sessionId;\n const executeRequest = {\n command,\n sessionId: targetSessionId,\n cwd: options.cwd,\n env: options.env,\n } satisfies ExecuteRequest;\n\n const response = await this.doFetch(`/api/execute`, {\n body: JSON.stringify(executeRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ExecuteResponse = await response.json();\n console.log(\n `[HTTP Client] Command executed: ${command}, Success: ${data.success}`\n );\n\n // Call the callback if provided\n this.options.onCommandComplete?.(\n data.success,\n data.exitCode,\n data.stdout,\n data.stderr,\n data.command\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error executing command:\", error);\n this.options.onError?.(\n error instanceof Error ? error.message : \"Unknown error\",\n command\n );\n throw error;\n }\n }\n\n async executeCommandStream(\n command: string,\n sessionId?: string\n ): Promise<ReadableStream<Uint8Array>> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/execute/stream`, {\n body: JSON.stringify({\n command,\n sessionId: targetSessionId,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"text/event-stream\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for streaming request\");\n }\n\n console.log(`[HTTP Client] Started command stream: ${command}`);\n\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error in command stream:\", error);\n throw error;\n }\n }\n\n async gitCheckout(\n repoUrl: string,\n branch: string = \"main\",\n targetDir?: string,\n sessionId?: string\n ): Promise<GitCheckoutResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/git/checkout`, {\n body: JSON.stringify({\n branch,\n repoUrl,\n sessionId: targetSessionId,\n targetDir,\n } as GitCheckoutRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GitCheckoutResponse = await response.json();\n console.log(\n `[HTTP Client] Git checkout completed: ${repoUrl}, Success: ${data.success}, Target: ${data.targetDir}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error in git checkout:\", error);\n throw error;\n }\n }\n\n async mkdir(\n path: string,\n recursive: boolean = false,\n sessionId?: string\n ): Promise<MkdirResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/mkdir`, {\n body: JSON.stringify({\n path,\n recursive,\n sessionId: targetSessionId,\n } as MkdirRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: MkdirResponse = await response.json();\n console.log(\n `[HTTP Client] Directory created: ${path}, Success: ${data.success}, Recursive: ${data.recursive}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error creating directory:\", error);\n throw error;\n }\n }\n\n async writeFile(\n path: string,\n content: string,\n encoding: string = \"utf-8\",\n sessionId?: string\n ): Promise<WriteFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/write`, {\n body: JSON.stringify({\n content,\n encoding,\n path,\n sessionId: targetSessionId,\n } as WriteFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: WriteFileResponse = await response.json();\n console.log(\n `[HTTP Client] File written: ${path}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error writing file:\", error);\n throw error;\n }\n }\n\n async readFile(\n path: string,\n encoding: string = \"utf-8\",\n sessionId?: string\n ): Promise<ReadFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/read`, {\n body: JSON.stringify({\n encoding,\n path,\n sessionId: targetSessionId,\n } as ReadFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ReadFileResponse = await response.json();\n console.log(\n `[HTTP Client] File read: ${path}, Success: ${data.success}, Content length: ${data.content.length}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error reading file:\", error);\n throw error;\n }\n }\n\n async deleteFile(\n path: string,\n sessionId?: string\n ): Promise<DeleteFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/delete`, {\n body: JSON.stringify({\n path,\n sessionId: targetSessionId,\n } as DeleteFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: DeleteFileResponse = await response.json();\n console.log(\n `[HTTP Client] File deleted: ${path}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error deleting file:\", error);\n throw error;\n }\n }\n\n async renameFile(\n oldPath: string,\n newPath: string,\n sessionId?: string\n ): Promise<RenameFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/rename`, {\n body: JSON.stringify({\n newPath,\n oldPath,\n sessionId: targetSessionId,\n } as RenameFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: RenameFileResponse = await response.json();\n console.log(\n `[HTTP Client] File renamed: ${oldPath} -> ${newPath}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error renaming file:\", error);\n throw error;\n }\n }\n\n async moveFile(\n sourcePath: string,\n destinationPath: string,\n sessionId?: string\n ): Promise<MoveFileResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/move`, {\n body: JSON.stringify({\n destinationPath,\n sessionId: targetSessionId,\n sourcePath,\n } as MoveFileRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: MoveFileResponse = await response.json();\n console.log(\n `[HTTP Client] File moved: ${sourcePath} -> ${destinationPath}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error moving file:\", error);\n throw error;\n }\n }\n\n async listFiles(\n path: string,\n options?: {\n recursive?: boolean;\n includeHidden?: boolean;\n },\n sessionId?: string\n ): Promise<ListFilesResponse> {\n try {\n const targetSessionId = sessionId || this.sessionId;\n\n const response = await this.doFetch(`/api/list-files`, {\n body: JSON.stringify({\n path,\n options,\n sessionId: targetSessionId,\n } as ListFilesRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ListFilesResponse = await response.json();\n console.log(\n `[HTTP Client] Listed ${data.files.length} files in: ${path}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error listing files:\", error);\n throw error;\n }\n }\n\n async exposePort(port: number, name?: string): Promise<ExposePortResponse> {\n try {\n const response = await this.doFetch(`/api/expose-port`, {\n body: JSON.stringify({\n port,\n name,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n console.log(errorData);\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ExposePortResponse = await response.json();\n console.log(\n `[HTTP Client] Port exposed: ${port}${\n name ? ` (${name})` : \"\"\n }, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error exposing port:\", error);\n throw error;\n }\n }\n\n async unexposePort(port: number): Promise<UnexposePortResponse> {\n try {\n const response = await this.doFetch(`/api/unexpose-port`, {\n body: JSON.stringify({\n port,\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: UnexposePortResponse = await response.json();\n console.log(\n `[HTTP Client] Port unexposed: ${port}, Success: ${data.success}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error unexposing port:\", error);\n throw error;\n }\n }\n\n async getExposedPorts(): Promise<GetExposedPortsResponse> {\n try {\n const response = await this.doFetch(`/api/exposed-ports`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetExposedPortsResponse = await response.json();\n console.log(`[HTTP Client] Got ${data.count} exposed ports`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting exposed ports:\", error);\n throw error;\n }\n }\n\n async ping(): Promise<string> {\n try {\n const response = await this.doFetch(`/api/ping`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data: PingResponse = await response.json();\n console.log(`[HTTP Client] Ping response: ${data.message}`);\n return data.timestamp;\n } catch (error) {\n console.error(\"[HTTP Client] Error pinging server:\", error);\n throw error;\n }\n }\n\n async getCommands(): Promise<string[]> {\n try {\n const response = await fetch(`${this.baseUrl}/api/commands`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const data: CommandsResponse = await response.json();\n console.log(\n `[HTTP Client] Available commands: ${data.availableCommands.length}`\n );\n return data.availableCommands;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting commands:\", error);\n throw error;\n }\n }\n\n getSessionId(): string | null {\n return this.sessionId;\n }\n\n setSessionId(sessionId: string): void {\n this.sessionId = sessionId;\n }\n\n clearSession(): void {\n this.sessionId = null;\n }\n\n // Process management methods\n async startProcess(\n command: string,\n options?: {\n processId?: string;\n sessionId?: string;\n timeout?: number;\n env?: Record<string, string>;\n cwd?: string;\n encoding?: string;\n autoCleanup?: boolean;\n }\n ): Promise<StartProcessResponse> {\n try {\n const targetSessionId = options?.sessionId || this.sessionId;\n\n const response = await this.doFetch(\"/api/process/start\", {\n body: JSON.stringify({\n command,\n options: {\n ...options,\n sessionId: targetSessionId,\n },\n } as StartProcessRequest),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: StartProcessResponse = await response.json();\n console.log(\n `[HTTP Client] Process started: ${command}, ID: ${data.process.id}`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error starting process:\", error);\n throw error;\n }\n }\n\n async listProcesses(): Promise<ListProcessesResponse> {\n try {\n const response = await this.doFetch(\"/api/process/list\", {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: ListProcessesResponse = await response.json();\n console.log(`[HTTP Client] Listed ${data.processes.length} processes`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error listing processes:\", error);\n throw error;\n }\n }\n\n async getProcess(processId: string): Promise<GetProcessResponse> {\n try {\n const response = await this.doFetch(`/api/process/${processId}`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetProcessResponse = await response.json();\n console.log(\n `[HTTP Client] Got process ${processId}: ${\n data.process?.status || \"not found\"\n }`\n );\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting process:\", error);\n throw error;\n }\n }\n\n async killProcess(\n processId: string\n ): Promise<{ success: boolean; message: string }> {\n try {\n const response = await this.doFetch(`/api/process/${processId}`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = (await response.json()) as {\n success: boolean;\n message: string;\n };\n console.log(`[HTTP Client] Killed process ${processId}`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error killing process:\", error);\n throw error;\n }\n }\n\n async killAllProcesses(): Promise<{\n success: boolean;\n killedCount: number;\n message: string;\n }> {\n try {\n const response = await this.doFetch(\"/api/process/kill-all\", {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"DELETE\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = (await response.json()) as {\n success: boolean;\n killedCount: number;\n message: string;\n };\n console.log(`[HTTP Client] Killed ${data.killedCount} processes`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error killing all processes:\", error);\n throw error;\n }\n }\n\n async getProcessLogs(processId: string): Promise<GetProcessLogsResponse> {\n try {\n const response = await this.doFetch(`/api/process/${processId}/logs`, {\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n const data: GetProcessLogsResponse = await response.json();\n console.log(`[HTTP Client] Got logs for process ${processId}`);\n\n return data;\n } catch (error) {\n console.error(\"[HTTP Client] Error getting process logs:\", error);\n throw error;\n }\n }\n\n async streamProcessLogs(\n processId: string\n ): Promise<ReadableStream<Uint8Array>> {\n try {\n const response = await this.doFetch(`/api/process/${processId}/stream`, {\n headers: {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n },\n method: \"GET\",\n });\n\n if (!response.ok) {\n const errorData = (await response.json().catch(() => ({}))) as {\n error?: string;\n };\n throw new Error(\n errorData.error || `HTTP error! status: ${response.status}`\n );\n }\n\n if (!response.body) {\n throw new Error(\"No response body for streaming request\");\n }\n\n console.log(\n `[HTTP Client] Started streaming logs for process ${processId}`\n );\n\n return response.body;\n } catch (error) {\n console.error(\"[HTTP Client] Error streaming process logs:\", error);\n throw error;\n }\n }\n}\n"],"mappings":"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","names":[]}
|