@declaw/sdk 1.0.3 → 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,44 @@ 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
+
31
+ ## [1.0.4]
32
+
33
+ ### Changed
34
+
35
+ - `InjectionAction` — dropped the `"sanitize"` value. The server
36
+ never implemented request-body sanitization for detected injections
37
+ (the classifier returns a whole-request verdict, not span offsets),
38
+ so the value was accepted client-side but silently behaved like
39
+ `log_only` at the edge proxy. Valid values are now `"block"` and
40
+ `"log_only"`. Default action changes from `"sanitize"` to
41
+ `"log_only"` so existing enforcement behaviour is preserved.
42
+ Callers passing `action: "sanitize"` will now raise at construction
43
+ time; migrate to `"log_only"` (same behaviour) or `"block"`
44
+ (hard-reject on detection).
45
+
8
46
  ## [1.0.3]
9
47
 
10
48
  ### Added
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
 
@@ -506,7 +509,6 @@ var InjectionSensitivity = /* @__PURE__ */ ((InjectionSensitivity2) => {
506
509
  })(InjectionSensitivity || {});
507
510
  var InjectionAction = /* @__PURE__ */ ((InjectionAction2) => {
508
511
  InjectionAction2["Block"] = "block";
509
- InjectionAction2["Sanitize"] = "sanitize";
510
512
  InjectionAction2["LogOnly"] = "log_only";
511
513
  return InjectionAction2;
512
514
  })(InjectionAction || {});
@@ -516,7 +518,7 @@ function createInjectionDefenseConfig(opts) {
516
518
  const config = {
517
519
  enabled: opts?.enabled ?? false,
518
520
  sensitivity: opts?.sensitivity ?? "medium" /* Medium */,
519
- action: opts?.action ?? "sanitize" /* Sanitize */,
521
+ action: opts?.action ?? "log_only" /* LogOnly */,
520
522
  threshold: opts?.threshold ?? 0.8,
521
523
  domains: opts?.domains
522
524
  };
@@ -541,7 +543,7 @@ function parseInjectionDefenseConfig(data) {
541
543
  return {
542
544
  enabled: data.enabled ?? false,
543
545
  sensitivity: data.sensitivity ?? "medium" /* Medium */,
544
- action: data.action ?? "sanitize" /* Sanitize */,
546
+ action: data.action ?? "log_only" /* LogOnly */,
545
547
  threshold: data.threshold ?? 0.8,
546
548
  domains: data.domains
547
549
  };
@@ -1796,6 +1798,23 @@ var Pty = class {
1796
1798
  }
1797
1799
  };
1798
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
+
1799
1818
  // src/sandbox/sandbox.ts
1800
1819
  var DEFAULT_TEMPLATE = "base";
1801
1820
  var DEFAULT_TIMEOUT = 300;
@@ -1952,6 +1971,9 @@ var Sandbox = class _Sandbox {
1952
1971
  if (opts?.lifecycle) {
1953
1972
  body.lifecycle = lifecycleToJSON(opts.lifecycle);
1954
1973
  }
1974
+ if (opts?.volumes && opts.volumes.length > 0) {
1975
+ body.volumes = opts.volumes.map(volumeAttachmentToJSON);
1976
+ }
1955
1977
  const data = await client.post("/sandboxes", {
1956
1978
  json: body,
1957
1979
  timeout: opts?.requestTimeout
@@ -2545,6 +2567,97 @@ var Template = class {
2545
2567
  }
2546
2568
  }
2547
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
+ };
2548
2661
  // Annotate the CommonJS export names for ESM import in node:
2549
2662
  0 && (module.exports = {
2550
2663
  ALL_TRAFFIC,
@@ -2581,6 +2694,7 @@ var Template = class {
2581
2694
  TemplateError,
2582
2695
  TimeoutError,
2583
2696
  TransformDirection,
2697
+ Volumes,
2584
2698
  WatchHandle,
2585
2699
  applyTransformation,
2586
2700
  codeSecurityConfigToJSON,
@@ -2619,10 +2733,12 @@ var Template = class {
2619
2733
  parseSnapshotInfo,
2620
2734
  parseTemplateBuildStatus,
2621
2735
  parseToxicityConfig,
2736
+ parseVolumeInfo,
2622
2737
  parseWriteInfo,
2623
2738
  requiresTlsInterception,
2624
2739
  securityPolicyToJSON,
2625
2740
  toxicityConfigToJSON,
2626
- validateNetworkEntry
2741
+ validateNetworkEntry,
2742
+ volumeAttachmentToJSON
2627
2743
  });
2628
2744
  //# sourceMappingURL=index.cjs.map