@code-harness/sdk 0.0.0-stage → 0.1.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.
Files changed (42) hide show
  1. package/dist/browser.d.ts +65 -0
  2. package/dist/browser.js +60 -0
  3. package/dist/browser.js.map +1 -0
  4. package/dist/cjs/browser.d.ts +65 -0
  5. package/dist/cjs/core/client.d.ts +27 -0
  6. package/dist/cjs/core/index.d.ts +4 -0
  7. package/dist/cjs/core/index.js +304 -0
  8. package/dist/cjs/core/sandbox.d.ts +63 -0
  9. package/dist/cjs/errors.d.ts +8 -0
  10. package/dist/cjs/package.json +1 -0
  11. package/dist/client.d.ts +8 -0
  12. package/dist/client.js +16 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/core/client.d.ts +27 -0
  15. package/dist/core/client.js +66 -0
  16. package/dist/core/client.js.map +1 -0
  17. package/dist/core/index.d.ts +4 -0
  18. package/dist/core/index.js +5 -0
  19. package/dist/core/index.js.map +1 -0
  20. package/dist/core/sandbox.d.ts +63 -0
  21. package/dist/core/sandbox.js +117 -0
  22. package/dist/core/sandbox.js.map +1 -0
  23. package/dist/errors.d.ts +8 -0
  24. package/dist/errors.js +29 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.js +8 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/integration/langchain/browser-use.d.ts +7 -0
  30. package/dist/integration/langchain/browser-use.js +74 -0
  31. package/dist/integration/langchain/browser-use.js.map +1 -0
  32. package/dist/integration/langchain/code-harness.d.ts +5 -0
  33. package/dist/integration/langchain/code-harness.js +40 -0
  34. package/dist/integration/langchain/code-harness.js.map +1 -0
  35. package/dist/integration/langchain/skill-use.d.ts +6 -0
  36. package/dist/integration/langchain/skill-use.js +5 -0
  37. package/dist/integration/langchain/skill-use.js.map +1 -0
  38. package/dist/sandbox.d.ts +31 -0
  39. package/dist/sandbox.js +46 -0
  40. package/dist/sandbox.js.map +1 -0
  41. package/package.json +70 -4
  42. package/README.md +0 -3
