@declaw/sdk 1.0.4 → 1.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ All notable changes to the Declaw TypeScript / JavaScript SDK are documented in
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0]
9
+
10
+ ### Added
11
+
12
+ - **Volumes API.** Upload a tarball once, attach it to one or many
13
+ sandboxes at create time. The server streams the blob from object
14
+ storage into each sandbox's overlay filesystem at boot — so N
15
+ parallel sandboxes share a dataset without N uploads.
16
+ - New `Volumes` class with static `create` / `get` / `list` /
17
+ `delete` methods. Body is a `Uint8Array` or `ArrayBuffer`;
18
+ streams end-to-end with no in-memory buffering on the server.
19
+ - New types: `VolumeInfo`, `VolumeAttachment`, `VolumeCreateOpts`,
20
+ `VolumeRequestOpts` — exported from the package root.
21
+ - `SandboxOpts.volumes?: VolumeAttachment[]` on `Sandbox.create` —
22
+ each attachment is `{ volumeId, mountPath }`. Multiple sandboxes
23
+ can attach the same `volumeId` in parallel.
24
+ - Helper functions `parseVolumeInfo` (wire → shape) and
25
+ `volumeAttachmentToJSON` (shape → wire).
26
+ - Phase 1 limits: upload body capped at 4 GiB; format must be
27
+ `application/gzip` (tar.gz); volumes are read-at-boot (sandbox
28
+ writes do not flow back). Symlinks, hardlinks, device nodes, and
29
+ entries containing `..` are dropped server-side for safety.
30
+
8
31
  ## [1.0.4]
9
32
 
10
33
  ### Changed
