@spheron/sandbox 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,332 @@
1
+ import { Commands } from './commands.js';
2
+ import { NoOfferError, NotFoundError, SandboxFailedError, SpheronApiError, SpheronError, TimeoutError } from './errors.js';
3
+ import { Files } from './files.js';
4
+ import { HttpClient, sleep } from './http.js';
5
+ import { findOffers, pickOperatingSystem } from './offers.js';
6
+ /** Refusals that mean "try the next offer": nothing was created and nothing is billed. */
7
+ const NEXT_OFFER_CODES = new Set(['agent_access_unsupported', 'offer_unavailable']);
8
+ /** A create with this key is still running on the server (the first try timed out on our side): ask again. */
9
+ const CREATE_IN_PROGRESS = 'idempotency_in_progress';
10
+ const CREATE_IN_PROGRESS_RETRY_MS = 5_000;
11
+ const CREATE_IN_PROGRESS_BUDGET_MS = 10 * 60_000;
12
+ /** Start refusals where nothing changed: the provider is still stopping, has no capacity yet, or is busy. Tried again. */
13
+ const START_RETRY_CODES = new Set(['provider_start_failed', 'instance_busy', 'service_unavailable']);
14
+ const START_RETRY_MS = 30_000;
15
+ /** Hibernating a large machine's memory can take well over 4 minutes (seen live on a 1x A6000). */
16
+ const START_RETRY_BUDGET_MS = 10 * 60_000;
17
+ /** Answer Stop and Start with 202 and the operation after this many seconds, instead of a 504 at the server's 75. */
18
+ const LIFECYCLE_PREFER = 'respond-async, wait=60';
19
+ /** How often a running Stop or Start is checked. */
20
+ const OPERATION_CHECK_MS = 5_000;
21
+ /** The server gives a Stop 30 minutes: hibernating a large machine's memory takes long. */
22
+ const STOP_TIMEOUT_MS = 30 * 60_000;
23
+ /** A VM takes a few minutes to boot and install the sandbox. */
24
+ const READY_TIMEOUT_MS = 20 * 60_000;
25
+ /** After a create, the sandbox row can take a moment to be readable. */
26
+ const NOT_FOUND_GRACE_MS = 60_000;
27
+ /** A running instance gets this long for its sandbox to check in (after a boot or a Start) before it counts as broken. */
28
+ const RECONNECT_GRACE_MS = 5 * 60_000;
29
+ /** After a Start the instance can read stopped for a moment before it reads running; that long it counts as starting. */
30
+ const STARTING_GRACE_MS = 3 * 60_000;
31
+ /** Deployment states the instance does not come back from by itself (unless a Start is in flight). */
32
+ const DEAD = new Set(['failed', 'terminated', 'terminated-provider', 'stopped']);
33
+ const isActive = (op) => !!op && (op.status === 'queued' || op.status === 'running');
34
+ function newIdempotencyKey() {
35
+ const c = globalThis.crypto;
36
+ if (c?.randomUUID)
37
+ return c.randomUUID();
38
+ return `sdk-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
39
+ }
40
+ /**
41
+ * A Spheron Agent GPU Sandbox: a GPU VM you run commands on and move files to and from, over the Spheron API.
42
+ *
43
+ * ```ts
44
+ * const sandbox = await Sandbox.create({ gpu: 'h100', maxHourlyPrice: 3 });
45
+ * const r = await sandbox.commands.run('nvidia-smi');
46
+ * console.log(r.output);
47
+ * await sandbox.terminate();
48
+ * ```
49
+ *
50
+ * The instance bills at its normal hourly price from the moment it runs until it is stopped or terminated.
51
+ */
52
+ export class Sandbox {
53
+ /** The deployment id. */
54
+ id;
55
+ commands;
56
+ files;
57
+ #http;
58
+ constructor(http, deploymentId, pollIntervalMs) {
59
+ this.#http = http;
60
+ this.id = deploymentId;
61
+ const base = `/api/deployments/${encodeURIComponent(deploymentId)}/agent`;
62
+ this.commands = new Commands(http, base, pollIntervalMs);
63
+ this.files = new Files(http, base);
64
+ }
65
+ get path() {
66
+ return `/api/deployments/${encodeURIComponent(this.id)}`;
67
+ }
68
+ /** Offers a sandbox can be deployed on, cheapest first. Pass one to Sandbox.create({ offer }). */
69
+ static async findOffers(filter = {}) {
70
+ return findOffers(new HttpClient(filter), filter);
71
+ }
72
+ /**
73
+ * Connect to an instance that already has a sandbox (deployed with it on, by this SDK, the API or an AI agent).
74
+ * Only the person who deployed it can use its sandbox.
75
+ */
76
+ static async connect(deploymentId, opts = {}) {
77
+ const sandbox = new Sandbox(new HttpClient(opts), deploymentId, opts.pollIntervalMs ?? 2000);
78
+ await sandbox.status();
79
+ return sandbox;
80
+ }
81
+ /**
82
+ * Deploy a GPU VM with the sandbox on and, by default, wait until commands can run.
83
+ *
84
+ * Picks the cheapest offer matching the filter (or deploys `offer`), and moves on to the next one when an offer
85
+ * is sold out or cannot take the sandbox. Set maxHourlyPrice so a scarce GPU never lands on a pricey offer.
86
+ * Once an instance exists, any error names it in `deploymentId`: it bills until it is terminated.
87
+ */
88
+ static async create(opts = {}) {
89
+ const http = new HttpClient(opts);
90
+ // Offers first: a filter nothing matches fails before any account lookup.
91
+ const candidates = opts.offer ? [opts.offer] : await findOffers(http, opts);
92
+ if (!candidates.length) {
93
+ throw new NoOfferError('No GPU offer matches. Loosen the filter (gpu, gpuCount, regions, maxHourlyPrice) or try spot.');
94
+ }
95
+ const teamId = opts.teamId ?? (await defaultTeamId(http));
96
+ const ssh = opts.sshPublicKey ? { ssh_public_key: opts.sshPublicKey } : { sshKeyId: opts.sshKeyId ?? (await defaultSshKeyId(http, teamId)) };
97
+ const attempts = [];
98
+ for (const offer of candidates.slice(0, opts.offer ? 1 : Math.max(1, opts.maxOfferAttempts ?? 3))) {
99
+ const operatingSystem = pickOperatingSystem(offer, opts.operatingSystem);
100
+ if (!operatingSystem) {
101
+ attempts.push({ offerId: offer.offerId, provider: offer.provider, reason: `does not offer ${opts.operatingSystem}` });
102
+ continue;
103
+ }
104
+ let created;
105
+ try {
106
+ created = await createDeployment(http, {
107
+ teamId,
108
+ provider: offer.provider,
109
+ offerId: offer.offerId,
110
+ gpuType: offer.gpuType,
111
+ gpuCount: offer.gpuCount,
112
+ // Offers name their region in clusters.
113
+ region: offer.region ?? offer.clusters?.[0],
114
+ operatingSystem,
115
+ instanceType: offer.instanceType,
116
+ name: opts.name,
117
+ ...ssh,
118
+ volumeIds: opts.volumeIds,
119
+ // Deploying a spot offer is the acknowledgement that it can be reclaimed at any time.
120
+ spotTermsAcknowledged: String(offer.instanceType).toUpperCase() === 'SPOT' ? true : undefined,
121
+ // The API names the sandbox agentAccess.
122
+ agentAccess: true,
123
+ }, opts.signal);
124
+ }
125
+ catch (error) {
126
+ if (error instanceof SpheronApiError && error.code && NEXT_OFFER_CODES.has(error.code) && !opts.offer) {
127
+ attempts.push({ offerId: offer.offerId, provider: offer.provider, reason: error.message });
128
+ continue;
129
+ }
130
+ throw error;
131
+ }
132
+ const id = String(created.id ?? created._id ?? '');
133
+ if (!id)
134
+ throw new SpheronError('The API created the instance but did not return its id. Check your deployments list.');
135
+ const sandbox = new Sandbox(http, id, opts.pollIntervalMs ?? 2000);
136
+ if (opts.waitUntilReady !== false) {
137
+ await sandbox.waitUntilReady({ timeoutMs: opts.readyTimeoutMs, onStatus: opts.onStatus, checkEveryMs: opts.checkEveryMs, signal: opts.signal });
138
+ }
139
+ return sandbox;
140
+ }
141
+ const detail = attempts.map((a) => `${a.provider} ${a.offerId}: ${a.reason}`).join('; ');
142
+ throw new NoOfferError(`No offer could be deployed with the sandbox. ${detail}`, attempts);
143
+ }
144
+ /** The sandbox's state: installing, ready, failed or removed, and whether the instance is reachable now. */
145
+ async status(signal) {
146
+ return this.#http.request('GET', `${this.path}/agent`, { signal });
147
+ }
148
+ /** The instance: status, IP, SSH command, GPU, region, hourly rate and so on. */
149
+ async info(signal) {
150
+ return this.#http.request('GET', this.path, { signal });
151
+ }
152
+ /**
153
+ * Wait until the instance runs and the sandbox is reachable. Throws SandboxFailedError when the install fails or
154
+ * the instance dies, and TimeoutError (the instance keeps running and billing) when it takes too long.
155
+ */
156
+ async waitUntilReady(opts = {}) {
157
+ return this.tagged(() => this.waitReady(opts, false));
158
+ }
159
+ /** waitUntilReady, also for the moment after a Start when the instance still reads stopped. */
160
+ async waitReady(opts, afterStart) {
161
+ const timeoutMs = opts.timeoutMs ?? READY_TIMEOUT_MS;
162
+ const started = Date.now();
163
+ const deadline = started + timeoutMs;
164
+ /** Since when the running instance's sandbox has asked for its recovery command. */
165
+ let unreachableSince;
166
+ let phase;
167
+ const report = (p, s) => {
168
+ if (p !== phase)
169
+ opts.onStatus?.(p, s);
170
+ phase = p;
171
+ };
172
+ for (;;) {
173
+ let status = null;
174
+ try {
175
+ status = await this.status(opts.signal);
176
+ }
177
+ catch (error) {
178
+ if (!(error instanceof NotFoundError) || phase !== undefined || Date.now() - started > NOT_FOUND_GRACE_MS)
179
+ throw error;
180
+ }
181
+ if (status) {
182
+ unreachableSince = status.recoveryCommand && status.deploymentRunning ? (unreachableSince ?? Date.now()) : undefined;
183
+ if (status.state === 'ready' && status.reachable && status.deploymentRunning) {
184
+ report('ready', status);
185
+ return status;
186
+ }
187
+ if (status.state === 'removed')
188
+ throw new SandboxFailedError(`The sandbox on ${this.id} was removed.`, { state: status.state });
189
+ if (!status.deploymentRunning) {
190
+ const d = await this.info(opts.signal);
191
+ const starting = isActive(d.pendingOperation) || (afterStart && d.status === 'stopped' && Date.now() - started < STARTING_GRACE_MS);
192
+ if (DEAD.has(d.status) && !starting) {
193
+ const why = d.failureReason ? `: ${d.failureReason}` : '';
194
+ throw new SandboxFailedError(`Instance ${this.id} is ${d.status}${why}`, { state: status.state, failureReason: d.failureReason });
195
+ }
196
+ report('deploying', status);
197
+ }
198
+ else if (status.state === 'failed' || (unreachableSince !== undefined && Date.now() - unreachableSince > RECONNECT_GRACE_MS)) {
199
+ // The install failed, or the sandbox never checked in: only the person can fix it, over SSH.
200
+ throw new SandboxFailedError(`The sandbox on ${this.id} is not working (${status.failureReason ?? status.state}). SSH into the instance and run recoveryCommand to bring it back, or terminate it.`, { state: status.state, failureReason: status.failureReason, recoveryCommand: status.recoveryCommand });
201
+ }
202
+ else {
203
+ // Installing, or unreachable for a moment after a boot or a Start, until the agent checks in.
204
+ report('installing', status);
205
+ }
206
+ }
207
+ if (Date.now() >= deadline) {
208
+ throw new TimeoutError(`Sandbox ${this.id} was not ready within ${Math.round(timeoutMs / 1000)} s. It is still billed: terminate it if you no longer need it.`);
209
+ }
210
+ await sleep(Math.min(opts.checkEveryMs ?? 5_000, Math.max(0, deadline - Date.now())), opts.signal);
211
+ }
212
+ }
213
+ /**
214
+ * Stop the instance: it keeps its disk and bills only storage. Commands fail until it is started again. By default
215
+ * this waits until the provider has finished stopping it, which can take minutes on a large machine.
216
+ */
217
+ async stop(opts = {}) {
218
+ await this.tagged(async () => {
219
+ const answer = await this.lifecycleCall('pause', opts.signal);
220
+ if (opts.wait !== false)
221
+ await this.follow(answer, Date.now() + (opts.timeoutMs ?? STOP_TIMEOUT_MS), opts.checkEveryMs, opts.signal);
222
+ });
223
+ }
224
+ /**
225
+ * Start a stopped instance, and by default wait until the sandbox is reachable again (timeoutMs covers both).
226
+ * While the provider is still stopping it, has no capacity yet or is busy, nothing changes and the start is tried
227
+ * again every retryEveryMs (default 30 s) for up to retryForMs (default 10 minutes).
228
+ */
229
+ async start(opts = {}) {
230
+ const retryEveryMs = opts.retryEveryMs ?? START_RETRY_MS;
231
+ await this.tagged(async () => {
232
+ const deadline = Date.now() + (opts.timeoutMs ?? READY_TIMEOUT_MS);
233
+ const retryUntil = Date.now() + (opts.retryForMs ?? START_RETRY_BUDGET_MS);
234
+ for (;;) {
235
+ try {
236
+ await this.follow(await this.lifecycleCall('resume', opts.signal), deadline, opts.checkEveryMs, opts.signal);
237
+ break;
238
+ }
239
+ catch (error) {
240
+ const retry = error instanceof SpheronApiError && !!error.code && START_RETRY_CODES.has(error.code);
241
+ if (!retry || Date.now() + retryEveryMs > retryUntil)
242
+ throw error;
243
+ await sleep(retryEveryMs, opts.signal);
244
+ }
245
+ }
246
+ if (opts.waitUntilReady !== false)
247
+ await this.waitReady({ ...opts, timeoutMs: Math.max(0, deadline - Date.now()) }, true);
248
+ });
249
+ }
250
+ /**
251
+ * Terminate the instance. It is deleted with its disk and stops billing. An instance must run for its offer's
252
+ * minimum runtime (usually 20 minutes) first: before then the API refuses with minimum_runtime_not_met.
253
+ */
254
+ async terminate() {
255
+ await this.tagged(() => this.#http.request('DELETE', this.path, { timeoutMs: 120_000 }));
256
+ }
257
+ /** Uninstall the sandbox program. The instance keeps running and your SSH access is unaffected. */
258
+ async removeSandbox() {
259
+ await this.tagged(() => this.#http.request('DELETE', `${this.path}/agent`));
260
+ }
261
+ /**
262
+ * POST pause or resume. A slow one answers 202 (or, from a server that ignores Prefer, 504) with the operation
263
+ * still running: both are handed back to follow, not thrown.
264
+ */
265
+ async lifecycleCall(action, signal) {
266
+ try {
267
+ return await this.#http.request('POST', `${this.path}/${action}`, { timeoutMs: 120_000, headers: { Prefer: LIFECYCLE_PREFER }, signal });
268
+ }
269
+ catch (error) {
270
+ if (error instanceof SpheronApiError && error.status === 504 && isActive(error.body?.operation))
271
+ return error.body;
272
+ throw error;
273
+ }
274
+ }
275
+ /** Wait for a Stop or Start to finish. Throws the operation's own error when it fails. */
276
+ async follow(answer, deadline, everyMs = OPERATION_CHECK_MS, signal) {
277
+ let op = answer?.operation;
278
+ while (op && isActive(op)) {
279
+ if (Date.now() >= deadline)
280
+ throw new TimeoutError(`${op.type === 'instance_pause' ? 'Stop' : 'Start'} of ${this.id} is still running (operation ${op.id}).`);
281
+ await sleep(everyMs, signal);
282
+ op = await this.#http.request('GET', `/api/operations/${encodeURIComponent(op.id)}`, { signal });
283
+ }
284
+ if (op?.status === 'failed') {
285
+ const e = op.error ?? {};
286
+ throw new SpheronApiError(e.status ?? 500, e.message ?? 'The operation failed.', { code: e.code, body: op });
287
+ }
288
+ }
289
+ /** Runs fn and names this instance on any error it throws, wrapping errors that are not ours. */
290
+ async tagged(fn) {
291
+ try {
292
+ return await fn();
293
+ }
294
+ catch (error) {
295
+ const tagged = error instanceof SpheronError ? error : Object.assign(new SpheronError(String(error?.message ?? error)), { cause: error });
296
+ tagged.deploymentId ??= this.id;
297
+ throw tagged;
298
+ }
299
+ }
300
+ }
301
+ /** POST /api/deployments with one idempotency key, asking again while a timed-out first try is still running. */
302
+ async function createDeployment(http, body, signal) {
303
+ const key = newIdempotencyKey();
304
+ const until = Date.now() + CREATE_IN_PROGRESS_BUDGET_MS;
305
+ for (;;) {
306
+ try {
307
+ // The provider create can take most of a minute. The key makes a retried request return the same instance.
308
+ return await http.request('POST', '/api/deployments', { body, timeoutMs: 120_000, headers: { 'Idempotency-Key': key }, idempotent: true, signal });
309
+ }
310
+ catch (error) {
311
+ if (!(error instanceof SpheronApiError && error.code === CREATE_IN_PROGRESS) || Date.now() > until)
312
+ throw error;
313
+ await sleep(CREATE_IN_PROGRESS_RETRY_MS, signal);
314
+ }
315
+ }
316
+ }
317
+ async function defaultTeamId(http) {
318
+ const teams = await http.request('GET', '/api/teams');
319
+ if (Array.isArray(teams) && teams.length === 1)
320
+ return teams[0].id;
321
+ const names = (teams ?? []).map((t) => `${t.name ?? 'team'} (${t.id})`).join(', ');
322
+ throw new SpheronError(`Pass teamId: this key reaches ${teams?.length ?? 0} teams: ${names}`);
323
+ }
324
+ async function defaultSshKeyId(http, teamId) {
325
+ const keys = await http.request('GET', '/api/ssh-keys', { query: { teamId } });
326
+ const first = Array.isArray(keys) ? keys[0] : undefined;
327
+ if (!first) {
328
+ throw new SpheronError('An SSH key is required so you can always reach the instance yourself. Pass sshPublicKey, or save a key in the dashboard and pass sshKeyId.');
329
+ }
330
+ return first.id;
331
+ }
332
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ import type { Recovery } from './errors.js';
2
+ /** queued: not on the instance yet (it runs once the sandbox is reachable). running, then one of the final states. */
3
+ export type CommandStatus = 'queued' | 'running' | 'done' | 'failed' | 'killed' | 'expired';
4
+ export declare const FINAL_STATUSES: ReadonlySet<CommandStatus>;
5
+ /** One answer from the commands API (internal: what the SDK reads, not part of its interface). */
6
+ export interface CommandView {
7
+ commandId: string;
8
+ status: CommandStatus;
9
+ exitCode?: number;
10
+ /** Output (stdout and stderr together) from outputOffset on, as UTF-8. */
11
+ output: string;
12
+ outputOffset: number;
13
+ /** Where the next read starts, in bytes. */
14
+ nextOffset?: number;
15
+ /** True once the command wrote more than 1 MB: the rest is only in logPath on the instance. */
16
+ outputTruncated: boolean;
17
+ /** The full log on the instance. */
18
+ logPath?: string;
19
+ error?: string;
20
+ /** Present while the instance cannot be reached. A queued command still runs once it is back. */
21
+ recovery?: Recovery;
22
+ }
23
+ /** A finished command. */
24
+ export interface CommandResult {
25
+ commandId: string;
26
+ status: CommandStatus;
27
+ /** Set when the command ran to an exit. */
28
+ exitCode?: number;
29
+ /** It finished with status done and exit code 0. */
30
+ ok: boolean;
31
+ /** Everything it printed (stdout and stderr together), up to 1 MB. */
32
+ output: string;
33
+ outputTruncated: boolean;
34
+ logPath?: string;
35
+ error?: string;
36
+ }
37
+ export interface SandboxStatus {
38
+ /** installing, ready, failed or removed. */
39
+ state: 'installing' | 'ready' | 'failed' | 'removed' | string;
40
+ installMethod: string;
41
+ lastSeenAt: string | null;
42
+ /** True when the sandbox program checked in within the last few minutes. */
43
+ reachable: boolean;
44
+ /** False when the instance is stopped, terminated or failed. */
45
+ deploymentRunning: boolean;
46
+ failureReason?: string;
47
+ /** Run this over SSH on the instance to bring the sandbox back. Present only when it would help. */
48
+ recoveryCommand?: string;
49
+ }
50
+ export interface FileEntry {
51
+ name: string;
52
+ type: 'file' | 'dir' | 'other' | string;
53
+ size?: number;
54
+ }
55
+ /** A deployment as GET /api/deployments/:id returns it. Only the commonly used fields are typed. */
56
+ export interface Deployment {
57
+ id: string;
58
+ name?: string;
59
+ status: string;
60
+ teamId?: string;
61
+ /** The provider id, e.g. hyperstack or nebius. */
62
+ providerId?: string;
63
+ gpuType?: string;
64
+ gpuCount?: number;
65
+ region?: string;
66
+ operatingSystem?: string;
67
+ instanceType?: string;
68
+ ipAddress?: string;
69
+ /** For example `ssh -p 22 ubuntu@203.0.113.7`. */
70
+ sshCommand?: string;
71
+ sshPort?: number;
72
+ hourlyRate?: number;
73
+ createdAt?: string;
74
+ startedAt?: string;
75
+ pausedAt?: string;
76
+ /** Why a failed instance failed. */
77
+ failureReason?: string;
78
+ [key: string]: unknown;
79
+ }
80
+ /** One offer from GET /api/gpu-offers, with its group's gpuType. */
81
+ export interface GpuOffer {
82
+ provider: string;
83
+ offerId: string;
84
+ gpuType: string;
85
+ name: string;
86
+ gpuCount: number;
87
+ vcpus: number;
88
+ memory: number;
89
+ storage: number;
90
+ price: number;
91
+ spot_price?: number;
92
+ region?: string;
93
+ /** The regions this offer deploys to; the first one is used when region is absent. */
94
+ clusters?: string[];
95
+ os_options?: string[];
96
+ instanceType: string;
97
+ available: boolean;
98
+ maintenance?: boolean;
99
+ supportsCloudInit: boolean;
100
+ minimumRuntimeMinutes?: number;
101
+ [key: string]: unknown;
102
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export const FINAL_STATUSES = new Set(['done', 'failed', 'killed', 'expired']);
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,cAAc,GAA+B,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,45 @@
1
1
  {
2
2
  "name": "@spheron/sandbox",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Run commands and move files on a Spheron Agent GPU Sandbox from code.",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://docs.spheron.ai/sandbox-sdk",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE",
20
+ "NOTICE"
21
+ ],
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "keywords": [
26
+ "spheron",
27
+ "gpu",
28
+ "sandbox",
29
+ "agent",
30
+ "ai"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.build.json",
37
+ "lint": "tsc --noEmit",
38
+ "test": "bun test tests/",
39
+ "prepublishOnly": "bun run lint && bun run build"
40
+ },
41
+ "devDependencies": {
42
+ "@types/bun": "^1.1.0",
43
+ "typescript": "^5.0.0"
44
+ }
45
+ }