@computesdk/hopx 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.
- package/LICENSE +21 -0
- package/dist/index.d.mts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +359 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +334 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 computesdk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
import { Sandbox } from '@hopx-ai/sdk';
|
|
3
|
+
export { Sandbox as HopxSandbox } from '@hopx-ai/sdk';
|
|
4
|
+
import { Runtime } from 'computesdk';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HopX-specific configuration options
|
|
8
|
+
*/
|
|
9
|
+
interface HopxConfig {
|
|
10
|
+
/** HopX API key - if not provided, will fallback to HOPX_API_KEY environment variable */
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
/** Default runtime environment */
|
|
13
|
+
runtime?: Runtime;
|
|
14
|
+
/** Execution timeout in milliseconds */
|
|
15
|
+
timeout?: number;
|
|
16
|
+
/** Template name for sandbox creation (e.g., 'code-interpreter') */
|
|
17
|
+
template?: string;
|
|
18
|
+
/** Base API URL for custom/staging environments */
|
|
19
|
+
baseURL?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a HopX provider instance using the factory pattern
|
|
23
|
+
*
|
|
24
|
+
* HopX provides isolated cloud sandboxes with:
|
|
25
|
+
* - Full root access to the VM
|
|
26
|
+
* - Pre-installed language runtimes (Python, Node.js, etc.)
|
|
27
|
+
* - Persistent filesystem during session
|
|
28
|
+
* - Automatic cleanup after timeout
|
|
29
|
+
*/
|
|
30
|
+
declare const hopx: (config: HopxConfig) => _computesdk_provider.Provider<Sandbox, any, any>;
|
|
31
|
+
|
|
32
|
+
export { type HopxConfig, hopx };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as _computesdk_provider from '@computesdk/provider';
|
|
2
|
+
import { Sandbox } from '@hopx-ai/sdk';
|
|
3
|
+
export { Sandbox as HopxSandbox } from '@hopx-ai/sdk';
|
|
4
|
+
import { Runtime } from 'computesdk';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HopX-specific configuration options
|
|
8
|
+
*/
|
|
9
|
+
interface HopxConfig {
|
|
10
|
+
/** HopX API key - if not provided, will fallback to HOPX_API_KEY environment variable */
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
/** Default runtime environment */
|
|
13
|
+
runtime?: Runtime;
|
|
14
|
+
/** Execution timeout in milliseconds */
|
|
15
|
+
timeout?: number;
|
|
16
|
+
/** Template name for sandbox creation (e.g., 'code-interpreter') */
|
|
17
|
+
template?: string;
|
|
18
|
+
/** Base API URL for custom/staging environments */
|
|
19
|
+
baseURL?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a HopX provider instance using the factory pattern
|
|
23
|
+
*
|
|
24
|
+
* HopX provides isolated cloud sandboxes with:
|
|
25
|
+
* - Full root access to the VM
|
|
26
|
+
* - Pre-installed language runtimes (Python, Node.js, etc.)
|
|
27
|
+
* - Persistent filesystem during session
|
|
28
|
+
* - Automatic cleanup after timeout
|
|
29
|
+
*/
|
|
30
|
+
declare const hopx: (config: HopxConfig) => _computesdk_provider.Provider<Sandbox, any, any>;
|
|
31
|
+
|
|
32
|
+
export { type HopxConfig, hopx };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
hopx: () => hopx
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
var import_sdk = require("@hopx-ai/sdk");
|
|
27
|
+
var import_provider = require("@computesdk/provider");
|
|
28
|
+
var hopx = (0, import_provider.defineProvider)({
|
|
29
|
+
name: "hopx",
|
|
30
|
+
methods: {
|
|
31
|
+
sandbox: {
|
|
32
|
+
/**
|
|
33
|
+
* Create a new HopX sandbox
|
|
34
|
+
*
|
|
35
|
+
* Uses Sandbox.create() from @hopx-ai/sdk to provision a new sandbox.
|
|
36
|
+
* Default template is 'code-interpreter' if not specified.
|
|
37
|
+
*/
|
|
38
|
+
create: async (config, options) => {
|
|
39
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
40
|
+
if (!apiKey) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Missing HopX API key. Provide 'apiKey' in config or set HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const timeoutSeconds = config.timeout ? Math.ceil(config.timeout / 1e3) : void 0;
|
|
46
|
+
try {
|
|
47
|
+
let sandbox;
|
|
48
|
+
let sandboxId;
|
|
49
|
+
if (options?.sandboxId) {
|
|
50
|
+
sandbox = await import_sdk.Sandbox.connect(options.sandboxId, apiKey, config.baseURL);
|
|
51
|
+
sandboxId = options.sandboxId;
|
|
52
|
+
} else {
|
|
53
|
+
const createOptions = {
|
|
54
|
+
apiKey,
|
|
55
|
+
baseURL: config.baseURL,
|
|
56
|
+
timeoutSeconds
|
|
57
|
+
};
|
|
58
|
+
if (options?.templateId) {
|
|
59
|
+
createOptions.templateId = options.templateId;
|
|
60
|
+
} else {
|
|
61
|
+
createOptions.template = config.template || "code-interpreter";
|
|
62
|
+
}
|
|
63
|
+
if (options?.envs) {
|
|
64
|
+
createOptions.envVars = options.envs;
|
|
65
|
+
}
|
|
66
|
+
sandbox = await import_sdk.Sandbox.create(createOptions);
|
|
67
|
+
sandboxId = sandbox.sandboxId;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
sandbox,
|
|
71
|
+
sandboxId
|
|
72
|
+
};
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error instanceof Error) {
|
|
75
|
+
if (error.message.includes("unauthorized") || error.message.includes("API key") || error.message.includes("401")) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`HopX authentication failed. Please check your HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (error.message.includes("quota") || error.message.includes("limit")) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`HopX quota exceeded. Please check your usage at https://hopx.ai/dashboard`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Failed to create HopX sandbox: ${error instanceof Error ? error.message : String(error)}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* Connect to an existing HopX sandbox by ID
|
|
93
|
+
*
|
|
94
|
+
* Uses Sandbox.connect() to reconnect to a running sandbox.
|
|
95
|
+
* Returns null if sandbox doesn't exist or can't be accessed.
|
|
96
|
+
*/
|
|
97
|
+
getById: async (config, sandboxId) => {
|
|
98
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
99
|
+
if (!apiKey) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const sandbox = await import_sdk.Sandbox.connect(sandboxId, apiKey, config.baseURL);
|
|
104
|
+
return {
|
|
105
|
+
sandbox,
|
|
106
|
+
sandboxId
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
/**
|
|
113
|
+
* List all active HopX sandboxes
|
|
114
|
+
*
|
|
115
|
+
* Uses Sandbox.list() to get all sandboxes for the account.
|
|
116
|
+
*/
|
|
117
|
+
list: async (config) => {
|
|
118
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
119
|
+
if (!apiKey) {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const sandboxes = await import_sdk.Sandbox.list({
|
|
124
|
+
apiKey,
|
|
125
|
+
baseURL: config.baseURL
|
|
126
|
+
});
|
|
127
|
+
return sandboxes.map((sandbox) => ({
|
|
128
|
+
sandbox,
|
|
129
|
+
sandboxId: sandbox.sandboxId
|
|
130
|
+
}));
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
/**
|
|
136
|
+
* Destroy a HopX sandbox
|
|
137
|
+
*
|
|
138
|
+
* Uses sandbox.kill() to terminate and clean up the sandbox.
|
|
139
|
+
*/
|
|
140
|
+
destroy: async (config, sandboxId) => {
|
|
141
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
142
|
+
if (!apiKey) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const sandbox = await import_sdk.Sandbox.connect(sandboxId, apiKey, config.baseURL);
|
|
147
|
+
await sandbox.kill();
|
|
148
|
+
} catch (error) {
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
/**
|
|
152
|
+
* Execute code in the sandbox
|
|
153
|
+
*
|
|
154
|
+
* Uses sandbox.runCode() with auto-detected runtime.
|
|
155
|
+
* Maps ComputeSDK runtime ('node'/'python') to HopX language ('javascript'/'python').
|
|
156
|
+
*/
|
|
157
|
+
runCode: async (sandbox, code, runtime) => {
|
|
158
|
+
try {
|
|
159
|
+
const effectiveRuntime = runtime || // Strong Python indicators
|
|
160
|
+
(code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes("__") || code.includes('f"') || code.includes("f'") || code.includes("raise ") ? "python" : "node");
|
|
161
|
+
const language = effectiveRuntime === "node" ? "javascript" : "python";
|
|
162
|
+
const result = await sandbox.runCode(code, { language });
|
|
163
|
+
const output = result.stderr ? `${result.stdout}${result.stdout && result.stderr ? "\n" : ""}${result.stderr}` : result.stdout;
|
|
164
|
+
const combinedOutput = `${result.stdout || ""} ${result.stderr || ""}`;
|
|
165
|
+
const hasSyntaxError = (
|
|
166
|
+
// Python syntax errors
|
|
167
|
+
combinedOutput.includes("SyntaxError") || combinedOutput.includes("invalid syntax") || combinedOutput.includes("IndentationError") || combinedOutput.includes("TabError") || combinedOutput.includes("NameError") && combinedOutput.includes("is not defined") || // JavaScript/Node.js syntax errors
|
|
168
|
+
combinedOutput.includes("Unexpected token") || combinedOutput.includes("Unexpected identifier") || // Bun parser errors (JavaScript in HopX runs via Bun)
|
|
169
|
+
combinedOutput.includes("Unexpected end of file") || combinedOutput.includes("Expected") && combinedOutput.includes("but found")
|
|
170
|
+
);
|
|
171
|
+
const isRuntimeError = combinedOutput.includes("throw ") || combinedOutput.includes("raise ") || combinedOutput.includes("Traceback (most recent call last)") || combinedOutput.includes("Error:") && !combinedOutput.includes("SyntaxError") || combinedOutput.includes("Exception:") && !combinedOutput.includes("SyntaxError");
|
|
172
|
+
if (hasSyntaxError && !isRuntimeError) {
|
|
173
|
+
throw new Error(`Syntax error: ${(result.stderr || result.stdout || "").trim()}`);
|
|
174
|
+
}
|
|
175
|
+
if (result.exit_code !== 0 && !result.stdout && !result.stderr) {
|
|
176
|
+
throw new Error(`Code execution failed with exit code ${result.exit_code}`);
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
output,
|
|
180
|
+
exitCode: result.exit_code,
|
|
181
|
+
language: effectiveRuntime
|
|
182
|
+
};
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (error instanceof Error && error.message.includes("Syntax error")) {
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
throw new Error(
|
|
188
|
+
`HopX execution failed: ${error instanceof Error ? error.message : String(error)}`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
/**
|
|
193
|
+
* Execute a shell command in the sandbox
|
|
194
|
+
*
|
|
195
|
+
* Uses sandbox.commands.run() to execute shell commands.
|
|
196
|
+
* Arguments are properly quoted to handle special characters.
|
|
197
|
+
*/
|
|
198
|
+
runCommand: async (sandbox, command, options) => {
|
|
199
|
+
const startTime = Date.now();
|
|
200
|
+
try {
|
|
201
|
+
let fullCommand = command;
|
|
202
|
+
if (options?.env && Object.keys(options.env).length > 0) {
|
|
203
|
+
const envPrefix = Object.entries(options.env).map(([k, v]) => `${k}="${v.replace(/"/g, '\\"')}"`).join(" ");
|
|
204
|
+
fullCommand = `${envPrefix} ${fullCommand}`;
|
|
205
|
+
}
|
|
206
|
+
if (options?.cwd) {
|
|
207
|
+
fullCommand = `cd "${options.cwd.replace(/"/g, '\\"')}" && ${fullCommand}`;
|
|
208
|
+
}
|
|
209
|
+
if (options?.background) {
|
|
210
|
+
fullCommand = `nohup ${fullCommand} > /dev/null 2>&1 &`;
|
|
211
|
+
}
|
|
212
|
+
const result = await sandbox.commands.run(fullCommand);
|
|
213
|
+
return {
|
|
214
|
+
stdout: result.stdout || "",
|
|
215
|
+
stderr: result.stderr || "",
|
|
216
|
+
exitCode: result.exit_code || 0,
|
|
217
|
+
// Note: HopX uses snake_case
|
|
218
|
+
durationMs: Date.now() - startTime
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
const result = error?.result;
|
|
222
|
+
if (result) {
|
|
223
|
+
return {
|
|
224
|
+
stdout: result.stdout || "",
|
|
225
|
+
stderr: result.stderr || "",
|
|
226
|
+
exitCode: result.exit_code || 1,
|
|
227
|
+
durationMs: Date.now() - startTime
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
stdout: "",
|
|
232
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
233
|
+
exitCode: 127,
|
|
234
|
+
durationMs: Date.now() - startTime
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
/**
|
|
239
|
+
* Get sandbox information
|
|
240
|
+
*
|
|
241
|
+
* Uses sandbox.getInfo() to retrieve sandbox metadata.
|
|
242
|
+
*/
|
|
243
|
+
getInfo: async (sandbox) => {
|
|
244
|
+
try {
|
|
245
|
+
const info = await sandbox.getInfo();
|
|
246
|
+
return {
|
|
247
|
+
id: sandbox.sandboxId,
|
|
248
|
+
provider: "hopx",
|
|
249
|
+
runtime: "python",
|
|
250
|
+
// HopX default runtime
|
|
251
|
+
status: info.status || "running",
|
|
252
|
+
createdAt: info.createdAt ? new Date(info.createdAt) : /* @__PURE__ */ new Date(),
|
|
253
|
+
timeout: info.timeoutSeconds ? info.timeoutSeconds * 1e3 : 3e5,
|
|
254
|
+
metadata: {
|
|
255
|
+
templateName: info.templateName,
|
|
256
|
+
templateId: info.templateId,
|
|
257
|
+
region: info.region,
|
|
258
|
+
publicHost: info.publicHost
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return {
|
|
263
|
+
id: sandbox.sandboxId,
|
|
264
|
+
provider: "hopx",
|
|
265
|
+
runtime: "python",
|
|
266
|
+
status: "running",
|
|
267
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
268
|
+
timeout: 3e5,
|
|
269
|
+
metadata: {}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
/**
|
|
274
|
+
* Get preview URL for a specific port
|
|
275
|
+
*
|
|
276
|
+
* Uses sandbox.getPreviewUrl() to get the public URL for accessing
|
|
277
|
+
* services running on a specific port in the sandbox.
|
|
278
|
+
*/
|
|
279
|
+
getUrl: async (sandbox, options) => {
|
|
280
|
+
try {
|
|
281
|
+
const url = await sandbox.getPreviewUrl(options.port);
|
|
282
|
+
return url;
|
|
283
|
+
} catch (error) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
`Failed to get HopX URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
/**
|
|
290
|
+
* Native filesystem operations using HopX's files API
|
|
291
|
+
*
|
|
292
|
+
* HopX has full native filesystem support via sandbox.files.*
|
|
293
|
+
* This is Option A from the documentation (native API, not shell-based).
|
|
294
|
+
*/
|
|
295
|
+
filesystem: {
|
|
296
|
+
/**
|
|
297
|
+
* Read file contents from sandbox
|
|
298
|
+
*/
|
|
299
|
+
readFile: async (sandbox, path) => {
|
|
300
|
+
return await sandbox.files.read(path);
|
|
301
|
+
},
|
|
302
|
+
/**
|
|
303
|
+
* Write content to a file in the sandbox
|
|
304
|
+
*/
|
|
305
|
+
writeFile: async (sandbox, path, content) => {
|
|
306
|
+
await sandbox.files.write(path, content);
|
|
307
|
+
},
|
|
308
|
+
/**
|
|
309
|
+
* Create a directory in the sandbox
|
|
310
|
+
*/
|
|
311
|
+
mkdir: async (sandbox, path) => {
|
|
312
|
+
await sandbox.files.mkdir(path);
|
|
313
|
+
},
|
|
314
|
+
/**
|
|
315
|
+
* List directory contents
|
|
316
|
+
*
|
|
317
|
+
* Maps HopX's EnhancedFileInfo to ComputeSDK's FileEntry format.
|
|
318
|
+
*/
|
|
319
|
+
readdir: async (sandbox, path) => {
|
|
320
|
+
const entries = await sandbox.files.list(path);
|
|
321
|
+
return entries.map((entry) => ({
|
|
322
|
+
name: entry.name,
|
|
323
|
+
type: entry.isDir || entry.isDirectory ? "directory" : "file",
|
|
324
|
+
path: entry.path || `${path}/${entry.name}`.replace(/\/+/g, "/"),
|
|
325
|
+
isDirectory: Boolean(entry.isDir || entry.isDirectory || entry.type === "directory"),
|
|
326
|
+
size: entry.size || 0,
|
|
327
|
+
lastModified: entry.modTime ? new Date(entry.modTime) : /* @__PURE__ */ new Date()
|
|
328
|
+
}));
|
|
329
|
+
},
|
|
330
|
+
/**
|
|
331
|
+
* Check if a file or directory exists
|
|
332
|
+
*/
|
|
333
|
+
exists: async (sandbox, path) => {
|
|
334
|
+
return await sandbox.files.exists(path);
|
|
335
|
+
},
|
|
336
|
+
/**
|
|
337
|
+
* Remove a file or directory
|
|
338
|
+
*/
|
|
339
|
+
remove: async (sandbox, path) => {
|
|
340
|
+
await sandbox.files.remove(path);
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
/**
|
|
344
|
+
* Get the native HopX Sandbox instance for advanced usage
|
|
345
|
+
*
|
|
346
|
+
* This allows users to access HopX-specific features not exposed
|
|
347
|
+
* through the ComputeSDK interface.
|
|
348
|
+
*/
|
|
349
|
+
getInstance: (sandbox) => {
|
|
350
|
+
return sandbox;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
356
|
+
0 && (module.exports = {
|
|
357
|
+
hopx
|
|
358
|
+
});
|
|
359
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * HopX Provider - Factory-based Implementation\n * \n * Full-featured provider with native filesystem support using the factory pattern.\n * HopX provides isolated cloud sandboxes (lightweight VMs) with full Linux environments.\n * \n * Features:\n * - Native filesystem API via sandbox.files.*\n * - Code execution in Python, JavaScript, Bash\n * - Shell command execution\n * - Preview URLs for accessing sandbox services\n */\n\nimport { Sandbox as HopxSandbox } from '@hopx-ai/sdk';\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CodeResult,\n CommandResult,\n SandboxInfo,\n Runtime,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions\n} from 'computesdk';\n\n/**\n * HopX-specific configuration options\n */\nexport interface HopxConfig {\n /** HopX API key - if not provided, will fallback to HOPX_API_KEY environment variable */\n apiKey?: string;\n /** Default runtime environment */\n runtime?: Runtime;\n /** Execution timeout in milliseconds */\n timeout?: number;\n /** Template name for sandbox creation (e.g., 'code-interpreter') */\n template?: string;\n /** Base API URL for custom/staging environments */\n baseURL?: string;\n}\n\n/**\n * Create a HopX provider instance using the factory pattern\n * \n * HopX provides isolated cloud sandboxes with:\n * - Full root access to the VM\n * - Pre-installed language runtimes (Python, Node.js, etc.)\n * - Persistent filesystem during session\n * - Automatic cleanup after timeout\n */\nexport const hopx = defineProvider<HopxSandbox, HopxConfig>({\n name: 'hopx',\n methods: {\n sandbox: {\n /**\n * Create a new HopX sandbox\n * \n * Uses Sandbox.create() from @hopx-ai/sdk to provision a new sandbox.\n * Default template is 'code-interpreter' if not specified.\n */\n create: async (config: HopxConfig, options?: CreateSandboxOptions) => {\n // Validate API key - fail fast with helpful error message\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n throw new Error(\n `Missing HopX API key. Provide 'apiKey' in config or set HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`\n );\n }\n\n // Convert timeout from milliseconds to seconds (HopX uses seconds)\n const timeoutSeconds = config.timeout ? Math.ceil(config.timeout / 1000) : undefined;\n\n try {\n let sandbox: HopxSandbox;\n let sandboxId: string;\n\n if (options?.sandboxId) {\n // Reconnect to existing HopX sandbox using Sandbox.connect()\n sandbox = await HopxSandbox.connect(options.sandboxId, apiKey, config.baseURL);\n sandboxId = options.sandboxId;\n } else {\n // Create new HopX sandbox using Sandbox.create()\n // Use templateId if provided, otherwise use template name or default to 'code-interpreter'\n const createOptions: any = {\n apiKey,\n baseURL: config.baseURL,\n timeoutSeconds,\n };\n\n // Handle template specification (templateId takes precedence)\n if (options?.templateId) {\n createOptions.templateId = options.templateId;\n } else {\n createOptions.template = config.template || 'code-interpreter';\n }\n\n // Pass environment variables if provided\n if (options?.envs) {\n createOptions.envVars = options.envs;\n }\n\n sandbox = await HopxSandbox.create(createOptions);\n sandboxId = sandbox.sandboxId;\n }\n\n return {\n sandbox,\n sandboxId\n };\n } catch (error) {\n // Provide helpful error messages for common issues\n if (error instanceof Error) {\n if (error.message.includes('unauthorized') || error.message.includes('API key') || error.message.includes('401')) {\n throw new Error(\n `HopX authentication failed. Please check your HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`\n );\n }\n if (error.message.includes('quota') || error.message.includes('limit')) {\n throw new Error(\n `HopX quota exceeded. Please check your usage at https://hopx.ai/dashboard`\n );\n }\n }\n throw new Error(\n `Failed to create HopX sandbox: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n /**\n * Connect to an existing HopX sandbox by ID\n * \n * Uses Sandbox.connect() to reconnect to a running sandbox.\n * Returns null if sandbox doesn't exist or can't be accessed.\n */\n getById: async (config: HopxConfig, sandboxId: string) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n return null;\n }\n\n try {\n // Connect to existing sandbox using Sandbox.connect()\n const sandbox = await HopxSandbox.connect(sandboxId, apiKey, config.baseURL);\n\n return {\n sandbox,\n sandboxId\n };\n } catch (error) {\n // Sandbox doesn't exist or can't be accessed\n return null;\n }\n },\n\n /**\n * List all active HopX sandboxes\n * \n * Uses Sandbox.list() to get all sandboxes for the account.\n */\n list: async (config: HopxConfig) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n return [];\n }\n\n try {\n // List sandboxes using Sandbox.list()\n const sandboxes = await HopxSandbox.list({\n apiKey,\n baseURL: config.baseURL,\n });\n\n return sandboxes.map((sandbox: HopxSandbox) => ({\n sandbox,\n sandboxId: sandbox.sandboxId\n }));\n } catch (error) {\n // Return empty array if listing fails\n return [];\n }\n },\n\n /**\n * Destroy a HopX sandbox\n * \n * Uses sandbox.kill() to terminate and clean up the sandbox.\n */\n destroy: async (config: HopxConfig, sandboxId: string) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n return;\n }\n\n try {\n // Connect to sandbox and kill it\n const sandbox = await HopxSandbox.connect(sandboxId, apiKey, config.baseURL);\n await sandbox.kill();\n } catch (error) {\n // Sandbox might already be destroyed or doesn't exist\n // This is acceptable for destroy operations\n }\n },\n\n /**\n * Execute code in the sandbox\n * \n * Uses sandbox.runCode() with auto-detected runtime.\n * Maps ComputeSDK runtime ('node'/'python') to HopX language ('javascript'/'python').\n */\n runCode: async (sandbox: HopxSandbox, code: string, runtime?: Runtime): Promise<CodeResult> => {\n try {\n // Auto-detect runtime if not specified using Python indicators\n const effectiveRuntime = runtime || (\n // Strong Python indicators\n code.includes('print(') ||\n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('__') ||\n code.includes('f\"') ||\n code.includes(\"f'\") ||\n code.includes('raise ')\n ? 'python'\n // Default to Node.js for all other cases (including ambiguous)\n : 'node'\n );\n\n // Map ComputeSDK runtime to HopX language\n // HopX uses 'javascript' instead of 'node'\n const language = effectiveRuntime === 'node' ? 'javascript' : 'python';\n\n // Execute code using sandbox.runCode()\n const result = await sandbox.runCode(code, { language });\n\n // Combine stdout and stderr for output (following E2B pattern)\n const output = result.stderr\n ? `${result.stdout}${result.stdout && result.stderr ? '\\n' : ''}${result.stderr}`\n : result.stdout;\n\n // Check for syntax/parse errors in stderr or output and throw them\n // This ensures invalid code triggers an exception as expected by the test suite\n // \n // IMPORTANT: We distinguish between:\n // - Syntax errors (invalid code that couldn't parse) → should THROW\n // - Runtime errors (valid code that threw during execution) → should RETURN with non-zero exit\n const combinedOutput = `${result.stdout || ''} ${result.stderr || ''}`;\n \n // Indicators of syntax/parse errors (code couldn't be executed)\n // Covers Python, Node.js, and Bun error formats\n const hasSyntaxError = \n // Python syntax errors\n combinedOutput.includes('SyntaxError') ||\n combinedOutput.includes('invalid syntax') ||\n combinedOutput.includes('IndentationError') ||\n combinedOutput.includes('TabError') ||\n (combinedOutput.includes('NameError') && combinedOutput.includes('is not defined')) ||\n // JavaScript/Node.js syntax errors\n combinedOutput.includes('Unexpected token') ||\n combinedOutput.includes('Unexpected identifier') ||\n // Bun parser errors (JavaScript in HopX runs via Bun)\n combinedOutput.includes('Unexpected end of file') ||\n (combinedOutput.includes('Expected') && combinedOutput.includes('but found'));\n \n // Indicators of runtime errors (code ran but threw - should NOT throw, return result)\n const isRuntimeError = \n combinedOutput.includes('throw ') ||\n combinedOutput.includes('raise ') ||\n combinedOutput.includes('Traceback (most recent call last)') ||\n (combinedOutput.includes('Error:') && !combinedOutput.includes('SyntaxError')) ||\n (combinedOutput.includes('Exception:') && !combinedOutput.includes('SyntaxError'));\n\n // Throw for syntax errors (unless it looks like a runtime error)\n if (hasSyntaxError && !isRuntimeError) {\n throw new Error(`Syntax error: ${(result.stderr || result.stdout || '').trim()}`);\n }\n\n // For non-zero exit codes with empty output, this indicates a parse failure\n // that couldn't produce any output - throw as a syntax error\n // Note: HopX SDK returns exit_code (snake_case) in ExecutionResult\n if (result.exit_code !== 0 && !result.stdout && !result.stderr) {\n throw new Error(`Code execution failed with exit code ${result.exit_code}`);\n }\n\n return {\n output,\n exitCode: result.exit_code,\n language: effectiveRuntime\n };\n } catch (error) {\n // Re-throw syntax errors\n if (error instanceof Error && error.message.includes('Syntax error')) {\n throw error;\n }\n throw new Error(\n `HopX execution failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n /**\n * Execute a shell command in the sandbox\n * \n * Uses sandbox.commands.run() to execute shell commands.\n * Arguments are properly quoted to handle special characters.\n */\n runCommand: async (sandbox: HopxSandbox, command: string, options?: RunCommandOptions): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n // Build command with options\n let fullCommand = command;\n \n // Handle environment variables\n if (options?.env && Object.keys(options.env).length > 0) {\n const envPrefix = Object.entries(options.env)\n .map(([k, v]) => `${k}=\"${v.replace(/\"/g, '\\\\\"')}\"`)\n .join(' ');\n fullCommand = `${envPrefix} ${fullCommand}`;\n }\n \n // Handle working directory\n if (options?.cwd) {\n fullCommand = `cd \"${options.cwd.replace(/\"/g, '\\\\\"')}\" && ${fullCommand}`;\n }\n \n // Handle background execution\n if (options?.background) {\n fullCommand = `nohup ${fullCommand} > /dev/null 2>&1 &`;\n }\n \n // Execute command using sandbox.commands.run()\n const result = await sandbox.commands.run(fullCommand);\n \n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: result.exit_code || 0, // Note: HopX uses snake_case\n durationMs: Date.now() - startTime\n };\n } catch (error) {\n // Extract result from error if available (some errors include partial results)\n const result = (error as any)?.result;\n if (result) {\n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: result.exit_code || 1,\n durationMs: Date.now() - startTime\n };\n }\n \n // Fallback for other errors (command not found, etc.)\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127,\n durationMs: Date.now() - startTime\n };\n }\n },\n\n /**\n * Get sandbox information\n * \n * Uses sandbox.getInfo() to retrieve sandbox metadata.\n */\n getInfo: async (sandbox: HopxSandbox): Promise<SandboxInfo> => {\n try {\n const info = await sandbox.getInfo();\n\n return {\n id: sandbox.sandboxId,\n provider: 'hopx',\n runtime: 'python', // HopX default runtime\n status: (info.status as 'running' | 'stopped' | 'error') || 'running',\n createdAt: info.createdAt ? new Date(info.createdAt) : new Date(),\n timeout: info.timeoutSeconds ? info.timeoutSeconds * 1000 : 300000,\n metadata: {\n templateName: info.templateName,\n templateId: info.templateId,\n region: info.region,\n publicHost: info.publicHost,\n }\n };\n } catch (error) {\n // Return basic info if getInfo fails\n return {\n id: sandbox.sandboxId,\n provider: 'hopx',\n runtime: 'python',\n status: 'running',\n createdAt: new Date(),\n timeout: 300000,\n metadata: {}\n };\n }\n },\n\n /**\n * Get preview URL for a specific port\n * \n * Uses sandbox.getPreviewUrl() to get the public URL for accessing\n * services running on a specific port in the sandbox.\n */\n getUrl: async (sandbox: HopxSandbox, options: { port: number; protocol?: string }): Promise<string> => {\n try {\n // Use HopX's built-in getPreviewUrl method\n const url = await sandbox.getPreviewUrl(options.port);\n return url;\n } catch (error) {\n throw new Error(\n `Failed to get HopX URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n /**\n * Native filesystem operations using HopX's files API\n * \n * HopX has full native filesystem support via sandbox.files.*\n * This is Option A from the documentation (native API, not shell-based).\n */\n filesystem: {\n /**\n * Read file contents from sandbox\n */\n readFile: async (sandbox: HopxSandbox, path: string): Promise<string> => {\n return await sandbox.files.read(path);\n },\n\n /**\n * Write content to a file in the sandbox\n */\n writeFile: async (sandbox: HopxSandbox, path: string, content: string): Promise<void> => {\n await sandbox.files.write(path, content);\n },\n\n /**\n * Create a directory in the sandbox\n */\n mkdir: async (sandbox: HopxSandbox, path: string): Promise<void> => {\n await sandbox.files.mkdir(path);\n },\n\n /**\n * List directory contents\n * \n * Maps HopX's EnhancedFileInfo to ComputeSDK's FileEntry format.\n */\n readdir: async (sandbox: HopxSandbox, path: string): Promise<FileEntry[]> => {\n const entries = await sandbox.files.list(path);\n\n return entries.map((entry: any) => ({\n name: entry.name,\n type: (entry.isDir || entry.isDirectory) ? 'directory' as const : 'file' as const,\n path: entry.path || `${path}/${entry.name}`.replace(/\\/+/g, '/'),\n isDirectory: Boolean(entry.isDir || entry.isDirectory || entry.type === 'directory'),\n size: entry.size || 0,\n lastModified: entry.modTime ? new Date(entry.modTime) : new Date()\n }));\n },\n\n /**\n * Check if a file or directory exists\n */\n exists: async (sandbox: HopxSandbox, path: string): Promise<boolean> => {\n return await sandbox.files.exists(path);\n },\n\n /**\n * Remove a file or directory\n */\n remove: async (sandbox: HopxSandbox, path: string): Promise<void> => {\n await sandbox.files.remove(path);\n }\n },\n\n /**\n * Get the native HopX Sandbox instance for advanced usage\n * \n * This allows users to access HopX-specific features not exposed\n * through the ComputeSDK interface.\n */\n getInstance: (sandbox: HopxSandbox): HopxSandbox => {\n return sandbox;\n },\n }\n }\n});\n\n// Export HopX sandbox type for explicit typing\nexport type { Sandbox as HopxSandbox } from '@hopx-ai/sdk';\n\n"],"mappings":"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","names":["HopxSandbox"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Sandbox as HopxSandbox } from "@hopx-ai/sdk";
|
|
3
|
+
import { defineProvider } from "@computesdk/provider";
|
|
4
|
+
var hopx = defineProvider({
|
|
5
|
+
name: "hopx",
|
|
6
|
+
methods: {
|
|
7
|
+
sandbox: {
|
|
8
|
+
/**
|
|
9
|
+
* Create a new HopX sandbox
|
|
10
|
+
*
|
|
11
|
+
* Uses Sandbox.create() from @hopx-ai/sdk to provision a new sandbox.
|
|
12
|
+
* Default template is 'code-interpreter' if not specified.
|
|
13
|
+
*/
|
|
14
|
+
create: async (config, options) => {
|
|
15
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`Missing HopX API key. Provide 'apiKey' in config or set HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const timeoutSeconds = config.timeout ? Math.ceil(config.timeout / 1e3) : void 0;
|
|
22
|
+
try {
|
|
23
|
+
let sandbox;
|
|
24
|
+
let sandboxId;
|
|
25
|
+
if (options?.sandboxId) {
|
|
26
|
+
sandbox = await HopxSandbox.connect(options.sandboxId, apiKey, config.baseURL);
|
|
27
|
+
sandboxId = options.sandboxId;
|
|
28
|
+
} else {
|
|
29
|
+
const createOptions = {
|
|
30
|
+
apiKey,
|
|
31
|
+
baseURL: config.baseURL,
|
|
32
|
+
timeoutSeconds
|
|
33
|
+
};
|
|
34
|
+
if (options?.templateId) {
|
|
35
|
+
createOptions.templateId = options.templateId;
|
|
36
|
+
} else {
|
|
37
|
+
createOptions.template = config.template || "code-interpreter";
|
|
38
|
+
}
|
|
39
|
+
if (options?.envs) {
|
|
40
|
+
createOptions.envVars = options.envs;
|
|
41
|
+
}
|
|
42
|
+
sandbox = await HopxSandbox.create(createOptions);
|
|
43
|
+
sandboxId = sandbox.sandboxId;
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
sandbox,
|
|
47
|
+
sandboxId
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof Error) {
|
|
51
|
+
if (error.message.includes("unauthorized") || error.message.includes("API key") || error.message.includes("401")) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`HopX authentication failed. Please check your HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (error.message.includes("quota") || error.message.includes("limit")) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`HopX quota exceeded. Please check your usage at https://hopx.ai/dashboard`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Failed to create HopX sandbox: ${error instanceof Error ? error.message : String(error)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* Connect to an existing HopX sandbox by ID
|
|
69
|
+
*
|
|
70
|
+
* Uses Sandbox.connect() to reconnect to a running sandbox.
|
|
71
|
+
* Returns null if sandbox doesn't exist or can't be accessed.
|
|
72
|
+
*/
|
|
73
|
+
getById: async (config, sandboxId) => {
|
|
74
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
75
|
+
if (!apiKey) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const sandbox = await HopxSandbox.connect(sandboxId, apiKey, config.baseURL);
|
|
80
|
+
return {
|
|
81
|
+
sandbox,
|
|
82
|
+
sandboxId
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
/**
|
|
89
|
+
* List all active HopX sandboxes
|
|
90
|
+
*
|
|
91
|
+
* Uses Sandbox.list() to get all sandboxes for the account.
|
|
92
|
+
*/
|
|
93
|
+
list: async (config) => {
|
|
94
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
95
|
+
if (!apiKey) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const sandboxes = await HopxSandbox.list({
|
|
100
|
+
apiKey,
|
|
101
|
+
baseURL: config.baseURL
|
|
102
|
+
});
|
|
103
|
+
return sandboxes.map((sandbox) => ({
|
|
104
|
+
sandbox,
|
|
105
|
+
sandboxId: sandbox.sandboxId
|
|
106
|
+
}));
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Destroy a HopX sandbox
|
|
113
|
+
*
|
|
114
|
+
* Uses sandbox.kill() to terminate and clean up the sandbox.
|
|
115
|
+
*/
|
|
116
|
+
destroy: async (config, sandboxId) => {
|
|
117
|
+
const apiKey = config.apiKey || typeof process !== "undefined" && process.env?.HOPX_API_KEY || "";
|
|
118
|
+
if (!apiKey) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const sandbox = await HopxSandbox.connect(sandboxId, apiKey, config.baseURL);
|
|
123
|
+
await sandbox.kill();
|
|
124
|
+
} catch (error) {
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
/**
|
|
128
|
+
* Execute code in the sandbox
|
|
129
|
+
*
|
|
130
|
+
* Uses sandbox.runCode() with auto-detected runtime.
|
|
131
|
+
* Maps ComputeSDK runtime ('node'/'python') to HopX language ('javascript'/'python').
|
|
132
|
+
*/
|
|
133
|
+
runCode: async (sandbox, code, runtime) => {
|
|
134
|
+
try {
|
|
135
|
+
const effectiveRuntime = runtime || // Strong Python indicators
|
|
136
|
+
(code.includes("print(") || code.includes("import ") || code.includes("def ") || code.includes("sys.") || code.includes("json.") || code.includes("__") || code.includes('f"') || code.includes("f'") || code.includes("raise ") ? "python" : "node");
|
|
137
|
+
const language = effectiveRuntime === "node" ? "javascript" : "python";
|
|
138
|
+
const result = await sandbox.runCode(code, { language });
|
|
139
|
+
const output = result.stderr ? `${result.stdout}${result.stdout && result.stderr ? "\n" : ""}${result.stderr}` : result.stdout;
|
|
140
|
+
const combinedOutput = `${result.stdout || ""} ${result.stderr || ""}`;
|
|
141
|
+
const hasSyntaxError = (
|
|
142
|
+
// Python syntax errors
|
|
143
|
+
combinedOutput.includes("SyntaxError") || combinedOutput.includes("invalid syntax") || combinedOutput.includes("IndentationError") || combinedOutput.includes("TabError") || combinedOutput.includes("NameError") && combinedOutput.includes("is not defined") || // JavaScript/Node.js syntax errors
|
|
144
|
+
combinedOutput.includes("Unexpected token") || combinedOutput.includes("Unexpected identifier") || // Bun parser errors (JavaScript in HopX runs via Bun)
|
|
145
|
+
combinedOutput.includes("Unexpected end of file") || combinedOutput.includes("Expected") && combinedOutput.includes("but found")
|
|
146
|
+
);
|
|
147
|
+
const isRuntimeError = combinedOutput.includes("throw ") || combinedOutput.includes("raise ") || combinedOutput.includes("Traceback (most recent call last)") || combinedOutput.includes("Error:") && !combinedOutput.includes("SyntaxError") || combinedOutput.includes("Exception:") && !combinedOutput.includes("SyntaxError");
|
|
148
|
+
if (hasSyntaxError && !isRuntimeError) {
|
|
149
|
+
throw new Error(`Syntax error: ${(result.stderr || result.stdout || "").trim()}`);
|
|
150
|
+
}
|
|
151
|
+
if (result.exit_code !== 0 && !result.stdout && !result.stderr) {
|
|
152
|
+
throw new Error(`Code execution failed with exit code ${result.exit_code}`);
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
output,
|
|
156
|
+
exitCode: result.exit_code,
|
|
157
|
+
language: effectiveRuntime
|
|
158
|
+
};
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (error instanceof Error && error.message.includes("Syntax error")) {
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
throw new Error(
|
|
164
|
+
`HopX execution failed: ${error instanceof Error ? error.message : String(error)}`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
/**
|
|
169
|
+
* Execute a shell command in the sandbox
|
|
170
|
+
*
|
|
171
|
+
* Uses sandbox.commands.run() to execute shell commands.
|
|
172
|
+
* Arguments are properly quoted to handle special characters.
|
|
173
|
+
*/
|
|
174
|
+
runCommand: async (sandbox, command, options) => {
|
|
175
|
+
const startTime = Date.now();
|
|
176
|
+
try {
|
|
177
|
+
let fullCommand = command;
|
|
178
|
+
if (options?.env && Object.keys(options.env).length > 0) {
|
|
179
|
+
const envPrefix = Object.entries(options.env).map(([k, v]) => `${k}="${v.replace(/"/g, '\\"')}"`).join(" ");
|
|
180
|
+
fullCommand = `${envPrefix} ${fullCommand}`;
|
|
181
|
+
}
|
|
182
|
+
if (options?.cwd) {
|
|
183
|
+
fullCommand = `cd "${options.cwd.replace(/"/g, '\\"')}" && ${fullCommand}`;
|
|
184
|
+
}
|
|
185
|
+
if (options?.background) {
|
|
186
|
+
fullCommand = `nohup ${fullCommand} > /dev/null 2>&1 &`;
|
|
187
|
+
}
|
|
188
|
+
const result = await sandbox.commands.run(fullCommand);
|
|
189
|
+
return {
|
|
190
|
+
stdout: result.stdout || "",
|
|
191
|
+
stderr: result.stderr || "",
|
|
192
|
+
exitCode: result.exit_code || 0,
|
|
193
|
+
// Note: HopX uses snake_case
|
|
194
|
+
durationMs: Date.now() - startTime
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
const result = error?.result;
|
|
198
|
+
if (result) {
|
|
199
|
+
return {
|
|
200
|
+
stdout: result.stdout || "",
|
|
201
|
+
stderr: result.stderr || "",
|
|
202
|
+
exitCode: result.exit_code || 1,
|
|
203
|
+
durationMs: Date.now() - startTime
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
stdout: "",
|
|
208
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
209
|
+
exitCode: 127,
|
|
210
|
+
durationMs: Date.now() - startTime
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
/**
|
|
215
|
+
* Get sandbox information
|
|
216
|
+
*
|
|
217
|
+
* Uses sandbox.getInfo() to retrieve sandbox metadata.
|
|
218
|
+
*/
|
|
219
|
+
getInfo: async (sandbox) => {
|
|
220
|
+
try {
|
|
221
|
+
const info = await sandbox.getInfo();
|
|
222
|
+
return {
|
|
223
|
+
id: sandbox.sandboxId,
|
|
224
|
+
provider: "hopx",
|
|
225
|
+
runtime: "python",
|
|
226
|
+
// HopX default runtime
|
|
227
|
+
status: info.status || "running",
|
|
228
|
+
createdAt: info.createdAt ? new Date(info.createdAt) : /* @__PURE__ */ new Date(),
|
|
229
|
+
timeout: info.timeoutSeconds ? info.timeoutSeconds * 1e3 : 3e5,
|
|
230
|
+
metadata: {
|
|
231
|
+
templateName: info.templateName,
|
|
232
|
+
templateId: info.templateId,
|
|
233
|
+
region: info.region,
|
|
234
|
+
publicHost: info.publicHost
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
} catch (error) {
|
|
238
|
+
return {
|
|
239
|
+
id: sandbox.sandboxId,
|
|
240
|
+
provider: "hopx",
|
|
241
|
+
runtime: "python",
|
|
242
|
+
status: "running",
|
|
243
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
244
|
+
timeout: 3e5,
|
|
245
|
+
metadata: {}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
/**
|
|
250
|
+
* Get preview URL for a specific port
|
|
251
|
+
*
|
|
252
|
+
* Uses sandbox.getPreviewUrl() to get the public URL for accessing
|
|
253
|
+
* services running on a specific port in the sandbox.
|
|
254
|
+
*/
|
|
255
|
+
getUrl: async (sandbox, options) => {
|
|
256
|
+
try {
|
|
257
|
+
const url = await sandbox.getPreviewUrl(options.port);
|
|
258
|
+
return url;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Failed to get HopX URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
/**
|
|
266
|
+
* Native filesystem operations using HopX's files API
|
|
267
|
+
*
|
|
268
|
+
* HopX has full native filesystem support via sandbox.files.*
|
|
269
|
+
* This is Option A from the documentation (native API, not shell-based).
|
|
270
|
+
*/
|
|
271
|
+
filesystem: {
|
|
272
|
+
/**
|
|
273
|
+
* Read file contents from sandbox
|
|
274
|
+
*/
|
|
275
|
+
readFile: async (sandbox, path) => {
|
|
276
|
+
return await sandbox.files.read(path);
|
|
277
|
+
},
|
|
278
|
+
/**
|
|
279
|
+
* Write content to a file in the sandbox
|
|
280
|
+
*/
|
|
281
|
+
writeFile: async (sandbox, path, content) => {
|
|
282
|
+
await sandbox.files.write(path, content);
|
|
283
|
+
},
|
|
284
|
+
/**
|
|
285
|
+
* Create a directory in the sandbox
|
|
286
|
+
*/
|
|
287
|
+
mkdir: async (sandbox, path) => {
|
|
288
|
+
await sandbox.files.mkdir(path);
|
|
289
|
+
},
|
|
290
|
+
/**
|
|
291
|
+
* List directory contents
|
|
292
|
+
*
|
|
293
|
+
* Maps HopX's EnhancedFileInfo to ComputeSDK's FileEntry format.
|
|
294
|
+
*/
|
|
295
|
+
readdir: async (sandbox, path) => {
|
|
296
|
+
const entries = await sandbox.files.list(path);
|
|
297
|
+
return entries.map((entry) => ({
|
|
298
|
+
name: entry.name,
|
|
299
|
+
type: entry.isDir || entry.isDirectory ? "directory" : "file",
|
|
300
|
+
path: entry.path || `${path}/${entry.name}`.replace(/\/+/g, "/"),
|
|
301
|
+
isDirectory: Boolean(entry.isDir || entry.isDirectory || entry.type === "directory"),
|
|
302
|
+
size: entry.size || 0,
|
|
303
|
+
lastModified: entry.modTime ? new Date(entry.modTime) : /* @__PURE__ */ new Date()
|
|
304
|
+
}));
|
|
305
|
+
},
|
|
306
|
+
/**
|
|
307
|
+
* Check if a file or directory exists
|
|
308
|
+
*/
|
|
309
|
+
exists: async (sandbox, path) => {
|
|
310
|
+
return await sandbox.files.exists(path);
|
|
311
|
+
},
|
|
312
|
+
/**
|
|
313
|
+
* Remove a file or directory
|
|
314
|
+
*/
|
|
315
|
+
remove: async (sandbox, path) => {
|
|
316
|
+
await sandbox.files.remove(path);
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
/**
|
|
320
|
+
* Get the native HopX Sandbox instance for advanced usage
|
|
321
|
+
*
|
|
322
|
+
* This allows users to access HopX-specific features not exposed
|
|
323
|
+
* through the ComputeSDK interface.
|
|
324
|
+
*/
|
|
325
|
+
getInstance: (sandbox) => {
|
|
326
|
+
return sandbox;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
export {
|
|
332
|
+
hopx
|
|
333
|
+
};
|
|
334
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * HopX Provider - Factory-based Implementation\n * \n * Full-featured provider with native filesystem support using the factory pattern.\n * HopX provides isolated cloud sandboxes (lightweight VMs) with full Linux environments.\n * \n * Features:\n * - Native filesystem API via sandbox.files.*\n * - Code execution in Python, JavaScript, Bash\n * - Shell command execution\n * - Preview URLs for accessing sandbox services\n */\n\nimport { Sandbox as HopxSandbox } from '@hopx-ai/sdk';\nimport { defineProvider } from '@computesdk/provider';\nimport type {\n CodeResult,\n CommandResult,\n SandboxInfo,\n Runtime,\n CreateSandboxOptions,\n FileEntry,\n RunCommandOptions\n} from 'computesdk';\n\n/**\n * HopX-specific configuration options\n */\nexport interface HopxConfig {\n /** HopX API key - if not provided, will fallback to HOPX_API_KEY environment variable */\n apiKey?: string;\n /** Default runtime environment */\n runtime?: Runtime;\n /** Execution timeout in milliseconds */\n timeout?: number;\n /** Template name for sandbox creation (e.g., 'code-interpreter') */\n template?: string;\n /** Base API URL for custom/staging environments */\n baseURL?: string;\n}\n\n/**\n * Create a HopX provider instance using the factory pattern\n * \n * HopX provides isolated cloud sandboxes with:\n * - Full root access to the VM\n * - Pre-installed language runtimes (Python, Node.js, etc.)\n * - Persistent filesystem during session\n * - Automatic cleanup after timeout\n */\nexport const hopx = defineProvider<HopxSandbox, HopxConfig>({\n name: 'hopx',\n methods: {\n sandbox: {\n /**\n * Create a new HopX sandbox\n * \n * Uses Sandbox.create() from @hopx-ai/sdk to provision a new sandbox.\n * Default template is 'code-interpreter' if not specified.\n */\n create: async (config: HopxConfig, options?: CreateSandboxOptions) => {\n // Validate API key - fail fast with helpful error message\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n throw new Error(\n `Missing HopX API key. Provide 'apiKey' in config or set HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`\n );\n }\n\n // Convert timeout from milliseconds to seconds (HopX uses seconds)\n const timeoutSeconds = config.timeout ? Math.ceil(config.timeout / 1000) : undefined;\n\n try {\n let sandbox: HopxSandbox;\n let sandboxId: string;\n\n if (options?.sandboxId) {\n // Reconnect to existing HopX sandbox using Sandbox.connect()\n sandbox = await HopxSandbox.connect(options.sandboxId, apiKey, config.baseURL);\n sandboxId = options.sandboxId;\n } else {\n // Create new HopX sandbox using Sandbox.create()\n // Use templateId if provided, otherwise use template name or default to 'code-interpreter'\n const createOptions: any = {\n apiKey,\n baseURL: config.baseURL,\n timeoutSeconds,\n };\n\n // Handle template specification (templateId takes precedence)\n if (options?.templateId) {\n createOptions.templateId = options.templateId;\n } else {\n createOptions.template = config.template || 'code-interpreter';\n }\n\n // Pass environment variables if provided\n if (options?.envs) {\n createOptions.envVars = options.envs;\n }\n\n sandbox = await HopxSandbox.create(createOptions);\n sandboxId = sandbox.sandboxId;\n }\n\n return {\n sandbox,\n sandboxId\n };\n } catch (error) {\n // Provide helpful error messages for common issues\n if (error instanceof Error) {\n if (error.message.includes('unauthorized') || error.message.includes('API key') || error.message.includes('401')) {\n throw new Error(\n `HopX authentication failed. Please check your HOPX_API_KEY environment variable. Get your API key from https://hopx.ai/dashboard`\n );\n }\n if (error.message.includes('quota') || error.message.includes('limit')) {\n throw new Error(\n `HopX quota exceeded. Please check your usage at https://hopx.ai/dashboard`\n );\n }\n }\n throw new Error(\n `Failed to create HopX sandbox: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n /**\n * Connect to an existing HopX sandbox by ID\n * \n * Uses Sandbox.connect() to reconnect to a running sandbox.\n * Returns null if sandbox doesn't exist or can't be accessed.\n */\n getById: async (config: HopxConfig, sandboxId: string) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n return null;\n }\n\n try {\n // Connect to existing sandbox using Sandbox.connect()\n const sandbox = await HopxSandbox.connect(sandboxId, apiKey, config.baseURL);\n\n return {\n sandbox,\n sandboxId\n };\n } catch (error) {\n // Sandbox doesn't exist or can't be accessed\n return null;\n }\n },\n\n /**\n * List all active HopX sandboxes\n * \n * Uses Sandbox.list() to get all sandboxes for the account.\n */\n list: async (config: HopxConfig) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n return [];\n }\n\n try {\n // List sandboxes using Sandbox.list()\n const sandboxes = await HopxSandbox.list({\n apiKey,\n baseURL: config.baseURL,\n });\n\n return sandboxes.map((sandbox: HopxSandbox) => ({\n sandbox,\n sandboxId: sandbox.sandboxId\n }));\n } catch (error) {\n // Return empty array if listing fails\n return [];\n }\n },\n\n /**\n * Destroy a HopX sandbox\n * \n * Uses sandbox.kill() to terminate and clean up the sandbox.\n */\n destroy: async (config: HopxConfig, sandboxId: string) => {\n const apiKey = config.apiKey || (typeof process !== 'undefined' && process.env?.HOPX_API_KEY) || '';\n\n if (!apiKey) {\n return;\n }\n\n try {\n // Connect to sandbox and kill it\n const sandbox = await HopxSandbox.connect(sandboxId, apiKey, config.baseURL);\n await sandbox.kill();\n } catch (error) {\n // Sandbox might already be destroyed or doesn't exist\n // This is acceptable for destroy operations\n }\n },\n\n /**\n * Execute code in the sandbox\n * \n * Uses sandbox.runCode() with auto-detected runtime.\n * Maps ComputeSDK runtime ('node'/'python') to HopX language ('javascript'/'python').\n */\n runCode: async (sandbox: HopxSandbox, code: string, runtime?: Runtime): Promise<CodeResult> => {\n try {\n // Auto-detect runtime if not specified using Python indicators\n const effectiveRuntime = runtime || (\n // Strong Python indicators\n code.includes('print(') ||\n code.includes('import ') ||\n code.includes('def ') ||\n code.includes('sys.') ||\n code.includes('json.') ||\n code.includes('__') ||\n code.includes('f\"') ||\n code.includes(\"f'\") ||\n code.includes('raise ')\n ? 'python'\n // Default to Node.js for all other cases (including ambiguous)\n : 'node'\n );\n\n // Map ComputeSDK runtime to HopX language\n // HopX uses 'javascript' instead of 'node'\n const language = effectiveRuntime === 'node' ? 'javascript' : 'python';\n\n // Execute code using sandbox.runCode()\n const result = await sandbox.runCode(code, { language });\n\n // Combine stdout and stderr for output (following E2B pattern)\n const output = result.stderr\n ? `${result.stdout}${result.stdout && result.stderr ? '\\n' : ''}${result.stderr}`\n : result.stdout;\n\n // Check for syntax/parse errors in stderr or output and throw them\n // This ensures invalid code triggers an exception as expected by the test suite\n // \n // IMPORTANT: We distinguish between:\n // - Syntax errors (invalid code that couldn't parse) → should THROW\n // - Runtime errors (valid code that threw during execution) → should RETURN with non-zero exit\n const combinedOutput = `${result.stdout || ''} ${result.stderr || ''}`;\n \n // Indicators of syntax/parse errors (code couldn't be executed)\n // Covers Python, Node.js, and Bun error formats\n const hasSyntaxError = \n // Python syntax errors\n combinedOutput.includes('SyntaxError') ||\n combinedOutput.includes('invalid syntax') ||\n combinedOutput.includes('IndentationError') ||\n combinedOutput.includes('TabError') ||\n (combinedOutput.includes('NameError') && combinedOutput.includes('is not defined')) ||\n // JavaScript/Node.js syntax errors\n combinedOutput.includes('Unexpected token') ||\n combinedOutput.includes('Unexpected identifier') ||\n // Bun parser errors (JavaScript in HopX runs via Bun)\n combinedOutput.includes('Unexpected end of file') ||\n (combinedOutput.includes('Expected') && combinedOutput.includes('but found'));\n \n // Indicators of runtime errors (code ran but threw - should NOT throw, return result)\n const isRuntimeError = \n combinedOutput.includes('throw ') ||\n combinedOutput.includes('raise ') ||\n combinedOutput.includes('Traceback (most recent call last)') ||\n (combinedOutput.includes('Error:') && !combinedOutput.includes('SyntaxError')) ||\n (combinedOutput.includes('Exception:') && !combinedOutput.includes('SyntaxError'));\n\n // Throw for syntax errors (unless it looks like a runtime error)\n if (hasSyntaxError && !isRuntimeError) {\n throw new Error(`Syntax error: ${(result.stderr || result.stdout || '').trim()}`);\n }\n\n // For non-zero exit codes with empty output, this indicates a parse failure\n // that couldn't produce any output - throw as a syntax error\n // Note: HopX SDK returns exit_code (snake_case) in ExecutionResult\n if (result.exit_code !== 0 && !result.stdout && !result.stderr) {\n throw new Error(`Code execution failed with exit code ${result.exit_code}`);\n }\n\n return {\n output,\n exitCode: result.exit_code,\n language: effectiveRuntime\n };\n } catch (error) {\n // Re-throw syntax errors\n if (error instanceof Error && error.message.includes('Syntax error')) {\n throw error;\n }\n throw new Error(\n `HopX execution failed: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n /**\n * Execute a shell command in the sandbox\n * \n * Uses sandbox.commands.run() to execute shell commands.\n * Arguments are properly quoted to handle special characters.\n */\n runCommand: async (sandbox: HopxSandbox, command: string, options?: RunCommandOptions): Promise<CommandResult> => {\n const startTime = Date.now();\n try {\n // Build command with options\n let fullCommand = command;\n \n // Handle environment variables\n if (options?.env && Object.keys(options.env).length > 0) {\n const envPrefix = Object.entries(options.env)\n .map(([k, v]) => `${k}=\"${v.replace(/\"/g, '\\\\\"')}\"`)\n .join(' ');\n fullCommand = `${envPrefix} ${fullCommand}`;\n }\n \n // Handle working directory\n if (options?.cwd) {\n fullCommand = `cd \"${options.cwd.replace(/\"/g, '\\\\\"')}\" && ${fullCommand}`;\n }\n \n // Handle background execution\n if (options?.background) {\n fullCommand = `nohup ${fullCommand} > /dev/null 2>&1 &`;\n }\n \n // Execute command using sandbox.commands.run()\n const result = await sandbox.commands.run(fullCommand);\n \n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: result.exit_code || 0, // Note: HopX uses snake_case\n durationMs: Date.now() - startTime\n };\n } catch (error) {\n // Extract result from error if available (some errors include partial results)\n const result = (error as any)?.result;\n if (result) {\n return {\n stdout: result.stdout || '',\n stderr: result.stderr || '',\n exitCode: result.exit_code || 1,\n durationMs: Date.now() - startTime\n };\n }\n \n // Fallback for other errors (command not found, etc.)\n return {\n stdout: '',\n stderr: error instanceof Error ? error.message : String(error),\n exitCode: 127,\n durationMs: Date.now() - startTime\n };\n }\n },\n\n /**\n * Get sandbox information\n * \n * Uses sandbox.getInfo() to retrieve sandbox metadata.\n */\n getInfo: async (sandbox: HopxSandbox): Promise<SandboxInfo> => {\n try {\n const info = await sandbox.getInfo();\n\n return {\n id: sandbox.sandboxId,\n provider: 'hopx',\n runtime: 'python', // HopX default runtime\n status: (info.status as 'running' | 'stopped' | 'error') || 'running',\n createdAt: info.createdAt ? new Date(info.createdAt) : new Date(),\n timeout: info.timeoutSeconds ? info.timeoutSeconds * 1000 : 300000,\n metadata: {\n templateName: info.templateName,\n templateId: info.templateId,\n region: info.region,\n publicHost: info.publicHost,\n }\n };\n } catch (error) {\n // Return basic info if getInfo fails\n return {\n id: sandbox.sandboxId,\n provider: 'hopx',\n runtime: 'python',\n status: 'running',\n createdAt: new Date(),\n timeout: 300000,\n metadata: {}\n };\n }\n },\n\n /**\n * Get preview URL for a specific port\n * \n * Uses sandbox.getPreviewUrl() to get the public URL for accessing\n * services running on a specific port in the sandbox.\n */\n getUrl: async (sandbox: HopxSandbox, options: { port: number; protocol?: string }): Promise<string> => {\n try {\n // Use HopX's built-in getPreviewUrl method\n const url = await sandbox.getPreviewUrl(options.port);\n return url;\n } catch (error) {\n throw new Error(\n `Failed to get HopX URL for port ${options.port}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n },\n\n /**\n * Native filesystem operations using HopX's files API\n * \n * HopX has full native filesystem support via sandbox.files.*\n * This is Option A from the documentation (native API, not shell-based).\n */\n filesystem: {\n /**\n * Read file contents from sandbox\n */\n readFile: async (sandbox: HopxSandbox, path: string): Promise<string> => {\n return await sandbox.files.read(path);\n },\n\n /**\n * Write content to a file in the sandbox\n */\n writeFile: async (sandbox: HopxSandbox, path: string, content: string): Promise<void> => {\n await sandbox.files.write(path, content);\n },\n\n /**\n * Create a directory in the sandbox\n */\n mkdir: async (sandbox: HopxSandbox, path: string): Promise<void> => {\n await sandbox.files.mkdir(path);\n },\n\n /**\n * List directory contents\n * \n * Maps HopX's EnhancedFileInfo to ComputeSDK's FileEntry format.\n */\n readdir: async (sandbox: HopxSandbox, path: string): Promise<FileEntry[]> => {\n const entries = await sandbox.files.list(path);\n\n return entries.map((entry: any) => ({\n name: entry.name,\n type: (entry.isDir || entry.isDirectory) ? 'directory' as const : 'file' as const,\n path: entry.path || `${path}/${entry.name}`.replace(/\\/+/g, '/'),\n isDirectory: Boolean(entry.isDir || entry.isDirectory || entry.type === 'directory'),\n size: entry.size || 0,\n lastModified: entry.modTime ? new Date(entry.modTime) : new Date()\n }));\n },\n\n /**\n * Check if a file or directory exists\n */\n exists: async (sandbox: HopxSandbox, path: string): Promise<boolean> => {\n return await sandbox.files.exists(path);\n },\n\n /**\n * Remove a file or directory\n */\n remove: async (sandbox: HopxSandbox, path: string): Promise<void> => {\n await sandbox.files.remove(path);\n }\n },\n\n /**\n * Get the native HopX Sandbox instance for advanced usage\n * \n * This allows users to access HopX-specific features not exposed\n * through the ComputeSDK interface.\n */\n getInstance: (sandbox: HopxSandbox): HopxSandbox => {\n return sandbox;\n },\n }\n }\n});\n\n// Export HopX sandbox type for explicit typing\nexport type { Sandbox as HopxSandbox } from '@hopx-ai/sdk';\n\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@computesdk/hopx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "HopX provider for ComputeSDK - cloud sandboxes with full Linux environments, filesystem access, and microVM isolation",
|
|
5
|
+
"author": "ComputeSDK",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@hopx-ai/sdk": "^0.3.8",
|
|
22
|
+
"@computesdk/provider": "1.0.18",
|
|
23
|
+
"computesdk": "1.20.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.0.0",
|
|
27
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
28
|
+
"eslint": "^8.37.0",
|
|
29
|
+
"rimraf": "^5.0.0",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"typescript": "^5.0.0",
|
|
32
|
+
"vitest": "^1.0.0",
|
|
33
|
+
"@computesdk/test-utils": "1.5.1"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"computesdk",
|
|
37
|
+
"hopx",
|
|
38
|
+
"sandbox",
|
|
39
|
+
"code-execution",
|
|
40
|
+
"python",
|
|
41
|
+
"javascript",
|
|
42
|
+
"cloud",
|
|
43
|
+
"compute",
|
|
44
|
+
"provider",
|
|
45
|
+
"containers"
|
|
46
|
+
],
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/computesdk/computesdk.git",
|
|
50
|
+
"directory": "packages/hopx"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://www.computesdk.com",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/computesdk/computesdk/issues"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"clean": "rimraf dist",
|
|
59
|
+
"dev": "tsup --watch",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"test:watch": "vitest watch",
|
|
62
|
+
"test:coverage": "vitest run --coverage",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"lint": "eslint"
|
|
65
|
+
}
|
|
66
|
+
}
|