@sylphx/sdk 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +168 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +167 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
NotFoundError: () => NotFoundError,
|
|
40
40
|
RETRYABLE_CODES: () => RETRYABLE_CODES,
|
|
41
41
|
RateLimitError: () => RateLimitError,
|
|
42
|
+
SandboxClient: () => SandboxClient,
|
|
42
43
|
SylphxError: () => SylphxError,
|
|
43
44
|
TimeoutError: () => TimeoutError,
|
|
44
45
|
ValidationError: () => ValidationError,
|
|
@@ -3084,6 +3085,172 @@ async function captureMessage(config, message, options = {}) {
|
|
|
3084
3085
|
body: request
|
|
3085
3086
|
});
|
|
3086
3087
|
}
|
|
3088
|
+
|
|
3089
|
+
// src/sandbox.ts
|
|
3090
|
+
var DEFAULT_IMAGE = "registry.sylphx.com/library/exec-server:latest";
|
|
3091
|
+
var SandboxClient = class _SandboxClient {
|
|
3092
|
+
id;
|
|
3093
|
+
config;
|
|
3094
|
+
constructor(id, config) {
|
|
3095
|
+
this.id = id;
|
|
3096
|
+
this.config = config;
|
|
3097
|
+
}
|
|
3098
|
+
// ---------------------------------------------------------------------------
|
|
3099
|
+
// Factory
|
|
3100
|
+
// ---------------------------------------------------------------------------
|
|
3101
|
+
/**
|
|
3102
|
+
* Create a new sandbox.
|
|
3103
|
+
*
|
|
3104
|
+
* The sandbox pod starts asynchronously — it will be in `starting` status
|
|
3105
|
+
* after creation. File/run operations will block until the pod is ready.
|
|
3106
|
+
*
|
|
3107
|
+
* @param config - Sylphx config with a secret key (sk_*) and project ref
|
|
3108
|
+
* @param options - Sandbox creation options
|
|
3109
|
+
*/
|
|
3110
|
+
static async create(config, options) {
|
|
3111
|
+
const record = await callApi(config, `/sandboxes`, {
|
|
3112
|
+
method: "POST",
|
|
3113
|
+
body: {
|
|
3114
|
+
image: options?.image ?? DEFAULT_IMAGE,
|
|
3115
|
+
idleTimeoutMs: options?.idleTimeoutMs ?? 3e5,
|
|
3116
|
+
resources: options?.resources,
|
|
3117
|
+
env: options?.env,
|
|
3118
|
+
storage: options?.storageGi !== void 0 ? { enabled: true, sizeGi: options.storageGi } : void 0
|
|
3119
|
+
}
|
|
3120
|
+
});
|
|
3121
|
+
return new _SandboxClient(record.id, config);
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Reconnect to an existing sandbox by ID.
|
|
3125
|
+
*
|
|
3126
|
+
* Use this to resume operations on a sandbox created in a previous request.
|
|
3127
|
+
*/
|
|
3128
|
+
static fromId(config, sandboxId) {
|
|
3129
|
+
return new _SandboxClient(sandboxId, config);
|
|
3130
|
+
}
|
|
3131
|
+
// ---------------------------------------------------------------------------
|
|
3132
|
+
// Lifecycle
|
|
3133
|
+
// ---------------------------------------------------------------------------
|
|
3134
|
+
/**
|
|
3135
|
+
* Get the current status of this sandbox.
|
|
3136
|
+
*/
|
|
3137
|
+
async getStatus() {
|
|
3138
|
+
return callApi(
|
|
3139
|
+
this.config,
|
|
3140
|
+
`/sandboxes/${this.id}`,
|
|
3141
|
+
{ method: "GET" }
|
|
3142
|
+
);
|
|
3143
|
+
}
|
|
3144
|
+
/**
|
|
3145
|
+
* Terminate the sandbox immediately.
|
|
3146
|
+
*
|
|
3147
|
+
* Deletes the K8s Pod and Service. PVC (storage) is preserved for reuse.
|
|
3148
|
+
* This operation is idempotent — safe to call multiple times.
|
|
3149
|
+
*/
|
|
3150
|
+
async terminate() {
|
|
3151
|
+
await callApi(
|
|
3152
|
+
this.config,
|
|
3153
|
+
`/sandboxes/${this.id}`,
|
|
3154
|
+
{ method: "DELETE" }
|
|
3155
|
+
);
|
|
3156
|
+
}
|
|
3157
|
+
// ---------------------------------------------------------------------------
|
|
3158
|
+
// File Operations
|
|
3159
|
+
// ---------------------------------------------------------------------------
|
|
3160
|
+
/**
|
|
3161
|
+
* Write a file to the sandbox filesystem.
|
|
3162
|
+
*
|
|
3163
|
+
* @param path - Absolute path inside the sandbox (e.g. '/workspace/file.py')
|
|
3164
|
+
* @param content - File content as string or Buffer
|
|
3165
|
+
* @param encoding - 'utf8' (default) or 'base64' for binary files
|
|
3166
|
+
*/
|
|
3167
|
+
async writeFile(path, content, encoding = "utf8") {
|
|
3168
|
+
const contentStr = Buffer.isBuffer(content) ? content.toString("base64") : content;
|
|
3169
|
+
const effectiveEncoding = Buffer.isBuffer(content) ? "base64" : encoding;
|
|
3170
|
+
await callApi(
|
|
3171
|
+
this.config,
|
|
3172
|
+
`/sandboxes/${this.id}/files`,
|
|
3173
|
+
{
|
|
3174
|
+
method: "POST",
|
|
3175
|
+
body: { path, content: contentStr, encoding: effectiveEncoding }
|
|
3176
|
+
}
|
|
3177
|
+
);
|
|
3178
|
+
}
|
|
3179
|
+
/**
|
|
3180
|
+
* Read a file from the sandbox filesystem.
|
|
3181
|
+
*
|
|
3182
|
+
* @param path - Absolute path inside the sandbox
|
|
3183
|
+
* @returns File content as a string
|
|
3184
|
+
*/
|
|
3185
|
+
async readFile(path) {
|
|
3186
|
+
const result = await callApi(
|
|
3187
|
+
this.config,
|
|
3188
|
+
`/sandboxes/${this.id}/files`,
|
|
3189
|
+
{
|
|
3190
|
+
method: "GET",
|
|
3191
|
+
query: { path }
|
|
3192
|
+
}
|
|
3193
|
+
);
|
|
3194
|
+
return result.content;
|
|
3195
|
+
}
|
|
3196
|
+
/**
|
|
3197
|
+
* Delete a file from the sandbox filesystem.
|
|
3198
|
+
*
|
|
3199
|
+
* @param path - Absolute path inside the sandbox
|
|
3200
|
+
*/
|
|
3201
|
+
async deleteFile(path) {
|
|
3202
|
+
await callApi(
|
|
3203
|
+
this.config,
|
|
3204
|
+
`/sandboxes/${this.id}/files`,
|
|
3205
|
+
{
|
|
3206
|
+
method: "DELETE",
|
|
3207
|
+
query: { path }
|
|
3208
|
+
}
|
|
3209
|
+
);
|
|
3210
|
+
}
|
|
3211
|
+
/**
|
|
3212
|
+
* List files in a directory.
|
|
3213
|
+
*
|
|
3214
|
+
* @param path - Directory path inside the sandbox (default: '/')
|
|
3215
|
+
* @returns Array of file/directory paths
|
|
3216
|
+
*/
|
|
3217
|
+
async listFiles(path = "/") {
|
|
3218
|
+
const result = await callApi(
|
|
3219
|
+
this.config,
|
|
3220
|
+
`/sandboxes/${this.id}/list`,
|
|
3221
|
+
{
|
|
3222
|
+
method: "GET",
|
|
3223
|
+
query: { path }
|
|
3224
|
+
}
|
|
3225
|
+
);
|
|
3226
|
+
return result.files;
|
|
3227
|
+
}
|
|
3228
|
+
// ---------------------------------------------------------------------------
|
|
3229
|
+
// Command Execution
|
|
3230
|
+
// ---------------------------------------------------------------------------
|
|
3231
|
+
/**
|
|
3232
|
+
* Run a command inside the sandbox.
|
|
3233
|
+
*
|
|
3234
|
+
* The command runs synchronously inside the exec pod and returns when complete.
|
|
3235
|
+
*
|
|
3236
|
+
* @param command - Full command + args as array (e.g. ['python3', 'script.py'])
|
|
3237
|
+
* @param options - Optional cwd, env, timeout, stdin
|
|
3238
|
+
* @returns { stdout, stderr, exitCode, durationMs }
|
|
3239
|
+
*
|
|
3240
|
+
* @example
|
|
3241
|
+
* ```typescript
|
|
3242
|
+
* const { stdout, exitCode } = await sandbox.run(['python3', '-c', 'print(1+1)'])
|
|
3243
|
+
* console.log(stdout) // "2\n"
|
|
3244
|
+
* console.log(exitCode) // 0
|
|
3245
|
+
* ```
|
|
3246
|
+
*/
|
|
3247
|
+
async run(command, options) {
|
|
3248
|
+
return callApi(this.config, `/sandboxes/${this.id}/run`, {
|
|
3249
|
+
method: "POST",
|
|
3250
|
+
body: { command, ...options }
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
};
|
|
3087
3254
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3088
3255
|
0 && (module.exports = {
|
|
3089
3256
|
ACHIEVEMENT_TIER_CONFIG,
|
|
@@ -3095,6 +3262,7 @@ async function captureMessage(config, message, options = {}) {
|
|
|
3095
3262
|
NotFoundError,
|
|
3096
3263
|
RETRYABLE_CODES,
|
|
3097
3264
|
RateLimitError,
|
|
3265
|
+
SandboxClient,
|
|
3098
3266
|
SylphxError,
|
|
3099
3267
|
TimeoutError,
|
|
3100
3268
|
ValidationError,
|