package/dist/index.cjs CHANGED
@@ -54,6 +54,7 @@ __export(index_exports, {
54
54
  TemplateError: () => TemplateError,
55
55
  TimeoutError: () => TimeoutError,
56
56
  TransformDirection: () => TransformDirection,
57
+ Volumes: () => Volumes,
57
58
  WatchHandle: () => WatchHandle,
58
59
  applyTransformation: () => applyTransformation,
59
60
  codeSecurityConfigToJSON: () => codeSecurityConfigToJSON,
@@ -92,11 +93,13 @@ __export(index_exports, {
92
93
  parseSnapshotInfo: () => parseSnapshotInfo,
93
94
  parseTemplateBuildStatus: () => parseTemplateBuildStatus,
94
95
  parseToxicityConfig: () => parseToxicityConfig,
96
+ parseVolumeInfo: () => parseVolumeInfo,
95
97
  parseWriteInfo: () => parseWriteInfo,
96
98
  requiresTlsInterception: () => requiresTlsInterception,
97
99
  securityPolicyToJSON: () => securityPolicyToJSON,
98
100
  toxicityConfigToJSON: () => toxicityConfigToJSON,
99
- validateNetworkEntry: () => validateNetworkEntry
101
+ validateNetworkEntry: () => validateNetworkEntry,
102
+ volumeAttachmentToJSON: () => volumeAttachmentToJSON
100
103
  });
101
104
  module.exports = __toCommonJS(index_exports);
102
105
 
@@ -1795,6 +1798,23 @@ var Pty = class {
1795
1798
  }
1796
1799
  };
1797
1800
 
1801
+ // src/volumes/models.ts
1802
+ function parseVolumeInfo(data) {
1803
+ return {
1804
+ volumeId: String(data.volume_id ?? ""),
1805
+ ownerId: String(data.owner_id ?? ""),
1806
+ name: String(data.name ?? ""),
1807
+ blobKey: String(data.blob_key ?? ""),
1808
+ sizeBytes: Number(data.size_bytes ?? 0),
1809
+ contentType: String(data.content_type ?? ""),
1810
+ metadata: data.metadata ?? {},
1811
+ createdAt: String(data.created_at ?? "")
1812
+ };
1813
+ }
1814
+ function volumeAttachmentToJSON(att) {
1815
+ return { volume_id: att.volumeId, mount_path: att.mountPath };
1816
+ }
1817
+
1798
1818
  // src/sandbox/sandbox.ts
1799
1819
  var DEFAULT_TEMPLATE = "base";
1800
1820
  var DEFAULT_TIMEOUT = 300;
@@ -1951,6 +1971,9 @@ var Sandbox = class _Sandbox {
1951
1971
  if (opts?.lifecycle) {
1952
1972
  body.lifecycle = lifecycleToJSON(opts.lifecycle);
1953
1973
  }
1974
+ if (opts?.volumes && opts.volumes.length > 0) {
1975
+ body.volumes = opts.volumes.map(volumeAttachmentToJSON);
1976
+ }
1954
1977
  const data = await client.post("/sandboxes", {
1955
1978
  json: body,
1956
1979
  timeout: opts?.requestTimeout
@@ -2544,6 +2567,97 @@ var Template = class {
2544
2567
  }
2545
2568
  }
2546
2569
  };
2570
+
2571
+ // src/volumes/volumes.ts
2572
+ var VALID_VOLUME_ID_RE = /^[a-zA-Z0-9_-]+$/;
2573
+ function assertValidVolumeId(id) {
2574
+ if (!id || !VALID_VOLUME_ID_RE.test(id)) {
2575
+ throw new InvalidArgumentError(
2576
+ `Invalid volume ID: "${id}". Must be alphanumeric with hyphens/underscores only.`
2577
+ );
2578
+ }
2579
+ }
2580
+ var Volumes = class {
2581
+ /** Create a volume by streaming a tarball to the server. */
2582
+ static async create(name, data, opts) {
2583
+ if (!name) {
2584
+ throw new InvalidArgumentError("volume name is required");
2585
+ }
2586
+ const config = new ConnectionConfig({
2587
+ apiKey: opts?.apiKey,
2588
+ domain: opts?.domain,
2589
+ apiUrl: opts?.apiUrl,
2590
+ requestTimeout: opts?.requestTimeout
2591
+ });
2592
+ const client = new ApiClient(config);
2593
+ try {
2594
+ const body = data instanceof Uint8Array ? data : new Uint8Array(data);
2595
+ const resp = await client.post("/volumes", {
2596
+ params: { name },
2597
+ body,
2598
+ headers: { "Content-Type": opts?.contentType ?? "application/gzip" },
2599
+ timeout: opts?.requestTimeout
2600
+ });
2601
+ return parseVolumeInfo(resp);
2602
+ } finally {
2603
+ client.close();
2604
+ }
2605
+ }
2606
+ /** Fetch metadata for a single volume. */
2607
+ static async get(volumeId, opts) {
2608
+ assertValidVolumeId(volumeId);
2609
+ const config = new ConnectionConfig({
2610
+ apiKey: opts?.apiKey,
2611
+ domain: opts?.domain,
2612
+ apiUrl: opts?.apiUrl,
2613
+ requestTimeout: opts?.requestTimeout
2614
+ });
2615
+ const client = new ApiClient(config);
2616
+ try {
2617
+ const resp = await client.get(`/volumes/${volumeId}`, {
2618
+ timeout: opts?.requestTimeout
2619
+ });
2620
+ return parseVolumeInfo(resp);
2621
+ } finally {
2622
+ client.close();
2623
+ }
2624
+ }
2625
+ /** List all volumes owned by the caller, newest first. */
2626
+ static async list(opts) {
2627
+ const config = new ConnectionConfig({
2628
+ apiKey: opts?.apiKey,
2629
+ domain: opts?.domain,
2630
+ apiUrl: opts?.apiUrl,
2631
+ requestTimeout: opts?.requestTimeout
2632
+ });
2633
+ const client = new ApiClient(config);
2634
+ try {
2635
+ const resp = await client.get("/volumes", {
2636
+ timeout: opts?.requestTimeout
2637
+ });
2638
+ const rows = resp.volumes ?? [];
2639
+ return rows.map(parseVolumeInfo);
2640
+ } finally {
2641
+ client.close();
2642
+ }
2643
+ }
2644
+ /** Delete a volume and its blob. Idempotent on the wire. */
2645
+ static async delete(volumeId, opts) {
2646
+ assertValidVolumeId(volumeId);
2647
+ const config = new ConnectionConfig({
2648
+ apiKey: opts?.apiKey,
2649
+ domain: opts?.domain,
2650
+ apiUrl: opts?.apiUrl,
2651
+ requestTimeout: opts?.requestTimeout
2652
+ });
2653
+ const client = new ApiClient(config);
2654
+ try {
2655
+ await client.delete(`/volumes/${volumeId}`, { timeout: opts?.requestTimeout });
2656
+ } finally {
2657
+ client.close();
2658
+ }
2659
+ }
2660
+ };
2547
2661
  // Annotate the CommonJS export names for ESM import in node:
2548
2662
  0 && (module.exports = {
2549
2663
  ALL_TRAFFIC,
@@ -2580,6 +2694,7 @@ var Template = class {
2580
2694
  TemplateError,
2581
2695
  TimeoutError,
2582
2696
  TransformDirection,
2697
+ Volumes,
2583
2698
  WatchHandle,
2584
2699
  applyTransformation,
2585
2700
  codeSecurityConfigToJSON,
@@ -2618,10 +2733,12 @@ var Template = class {
2618
2733
  parseSnapshotInfo,
2619
2734
  parseTemplateBuildStatus,
2620
2735
  parseToxicityConfig,
2736
+ parseVolumeInfo,
2621
2737
  parseWriteInfo,
2622
2738
  requiresTlsInterception,
2623
2739
  securityPolicyToJSON,
2624
2740
  toxicityConfigToJSON,
2625
- validateNetworkEntry
2741
+ validateNetworkEntry,
2742
+ volumeAttachmentToJSON
2626
2743
  });
2627
2744
  //# sourceMappingURL=index.cjs.map