@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.
Files changed (94) hide show
  1. package/dist/deprecation.d.ts +20 -0
  2. package/dist/deprecation.d.ts.map +1 -0
  3. package/dist/deprecation.js +102 -0
  4. package/dist/deprecation.js.map +1 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/services/api.d.ts.map +1 -1
  10. package/dist/services/api.js +13 -5
  11. package/dist/services/api.js.map +1 -1
  12. package/dist/services/oauth/flow.d.ts +82 -0
  13. package/dist/services/oauth/flow.d.ts.map +1 -0
  14. package/dist/services/oauth/flow.js +308 -0
  15. package/dist/services/oauth/flow.js.map +1 -0
  16. package/dist/services/oauth/index.d.ts +2 -0
  17. package/dist/services/oauth/index.d.ts.map +1 -1
  18. package/dist/services/oauth/index.js +2 -0
  19. package/dist/services/oauth/index.js.map +1 -1
  20. package/dist/services/oauth/token-storage.d.ts +109 -0
  21. package/dist/services/oauth/token-storage.d.ts.map +1 -0
  22. package/dist/services/oauth/token-storage.js +140 -0
  23. package/dist/services/oauth/token-storage.js.map +1 -0
  24. package/dist/services/oauth/types.d.ts +63 -0
  25. package/dist/services/oauth/types.d.ts.map +1 -1
  26. package/dist/services/oauth/types.js +74 -0
  27. package/dist/services/oauth/types.js.map +1 -1
  28. package/dist/services/project/get.d.ts +12 -0
  29. package/dist/services/project/get.d.ts.map +1 -1
  30. package/dist/services/project/get.js +9 -0
  31. package/dist/services/project/get.js.map +1 -1
  32. package/dist/services/sandbox/client.d.ts +201 -1
  33. package/dist/services/sandbox/client.d.ts.map +1 -1
  34. package/dist/services/sandbox/client.js +276 -15
  35. package/dist/services/sandbox/client.js.map +1 -1
  36. package/dist/services/sandbox/create.d.ts +5 -0
  37. package/dist/services/sandbox/create.d.ts.map +1 -1
  38. package/dist/services/sandbox/create.js +11 -0
  39. package/dist/services/sandbox/create.js.map +1 -1
  40. package/dist/services/sandbox/execute.d.ts.map +1 -1
  41. package/dist/services/sandbox/execute.js +22 -11
  42. package/dist/services/sandbox/execute.js.map +1 -1
  43. package/dist/services/sandbox/execution.d.ts +1 -0
  44. package/dist/services/sandbox/execution.d.ts.map +1 -1
  45. package/dist/services/sandbox/execution.js +4 -2
  46. package/dist/services/sandbox/execution.js.map +1 -1
  47. package/dist/services/sandbox/files.js +1 -1
  48. package/dist/services/sandbox/files.js.map +1 -1
  49. package/dist/services/sandbox/index.d.ts +3 -1
  50. package/dist/services/sandbox/index.d.ts.map +1 -1
  51. package/dist/services/sandbox/index.js +1 -0
  52. package/dist/services/sandbox/index.js.map +1 -1
  53. package/dist/services/sandbox/job.d.ts +227 -0
  54. package/dist/services/sandbox/job.d.ts.map +1 -0
  55. package/dist/services/sandbox/job.js +109 -0
  56. package/dist/services/sandbox/job.js.map +1 -0
  57. package/dist/services/sandbox/run.d.ts +1 -0
  58. package/dist/services/sandbox/run.d.ts.map +1 -1
  59. package/dist/services/sandbox/run.js +83 -30
  60. package/dist/services/sandbox/run.js.map +1 -1
  61. package/dist/services/sandbox/types.d.ts +45 -0
  62. package/dist/services/sandbox/types.d.ts.map +1 -1
  63. package/dist/services/sandbox/types.js +42 -0
  64. package/dist/services/sandbox/types.js.map +1 -1
  65. package/dist/services/sandbox/util.d.ts +1 -0
  66. package/dist/services/sandbox/util.d.ts.map +1 -1
  67. package/dist/services/sandbox/util.js +1 -0
  68. package/dist/services/sandbox/util.js.map +1 -1
  69. package/dist/services/schedule/service.d.ts +5 -0
  70. package/dist/services/schedule/service.d.ts.map +1 -1
  71. package/dist/services/schedule/service.js +16 -0
  72. package/dist/services/schedule/service.js.map +1 -1
  73. package/dist/services/schedule/types.d.ts +1 -0
  74. package/dist/services/schedule/types.d.ts.map +1 -1
  75. package/package.json +2 -2
  76. package/src/deprecation.ts +120 -0
  77. package/src/index.ts +3 -0
  78. package/src/services/api.ts +15 -5
  79. package/src/services/oauth/flow.ts +356 -0
  80. package/src/services/oauth/index.ts +2 -0
  81. package/src/services/oauth/token-storage.ts +220 -0
  82. package/src/services/oauth/types.ts +95 -0
  83. package/src/services/project/get.ts +9 -0
  84. package/src/services/sandbox/client.ts +446 -16
  85. package/src/services/sandbox/create.ts +13 -0
  86. package/src/services/sandbox/execute.ts +26 -12
  87. package/src/services/sandbox/execution.ts +5 -2
  88. package/src/services/sandbox/files.ts +1 -1
  89. package/src/services/sandbox/index.ts +20 -0
  90. package/src/services/sandbox/job.ts +161 -0
  91. package/src/services/sandbox/run.ts +129 -34
  92. package/src/services/sandbox/types.ts +50 -0
  93. package/src/services/sandbox/util.ts +1 -0
  94. 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 { executionGet, type ExecutionInfo } from './execution.ts';
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
- // Server-side long-poll wait duration (max 5 minutes supported by server)
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
- * This is more efficient than client-side polling and provides immediate
44
- * error detection if the sandbox is terminated.
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
- if (signal?.aborted) {
53
- throw new DOMException('The operation was aborted.', 'AbortError');
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
- // Use server-side long-polling - the server will hold the connection
57
- // until the execution reaches a terminal state or the wait duration expires
58
- return executionGet(client, {
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
- throw new SandboxBusyError({
130
- message:
131
- 'Sandbox is currently busy executing another command. Please wait for the current execution to complete before submitting a new one.',
132
- sandboxId,
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,