chore: import upstream snapshot with attribution
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
20
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
# Alibaba Code Interpreter SDK for JavaScript/TypeScript
|
||||
|
||||
|
||||
A TypeScript/JavaScript SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java, Go, TypeScript, and other languages safely, with support for code execution contexts.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This SDK requires a Docker image containing the Code Interpreter runtime environment. You must use the `opensandbox/code-interpreter` image (or a derivative) which includes pre-installed runtimes for Python, Java, Go, Node.js, etc.
|
||||
|
||||
For detailed information about supported languages and versions, please refer to the [Environment Documentation](../../../sandboxes/code-interpreter/README.md).
|
||||
|
||||
## Installation
|
||||
|
||||
### npm
|
||||
|
||||
```bash
|
||||
npm install @alibaba-group/opensandbox-code-interpreter
|
||||
```
|
||||
|
||||
### pnpm
|
||||
|
||||
```bash
|
||||
pnpm add @alibaba-group/opensandbox-code-interpreter
|
||||
```
|
||||
|
||||
### yarn
|
||||
|
||||
```bash
|
||||
yarn add @alibaba-group/opensandbox-code-interpreter
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
The following example demonstrates how to create a sandbox with a specific runtime configuration and execute a simple script.
|
||||
|
||||
> **Note**: Before running this example, ensure the OpenSandbox service is running. See the root [README.md](../../../README.md) for startup instructions.
|
||||
|
||||
```ts
|
||||
import { ConnectionConfig, Sandbox } from "@alibaba-group/opensandbox";
|
||||
import { CodeInterpreter, SupportedLanguages } from "@alibaba-group/opensandbox-code-interpreter";
|
||||
|
||||
// 1. Configure connection
|
||||
const config = new ConnectionConfig({
|
||||
domain: "api.opensandbox.io",
|
||||
apiKey: "your-api-key",
|
||||
});
|
||||
|
||||
// 2. Create a Sandbox with the code-interpreter image + runtime versions
|
||||
const sandbox = await Sandbox.create({
|
||||
connectionConfig: config,
|
||||
image: "opensandbox/code-interpreter:v1.1.0",
|
||||
entrypoint: ["/opt/code-interpreter/code-interpreter.sh"],
|
||||
env: {
|
||||
PYTHON_VERSION: "3.11",
|
||||
JAVA_VERSION: "17",
|
||||
NODE_VERSION: "20",
|
||||
GO_VERSION: "1.24",
|
||||
},
|
||||
timeoutSeconds: 15 * 60,
|
||||
});
|
||||
|
||||
// 3. Create CodeInterpreter wrapper
|
||||
const ci = await CodeInterpreter.create(sandbox);
|
||||
|
||||
// 4. Create an execution context (Python)
|
||||
const ctx = await ci.codes.createContext(SupportedLanguages.PYTHON);
|
||||
|
||||
// 5. Run code
|
||||
const result = await ci.codes.run("import sys\nprint(sys.version)\nresult = 2 + 2\nresult", {
|
||||
context: ctx,
|
||||
});
|
||||
|
||||
// 6. Print output
|
||||
console.log(result.result[0]?.text);
|
||||
|
||||
// 7. Cleanup remote instance (optional but recommended)
|
||||
await sandbox.kill();
|
||||
await sandbox.close();
|
||||
```
|
||||
|
||||
## Runtime Configuration
|
||||
|
||||
### Docker Image
|
||||
|
||||
The Code Interpreter SDK relies on a specialized environment. Ensure your sandbox provider has the `opensandbox/code-interpreter` image available.
|
||||
|
||||
### Language Version Selection
|
||||
|
||||
You can specify the desired version of a programming language by setting the corresponding environment variable when creating the `Sandbox`.
|
||||
|
||||
| Language | Environment Variable | Example Value | Default (if unset) |
|
||||
| --- | --- | --- | --- |
|
||||
| Python | `PYTHON_VERSION` | `3.11` | Image default |
|
||||
| Java | `JAVA_VERSION` | `17` | Image default |
|
||||
| Node.js | `NODE_VERSION` | `20` | Image default |
|
||||
| Go | `GO_VERSION` | `1.24` | Image default |
|
||||
|
||||
```ts
|
||||
const sandbox = await Sandbox.create({
|
||||
connectionConfig: config,
|
||||
image: "opensandbox/code-interpreter:v1.1.0",
|
||||
entrypoint: ["/opt/code-interpreter/code-interpreter.sh"],
|
||||
env: {
|
||||
JAVA_VERSION: "17",
|
||||
GO_VERSION: "1.24",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### 0. Run with `language` (default language context)
|
||||
|
||||
If you don't need to manage explicit context IDs, you can run code by specifying only `language`.
|
||||
When `context.id` is omitted, execd can create/reuse a default session for that language, so state can persist across runs.
|
||||
|
||||
```ts
|
||||
import { SupportedLanguages } from "@alibaba-group/opensandbox-code-interpreter";
|
||||
|
||||
await ci.codes.run("x = 42", { language: SupportedLanguages.PYTHON });
|
||||
const execution = await ci.codes.run("result = x\nresult", { language: SupportedLanguages.PYTHON });
|
||||
console.log(execution.result[0]?.text); // "42"
|
||||
```
|
||||
|
||||
### 0.1 Context management (list/get/delete)
|
||||
|
||||
You can manage contexts explicitly (aligned with Python/Kotlin SDKs):
|
||||
|
||||
```ts
|
||||
const ctx = await ci.codes.createContext(SupportedLanguages.PYTHON);
|
||||
|
||||
const same = await ci.codes.getContext(ctx.id!);
|
||||
console.log(same.id, same.language);
|
||||
|
||||
const all = await ci.codes.listContexts();
|
||||
const pyOnly = await ci.codes.listContexts(SupportedLanguages.PYTHON);
|
||||
|
||||
await ci.codes.deleteContext(ctx.id!);
|
||||
await ci.codes.deleteContexts(SupportedLanguages.PYTHON); // bulk cleanup
|
||||
```
|
||||
|
||||
### 1. Java Code Execution
|
||||
|
||||
```ts
|
||||
import { SupportedLanguages } from "@alibaba-group/opensandbox-code-interpreter";
|
||||
|
||||
const javaCtx = await ci.codes.createContext(SupportedLanguages.JAVA);
|
||||
const execution = await ci.codes.run(
|
||||
[
|
||||
'System.out.println("Calculating sum...");',
|
||||
"int a = 10;",
|
||||
"int b = 20;",
|
||||
"int sum = a + b;",
|
||||
'System.out.println("Sum: " + sum);',
|
||||
"sum",
|
||||
].join("\n"),
|
||||
{ context: javaCtx },
|
||||
);
|
||||
console.log(execution.logs.stdout.map((m) => m.text));
|
||||
```
|
||||
|
||||
### 2. Streaming Output Handling
|
||||
|
||||
Handle stdout/stderr and execution events in real-time.
|
||||
|
||||
```ts
|
||||
import type { ExecutionHandlers } from "@alibaba-group/opensandbox";
|
||||
import { SupportedLanguages } from "@alibaba-group/opensandbox-code-interpreter";
|
||||
|
||||
const handlers: ExecutionHandlers = {
|
||||
onStdout: (m) => console.log("STDOUT:", m.text),
|
||||
onStderr: (m) => console.error("STDERR:", m.text),
|
||||
onResult: (r) => console.log("RESULT:", r.text),
|
||||
};
|
||||
|
||||
const pyCtx = await ci.codes.createContext(SupportedLanguages.PYTHON);
|
||||
await ci.codes.run("import time\nfor i in range(5):\n print(i)\n time.sleep(0.2)", {
|
||||
context: pyCtx,
|
||||
handlers,
|
||||
});
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **Lifecycle**: `CodeInterpreter` wraps an existing `Sandbox` instance and reuses its connection configuration. Each sandbox instance clones the transport via `ConnectionConfig.withTransportIfMissing()`, so call `sandbox.close()` when you are finished to release the Node.js keep-alive agent and avoid leak.
|
||||
- **Default context**: `codes.run(..., { language })` uses a language default context (state can persist across runs).
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { createBaseConfig } from "../../eslint.base.mjs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default createBaseConfig({
|
||||
tsconfigRootDir: __dirname,
|
||||
tsconfigPath: "./tsconfig.json",
|
||||
extraIgnores: ["src/**/*.d.ts", "src/**/*.js"],
|
||||
});
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "@alibaba-group/opensandbox-code-interpreter",
|
||||
"version": "0.1.4",
|
||||
"description": "OpenSandbox Code Interpreter TypeScript/JavaScript SDK",
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/cjs/index.cjs",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"browser": "./dist/index.js",
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opensandbox-group/OpenSandbox.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/opensandbox-group/OpenSandbox/issues"
|
||||
},
|
||||
"homepage": "https://open-sandbox.ai",
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"test": "pnpm run build && node --test tests/*.test.mjs",
|
||||
"lint": "eslint src --max-warnings 0",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alibaba-group/opensandbox": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.4",
|
||||
"eslint": "^9.39.4",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.59.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import type { ExecdClient, ExecdPaths } from "@alibaba-group/opensandbox/internal";
|
||||
import type { ServerStreamEvent } from "@alibaba-group/opensandbox";
|
||||
import type { Execution, ExecutionHandlers } from "@alibaba-group/opensandbox";
|
||||
import {
|
||||
ExecutionEventDispatcher,
|
||||
InvalidArgumentException,
|
||||
} from "@alibaba-group/opensandbox";
|
||||
|
||||
import type { Codes } from "../services/codes.js";
|
||||
import type { CodeContext, SupportedLanguage } from "../models.js";
|
||||
import { throwOnOpenApiFetchError } from "./openapiError.js";
|
||||
import { parseJsonEventStream } from "./sse.js";
|
||||
|
||||
type ApiCreateContextRequest =
|
||||
ExecdPaths["/code/context"]["post"]["requestBody"]["content"]["application/json"];
|
||||
type ApiCreateContextOk =
|
||||
ExecdPaths["/code/context"]["post"]["responses"][200]["content"]["application/json"];
|
||||
type ApiGetContextOk =
|
||||
ExecdPaths["/code/contexts/{context_id}"]["get"]["responses"][200]["content"]["application/json"];
|
||||
type ApiListContextsOk =
|
||||
ExecdPaths["/code/contexts"]["get"]["responses"][200]["content"]["application/json"];
|
||||
type ApiRunCodeRequest =
|
||||
ExecdPaths["/code"]["post"]["requestBody"]["content"]["application/json"];
|
||||
|
||||
/**
|
||||
* Single-layer codes adapter for the Code Interpreter SDK.
|
||||
*
|
||||
* - Handles HTTP/SSE streaming via the underlying execd adapter
|
||||
* - Builds the structured {@link Execution} result for `run(...)`
|
||||
*/
|
||||
function joinUrl(baseUrl: string, pathname: string): string {
|
||||
const base = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
||||
const path = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
||||
return `${base}${path}`;
|
||||
}
|
||||
|
||||
export class CodesAdapter implements Codes {
|
||||
private readonly fetch: typeof fetch;
|
||||
|
||||
constructor(
|
||||
private readonly client: ExecdClient,
|
||||
private readonly opts: { baseUrl: string; fetch?: typeof fetch; headers?: Record<string, string> },
|
||||
) {
|
||||
this.fetch = opts.fetch ?? fetch;
|
||||
}
|
||||
|
||||
async createContext(language: SupportedLanguage): Promise<CodeContext> {
|
||||
const body: ApiCreateContextRequest = { language };
|
||||
const { data, error, response } = await this.client.POST("/code/context", {
|
||||
body,
|
||||
});
|
||||
throwOnOpenApiFetchError({ error, response }, "Create code context failed");
|
||||
const ok = data as ApiCreateContextOk | undefined;
|
||||
if (!ok || typeof ok !== "object") {
|
||||
throw new Error("Create code context failed: unexpected response shape");
|
||||
}
|
||||
if (typeof ok.language !== "string" || !ok.language) {
|
||||
throw new Error("Create code context failed: missing language");
|
||||
}
|
||||
return { id: ok.id, language: ok.language };
|
||||
}
|
||||
|
||||
async getContext(contextId: string): Promise<CodeContext> {
|
||||
if (!contextId?.trim()) {
|
||||
throw new InvalidArgumentException({ message: "contextId cannot be empty" });
|
||||
}
|
||||
const { data, error, response } = await this.client.GET("/code/contexts/{context_id}", {
|
||||
params: { path: { context_id: contextId } },
|
||||
});
|
||||
throwOnOpenApiFetchError({ error, response }, "Get code context failed");
|
||||
const ok = data as ApiGetContextOk | undefined;
|
||||
if (!ok || typeof ok !== "object") {
|
||||
throw new Error("Get code context failed: unexpected response shape");
|
||||
}
|
||||
if (typeof (ok as any).language !== "string" || !(ok as any).language) {
|
||||
throw new Error("Get code context failed: missing language");
|
||||
}
|
||||
return { id: (ok as any).id, language: (ok as any).language };
|
||||
}
|
||||
|
||||
async listContexts(language?: SupportedLanguage): Promise<CodeContext[]> {
|
||||
const { data, error, response } = await this.client.GET("/code/contexts", {
|
||||
params: language ? { query: { language } } : undefined,
|
||||
} as any);
|
||||
throwOnOpenApiFetchError({ error, response }, "List code contexts failed");
|
||||
const ok = data as ApiListContextsOk | undefined;
|
||||
if (!Array.isArray(ok)) {
|
||||
throw new Error("List code contexts failed: unexpected response shape");
|
||||
}
|
||||
return ok
|
||||
.filter((c) => c && typeof c === "object")
|
||||
.map((c: any) => ({ id: c.id, language: c.language as any }));
|
||||
}
|
||||
|
||||
async deleteContext(contextId: string): Promise<void> {
|
||||
if (!contextId?.trim()) {
|
||||
throw new InvalidArgumentException({ message: "contextId cannot be empty" });
|
||||
}
|
||||
const { error, response } = await this.client.DELETE("/code/contexts/{context_id}", {
|
||||
params: { path: { context_id: contextId } },
|
||||
});
|
||||
throwOnOpenApiFetchError({ error, response }, "Delete code context failed");
|
||||
}
|
||||
|
||||
async deleteContexts(language: SupportedLanguage): Promise<void> {
|
||||
const { error, response } = await this.client.DELETE("/code/contexts", {
|
||||
params: { query: { language } },
|
||||
});
|
||||
throwOnOpenApiFetchError({ error, response }, "Delete code contexts failed");
|
||||
}
|
||||
|
||||
async interrupt(contextId: string): Promise<void> {
|
||||
const { error, response } = await this.client.DELETE("/code", {
|
||||
params: { query: { id: contextId } },
|
||||
});
|
||||
throwOnOpenApiFetchError({ error, response }, "Interrupt code failed");
|
||||
}
|
||||
|
||||
async *runStream(req: ApiRunCodeRequest, signal?: AbortSignal): AsyncIterable<ServerStreamEvent> {
|
||||
const url = joinUrl(this.opts.baseUrl, "/code");
|
||||
const body = JSON.stringify(req);
|
||||
const res = await this.fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"accept": "text/event-stream",
|
||||
"content-type": "application/json",
|
||||
...(this.opts.headers ?? {}),
|
||||
},
|
||||
body,
|
||||
signal,
|
||||
});
|
||||
|
||||
for await (const ev of parseJsonEventStream<ServerStreamEvent>(res, { fallbackErrorMessage: "Run code failed" })) {
|
||||
yield ev;
|
||||
}
|
||||
}
|
||||
|
||||
async run(
|
||||
code: string,
|
||||
opts: { context?: CodeContext; language?: SupportedLanguage; handlers?: ExecutionHandlers; signal?: AbortSignal } = {},
|
||||
): Promise<Execution> {
|
||||
if (!code.trim()) {
|
||||
throw new InvalidArgumentException({ message: "Code cannot be empty" });
|
||||
}
|
||||
|
||||
if (opts.context && opts.language) {
|
||||
throw new InvalidArgumentException({ message: "Provide either opts.context or opts.language, not both" });
|
||||
}
|
||||
|
||||
const context: CodeContext =
|
||||
opts.context ??
|
||||
(opts.language
|
||||
? { language: opts.language }
|
||||
: { language: "python" });
|
||||
|
||||
// Make the OpenAPI contract explicit so backend schema changes surface quickly.
|
||||
const req: ApiRunCodeRequest = {
|
||||
code,
|
||||
context: { id: context.id, language: context.language },
|
||||
};
|
||||
|
||||
const execution: Execution = {
|
||||
logs: { stdout: [], stderr: [] },
|
||||
result: [],
|
||||
};
|
||||
const dispatcher = new ExecutionEventDispatcher(execution, opts.handlers);
|
||||
|
||||
for await (const ev of this.runStream(req, opts.signal)) {
|
||||
await dispatcher.dispatch(ev as any);
|
||||
}
|
||||
|
||||
return execution;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { SandboxApiException, SandboxError } from "@alibaba-group/opensandbox";
|
||||
|
||||
export function throwOnOpenApiFetchError(
|
||||
result: { error?: unknown; response: Response },
|
||||
fallbackMessage: string,
|
||||
): void {
|
||||
if (!result.error) return;
|
||||
|
||||
const requestId = result.response.headers.get("x-request-id") ?? undefined;
|
||||
const status = (result.response as any).status ?? 0;
|
||||
|
||||
const err = result.error as any;
|
||||
const message =
|
||||
err?.message ??
|
||||
err?.error?.message ??
|
||||
fallbackMessage;
|
||||
|
||||
const code = err?.code ?? err?.error?.code;
|
||||
const msg = err?.message ?? err?.error?.message ?? message;
|
||||
|
||||
throw new SandboxApiException({
|
||||
message: msg,
|
||||
statusCode: status,
|
||||
requestId,
|
||||
error: code
|
||||
? new SandboxError(String(code), String(msg ?? ""))
|
||||
: new SandboxError(SandboxError.UNEXPECTED_RESPONSE, String(msg ?? "")),
|
||||
rawBody: result.error,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { SandboxApiException, SandboxError } from "@alibaba-group/opensandbox";
|
||||
|
||||
function tryParseJson(line: string): unknown | undefined {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an SSE-like stream that may be either:
|
||||
* - standard SSE frames (`data: {...}\n\n`)
|
||||
* - newline-delimited JSON (one JSON object per line)
|
||||
*/
|
||||
export async function* parseJsonEventStream<T>(
|
||||
res: Response,
|
||||
opts?: { fallbackErrorMessage?: string },
|
||||
): AsyncIterable<T> {
|
||||
if (!res.ok) {
|
||||
const text = await res.text().catch(() => "");
|
||||
const parsed = tryParseJson(text);
|
||||
const err = parsed && typeof parsed === "object" ? (parsed as any) : undefined;
|
||||
const requestId = res.headers.get("x-request-id") ?? undefined;
|
||||
const message = err?.message ?? opts?.fallbackErrorMessage ?? `Stream request failed (status=${res.status})`;
|
||||
const code = err?.code ? String(err.code) : SandboxError.UNEXPECTED_RESPONSE;
|
||||
throw new SandboxApiException({
|
||||
message,
|
||||
statusCode: res.status,
|
||||
requestId,
|
||||
error: new SandboxError(code, err?.message ? String(err.message) : message),
|
||||
rawBody: parsed ?? text,
|
||||
});
|
||||
}
|
||||
|
||||
if (!res.body) return;
|
||||
|
||||
const reader = res.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let buf = "";
|
||||
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
let idx: number;
|
||||
|
||||
while ((idx = buf.indexOf("\n")) >= 0) {
|
||||
const rawLine = buf.slice(0, idx);
|
||||
buf = buf.slice(idx + 1);
|
||||
|
||||
const line = rawLine.trim();
|
||||
if (!line) continue;
|
||||
|
||||
// Support standard SSE "data:" prefix
|
||||
if (line.startsWith(":")) continue;
|
||||
if (line.startsWith("event:") || line.startsWith("id:") || line.startsWith("retry:")) continue;
|
||||
|
||||
const jsonLine = line.startsWith("data:") ? line.slice("data:".length).trim() : line;
|
||||
if (!jsonLine) continue;
|
||||
|
||||
const parsed = tryParseJson(jsonLine);
|
||||
if (!parsed) continue;
|
||||
yield parsed as T;
|
||||
}
|
||||
}
|
||||
|
||||
// flush last line if exists
|
||||
const last = buf.trim();
|
||||
if (last) {
|
||||
const jsonLine = last.startsWith("data:") ? last.slice("data:".length).trim() : last;
|
||||
const parsed = tryParseJson(jsonLine);
|
||||
if (parsed) yield parsed as T;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import type { Sandbox } from "@alibaba-group/opensandbox";
|
||||
import type { Codes } from "../services/codes.js";
|
||||
|
||||
export interface CreateCodesStackOptions {
|
||||
sandbox: Sandbox;
|
||||
execdBaseUrl: string;
|
||||
endpointHeaders?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory abstraction for Code Interpreter SDK to decouple from concrete adapters/clients.
|
||||
*/
|
||||
export interface AdapterFactory {
|
||||
createCodes(opts: CreateCodesStackOptions): Codes;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { createExecdClient } from "@alibaba-group/opensandbox/internal";
|
||||
import type { AdapterFactory, CreateCodesStackOptions } from "./adapterFactory.js";
|
||||
import { CodesAdapter } from "../adapters/codesAdapter.js";
|
||||
import type { Codes } from "../services/codes.js";
|
||||
|
||||
export class DefaultAdapterFactory implements AdapterFactory {
|
||||
createCodes(opts: CreateCodesStackOptions): Codes {
|
||||
const headers: Record<string, string> = {
|
||||
...(opts.sandbox.connectionConfig.headers ?? {}),
|
||||
...(opts.endpointHeaders ?? {}),
|
||||
};
|
||||
const client = createExecdClient({
|
||||
baseUrl: opts.execdBaseUrl,
|
||||
headers,
|
||||
fetch: opts.sandbox.connectionConfig.fetch,
|
||||
});
|
||||
|
||||
return new CodesAdapter(client, {
|
||||
baseUrl: opts.execdBaseUrl,
|
||||
headers,
|
||||
// Streaming calls (SSE) use a dedicated fetch, aligned with Kotlin/Python SDKs.
|
||||
fetch: opts.sandbox.connectionConfig.sseFetch,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function createDefaultAdapterFactory(): AdapterFactory {
|
||||
return new DefaultAdapterFactory();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
export { CodeInterpreter } from "./interpreter.js";
|
||||
export type { CodeInterpreterCreateOptions } from "./interpreter.js";
|
||||
|
||||
export type { AdapterFactory } from "./factory/adapterFactory.js";
|
||||
export { DefaultAdapterFactory, createDefaultAdapterFactory } from "./factory/defaultAdapterFactory.js";
|
||||
|
||||
export type { CodeContext, SupportedLanguage } from "./models.js";
|
||||
export { SupportedLanguage as SupportedLanguages } from "./models.js";
|
||||
|
||||
export type { Codes } from "./services/codes.js";
|
||||
|
||||
export type {
|
||||
Execution,
|
||||
ExecutionComplete,
|
||||
ExecutionError,
|
||||
ExecutionHandlers,
|
||||
ExecutionInit,
|
||||
ExecutionResult,
|
||||
OutputMessage,
|
||||
} from "@alibaba-group/opensandbox";
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { DEFAULT_EXECD_PORT } from "@alibaba-group/opensandbox";
|
||||
import type { Sandbox } from "@alibaba-group/opensandbox";
|
||||
|
||||
import { createDefaultAdapterFactory } from "./factory/defaultAdapterFactory.js";
|
||||
import type { AdapterFactory } from "./factory/adapterFactory.js";
|
||||
import type { Codes } from "./services/codes.js";
|
||||
|
||||
export interface CodeInterpreterCreateOptions {
|
||||
adapterFactory?: AdapterFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Code interpreter facade (JS/TS).
|
||||
*
|
||||
* This class wraps an existing {@link Sandbox} and provides a high-level API for code execution.
|
||||
*
|
||||
* - Use {@link codes} to create contexts and run code.
|
||||
* - {@link files}, {@link commands}, and {@link metrics} are exposed for convenience and are
|
||||
* the same instances as on the underlying {@link Sandbox}.
|
||||
*/
|
||||
export class CodeInterpreter {
|
||||
private constructor(
|
||||
readonly sandbox: Sandbox,
|
||||
readonly codes: Codes,
|
||||
) {}
|
||||
|
||||
static async create(sandbox: Sandbox, opts: CodeInterpreterCreateOptions = {}): Promise<CodeInterpreter> {
|
||||
const endpoint = await sandbox.getEndpoint(DEFAULT_EXECD_PORT);
|
||||
const execdBaseUrl = `${sandbox.connectionConfig.protocol}://${endpoint.endpoint}`;
|
||||
const adapterFactory = opts.adapterFactory ?? createDefaultAdapterFactory();
|
||||
const codes = adapterFactory.createCodes({
|
||||
sandbox,
|
||||
execdBaseUrl,
|
||||
endpointHeaders: endpoint.headers,
|
||||
});
|
||||
|
||||
return new CodeInterpreter(sandbox, codes);
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.sandbox.id;
|
||||
}
|
||||
|
||||
get files() {
|
||||
return this.sandbox.files;
|
||||
}
|
||||
|
||||
get commands() {
|
||||
return this.sandbox.commands;
|
||||
}
|
||||
|
||||
get metrics() {
|
||||
return this.sandbox.metrics;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
export const SupportedLanguage = {
|
||||
PYTHON: "python",
|
||||
JAVA: "java",
|
||||
GO: "go",
|
||||
TYPESCRIPT: "typescript",
|
||||
JAVASCRIPT: "javascript",
|
||||
BASH: "bash",
|
||||
} as const;
|
||||
|
||||
export type SupportedLanguage =
|
||||
(typeof SupportedLanguage)[keyof typeof SupportedLanguage];
|
||||
|
||||
export interface CodeContext {
|
||||
id?: string;
|
||||
language: SupportedLanguage | (string & {});
|
||||
}
|
||||
|
||||
export interface RunCodeRequest {
|
||||
code: string;
|
||||
context: CodeContext;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import type { ServerStreamEvent } from "@alibaba-group/opensandbox";
|
||||
import type { Execution, ExecutionHandlers } from "@alibaba-group/opensandbox";
|
||||
import type { CodeContext, RunCodeRequest, SupportedLanguage } from "../models.js";
|
||||
|
||||
export interface Codes {
|
||||
createContext(language: SupportedLanguage): Promise<CodeContext>;
|
||||
/**
|
||||
* Get an existing context by id.
|
||||
*/
|
||||
getContext(contextId: string): Promise<CodeContext>;
|
||||
/**
|
||||
* List active contexts. If language is provided, filters by language/runtime.
|
||||
*/
|
||||
listContexts(language?: SupportedLanguage): Promise<CodeContext[]>;
|
||||
/**
|
||||
* Delete a context by id.
|
||||
*/
|
||||
deleteContext(contextId: string): Promise<void>;
|
||||
/**
|
||||
* Delete all contexts under the specified language/runtime.
|
||||
*/
|
||||
deleteContexts(language: SupportedLanguage): Promise<void>;
|
||||
|
||||
run(
|
||||
code: string,
|
||||
opts?: { context?: CodeContext; language?: SupportedLanguage; handlers?: ExecutionHandlers; signal?: AbortSignal },
|
||||
): Promise<Execution>;
|
||||
|
||||
runStream(
|
||||
req: RunCodeRequest,
|
||||
signal?: AbortSignal,
|
||||
): AsyncIterable<ServerStreamEvent>;
|
||||
|
||||
interrupt(contextId: string): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { DefaultAdapterFactory, SupportedLanguages } from "../dist/index.js";
|
||||
|
||||
test("DefaultAdapterFactory exposes context CRUD and interrupt operations", async () => {
|
||||
const recorded = [];
|
||||
const fetchImpl = async (input, init = {}) => {
|
||||
const request = input instanceof Request ? input : new Request(input, init);
|
||||
const url = new URL(request.url);
|
||||
recorded.push({
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
headers: Object.fromEntries(request.headers.entries()),
|
||||
});
|
||||
|
||||
if (url.pathname === "/code/context" && request.method === "POST") {
|
||||
return Response.json({ id: "ctx-1", language: "python" });
|
||||
}
|
||||
if (url.pathname === "/code/contexts/ctx-1" && request.method === "GET") {
|
||||
return Response.json({ id: "ctx-1", language: "python" });
|
||||
}
|
||||
if (url.pathname === "/code/contexts" && request.method === "GET") {
|
||||
const language = url.searchParams.get("language");
|
||||
return Response.json(
|
||||
language
|
||||
? [{ id: "ctx-2", language }]
|
||||
: [
|
||||
{ id: "ctx-1", language: "python" },
|
||||
{ id: "ctx-2", language: "go" },
|
||||
],
|
||||
);
|
||||
}
|
||||
if (url.pathname === "/code/contexts/ctx-1" && request.method === "DELETE") {
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
if (url.pathname === "/code/contexts" && request.method === "DELETE") {
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
if (url.pathname === "/code" && request.method === "DELETE") {
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
throw new Error(`Unexpected request: ${request.method} ${request.url}`);
|
||||
};
|
||||
|
||||
const factory = new DefaultAdapterFactory();
|
||||
const codes = factory.createCodes({
|
||||
sandbox: {
|
||||
connectionConfig: {
|
||||
headers: { "x-global": "global" },
|
||||
fetch: fetchImpl,
|
||||
sseFetch: fetchImpl,
|
||||
},
|
||||
},
|
||||
execdBaseUrl: "http://sandbox.internal:3456",
|
||||
endpointHeaders: { "x-endpoint": "endpoint" },
|
||||
});
|
||||
|
||||
const created = await codes.createContext(SupportedLanguages.PYTHON);
|
||||
const fetched = await codes.getContext("ctx-1");
|
||||
const allContexts = await codes.listContexts();
|
||||
const goContexts = await codes.listContexts(SupportedLanguages.GO);
|
||||
await codes.deleteContext("ctx-1");
|
||||
await codes.deleteContexts(SupportedLanguages.GO);
|
||||
await codes.interrupt("exec-1");
|
||||
|
||||
assert.deepEqual(created, { id: "ctx-1", language: "python" });
|
||||
assert.deepEqual(fetched, { id: "ctx-1", language: "python" });
|
||||
assert.equal(allContexts.length, 2);
|
||||
assert.deepEqual(goContexts, [{ id: "ctx-2", language: "go" }]);
|
||||
assert.deepEqual(
|
||||
recorded.map((entry) => `${entry.method} ${entry.url}`),
|
||||
[
|
||||
"POST http://sandbox.internal:3456/code/context",
|
||||
"GET http://sandbox.internal:3456/code/contexts/ctx-1",
|
||||
"GET http://sandbox.internal:3456/code/contexts",
|
||||
"GET http://sandbox.internal:3456/code/contexts?language=go",
|
||||
"DELETE http://sandbox.internal:3456/code/contexts/ctx-1",
|
||||
"DELETE http://sandbox.internal:3456/code/contexts?language=go",
|
||||
"DELETE http://sandbox.internal:3456/code?id=exec-1",
|
||||
],
|
||||
);
|
||||
for (const entry of recorded) {
|
||||
assert.equal(entry.headers["x-global"], "global");
|
||||
assert.equal(entry.headers["x-endpoint"], "endpoint");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { DefaultAdapterFactory } from "../dist/index.js";
|
||||
|
||||
test("DefaultAdapterFactory merges sandbox and endpoint headers for code requests", async () => {
|
||||
const recorded = [];
|
||||
const fetchImpl = async (input, init = {}) => {
|
||||
const request = input instanceof Request ? input : new Request(input, init);
|
||||
const url = new URL(request.url);
|
||||
const headers = Object.fromEntries(request.headers.entries());
|
||||
recorded.push({
|
||||
url: request.url,
|
||||
method: request.method,
|
||||
headers,
|
||||
});
|
||||
|
||||
if (url.pathname === "/code/context") {
|
||||
return new Response(JSON.stringify({ id: "ctx-1", language: "python" }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(
|
||||
[
|
||||
JSON.stringify({ type: "stdout", text: "hello", timestamp: 1 }),
|
||||
JSON.stringify({ type: "execution_complete", execution_time: 2, timestamp: 2 }),
|
||||
].join("\n"),
|
||||
{
|
||||
status: 200,
|
||||
headers: { "content-type": "text/event-stream" },
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const sandbox = {
|
||||
connectionConfig: {
|
||||
headers: { "x-global": "global" },
|
||||
fetch: fetchImpl,
|
||||
sseFetch: fetchImpl,
|
||||
},
|
||||
};
|
||||
|
||||
const factory = new DefaultAdapterFactory();
|
||||
const codes = factory.createCodes({
|
||||
sandbox,
|
||||
execdBaseUrl: "http://sandbox.internal:3456",
|
||||
endpointHeaders: { "x-endpoint": "endpoint" },
|
||||
});
|
||||
|
||||
const context = await codes.createContext("python");
|
||||
assert.equal(context.id, "ctx-1");
|
||||
|
||||
const execution = await codes.run("print('hello')");
|
||||
assert.equal(execution.logs.stdout[0]?.text, "hello");
|
||||
|
||||
assert.equal(recorded.length, 2);
|
||||
assert.equal(recorded[0].url, "http://sandbox.internal:3456/code/context");
|
||||
assert.equal(recorded[0].headers["x-global"], "global");
|
||||
assert.equal(recorded[0].headers["x-endpoint"], "endpoint");
|
||||
assert.equal(recorded[1].url, "http://sandbox.internal:3456/code");
|
||||
assert.equal(recorded[1].headers["x-global"], "global");
|
||||
assert.equal(recorded[1].headers["x-endpoint"], "endpoint");
|
||||
assert.equal(recorded[1].headers.accept, "text/event-stream");
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { CodeInterpreter } from "../dist/index.js";
|
||||
import { DEFAULT_EXECD_PORT } from "../../../sandbox/javascript/dist/index.js";
|
||||
|
||||
test("CodeInterpreter.create forwards endpoint headers to adapter factory", async () => {
|
||||
const calls = [];
|
||||
const sandbox = {
|
||||
connectionConfig: {
|
||||
protocol: "https",
|
||||
headers: { "x-global": "global" },
|
||||
},
|
||||
async getEndpoint(port) {
|
||||
assert.equal(port, DEFAULT_EXECD_PORT);
|
||||
return {
|
||||
endpoint: "sandbox.internal:3456",
|
||||
headers: { "x-endpoint": "endpoint" },
|
||||
};
|
||||
},
|
||||
};
|
||||
const codes = { kind: "codes" };
|
||||
const adapterFactory = {
|
||||
createCodes(opts) {
|
||||
calls.push(opts);
|
||||
return codes;
|
||||
},
|
||||
};
|
||||
|
||||
const interpreter = await CodeInterpreter.create(sandbox, { adapterFactory });
|
||||
|
||||
assert.equal(interpreter.codes, codes);
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0].execdBaseUrl, "https://sandbox.internal:3456");
|
||||
assert.deepEqual(calls[0].endpointHeaders, { "x-endpoint": "endpoint" });
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2026 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
const entries = ["src/index.ts"];
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: entries,
|
||||
format: ["esm"],
|
||||
dts: true,
|
||||
outDir: "dist",
|
||||
clean: true,
|
||||
sourcemap: true,
|
||||
target: "es2022",
|
||||
},
|
||||
{
|
||||
entry: entries,
|
||||
format: ["cjs"],
|
||||
outDir: "dist/cjs",
|
||||
clean: false,
|
||||
sourcemap: true,
|
||||
target: "es2022",
|
||||
outExtension: () => ({ js: ".cjs" }),
|
||||
},
|
||||
]);
|
||||
Reference in New Issue
Block a user