@agentuity/core 2.0.0-beta.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deprecation.d.ts +20 -0
- package/dist/deprecation.d.ts.map +1 -0
- package/dist/deprecation.js +102 -0
- package/dist/deprecation.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/services/api.d.ts.map +1 -1
- package/dist/services/api.js +13 -5
- package/dist/services/api.js.map +1 -1
- package/dist/services/oauth/flow.d.ts +82 -0
- package/dist/services/oauth/flow.d.ts.map +1 -0
- package/dist/services/oauth/flow.js +308 -0
- package/dist/services/oauth/flow.js.map +1 -0
- package/dist/services/oauth/index.d.ts +2 -0
- package/dist/services/oauth/index.d.ts.map +1 -1
- package/dist/services/oauth/index.js +2 -0
- package/dist/services/oauth/index.js.map +1 -1
- package/dist/services/oauth/token-storage.d.ts +109 -0
- package/dist/services/oauth/token-storage.d.ts.map +1 -0
- package/dist/services/oauth/token-storage.js +140 -0
- package/dist/services/oauth/token-storage.js.map +1 -0
- package/dist/services/oauth/types.d.ts +63 -0
- package/dist/services/oauth/types.d.ts.map +1 -1
- package/dist/services/oauth/types.js +74 -0
- package/dist/services/oauth/types.js.map +1 -1
- package/dist/services/project/get.d.ts +12 -0
- package/dist/services/project/get.d.ts.map +1 -1
- package/dist/services/project/get.js +9 -0
- package/dist/services/project/get.js.map +1 -1
- package/dist/services/sandbox/client.d.ts +201 -1
- package/dist/services/sandbox/client.d.ts.map +1 -1
- package/dist/services/sandbox/client.js +276 -15
- package/dist/services/sandbox/client.js.map +1 -1
- package/dist/services/sandbox/create.d.ts +5 -0
- package/dist/services/sandbox/create.d.ts.map +1 -1
- package/dist/services/sandbox/create.js +11 -0
- package/dist/services/sandbox/create.js.map +1 -1
- package/dist/services/sandbox/execute.d.ts.map +1 -1
- package/dist/services/sandbox/execute.js +22 -11
- package/dist/services/sandbox/execute.js.map +1 -1
- package/dist/services/sandbox/execution.d.ts +1 -0
- package/dist/services/sandbox/execution.d.ts.map +1 -1
- package/dist/services/sandbox/execution.js +4 -2
- package/dist/services/sandbox/execution.js.map +1 -1
- package/dist/services/sandbox/files.js +1 -1
- package/dist/services/sandbox/files.js.map +1 -1
- package/dist/services/sandbox/index.d.ts +3 -1
- package/dist/services/sandbox/index.d.ts.map +1 -1
- package/dist/services/sandbox/index.js +1 -0
- package/dist/services/sandbox/index.js.map +1 -1
- package/dist/services/sandbox/job.d.ts +227 -0
- package/dist/services/sandbox/job.d.ts.map +1 -0
- package/dist/services/sandbox/job.js +109 -0
- package/dist/services/sandbox/job.js.map +1 -0
- package/dist/services/sandbox/run.d.ts +1 -0
- package/dist/services/sandbox/run.d.ts.map +1 -1
- package/dist/services/sandbox/run.js +83 -30
- package/dist/services/sandbox/run.js.map +1 -1
- package/dist/services/sandbox/types.d.ts +45 -0
- package/dist/services/sandbox/types.d.ts.map +1 -1
- package/dist/services/sandbox/types.js +42 -0
- package/dist/services/sandbox/types.js.map +1 -1
- package/dist/services/sandbox/util.d.ts +1 -0
- package/dist/services/sandbox/util.d.ts.map +1 -1
- package/dist/services/sandbox/util.js +1 -0
- package/dist/services/sandbox/util.js.map +1 -1
- package/dist/services/schedule/service.d.ts +5 -0
- package/dist/services/schedule/service.d.ts.map +1 -1
- package/dist/services/schedule/service.js +16 -0
- package/dist/services/schedule/service.js.map +1 -1
- package/dist/services/schedule/types.d.ts +1 -0
- package/dist/services/schedule/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/deprecation.ts +120 -0
- package/src/index.ts +3 -0
- package/src/services/api.ts +15 -5
- package/src/services/oauth/flow.ts +356 -0
- package/src/services/oauth/index.ts +2 -0
- package/src/services/oauth/token-storage.ts +220 -0
- package/src/services/oauth/types.ts +95 -0
- package/src/services/project/get.ts +9 -0
- package/src/services/sandbox/client.ts +446 -16
- package/src/services/sandbox/create.ts +13 -0
- package/src/services/sandbox/execute.ts +26 -12
- package/src/services/sandbox/execution.ts +5 -2
- package/src/services/sandbox/files.ts +1 -1
- package/src/services/sandbox/index.ts +20 -0
- package/src/services/sandbox/job.ts +161 -0
- package/src/services/sandbox/run.ts +129 -34
- package/src/services/sandbox/types.ts +50 -0
- package/src/services/sandbox/util.ts +1 -0
- package/src/services/schedule/service.ts +20 -0
|
@@ -5,9 +5,16 @@ import {
|
|
|
5
5
|
type SandboxInfo,
|
|
6
6
|
type SandboxStatus,
|
|
7
7
|
type Execution,
|
|
8
|
+
type ExecutionStatus,
|
|
8
9
|
type FileToWrite,
|
|
9
10
|
type SandboxRunOptions,
|
|
10
11
|
type SandboxRunResult,
|
|
12
|
+
type ListSandboxesParams,
|
|
13
|
+
type ListSandboxesResponse,
|
|
14
|
+
type ListRuntimesParams,
|
|
15
|
+
type ListRuntimesResponse,
|
|
16
|
+
type Job,
|
|
17
|
+
type CreateJobOptions,
|
|
11
18
|
} from './types.ts';
|
|
12
19
|
import type { Logger } from '../../logger.ts';
|
|
13
20
|
import type { Readable, Writable } from 'node:stream';
|
|
@@ -30,18 +37,59 @@ import {
|
|
|
30
37
|
import { sandboxPause } from './pause.ts';
|
|
31
38
|
import { sandboxResume } from './resume.ts';
|
|
32
39
|
import { sandboxRun } from './run.ts';
|
|
33
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
executionGet,
|
|
42
|
+
executionList,
|
|
43
|
+
type ExecutionInfo,
|
|
44
|
+
type ExecutionListResponse,
|
|
45
|
+
} from './execution.ts';
|
|
34
46
|
import { createMinimalLogger } from '../logger.ts';
|
|
35
47
|
import { getServiceUrls } from '../config.ts';
|
|
36
48
|
import { writeAndDrain } from './util.ts';
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
import { sandboxList } from './list.ts';
|
|
50
|
+
import { runtimeList } from './runtime.ts';
|
|
51
|
+
import { jobCreate, jobGet, jobList, jobStop, type JobListResponse } from './job.ts';
|
|
52
|
+
import {
|
|
53
|
+
diskCheckpointCreate,
|
|
54
|
+
diskCheckpointList,
|
|
55
|
+
diskCheckpointRestore,
|
|
56
|
+
diskCheckpointDelete,
|
|
57
|
+
type DiskCheckpointInfo,
|
|
58
|
+
} from './disk-checkpoint.ts';
|
|
59
|
+
import {
|
|
60
|
+
snapshotCreate,
|
|
61
|
+
snapshotGet,
|
|
62
|
+
snapshotList,
|
|
63
|
+
snapshotDelete,
|
|
64
|
+
snapshotTag,
|
|
65
|
+
snapshotLineage,
|
|
66
|
+
type SnapshotInfo,
|
|
67
|
+
type SnapshotListResponse,
|
|
68
|
+
type SnapshotLineageResponse,
|
|
69
|
+
type SnapshotListParams,
|
|
70
|
+
type SnapshotLineageParams,
|
|
71
|
+
} from './snapshot.ts';
|
|
72
|
+
import { sandboxEventList, type SandboxEventListResponse } from './events.ts';
|
|
73
|
+
|
|
74
|
+
// Server-side long-poll wait duration per iteration (max 5 minutes supported by server)
|
|
39
75
|
const EXECUTION_WAIT_DURATION = '5m';
|
|
40
76
|
|
|
77
|
+
/** Terminal execution statuses that indicate the command has finished. */
|
|
78
|
+
const TERMINAL_STATUSES: Set<ExecutionStatus> = new Set([
|
|
79
|
+
'completed',
|
|
80
|
+
'failed',
|
|
81
|
+
'timeout',
|
|
82
|
+
'cancelled',
|
|
83
|
+
]);
|
|
84
|
+
|
|
41
85
|
/**
|
|
42
|
-
* Wait for execution completion using server-side long-polling.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
86
|
+
* Wait for execution completion using server-side long-polling with automatic retry.
|
|
87
|
+
*
|
|
88
|
+
* Each iteration asks the server to hold the connection for up to
|
|
89
|
+
* EXECUTION_WAIT_DURATION. If the execution is still running when the
|
|
90
|
+
* server-side wait expires, we loop and issue another long-poll request.
|
|
91
|
+
* This continues until the execution reaches a terminal state or the
|
|
92
|
+
* caller's AbortSignal fires.
|
|
45
93
|
*/
|
|
46
94
|
async function waitForExecution(
|
|
47
95
|
client: APIClient,
|
|
@@ -49,17 +97,30 @@ async function waitForExecution(
|
|
|
49
97
|
orgId?: string,
|
|
50
98
|
signal?: AbortSignal
|
|
51
99
|
): Promise<ExecutionInfo> {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
100
|
+
while (true) {
|
|
101
|
+
if (signal?.aborted) {
|
|
102
|
+
throw new DOMException('The operation was aborted.', 'AbortError');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Use server-side long-polling - the server will hold the connection
|
|
106
|
+
// until the execution reaches a terminal state or the wait duration expires.
|
|
107
|
+
// The signal is forwarded so the in-flight fetch is cancelled immediately
|
|
108
|
+
// when the caller aborts, rather than waiting the full poll duration.
|
|
109
|
+
const result = await executionGet(client, {
|
|
110
|
+
executionId,
|
|
111
|
+
orgId,
|
|
112
|
+
wait: EXECUTION_WAIT_DURATION,
|
|
113
|
+
signal,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// If the execution reached a terminal state, return immediately
|
|
117
|
+
if (TERMINAL_STATUSES.has(result.status as ExecutionStatus)) {
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
55
120
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
executionId,
|
|
60
|
-
orgId,
|
|
61
|
-
wait: EXECUTION_WAIT_DURATION,
|
|
62
|
-
});
|
|
121
|
+
// Non-terminal status (e.g., 'running', 'queued') — the server-side
|
|
122
|
+
// long-poll expired before the command finished. Loop to poll again.
|
|
123
|
+
}
|
|
63
124
|
}
|
|
64
125
|
|
|
65
126
|
/**
|
|
@@ -350,6 +411,117 @@ function createSandboxInstanceMethods(
|
|
|
350
411
|
};
|
|
351
412
|
}
|
|
352
413
|
|
|
414
|
+
/**
|
|
415
|
+
* A job instance returned by SandboxClient.createJob() or SandboxClient.getJob()
|
|
416
|
+
*/
|
|
417
|
+
export interface JobInstance {
|
|
418
|
+
/**
|
|
419
|
+
* Unique job identifier
|
|
420
|
+
*/
|
|
421
|
+
readonly id: string;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* ID of the sandbox this job belongs to
|
|
425
|
+
*/
|
|
426
|
+
readonly sandboxId: string;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Current job status
|
|
430
|
+
*/
|
|
431
|
+
readonly status: string;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Get the current job status and details
|
|
435
|
+
*/
|
|
436
|
+
get(): Promise<Job>;
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Stop the job
|
|
440
|
+
* @param force - Force termination with SIGKILL
|
|
441
|
+
*/
|
|
442
|
+
stop(force?: boolean): Promise<Job>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Creates the method implementations for JobInstance
|
|
447
|
+
*/
|
|
448
|
+
function createJobInstanceMethods(
|
|
449
|
+
client: APIClient,
|
|
450
|
+
sandboxId: string,
|
|
451
|
+
jobId: string,
|
|
452
|
+
orgId?: string
|
|
453
|
+
): Omit<JobInstance, 'id' | 'sandboxId' | 'status'> {
|
|
454
|
+
return {
|
|
455
|
+
async get(): Promise<Job> {
|
|
456
|
+
return jobGet(client, { sandboxId, jobId, orgId });
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
async stop(force?: boolean): Promise<Job> {
|
|
460
|
+
return jobStop(client, { sandboxId, jobId, force, orgId });
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* A disk checkpoint instance returned by SandboxClient.createDiskCheckpoint() or SandboxClient.getDiskCheckpoint()
|
|
467
|
+
*/
|
|
468
|
+
export interface DiskCheckpointInstance {
|
|
469
|
+
/**
|
|
470
|
+
* Unique checkpoint identifier
|
|
471
|
+
*/
|
|
472
|
+
readonly id: string;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* User-provided checkpoint name
|
|
476
|
+
*/
|
|
477
|
+
readonly name: string;
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* ID of the sandbox this checkpoint belongs to
|
|
481
|
+
*/
|
|
482
|
+
readonly sandboxId: string;
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* ISO timestamp of creation
|
|
486
|
+
*/
|
|
487
|
+
readonly createdAt: string;
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Parent checkpoint name
|
|
491
|
+
*/
|
|
492
|
+
readonly parent: string;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Restore the sandbox to this checkpoint
|
|
496
|
+
*/
|
|
497
|
+
restore(): Promise<void>;
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Delete this checkpoint
|
|
501
|
+
*/
|
|
502
|
+
delete(): Promise<void>;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Creates the method implementations for DiskCheckpointInstance
|
|
507
|
+
*/
|
|
508
|
+
function createDiskCheckpointInstanceMethods(
|
|
509
|
+
client: APIClient,
|
|
510
|
+
sandboxId: string,
|
|
511
|
+
checkpointId: string,
|
|
512
|
+
orgId?: string
|
|
513
|
+
): Omit<DiskCheckpointInstance, 'id' | 'name' | 'sandboxId' | 'createdAt' | 'parent'> {
|
|
514
|
+
return {
|
|
515
|
+
async restore(): Promise<void> {
|
|
516
|
+
return diskCheckpointRestore(client, { sandboxId, checkpointId, orgId });
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
async delete(): Promise<void> {
|
|
520
|
+
return diskCheckpointDelete(client, { sandboxId, checkpointId, orgId });
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
353
525
|
/**
|
|
354
526
|
* Convenience client for sandbox operations.
|
|
355
527
|
*
|
|
@@ -563,4 +735,262 @@ export class SandboxClient {
|
|
|
563
735
|
async resume(sandboxId: string): Promise<void> {
|
|
564
736
|
return sandboxResume(this.#client, { sandboxId, orgId: this.#orgId });
|
|
565
737
|
}
|
|
738
|
+
|
|
739
|
+
// ===== List Operations =====
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* List all sandboxes with optional filtering and pagination
|
|
743
|
+
*
|
|
744
|
+
* @param params - Optional parameters for filtering by project, status, and pagination
|
|
745
|
+
* @returns Paginated list of sandboxes with total count
|
|
746
|
+
*/
|
|
747
|
+
async list(params?: ListSandboxesParams): Promise<ListSandboxesResponse> {
|
|
748
|
+
return sandboxList(this.#client, { ...params, orgId: this.#orgId });
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* List available sandbox runtimes
|
|
753
|
+
*
|
|
754
|
+
* @param params - Optional parameters for pagination
|
|
755
|
+
* @returns List of runtimes with total count
|
|
756
|
+
*/
|
|
757
|
+
async listRuntimes(params?: ListRuntimesParams): Promise<ListRuntimesResponse> {
|
|
758
|
+
return runtimeList(this.#client, { ...params, orgId: this.#orgId });
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// ===== Job Operations =====
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Create a new job in a sandbox
|
|
765
|
+
*
|
|
766
|
+
* @param sandboxId - The sandbox ID where the job should run
|
|
767
|
+
* @param options - Job creation options including command
|
|
768
|
+
* @returns A job instance with get() and stop() methods
|
|
769
|
+
*/
|
|
770
|
+
async createJob(sandboxId: string, options: CreateJobOptions): Promise<JobInstance> {
|
|
771
|
+
const job = await jobCreate(this.#client, { sandboxId, options, orgId: this.#orgId });
|
|
772
|
+
|
|
773
|
+
return {
|
|
774
|
+
id: job.jobId,
|
|
775
|
+
sandboxId,
|
|
776
|
+
status: job.status,
|
|
777
|
+
...createJobInstanceMethods(this.#client, sandboxId, job.jobId, this.#orgId),
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Get a job instance by ID
|
|
783
|
+
*
|
|
784
|
+
* @param sandboxId - The sandbox ID
|
|
785
|
+
* @param jobId - The job ID
|
|
786
|
+
* @returns A job instance with get() and stop() methods
|
|
787
|
+
*/
|
|
788
|
+
async getJob(sandboxId: string, jobId: string): Promise<JobInstance> {
|
|
789
|
+
const job = await jobGet(this.#client, { sandboxId, jobId, orgId: this.#orgId });
|
|
790
|
+
|
|
791
|
+
return {
|
|
792
|
+
id: job.jobId,
|
|
793
|
+
sandboxId,
|
|
794
|
+
status: job.status,
|
|
795
|
+
...createJobInstanceMethods(this.#client, sandboxId, job.jobId, this.#orgId),
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* List all jobs in a sandbox
|
|
801
|
+
*
|
|
802
|
+
* @param sandboxId - The sandbox ID
|
|
803
|
+
* @param limit - Maximum number of results
|
|
804
|
+
* @returns List of jobs
|
|
805
|
+
*/
|
|
806
|
+
async listJobs(sandboxId: string, limit?: number): Promise<JobListResponse> {
|
|
807
|
+
return jobList(this.#client, { sandboxId, limit, orgId: this.#orgId });
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// ===== Disk Checkpoint Operations =====
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Create a disk checkpoint of a sandbox
|
|
814
|
+
*
|
|
815
|
+
* @param sandboxId - The sandbox ID
|
|
816
|
+
* @param name - Name for the checkpoint
|
|
817
|
+
* @returns A checkpoint instance with restore() and delete() methods
|
|
818
|
+
*/
|
|
819
|
+
async createDiskCheckpoint(sandboxId: string, name: string): Promise<DiskCheckpointInstance> {
|
|
820
|
+
const checkpoint = await diskCheckpointCreate(this.#client, {
|
|
821
|
+
sandboxId,
|
|
822
|
+
name,
|
|
823
|
+
orgId: this.#orgId,
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
return {
|
|
827
|
+
id: checkpoint.id,
|
|
828
|
+
name: checkpoint.name,
|
|
829
|
+
sandboxId,
|
|
830
|
+
createdAt: checkpoint.createdAt,
|
|
831
|
+
parent: checkpoint.parent,
|
|
832
|
+
...createDiskCheckpointInstanceMethods(
|
|
833
|
+
this.#client,
|
|
834
|
+
sandboxId,
|
|
835
|
+
checkpoint.id,
|
|
836
|
+
this.#orgId
|
|
837
|
+
),
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* List all disk checkpoints for a sandbox
|
|
843
|
+
*
|
|
844
|
+
* @param sandboxId - The sandbox ID
|
|
845
|
+
* @returns List of checkpoint info objects
|
|
846
|
+
*/
|
|
847
|
+
async listDiskCheckpoints(sandboxId: string): Promise<DiskCheckpointInfo[]> {
|
|
848
|
+
return diskCheckpointList(this.#client, { sandboxId, orgId: this.#orgId });
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Get a disk checkpoint instance by ID
|
|
853
|
+
*
|
|
854
|
+
* @param sandboxId - The sandbox ID
|
|
855
|
+
* @param checkpointId - The checkpoint ID
|
|
856
|
+
* @returns A checkpoint instance with restore() and delete() methods
|
|
857
|
+
*/
|
|
858
|
+
async getDiskCheckpoint(
|
|
859
|
+
sandboxId: string,
|
|
860
|
+
checkpointId: string
|
|
861
|
+
): Promise<DiskCheckpointInstance> {
|
|
862
|
+
const checkpoints = await diskCheckpointList(this.#client, {
|
|
863
|
+
sandboxId,
|
|
864
|
+
orgId: this.#orgId,
|
|
865
|
+
});
|
|
866
|
+
const checkpoint = checkpoints.find((c) => c.id === checkpointId);
|
|
867
|
+
if (!checkpoint) {
|
|
868
|
+
throw new Error(`Checkpoint ${checkpointId} not found in sandbox ${sandboxId}`);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return {
|
|
872
|
+
id: checkpoint.id,
|
|
873
|
+
name: checkpoint.name,
|
|
874
|
+
sandboxId,
|
|
875
|
+
createdAt: checkpoint.createdAt,
|
|
876
|
+
parent: checkpoint.parent,
|
|
877
|
+
...createDiskCheckpointInstanceMethods(
|
|
878
|
+
this.#client,
|
|
879
|
+
sandboxId,
|
|
880
|
+
checkpoint.id,
|
|
881
|
+
this.#orgId
|
|
882
|
+
),
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// ===== Snapshot Operations =====
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Create a snapshot of a sandbox
|
|
890
|
+
*
|
|
891
|
+
* @param sandboxId - The sandbox ID to snapshot
|
|
892
|
+
* @param params - Optional snapshot parameters (name, tag, public, etc.)
|
|
893
|
+
* @returns The created snapshot information
|
|
894
|
+
*/
|
|
895
|
+
async createSnapshot(
|
|
896
|
+
sandboxId: string,
|
|
897
|
+
params?: {
|
|
898
|
+
name?: string;
|
|
899
|
+
description?: string;
|
|
900
|
+
tag?: string;
|
|
901
|
+
public?: boolean;
|
|
902
|
+
}
|
|
903
|
+
): Promise<SnapshotInfo> {
|
|
904
|
+
return snapshotCreate(this.#client, { sandboxId, ...params, orgId: this.#orgId });
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Get snapshot information by ID
|
|
909
|
+
*
|
|
910
|
+
* @param snapshotId - The snapshot ID
|
|
911
|
+
* @returns Snapshot information
|
|
912
|
+
*/
|
|
913
|
+
async getSnapshot(snapshotId: string): Promise<SnapshotInfo> {
|
|
914
|
+
return snapshotGet(this.#client, { snapshotId, orgId: this.#orgId });
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* List snapshots with optional filtering and pagination
|
|
919
|
+
*
|
|
920
|
+
* @param params - Optional parameters for filtering and pagination
|
|
921
|
+
* @returns Paginated list of snapshots
|
|
922
|
+
*/
|
|
923
|
+
async listSnapshots(params?: SnapshotListParams): Promise<SnapshotListResponse> {
|
|
924
|
+
return snapshotList(this.#client, { ...params, orgId: this.#orgId });
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Delete a snapshot
|
|
929
|
+
*
|
|
930
|
+
* @param snapshotId - The snapshot ID to delete
|
|
931
|
+
*/
|
|
932
|
+
async deleteSnapshot(snapshotId: string): Promise<void> {
|
|
933
|
+
return snapshotDelete(this.#client, { snapshotId, orgId: this.#orgId });
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Update the tag on a snapshot
|
|
938
|
+
*
|
|
939
|
+
* @param snapshotId - The snapshot ID
|
|
940
|
+
* @param tag - New tag (or null to remove)
|
|
941
|
+
* @returns Updated snapshot information
|
|
942
|
+
*/
|
|
943
|
+
async tagSnapshot(snapshotId: string, tag: string | null): Promise<SnapshotInfo> {
|
|
944
|
+
return snapshotTag(this.#client, { snapshotId, tag, orgId: this.#orgId });
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Get the lineage (ancestry chain) of a snapshot
|
|
949
|
+
*
|
|
950
|
+
* @param params - Parameters specifying which snapshot to get lineage for
|
|
951
|
+
* @returns Ordered list of snapshots in the lineage
|
|
952
|
+
*/
|
|
953
|
+
async getSnapshotLineage(params?: SnapshotLineageParams): Promise<SnapshotLineageResponse> {
|
|
954
|
+
return snapshotLineage(this.#client, { ...params, orgId: this.#orgId });
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// ===== Execution Operations =====
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* Get execution information by ID
|
|
961
|
+
*
|
|
962
|
+
* @param executionId - The execution ID
|
|
963
|
+
* @param wait - Optional wait duration for long-polling (e.g., "5m")
|
|
964
|
+
* @returns Execution information
|
|
965
|
+
*/
|
|
966
|
+
async getExecution(executionId: string, wait?: string): Promise<ExecutionInfo> {
|
|
967
|
+
return executionGet(this.#client, { executionId, wait, orgId: this.#orgId });
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* List executions for a sandbox
|
|
972
|
+
*
|
|
973
|
+
* @param sandboxId - The sandbox ID
|
|
974
|
+
* @param limit - Maximum number of results
|
|
975
|
+
* @returns List of executions
|
|
976
|
+
*/
|
|
977
|
+
async listExecutions(sandboxId: string, limit?: number): Promise<ExecutionListResponse> {
|
|
978
|
+
return executionList(this.#client, { sandboxId, limit, orgId: this.#orgId });
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// ===== Event Operations =====
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* List events for a sandbox
|
|
985
|
+
*
|
|
986
|
+
* @param sandboxId - The sandbox ID
|
|
987
|
+
* @param params - Optional parameters for limit and sort direction
|
|
988
|
+
* @returns List of sandbox events
|
|
989
|
+
*/
|
|
990
|
+
async listEvents(
|
|
991
|
+
sandboxId: string,
|
|
992
|
+
params?: { limit?: number; direction?: 'asc' | 'desc' }
|
|
993
|
+
): Promise<SandboxEventListResponse> {
|
|
994
|
+
return sandboxEventList(this.#client, { sandboxId, ...params, orgId: this.#orgId });
|
|
995
|
+
}
|
|
566
996
|
}
|
|
@@ -102,6 +102,12 @@ export const SandboxCreateRequestSchema = z
|
|
|
102
102
|
.record(z.string(), z.unknown())
|
|
103
103
|
.optional()
|
|
104
104
|
.describe('Optional user-defined metadata to associate with the sandbox'),
|
|
105
|
+
scopes: z
|
|
106
|
+
.array(z.string())
|
|
107
|
+
.optional()
|
|
108
|
+
.describe(
|
|
109
|
+
'Permission scopes for automatic service access (e.g., "services:read", "services:write").'
|
|
110
|
+
),
|
|
105
111
|
})
|
|
106
112
|
.refine(
|
|
107
113
|
(data) => {
|
|
@@ -133,6 +139,10 @@ export const SandboxCreateDataSchema = z
|
|
|
133
139
|
'failed',
|
|
134
140
|
])
|
|
135
141
|
.describe('Current status of the sandbox'),
|
|
142
|
+
url: z
|
|
143
|
+
.string()
|
|
144
|
+
.optional()
|
|
145
|
+
.describe('Public URL for the sandbox (only set when a network port is configured)'),
|
|
136
146
|
stdoutStreamId: z.string().optional().describe('Stream ID for reading stdout'),
|
|
137
147
|
stdoutStreamUrl: z.string().optional().describe('URL for streaming stdout output'),
|
|
138
148
|
stderrStreamId: z.string().optional().describe('Stream ID for reading stderr'),
|
|
@@ -226,6 +236,9 @@ export async function sandboxCreate(
|
|
|
226
236
|
if (options.metadata) {
|
|
227
237
|
body.metadata = options.metadata;
|
|
228
238
|
}
|
|
239
|
+
if (options.scopes && options.scopes.length > 0) {
|
|
240
|
+
body.scopes = options.scopes;
|
|
241
|
+
}
|
|
229
242
|
|
|
230
243
|
const queryParams = new URLSearchParams();
|
|
231
244
|
if (orgId) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExecuteOptions, Execution, ExecutionStatus } from './types.ts';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { APIClient } from '../api.ts';
|
|
4
|
-
import { SandboxBusyError, throwSandboxError } from './util.ts';
|
|
4
|
+
import { SandboxBusyError, SandboxNotFoundError, throwSandboxError } from './util.ts';
|
|
5
5
|
|
|
6
6
|
export const ExecuteRequestSchema = z
|
|
7
7
|
.object({
|
|
@@ -114,23 +114,37 @@ export async function sandboxExecute(
|
|
|
114
114
|
signal ?? options.signal
|
|
115
115
|
);
|
|
116
116
|
} catch (error: unknown) {
|
|
117
|
-
// Detect 409 Conflict (sandbox busy) and throw a specific error.
|
|
118
|
-
// The sandbox API client is configured with maxRetries: 0 to fail fast
|
|
119
|
-
// when sandbox is busy (retrying wouldn't help - sandbox is still busy).
|
|
120
|
-
// Convert APIErrorResponse with status 409 to SandboxBusyError for clarity.
|
|
121
117
|
if (
|
|
122
118
|
error &&
|
|
123
119
|
typeof error === 'object' &&
|
|
124
120
|
'_tag' in error &&
|
|
125
121
|
(error as { _tag: string })._tag === 'APIErrorResponse' &&
|
|
126
|
-
'status' in error
|
|
127
|
-
(error as { status: number }).status === 409
|
|
122
|
+
'status' in error
|
|
128
123
|
) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
124
|
+
const status = (error as { status: number }).status;
|
|
125
|
+
|
|
126
|
+
// Detect 404 Not Found — sandbox may not exist or may still be initializing.
|
|
127
|
+
// The server normally handles the creating→idle wait transparently, but this
|
|
128
|
+
// provides a clear typed error if a 404 reaches the SDK for any reason.
|
|
129
|
+
if (status === 404) {
|
|
130
|
+
throw new SandboxNotFoundError({
|
|
131
|
+
message:
|
|
132
|
+
'Sandbox not found. If you just created this sandbox, it may still be initializing. The server should handle this automatically — if this persists, the sandbox may have been destroyed.',
|
|
133
|
+
sandboxId,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Detect 409 Conflict (sandbox busy) and throw a specific error.
|
|
138
|
+
// The sandbox API client is configured with maxRetries: 0 to fail fast
|
|
139
|
+
// when sandbox is busy (retrying wouldn't help - sandbox is still busy).
|
|
140
|
+
// Convert APIErrorResponse with status 409 to SandboxBusyError for clarity.
|
|
141
|
+
if (status === 409) {
|
|
142
|
+
throw new SandboxBusyError({
|
|
143
|
+
message:
|
|
144
|
+
'Sandbox is currently busy executing another command. Please wait for the current execution to complete before submitting a new one.',
|
|
145
|
+
sandboxId,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
134
148
|
}
|
|
135
149
|
throw error;
|
|
136
150
|
}
|
|
@@ -44,6 +44,8 @@ export const ExecutionGetParamsSchema = z.object({
|
|
|
44
44
|
orgId: z.string().optional().describe('organization id'),
|
|
45
45
|
/** Optional wait duration for long-polling. */
|
|
46
46
|
wait: z.string().optional().describe('wait duration for long-polling'),
|
|
47
|
+
/** Optional AbortSignal to cancel the in-flight request. */
|
|
48
|
+
signal: z.custom<AbortSignal>().optional().describe('abort signal for cancellation'),
|
|
47
49
|
});
|
|
48
50
|
export type ExecutionGetParams = z.infer<typeof ExecutionGetParamsSchema>;
|
|
49
51
|
|
|
@@ -67,7 +69,7 @@ export async function executionGet(
|
|
|
67
69
|
client: APIClient,
|
|
68
70
|
params: ExecutionGetParams
|
|
69
71
|
): Promise<ExecutionInfo> {
|
|
70
|
-
const { executionId, orgId, wait } = params;
|
|
72
|
+
const { executionId, orgId, wait, signal } = params;
|
|
71
73
|
const queryParams = new URLSearchParams();
|
|
72
74
|
if (orgId) {
|
|
73
75
|
queryParams.set('orgId', orgId);
|
|
@@ -80,7 +82,8 @@ export async function executionGet(
|
|
|
80
82
|
|
|
81
83
|
const resp = await client.get<z.infer<typeof ExecutionGetResponseSchema>>(
|
|
82
84
|
url,
|
|
83
|
-
ExecutionGetResponseSchema
|
|
85
|
+
ExecutionGetResponseSchema,
|
|
86
|
+
signal
|
|
84
87
|
);
|
|
85
88
|
|
|
86
89
|
if (resp.success) {
|
|
@@ -124,7 +124,7 @@ export async function sandboxReadFile(
|
|
|
124
124
|
if (!response.ok) {
|
|
125
125
|
const text = await response.text().catch(() => 'Unknown error');
|
|
126
126
|
throw new SandboxResponseError({
|
|
127
|
-
message: `Failed to read file: ${response.status} ${text}`,
|
|
127
|
+
message: `Failed to read file "${path}": ${response.status} ${text}`,
|
|
128
128
|
sandboxId,
|
|
129
129
|
sessionId,
|
|
130
130
|
});
|
|
@@ -11,6 +11,8 @@ export type {
|
|
|
11
11
|
SandboxClientOptions,
|
|
12
12
|
SandboxClientRunIO,
|
|
13
13
|
SandboxInstance,
|
|
14
|
+
JobInstance,
|
|
15
|
+
DiskCheckpointInstance,
|
|
14
16
|
} from './client.ts';
|
|
15
17
|
export {
|
|
16
18
|
ExecuteOptionsSchema as SandboxClientExecuteOptionsSchema,
|
|
@@ -78,6 +80,24 @@ export {
|
|
|
78
80
|
executionGet,
|
|
79
81
|
executionList,
|
|
80
82
|
} from './execution.ts';
|
|
83
|
+
export type {
|
|
84
|
+
JobCreateParams,
|
|
85
|
+
JobGetParams,
|
|
86
|
+
JobListParams,
|
|
87
|
+
JobListResponse,
|
|
88
|
+
JobStopParams,
|
|
89
|
+
} from './job.ts';
|
|
90
|
+
export {
|
|
91
|
+
JobCreateParamsSchema,
|
|
92
|
+
JobGetParamsSchema,
|
|
93
|
+
JobListParamsSchema,
|
|
94
|
+
JobListResponseSchema,
|
|
95
|
+
JobStopParamsSchema,
|
|
96
|
+
jobCreate,
|
|
97
|
+
jobGet,
|
|
98
|
+
jobList,
|
|
99
|
+
jobStop,
|
|
100
|
+
} from './job.ts';
|
|
81
101
|
export type {
|
|
82
102
|
SandboxEventInfo,
|
|
83
103
|
SandboxEventListParams,
|