cloudflare-openhands-sdk 0.1.0

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.
@@ -0,0 +1,178 @@
1
+ // src/utils/agent-server.ts
2
+ var DEFAULT_AGENT_SERVER_PATH = "/container-server/software-agent-sdk/.venv/bin/agent-server";
3
+ var DEFAULT_AGENT_SERVER_DIR = "/container-server/software-agent-sdk";
4
+ function getDefaultAgentServerPath() {
5
+ return DEFAULT_AGENT_SERVER_PATH;
6
+ }
7
+ function getDefaultAgentServerDir() {
8
+ return DEFAULT_AGENT_SERVER_DIR;
9
+ }
10
+ async function checkAgentServerExists(sandbox, path = DEFAULT_AGENT_SERVER_PATH) {
11
+ try {
12
+ const result = await sandbox.exec(`test -f ${path} && echo "EXISTS" || echo "NOT_FOUND"`);
13
+ return result.stdout.trim() === "EXISTS";
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ async function findAgentServerPath(sandbox) {
19
+ const locations = [
20
+ DEFAULT_AGENT_SERVER_PATH,
21
+ "/software-agent-sdk/.venv/bin/agent-server",
22
+ "/root/software-agent-sdk/.venv/bin/agent-server"
23
+ ];
24
+ for (const location of locations) {
25
+ if (await checkAgentServerExists(sandbox, location)) {
26
+ return location;
27
+ }
28
+ }
29
+ try {
30
+ const result = await sandbox.exec("which agent-server");
31
+ const path = result.stdout.trim();
32
+ if (path && !path.includes("not found")) {
33
+ return path;
34
+ }
35
+ } catch {}
36
+ return null;
37
+ }
38
+ function buildAgentServerCommand(port, directory) {
39
+ const command = `${DEFAULT_AGENT_SERVER_PATH} --host 0.0.0.0 --port ${port}`;
40
+ return directory && directory !== DEFAULT_AGENT_SERVER_DIR ? `cd ${directory} && ${command}` : command;
41
+ }
42
+
43
+ // src/openhands/types.ts
44
+ class OpenhandsStartupError extends Error {
45
+ code = "OPENHANDS_STARTUP_FAILED";
46
+ context;
47
+ constructor(message, context, options) {
48
+ super(message, options);
49
+ this.name = "OpenhandsStartupError";
50
+ this.context = context;
51
+ }
52
+ }
53
+ // src/openhands/openhands.ts
54
+ var DEFAULT_PORT = 8001;
55
+ async function findExistingAgentServer(sandbox, port) {
56
+ const processes = await sandbox.listProcesses();
57
+ const commandPattern = DEFAULT_AGENT_SERVER_PATH;
58
+ for (const proc of processes) {
59
+ if (proc.command.includes(commandPattern)) {
60
+ if (proc.command.includes(`--port ${port}`) || proc.command.includes(`--port=${port}`)) {
61
+ if (proc.status === "starting" || proc.status === "running") {
62
+ return proc;
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+ async function startAgentServer(sandbox, port, options) {
70
+ const directory = options.directory || DEFAULT_AGENT_SERVER_DIR;
71
+ const command = buildAgentServerCommand(port, directory);
72
+ const process = await sandbox.startProcess(command, {
73
+ cwd: directory,
74
+ env: options.env
75
+ });
76
+ try {
77
+ await process.waitForPort(port, {
78
+ mode: "http",
79
+ path: "/",
80
+ timeout: 60000
81
+ });
82
+ } catch (e) {
83
+ const logs = await process.getLogs();
84
+ throw new OpenhandsStartupError(`agent-server failed to start on port ${port}. Stderr: ${logs.stderr || "(empty)"}`, {
85
+ port,
86
+ stderr: logs.stderr,
87
+ command: process.command,
88
+ processId: process.id
89
+ }, { cause: e });
90
+ }
91
+ return process;
92
+ }
93
+ async function ensureAgentServer(sandbox, port, options) {
94
+ const existingProcess = await findExistingAgentServer(sandbox, port);
95
+ if (existingProcess) {
96
+ if (existingProcess.status === "starting") {
97
+ try {
98
+ await existingProcess.waitForPort(port, {
99
+ mode: "http",
100
+ path: "/",
101
+ timeout: 60000
102
+ });
103
+ } catch (e) {
104
+ const logs = await existingProcess.getLogs();
105
+ throw new OpenhandsStartupError(`agent-server failed to start. Stderr: ${logs.stderr || "(empty)"}`, {
106
+ port,
107
+ stderr: logs.stderr,
108
+ command: existingProcess.command,
109
+ processId: existingProcess.id
110
+ }, { cause: e });
111
+ }
112
+ }
113
+ return existingProcess;
114
+ }
115
+ try {
116
+ return await startAgentServer(sandbox, port, options);
117
+ } catch (startupError) {
118
+ const retryProcess = await findExistingAgentServer(sandbox, port);
119
+ if (retryProcess) {
120
+ if (retryProcess.status === "starting") {
121
+ try {
122
+ await retryProcess.waitForPort(port, {
123
+ mode: "http",
124
+ path: "/",
125
+ timeout: 60000
126
+ });
127
+ } catch (e) {
128
+ const logs = await retryProcess.getLogs();
129
+ throw new OpenhandsStartupError(`agent-server failed to start. Stderr: ${logs.stderr || "(empty)"}`, {
130
+ port,
131
+ stderr: logs.stderr,
132
+ command: retryProcess.command,
133
+ processId: retryProcess.id
134
+ }, { cause: e });
135
+ }
136
+ }
137
+ return retryProcess;
138
+ }
139
+ throw startupError;
140
+ }
141
+ }
142
+ async function createOpenhandsServer(sandbox, options = {}) {
143
+ const port = options.port ?? DEFAULT_PORT;
144
+ const process = await ensureAgentServer(sandbox, port, options);
145
+ let previewUrl;
146
+ if (options.exposePort) {
147
+ if (!options.hostname) {
148
+ throw new Error("hostname is required when exposePort is true. Provide hostname in options or extract from request URL.");
149
+ }
150
+ try {
151
+ const exposed = await sandbox.exposePort(port, {
152
+ hostname: options.hostname
153
+ });
154
+ previewUrl = typeof exposed === "string" ? exposed : exposed.url;
155
+ } catch (error) {
156
+ console.warn("Failed to expose port:", error);
157
+ }
158
+ }
159
+ return {
160
+ port,
161
+ url: `http://localhost:${port}`,
162
+ previewUrl,
163
+ processId: process.id,
164
+ async close() {
165
+ await process.kill("SIGTERM");
166
+ }
167
+ };
168
+ }
169
+ function proxyToOpenhands(request, sandbox, server) {
170
+ return sandbox.containerFetch(request, server.port);
171
+ }
172
+ export {
173
+ proxyToOpenhands,
174
+ createOpenhandsServer,
175
+ OpenhandsStartupError
176
+ };
177
+
178
+ //# debugId=4C4FB2DEC1D2C02F64756E2164756E21
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/utils/agent-server.ts", "../src/openhands/types.ts", "../src/openhands/openhands.ts"],
4
+ "sourcesContent": [
5
+ "import type { Sandbox } from '@cloudflare/sandbox';\n\n/**\n * Default path to agent-server executable\n */\nexport const DEFAULT_AGENT_SERVER_PATH =\n '/container-server/software-agent-sdk/.venv/bin/agent-server';\n\n/**\n * Default working directory for agent-server\n */\nexport const DEFAULT_AGENT_SERVER_DIR =\n '/container-server/software-agent-sdk';\n\n/**\n * Get the default agent-server executable path\n */\nexport function getDefaultAgentServerPath(): string {\n return DEFAULT_AGENT_SERVER_PATH;\n}\n\n/**\n * Get the default agent-server working directory\n */\nexport function getDefaultAgentServerDir(): string {\n return DEFAULT_AGENT_SERVER_DIR;\n}\n\n/**\n * Check if agent-server exists at the given path\n */\nexport async function checkAgentServerExists(\n sandbox: Sandbox<unknown>,\n path: string = DEFAULT_AGENT_SERVER_PATH\n): Promise<boolean> {\n try {\n const result = await sandbox.exec(\n `test -f ${path} && echo \"EXISTS\" || echo \"NOT_FOUND\"`\n );\n return result.stdout.trim() === 'EXISTS';\n } catch {\n return false;\n }\n}\n\n/**\n * Find agent-server executable by checking common locations\n */\nexport async function findAgentServerPath(\n sandbox: Sandbox<unknown>\n): Promise<string | null> {\n const locations = [\n DEFAULT_AGENT_SERVER_PATH,\n '/software-agent-sdk/.venv/bin/agent-server',\n '/root/software-agent-sdk/.venv/bin/agent-server',\n ];\n\n for (const location of locations) {\n if (await checkAgentServerExists(sandbox, location)) {\n return location;\n }\n }\n\n // Try which as fallback\n try {\n const result = await sandbox.exec('which agent-server');\n const path = result.stdout.trim();\n if (path && !path.includes('not found')) {\n return path;\n }\n } catch {\n // Ignore errors\n }\n\n return null;\n}\n\n/**\n * Build the agent-server command\n */\nexport function buildAgentServerCommand(\n port: number,\n directory?: string\n): string {\n const command = `${DEFAULT_AGENT_SERVER_PATH} --host 0.0.0.0 --port ${port}`;\n return directory && directory !== DEFAULT_AGENT_SERVER_DIR\n ? `cd ${directory} && ${command}`\n : command;\n}\n\n",
6
+ "import type { Process } from '@cloudflare/sandbox';\n\n/**\n * Configuration options for starting OpenHands agent-server\n */\nexport interface OpenhandsOptions {\n /** Port for agent-server (default: 8001) */\n port?: number;\n /** Working directory for agent-server (default: /container-server/software-agent-sdk) */\n directory?: string;\n /** Hostname for preview URL exposure (required if exposePort is true) */\n hostname?: string;\n /** Enable preview URL exposure (default: false) */\n exposePort?: boolean;\n /** Environment variables for agent-server */\n env?: Record<string, string>;\n /** Sandbox name/session ID (default: 'my-sandbox') */\n sandboxName?: string;\n}\n\n/**\n * Server lifecycle management\n */\nexport interface OpenhandsServer {\n /** Port the server is running on */\n port: number;\n /** Base URL for server (http://localhost:{port}) */\n url: string;\n /** Preview URL if port was exposed (optional) */\n previewUrl?: string;\n /** Process ID */\n processId: string;\n /** Close the server gracefully */\n close(): Promise<void>;\n}\n\n/**\n * Context information for startup errors\n */\nexport interface OpenhandsStartupContext {\n port: number;\n stderr?: string;\n command?: string;\n processId?: string;\n}\n\n/**\n * Error thrown when agent-server fails to start\n */\nexport class OpenhandsStartupError extends Error {\n readonly code = 'OPENHANDS_STARTUP_FAILED' as const;\n readonly context: OpenhandsStartupContext;\n\n constructor(\n message: string,\n context: OpenhandsStartupContext,\n options?: ErrorOptions\n ) {\n super(message, options);\n this.name = 'OpenhandsStartupError';\n this.context = context;\n }\n}\n\n",
7
+ "import type { Sandbox, Process } from '@cloudflare/sandbox';\nimport type {\n OpenhandsOptions,\n OpenhandsServer,\n OpenhandsStartupContext,\n} from './types';\nimport { OpenhandsStartupError } from './types';\nimport {\n buildAgentServerCommand,\n DEFAULT_AGENT_SERVER_DIR,\n DEFAULT_AGENT_SERVER_PATH,\n} from '../utils/agent-server';\n\nconst DEFAULT_PORT = 8001;\n\n/**\n * Find an existing agent-server process running on the specified port.\n * Returns the process if found and still active, null otherwise.\n */\nasync function findExistingAgentServer(\n sandbox: Sandbox<unknown>,\n port: number\n): Promise<Process | null> {\n const processes = await sandbox.listProcesses();\n const commandPattern = DEFAULT_AGENT_SERVER_PATH;\n\n for (const proc of processes) {\n // Match commands that contain the agent-server path\n if (proc.command.includes(commandPattern)) {\n // Check if the command includes the port\n if (\n proc.command.includes(`--port ${port}`) ||\n proc.command.includes(`--port=${port}`)\n ) {\n if (proc.status === 'starting' || proc.status === 'running') {\n return proc;\n }\n }\n }\n }\n\n return null;\n}\n\n/**\n * Start a new agent-server process\n */\nasync function startAgentServer(\n sandbox: Sandbox<unknown>,\n port: number,\n options: OpenhandsOptions\n): Promise<Process> {\n const directory = options.directory || DEFAULT_AGENT_SERVER_DIR;\n const command = buildAgentServerCommand(port, directory);\n\n const process = await sandbox.startProcess(command, {\n cwd: directory,\n env: options.env,\n });\n\n // Wait for the server to be ready\n try {\n await process.waitForPort(port, {\n mode: 'http',\n path: '/',\n timeout: 60_000, // 60 seconds\n });\n } catch (e) {\n const logs = await process.getLogs();\n throw new OpenhandsStartupError(\n `agent-server failed to start on port ${port}. Stderr: ${logs.stderr || '(empty)'}`,\n {\n port,\n stderr: logs.stderr,\n command: process.command,\n processId: process.id,\n },\n { cause: e }\n );\n }\n\n return process;\n}\n\n/**\n * Ensures agent-server is running in the container.\n * Reuses existing process if one is already running on the specified port.\n * Handles concurrent startup attempts gracefully by retrying on failure.\n */\nasync function ensureAgentServer(\n sandbox: Sandbox<unknown>,\n port: number,\n options: OpenhandsOptions\n): Promise<Process> {\n // Check if agent-server is already running on this port\n const existingProcess = await findExistingAgentServer(sandbox, port);\n if (existingProcess) {\n // Reuse existing process - wait for it to be ready if still starting\n if (existingProcess.status === 'starting') {\n try {\n await existingProcess.waitForPort(port, {\n mode: 'http',\n path: '/',\n timeout: 60_000,\n });\n } catch (e) {\n const logs = await existingProcess.getLogs();\n throw new OpenhandsStartupError(\n `agent-server failed to start. Stderr: ${logs.stderr || '(empty)'}`,\n {\n port,\n stderr: logs.stderr,\n command: existingProcess.command,\n processId: existingProcess.id,\n },\n { cause: e }\n );\n }\n }\n return existingProcess;\n }\n\n // Try to start a new agent-server\n try {\n return await startAgentServer(sandbox, port, options);\n } catch (startupError) {\n // Startup failed - check if another concurrent request started the server\n // This handles the race condition where multiple requests try to start simultaneously\n const retryProcess = await findExistingAgentServer(sandbox, port);\n if (retryProcess) {\n // Wait for the concurrent server to be ready\n if (retryProcess.status === 'starting') {\n try {\n await retryProcess.waitForPort(port, {\n mode: 'http',\n path: '/',\n timeout: 60_000,\n });\n } catch (e) {\n const logs = await retryProcess.getLogs();\n throw new OpenhandsStartupError(\n `agent-server failed to start. Stderr: ${logs.stderr || '(empty)'}`,\n {\n port,\n stderr: logs.stderr,\n command: retryProcess.command,\n processId: retryProcess.id,\n },\n { cause: e }\n );\n }\n }\n return retryProcess;\n }\n\n // No concurrent process found, rethrow the original error\n throw startupError;\n }\n}\n\n/**\n * Starts an agent-server inside a Sandbox container.\n *\n * This function manages the server lifecycle only. If an agent-server is already\n * running on the specified port, this function will reuse it instead of starting\n * a new one.\n *\n * @param sandbox - The Sandbox instance to run agent-server in\n * @param options - Configuration options\n * @returns Promise resolving to server handle { port, url, previewUrl?, close() }\n *\n * @example\n * ```typescript\n * import { getSandbox } from '@cloudflare/sandbox'\n * import { createOpenhandsServer } from 'openhands-sdk/openhands'\n *\n * const sandbox = getSandbox(env.Sandbox, 'my-sandbox')\n * const server = await createOpenhandsServer(sandbox, {\n * port: 8001,\n * exposePort: true,\n * hostname: 'yourdomain.com'\n * })\n *\n * // Proxy requests to the server\n * return sandbox.containerFetch(request, server.port)\n *\n * // When done\n * await server.close()\n * ```\n */\nexport async function createOpenhandsServer(\n sandbox: Sandbox<unknown>,\n options: OpenhandsOptions = {}\n): Promise<OpenhandsServer> {\n const port = options.port ?? DEFAULT_PORT;\n const process = await ensureAgentServer(sandbox, port, options);\n\n let previewUrl: string | undefined;\n\n // Optionally expose port for preview URL\n if (options.exposePort) {\n if (!options.hostname) {\n throw new Error(\n 'hostname is required when exposePort is true. Provide hostname in options or extract from request URL.'\n );\n }\n\n try {\n const exposed = await sandbox.exposePort(port, {\n hostname: options.hostname,\n });\n previewUrl = typeof exposed === 'string' ? exposed : exposed.url;\n } catch (error) {\n // Log but don't fail - server is still running\n console.warn('Failed to expose port:', error);\n }\n }\n\n return {\n port,\n url: `http://localhost:${port}`,\n previewUrl,\n processId: process.id,\n async close() {\n await process.kill('SIGTERM');\n },\n };\n}\n\n/**\n * Proxy a request to the agent-server.\n *\n * This function handles proxying only - you must start the server separately\n * using `createOpenhandsServer()`.\n *\n * @param request - The incoming HTTP request\n * @param sandbox - The Sandbox instance running agent-server\n * @param server - The agent-server handle from createOpenhandsServer()\n * @returns Response from agent-server\n *\n * @example\n * ```typescript\n * import { getSandbox } from '@cloudflare/sandbox'\n * import { createOpenhandsServer, proxyToOpenhands } from 'openhands-sdk/openhands'\n *\n * export default {\n * async fetch(request: Request, env: Env) {\n * const sandbox = getSandbox(env.Sandbox, 'my-sandbox')\n * const server = await createOpenhandsServer(sandbox, {\n * port: 8001,\n * exposePort: true,\n * hostname: 'yourdomain.com'\n * })\n * return proxyToOpenhands(request, sandbox, server)\n * }\n * }\n * ```\n */\nexport function proxyToOpenhands(\n request: Request,\n sandbox: Sandbox<unknown>,\n server: OpenhandsServer\n): Response | Promise<Response> {\n return sandbox.containerFetch(request, server.port);\n}\n\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "4C4FB2DEC1D2C02F64756E2164756E21",
11
+ "names": []
12
+ }
@@ -0,0 +1,64 @@
1
+ import type { Sandbox } from '@cloudflare/sandbox';
2
+ import type { OpenhandsOptions, OpenhandsServer } from './types';
3
+ /**
4
+ * Starts an agent-server inside a Sandbox container.
5
+ *
6
+ * This function manages the server lifecycle only. If an agent-server is already
7
+ * running on the specified port, this function will reuse it instead of starting
8
+ * a new one.
9
+ *
10
+ * @param sandbox - The Sandbox instance to run agent-server in
11
+ * @param options - Configuration options
12
+ * @returns Promise resolving to server handle { port, url, previewUrl?, close() }
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { getSandbox } from '@cloudflare/sandbox'
17
+ * import { createOpenhandsServer } from 'openhands-sdk/openhands'
18
+ *
19
+ * const sandbox = getSandbox(env.Sandbox, 'my-sandbox')
20
+ * const server = await createOpenhandsServer(sandbox, {
21
+ * port: 8001,
22
+ * exposePort: true,
23
+ * hostname: 'yourdomain.com'
24
+ * })
25
+ *
26
+ * // Proxy requests to the server
27
+ * return sandbox.containerFetch(request, server.port)
28
+ *
29
+ * // When done
30
+ * await server.close()
31
+ * ```
32
+ */
33
+ export declare function createOpenhandsServer(sandbox: Sandbox<unknown>, options?: OpenhandsOptions): Promise<OpenhandsServer>;
34
+ /**
35
+ * Proxy a request to the agent-server.
36
+ *
37
+ * This function handles proxying only - you must start the server separately
38
+ * using `createOpenhandsServer()`.
39
+ *
40
+ * @param request - The incoming HTTP request
41
+ * @param sandbox - The Sandbox instance running agent-server
42
+ * @param server - The agent-server handle from createOpenhandsServer()
43
+ * @returns Response from agent-server
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { getSandbox } from '@cloudflare/sandbox'
48
+ * import { createOpenhandsServer, proxyToOpenhands } from 'openhands-sdk/openhands'
49
+ *
50
+ * export default {
51
+ * async fetch(request: Request, env: Env) {
52
+ * const sandbox = getSandbox(env.Sandbox, 'my-sandbox')
53
+ * const server = await createOpenhandsServer(sandbox, {
54
+ * port: 8001,
55
+ * exposePort: true,
56
+ * hostname: 'yourdomain.com'
57
+ * })
58
+ * return proxyToOpenhands(request, sandbox, server)
59
+ * }
60
+ * }
61
+ * ```
62
+ */
63
+ export declare function proxyToOpenhands(request: Request, sandbox: Sandbox<unknown>, server: OpenhandsServer): Response | Promise<Response>;
64
+ //# sourceMappingURL=openhands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openhands.d.ts","sourceRoot":"","sources":["../../src/openhands/openhands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAW,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAEhB,MAAM,SAAS,CAAC;AA2JjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EACzB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAkC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EACzB,MAAM,EAAE,eAAe,GACtB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAE9B"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Configuration options for starting OpenHands agent-server
3
+ */
4
+ export interface OpenhandsOptions {
5
+ /** Port for agent-server (default: 8001) */
6
+ port?: number;
7
+ /** Working directory for agent-server (default: /container-server/software-agent-sdk) */
8
+ directory?: string;
9
+ /** Hostname for preview URL exposure (required if exposePort is true) */
10
+ hostname?: string;
11
+ /** Enable preview URL exposure (default: false) */
12
+ exposePort?: boolean;
13
+ /** Environment variables for agent-server */
14
+ env?: Record<string, string>;
15
+ /** Sandbox name/session ID (default: 'my-sandbox') */
16
+ sandboxName?: string;
17
+ }
18
+ /**
19
+ * Server lifecycle management
20
+ */
21
+ export interface OpenhandsServer {
22
+ /** Port the server is running on */
23
+ port: number;
24
+ /** Base URL for server (http://localhost:{port}) */
25
+ url: string;
26
+ /** Preview URL if port was exposed (optional) */
27
+ previewUrl?: string;
28
+ /** Process ID */
29
+ processId: string;
30
+ /** Close the server gracefully */
31
+ close(): Promise<void>;
32
+ }
33
+ /**
34
+ * Context information for startup errors
35
+ */
36
+ export interface OpenhandsStartupContext {
37
+ port: number;
38
+ stderr?: string;
39
+ command?: string;
40
+ processId?: string;
41
+ }
42
+ /**
43
+ * Error thrown when agent-server fails to start
44
+ */
45
+ export declare class OpenhandsStartupError extends Error {
46
+ readonly code: "OPENHANDS_STARTUP_FAILED";
47
+ readonly context: OpenhandsStartupContext;
48
+ constructor(message: string, context: OpenhandsStartupContext, options?: ErrorOptions);
49
+ }
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/openhands/types.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,IAAI,EAAG,0BAA0B,CAAU;IACpD,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;gBAGxC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,uBAAuB,EAChC,OAAO,CAAC,EAAE,YAAY;CAMzB"}
@@ -0,0 +1,51 @@
1
+ import { type OpenhandsOptions } from '../openhands';
2
+ export type OpenhandsEnv = {
3
+ Sandbox: {
4
+ get(id: {
5
+ toString(): string;
6
+ }): {
7
+ fetch(input: string | Request, init?: RequestInit): Promise<Response>;
8
+ };
9
+ };
10
+ };
11
+ /**
12
+ * Options for OpenHands route handler
13
+ */
14
+ export interface OpenhandsHandlerOptions extends OpenhandsOptions {
15
+ /** Base path for OpenHands routes (default: '') */
16
+ basePath?: string;
17
+ /** Custom sandbox name resolver */
18
+ getSandboxName?: (request: Request) => string | null;
19
+ }
20
+ /**
21
+ * Creates a middleware function that handles OpenHands routes.
22
+ * Returns null if the request doesn't match OpenHands routes (allows chaining).
23
+ *
24
+ * @param options - Configuration options
25
+ * @returns Middleware function
26
+ */
27
+ export declare function createOpenhandsHandler<Env extends OpenhandsEnv = OpenhandsEnv>(options?: OpenhandsHandlerOptions): (request: Request, env: Env) => Promise<Response | null>;
28
+ /**
29
+ * Attaches OpenHands routes to an existing fetch handler.
30
+ * This wraps your handler with OpenHands route handling and preview URL proxying.
31
+ *
32
+ * @param fetchHandler - Your existing fetch handler
33
+ * @param options - Configuration options
34
+ * @returns Wrapped fetch handler
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { attachOpenhandsRoutes } from 'openhands-sdk/routes';
39
+ *
40
+ * export default attachOpenhandsRoutes(async (request, env) => {
41
+ * // Your custom routes here
42
+ * return new Response('Not found', { status: 404 });
43
+ * }, {
44
+ * port: 8001,
45
+ * exposePort: true,
46
+ * hostname: 'yourdomain.com'
47
+ * });
48
+ * ```
49
+ */
50
+ export declare function attachOpenhandsRoutes<Env extends OpenhandsEnv = OpenhandsEnv>(fetchHandler: (request: Request, env: Env) => Response | Promise<Response>, options?: OpenhandsHandlerOptions): (request: Request, env: Env) => Promise<Response>;
51
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/routes/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AAOtB,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE;QACP,GAAG,CAAC,EAAE,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,GAAG;YAC/B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;SACvE,CAAC;KACH,CAAC;CACH,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,gBAAgB;IAC/D,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;CACtD;AA+CD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,SAAS,YAAY,GAAG,YAAY,EAC5E,OAAO,GAAE,uBAA4B,IASnC,SAAS,OAAO,EAChB,KAAK,GAAG,KACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAiH5B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,SAAS,YAAY,GAAG,YAAY,EAC3E,YAAY,EAAE,CACZ,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,KACL,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,EACjC,OAAO,GAAE,uBAA4B,GACpC,CACD,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,KACL,OAAO,CAAC,QAAQ,CAAC,CAgBrB"}