@@ -0,0 +1,65 @@
1
+ import type { BaseClient } from './core/client.js';
2
+ export interface WaitFor {
3
+ selector?: string;
4
+ text?: string;
5
+ timeoutMs?: number;
6
+ }
7
+ export interface PageRef {
8
+ ref: number;
9
+ tag: string;
10
+ role: string | null;
11
+ name: string;
12
+ value: string;
13
+ placeholder: string;
14
+ href: string;
15
+ }
16
+ export interface Page {
17
+ title: string;
18
+ url: string;
19
+ text: string;
20
+ refs: PageRef[];
21
+ }
22
+ export interface NavigatedPage extends Page {
23
+ status: number | null;
24
+ error: string | null;
25
+ }
26
+ export interface RunResult {
27
+ result: unknown;
28
+ error: string | null;
29
+ durationMs: number;
30
+ }
31
+ export declare class Browser {
32
+ #private;
33
+ readonly sandboxId: string | null;
34
+ readonly record: boolean;
35
+ constructor(client: BaseClient, { id, sandboxId, record }: {
36
+ id: string | null;
37
+ sandboxId: string | null;
38
+ record: boolean;
39
+ });
40
+ get id(): string | null;
41
+ navigate(url: string, options?: {
42
+ until?: WaitFor;
43
+ }): Promise<NavigatedPage>;
44
+ read(options?: {
45
+ maxChars?: number;
46
+ waitFor?: WaitFor;
47
+ }): Promise<Page>;
48
+ click(ref: number, options?: {
49
+ waitFor?: WaitFor;
50
+ }): Promise<Page>;
51
+ type(ref: number, text: string, options?: {
52
+ submit?: boolean;
53
+ waitFor?: WaitFor;
54
+ }): Promise<Page>;
55
+ screenshot(options?: {
56
+ fullPage?: boolean;
57
+ waitFor?: WaitFor;
58
+ }): Promise<{
59
+ path: string;
60
+ }>;
61
+ run(code: string, options?: {
62
+ timeoutMs?: number;
63
+ }): Promise<RunResult>;
64
+ close(): Promise<void>;
65
+ }
@@ -0,0 +1,60 @@
1
+ import { CodeHarnessError } from './errors.js';
2
+ export class Browser {
3
+ sandboxId;
4
+ record;
5
+ #client;
6
+ #id;
7
+ #creating = null;
8
+ constructor(client, { id, sandboxId, record }) {
9
+ this.#client = client;
10
+ this.#id = id;
11
+ this.sandboxId = sandboxId;
12
+ this.record = record;
13
+ }
14
+ get id() {
15
+ return this.#id;
16
+ }
17
+ navigate(url, options = {}) {
18
+ return this.#call('navigate', { url, ...options });
19
+ }
20
+ read(options = {}) {
21
+ return this.#call('read', options);
22
+ }
23
+ click(ref, options = {}) {
24
+ return this.#call('click', { ref, ...options });
25
+ }
26
+ type(ref, text, options = {}) {
27
+ return this.#call('type', { ref, text, ...options });
28
+ }
29
+ screenshot(options = {}) {
30
+ return this.#call('screenshot', options);
31
+ }
32
+ run(code, options = {}) {
33
+ return this.#call('run', { code, ...options });
34
+ }
35
+ async close() {
36
+ if (!this.#id)
37
+ return;
38
+ await this.#client.request('DELETE', `/browser/${this.#id}`).catch(err => {
39
+ // already closed, by an earlier close or the idle sweep
40
+ if (!(err instanceof CodeHarnessError && err.code === 'NOT_FOUND'))
41
+ throw err;
42
+ });
43
+ }
44
+ async #call(action, body) {
45
+ const id = await this.#sessionId();
46
+ return (await this.#client.request('POST', `/browser/${id}/${action}`, body)).json();
47
+ }
48
+ // A sandbox's browser has no session until its first call; concurrent first calls share one.
49
+ async #sessionId() {
50
+ if (this.#id)
51
+ return this.#id;
52
+ this.#creating ??= this.#client
53
+ .request('POST', '/browser', { workspaceId: this.sandboxId, record: this.record })
54
+ .then(response => response.json())
55
+ .then(session => (this.#id = session.id))
56
+ .finally(() => (this.#creating = null));
57
+ return this.#creating;
58
+ }
59
+ }
60
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { BaseClient } from './core/client.js';
2
+ export interface WaitFor {
3
+ selector?: string;
4
+ text?: string;
5
+ timeoutMs?: number;
6
+ }
7
+ export interface PageRef {
8
+ ref: number;
9
+ tag: string;
10
+ role: string | null;
11
+ name: string;
12
+ value: string;
13
+ placeholder: string;
14
+ href: string;
15
+ }
16
+ export interface Page {
17
+ title: string;
18
+ url: string;
19
+ text: string;
20
+ refs: PageRef[];
21
+ }
22
+ export interface NavigatedPage extends Page {
23
+ status: number | null;
24
+ error: string | null;
25
+ }
26
+ export interface RunResult {
27
+ result: unknown;
28
+ error: string | null;
29
+ durationMs: number;
30
+ }
31
+ export declare class Browser {
32
+ #private;
33
+ readonly sandboxId: string | null;
34
+ readonly record: boolean;
35
+ constructor(client: BaseClient, { id, sandboxId, record }: {
36
+ id: string | null;
37
+ sandboxId: string | null;
38
+ record: boolean;
39
+ });
40
+ get id(): string | null;
41
+ navigate(url: string, options?: {
42
+ until?: WaitFor;
43
+ }): Promise<NavigatedPage>;
44
+ read(options?: {
45
+ maxChars?: number;
46
+ waitFor?: WaitFor;
47
+ }): Promise<Page>;
48
+ click(ref: number, options?: {
49
+ waitFor?: WaitFor;
50
+ }): Promise<Page>;
51
+ type(ref: number, text: string, options?: {
52
+ submit?: boolean;
53
+ waitFor?: WaitFor;
54
+ }): Promise<Page>;
55
+ screenshot(options?: {
56
+ fullPage?: boolean;
57
+ waitFor?: WaitFor;
58
+ }): Promise<{
59
+ path: string;
60
+ }>;
61
+ run(code: string, options?: {
62
+ timeoutMs?: number;
63
+ }): Promise<RunResult>;
64
+ close(): Promise<void>;
65
+ }
@@ -0,0 +1,27 @@
1
+ import { Browser } from '../browser.js';
2
+ import { Sandbox, type SandboxOptions } from './sandbox.js';
3
+ export interface ClientOptions {
4
+ baseUrl?: string;
5
+ token?: string;
6
+ fetch?: typeof fetch;
7
+ }
8
+ export interface CreateSandboxOptions extends SandboxOptions {
9
+ name?: string;
10
+ env?: Record<string, string>;
11
+ }
12
+ export declare class BaseClient {
13
+ #private;
14
+ readonly baseUrl: string;
15
+ constructor(options?: ClientOptions);
16
+ me(): Promise<{
17
+ id: string;
18
+ email: string;
19
+ }>;
20
+ createBrowser(): Promise<Browser>;
21
+ getBrowser(id: string): Browser;
22
+ request(method: string, path: string, body?: unknown): Promise<Response>;
23
+ }
24
+ export declare class Client extends BaseClient {
25
+ createSandbox({ name, env, ...options }?: CreateSandboxOptions): Promise<Sandbox>;
26
+ getSandbox(id: string, options?: SandboxOptions): Promise<Sandbox>;
27
+ }
@@ -0,0 +1,4 @@
1
+ export { Browser, type NavigatedPage, type Page, type PageRef, type RunResult, type WaitFor } from '../browser.js';
2
+ export { CodeHarnessError, type CodeHarnessErrorCode } from '../errors.js';
3
+ export { Client, type ClientOptions, type CreateSandboxOptions } from './client.js';
4
+ export { Sandbox, type ExecuteResponse, type FileDownloadResponse, type FileOperationError, type FileUploadResponse, type LaunchOptions, type SandboxOptions, } from './sandbox.js';
@@ -0,0 +1,304 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+
39
+ // src/core/index.ts
40
+ var exports_core = {};
41
+ __export(exports_core, {
42
+ Browser: () => Browser,
43
+ Client: () => Client,
44
+ CodeHarnessError: () => CodeHarnessError,
45
+ Sandbox: () => Sandbox
46
+ });
47
+ module.exports = __toCommonJS(exports_core);
48
+
49
+ // src/errors.ts
50
+ var CODES = {
51
+ 400: "INVALID_REQUEST",
52
+ 401: "AUTHENTICATION_FAILED",
53
+ 404: "NOT_FOUND",
54
+ 409: "CONFLICT",
55
+ 413: "TOO_LARGE",
56
+ 429: "RATE_LIMITED",
57
+ 502: "UPSTREAM_FAILED",
58
+ 504: "TIMEOUT"
59
+ };
60
+ var SANDBOX_ERROR = Symbol.for("sandbox.error");
61
+
62
+ class CodeHarnessError extends Error {
63
+ code;
64
+ status;
65
+ name = "CodeHarnessError";
66
+ constructor(message, code, status = null, cause) {
67
+ super(message, { cause });
68
+ this.code = code;
69
+ this.status = status;
70
+ Object.defineProperty(this, SANDBOX_ERROR, { value: true });
71
+ }
72
+ static async fromResponse(response) {
73
+ const body = await response.json().catch(() => null);
74
+ const message = body?.message ?? body?.error ?? response.statusText;
75
+ return new CodeHarnessError(message, CODES[response.status] ?? "UNKNOWN", response.status);
76
+ }
77
+ }
78
+
79
+ // src/browser.ts
80
+ class Browser {
81
+ sandboxId;
82
+ record;
83
+ #client;
84
+ #id;
85
+ #creating = null;
86
+ constructor(client, { id, sandboxId, record }) {
87
+ this.#client = client;
88
+ this.#id = id;
89
+ this.sandboxId = sandboxId;
90
+ this.record = record;
91
+ }
92
+ get id() {
93
+ return this.#id;
94
+ }
95
+ navigate(url, options = {}) {
96
+ return this.#call("navigate", { url, ...options });
97
+ }
98
+ read(options = {}) {
99
+ return this.#call("read", options);
100
+ }
101
+ click(ref, options = {}) {
102
+ return this.#call("click", { ref, ...options });
103
+ }
104
+ type(ref, text, options = {}) {
105
+ return this.#call("type", { ref, text, ...options });
106
+ }
107
+ screenshot(options = {}) {
108
+ return this.#call("screenshot", options);
109
+ }
110
+ run(code, options = {}) {
111
+ return this.#call("run", { code, ...options });
112
+ }
113
+ async close() {
114
+ if (!this.#id)
115
+ return;
116
+ await this.#client.request("DELETE", `/browser/${this.#id}`).catch((err) => {
117
+ if (!(err instanceof CodeHarnessError && err.code === "NOT_FOUND"))
118
+ throw err;
119
+ });
120
+ }
121
+ async#call(action, body) {
122
+ const id = await this.#sessionId();
123
+ return (await this.#client.request("POST", `/browser/${id}/${action}`, body)).json();
124
+ }
125
+ async#sessionId() {
126
+ if (this.#id)
127
+ return this.#id;
128
+ this.#creating ??= this.#client.request("POST", "/browser", { workspaceId: this.sandboxId, record: this.record }).then((response) => response.json()).then((session) => this.#id = session.id).finally(() => this.#creating = null);
129
+ return this.#creating;
130
+ }
131
+ }
132
+ // src/core/sandbox.ts
133
+ var ROOT = "/workspace";
134
+ var DEFAULT_TIMEOUT_MS = 120000;
135
+ var UTF8 = new TextDecoder("utf-8", { fatal: true });
136
+ var relativePath = (path) => {
137
+ if (path === ROOT)
138
+ return "";
139
+ return path.startsWith(`${ROOT}/`) ? path.slice(ROOT.length + 1) : path.startsWith("/") ? null : path;
140
+ };
141
+ var encodePath = (relative) => relative.split("/").map(encodeURIComponent).join("/");
142
+ var fileError = (err) => {
143
+ if (err instanceof CodeHarnessError && err.code === "NOT_FOUND")
144
+ return "file_not_found";
145
+ if (err instanceof CodeHarnessError && err.code === "INVALID_REQUEST")
146
+ return "invalid_path";
147
+ throw err;
148
+ };
149
+
150
+ class Sandbox {
151
+ id;
152
+ name;
153
+ envKeys;
154
+ createdAt;
155
+ browser;
156
+ #client;
157
+ #timeoutMs;
158
+ constructor(client, workspace, { timeoutMs = DEFAULT_TIMEOUT_MS, record = false } = {}) {
159
+ this.#client = client;
160
+ this.#timeoutMs = timeoutMs;
161
+ this.id = workspace.id;
162
+ this.name = workspace.name;
163
+ this.envKeys = workspace.defaultEnvKeys;
164
+ this.createdAt = new Date(workspace.createdAt);
165
+ this.browser = new Browser(client, { id: null, sandboxId: workspace.id, record });
166
+ }
167
+ async execute(command) {
168
+ const response = await this.#client.request("POST", `/workspaces/${this.id}/sandbox/exec`, {
169
+ command,
170
+ timeoutMs: this.#timeoutMs
171
+ });
172
+ const result = await response.json();
173
+ return {
174
+ output: result.timedOut ? `${result.output}
175
+ timed out after ${this.#timeoutMs} ms` : result.output,
176
+ exitCode: result.exitCode,
177
+ truncated: false
178
+ };
179
+ }
180
+ async uploadFiles(files) {
181
+ const results = [];
182
+ for (const [path, bytes] of files) {
183
+ const relative = relativePath(path);
184
+ if (!relative) {
185
+ results.push({ path, error: "invalid_path" });
186
+ continue;
187
+ }
188
+ let content;
189
+ try {
190
+ content = UTF8.decode(bytes);
191
+ } catch {
192
+ results.push({ path, error: "invalid_path" });
193
+ continue;
194
+ }
195
+ const error = await this.#client.request("PUT", `/workspaces/${this.id}/files/${encodePath(relative)}`, { content }).then(() => null, fileError);
196
+ results.push({ path, error });
197
+ }
198
+ return results;
199
+ }
200
+ async downloadFiles(paths) {
201
+ const results = [];
202
+ for (const path of paths) {
203
+ const relative = relativePath(path);
204
+ if (!relative) {
205
+ results.push({ path, content: null, error: "invalid_path" });
206
+ continue;
207
+ }
208
+ try {
209
+ const response = await this.#client.request("GET", `/workspaces/${this.id}/files/${encodePath(relative)}?raw=true`);
210
+ results.push({ path, content: new Uint8Array(await response.arrayBuffer()), error: null });
211
+ } catch (err) {
212
+ results.push({ path, content: null, error: fileError(err) });
213
+ }
214
+ }
215
+ return results;
216
+ }
217
+ async uploadAsZip(zip, { path = "" } = {}) {
218
+ const relative = relativePath(path);
219
+ if (relative === null)
220
+ throw new CodeHarnessError(`${path} is outside ${ROOT}`, "INVALID_REQUEST");
221
+ const form = new FormData;
222
+ form.append("path", relative);
223
+ form.append("file", zip instanceof Blob ? zip : new Blob([zip]), "upload.zip");
224
+ return (await this.#client.request("PUT", `/workspaces/${this.id}/upload`, form)).json();
225
+ }
226
+ async launch(options) {
227
+ return (await this.#client.request("POST", `/workspaces/${this.id}/sandbox/launch`, options)).json();
228
+ }
229
+ async logs({ tail } = {}) {
230
+ const query = tail === undefined ? "" : `?tail=${tail}`;
231
+ const response = await this.#client.request("GET", `/workspaces/${this.id}/sandbox/logs${query}`);
232
+ return (await response.json()).lines;
233
+ }
234
+ async stop() {
235
+ await this.#client.request("POST", `/workspaces/${this.id}/sandbox/stop`);
236
+ }
237
+ async close() {
238
+ await this.browser.close();
239
+ await this.#client.request("DELETE", `/workspaces/${this.id}`);
240
+ }
241
+ }
242
+
243
+ // src/core/client.ts
244
+ var API_PREFIX = "/api/v1";
245
+
246
+ class BaseClient {
247
+ baseUrl;
248
+ #token;
249
+ #fetch;
250
+ constructor(options = {}) {
251
+ const env = typeof process === "undefined" ? {} : process.env;
252
+ const baseUrl = options.baseUrl ?? env.CODE_HARNESS_BASE_URL;
253
+ const token = options.token ?? env.CODE_HARNESS_TOKEN;
254
+ if (!baseUrl || !token)
255
+ throw new CodeHarnessError("Pass baseUrl and token, or set CODE_HARNESS_BASE_URL and CODE_HARNESS_TOKEN", "AUTHENTICATION_FAILED");
256
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
257
+ this.#token = token;
258
+ this.#fetch = options.fetch ?? fetch;
259
+ }
260
+ async me() {
261
+ return (await this.request("GET", "/me")).json();
262
+ }
263
+ async createBrowser() {
264
+ const session = await (await this.request("POST", "/browser", {})).json();
265
+ return new Browser(this, { id: session.id, sandboxId: null, record: false });
266
+ }
267
+ getBrowser(id) {
268
+ return new Browser(this, { id, sandboxId: null, record: false });
269
+ }
270
+ async request(method, path, body) {
271
+ const headers = { authorization: `Bearer ${this.#token}` };
272
+ const isJson = body !== undefined && !(body instanceof FormData);
273
+ if (isJson)
274
+ headers["content-type"] = "application/json";
275
+ let response;
276
+ try {
277
+ response = await this.#fetch(`${this.baseUrl}${API_PREFIX}${path}`, {
278
+ method,
279
+ headers,
280
+ body: isJson ? JSON.stringify(body) : body
281
+ });
282
+ } catch (err) {
283
+ const cause = err instanceof Error ? err : new Error(String(err));
284
+ throw new CodeHarnessError(`Can't reach ${this.baseUrl}: ${cause.message}`, "NETWORK_ERROR", null, cause);
285
+ }
286
+ if (!response.ok)
287
+ throw await CodeHarnessError.fromResponse(response);
288
+ return response;
289
+ }
290
+ }
291
+
292
+ class Client extends BaseClient {
293
+ async createSandbox({ name, env, ...options } = {}) {
294
+ const response = await this.request("POST", "/workspaces", {
295
+ name: name ?? `sandbox-${Date.now()}`,
296
+ defaultEnv: env
297
+ });
298
+ return new Sandbox(this, await response.json(), options);
299
+ }
300
+ async getSandbox(id, options = {}) {
301
+ const response = await this.request("GET", `/workspaces/${encodeURIComponent(id)}`);
302
+ return new Sandbox(this, await response.json(), options);
303
+ }
304
+ }
@@ -0,0 +1,63 @@
1
+ import { Browser } from '../browser.js';
2
+ import type { BaseClient } from './client.js';
3
+ export declare const ROOT = "/workspace";
4
+ export type FileOperationError = 'file_not_found' | 'permission_denied' | 'is_directory' | 'invalid_path';
5
+ export interface ExecuteResponse {
6
+ output: string;
7
+ exitCode: number | null;
8
+ truncated: boolean;
9
+ }
10
+ export interface FileUploadResponse {
11
+ path: string;
12
+ error: FileOperationError | null;
13
+ }
14
+ export interface FileDownloadResponse {
15
+ path: string;
16
+ content: Uint8Array | null;
17
+ error: FileOperationError | null;
18
+ }
19
+ export interface SandboxOptions {
20
+ timeoutMs?: number;
21
+ record?: boolean;
22
+ }
23
+ export interface LaunchOptions {
24
+ command: string;
25
+ port: number;
26
+ env?: Record<string, string>;
27
+ network?: {
28
+ allow?: string[];
29
+ deny?: string[];
30
+ };
31
+ }
32
+ export interface Workspace {
33
+ id: string;
34
+ name: string;
35
+ defaultEnvKeys: string[];
36
+ createdAt: string;
37
+ }
38
+ export declare class Sandbox {
39
+ #private;
40
+ readonly id: string;
41
+ readonly name: string;
42
+ readonly envKeys: string[];
43
+ readonly createdAt: Date;
44
+ readonly browser: Browser;
45
+ constructor(client: BaseClient, workspace: Workspace, { timeoutMs, record }?: SandboxOptions);
46
+ execute(command: string): Promise<ExecuteResponse>;
47
+ uploadFiles(files: Array<[string, Uint8Array]>): Promise<FileUploadResponse[]>;
48
+ downloadFiles(paths: string[]): Promise<FileDownloadResponse[]>;
49
+ uploadAsZip(zip: Blob | Uint8Array<ArrayBuffer> | ArrayBuffer, { path }?: {
50
+ path?: string;
51
+ }): Promise<{
52
+ path: string;
53
+ files: number;
54
+ }>;
55
+ launch(options: LaunchOptions): Promise<{
56
+ url: string;
57
+ }>;
58
+ logs({ tail }?: {
59
+ tail?: number;
60
+ }): Promise<string[]>;
61
+ stop(): Promise<void>;
62
+ close(): Promise<void>;
63
+ }
@@ -0,0 +1,8 @@
1
+ export type CodeHarnessErrorCode = 'INVALID_REQUEST' | 'AUTHENTICATION_FAILED' | 'NOT_FOUND' | 'CONFLICT' | 'TOO_LARGE' | 'RATE_LIMITED' | 'UPSTREAM_FAILED' | 'TIMEOUT' | 'NETWORK_ERROR' | 'UNKNOWN';
2
+ export declare class CodeHarnessError extends Error {
3
+ readonly code: CodeHarnessErrorCode;
4
+ readonly status: number | null;
5
+ readonly name: string;
6
+ constructor(message: string, code: CodeHarnessErrorCode, status?: number | null, cause?: Error);
7
+ static fromResponse(response: Response): Promise<CodeHarnessError>;
8
+ }
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,8 @@
1
+ import { BaseClient, type CreateSandboxOptions } from './core/client.js';
2
+ import type { SandboxOptions } from './core/sandbox.js';
3
+ import { Sandbox } from './sandbox.js';
4
+ export type { ClientOptions, CreateSandboxOptions } from './core/client.js';
5
+ export declare class Client extends BaseClient {
6
+ createSandbox({ name, env, ...options }?: CreateSandboxOptions): Promise<Sandbox>;
7
+ getSandbox(id: string, options?: SandboxOptions): Promise<Sandbox>;
8
+ }
package/dist/client.js ADDED
@@ -0,0 +1,16 @@
1
+ import { BaseClient } from './core/client.js';
2
+ import { Sandbox } from './sandbox.js';
3
+ export class Client extends BaseClient {
4
+ async createSandbox({ name, env, ...options } = {}) {
5
+ const response = await this.request('POST', '/workspaces', {
6
+ name: name ?? `sandbox-${Date.now()}`,
7
+ defaultEnv: env,
8
+ });
9
+ return new Sandbox(this, (await response.json()), options);
10
+ }
11
+ async getSandbox(id, options = {}) {
12
+ const response = await this.request('GET', `/workspaces/${encodeURIComponent(id)}`);
13
+ return new Sandbox(this, (await response.json()), options);
14
+ }
15
+ }
16
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA6B,MAAM,kBAAkB,CAAA;AAExE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAItC,MAAM,OAAO,MAAO,SAAQ,UAAU;IACpC,KAAK,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,EAAE,GAAyB,EAAE;QACtE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE;YACzD,IAAI,EAAE,IAAI,IAAI,WAAW,IAAI,CAAC,GAAG,EAAE,EAAE;YACrC,UAAU,EAAE,GAAG;SAChB,CAAC,CAAA;QAEF,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,EAAE,OAAO,CAAC,CAAA;IACzE,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,OAAO,GAAmB,EAAE;QACvD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,eAAe,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QAEnF,OAAO,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,EAAE,OAAO,CAAC,CAAA;IACzE,CAAC;CACF"}
@@ -0,0 +1,27 @@
1
+ import { Browser } from '../browser.js';
2
+ import { Sandbox, type SandboxOptions } from './sandbox.js';
3
+ export interface ClientOptions {
4
+ baseUrl?: string;
5
+ token?: string;
6
+ fetch?: typeof fetch;
7
+ }
8
+ export interface CreateSandboxOptions extends SandboxOptions {
9
+ name?: string;
10
+ env?: Record<string, string>;
11
+ }
12
+ export declare class BaseClient {
13
+ #private;
14
+ readonly baseUrl: string;
15
+ constructor(options?: ClientOptions);
16
+ me(): Promise<{
17
+ id: string;
18
+ email: string;
19
+ }>;
20
+ createBrowser(): Promise<Browser>;
21
+ getBrowser(id: string): Browser;
22
+ request(method: string, path: string, body?: unknown): Promise<Response>;
23
+ }
24
+ export declare class Client extends BaseClient {
25
+ createSandbox({ name, env, ...options }?: CreateSandboxOptions): Promise<Sandbox>;
26
+ getSandbox(id: string, options?: SandboxOptions): Promise<Sandbox>;
27
+ }