@declaw/sdk 1.1.13 → 1.2.1

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/index.cjs CHANGED
@@ -37,6 +37,7 @@ __export(index_exports, {
37
37
  CommandExitError: () => CommandExitError,
38
38
  CommandHandle: () => CommandHandle,
39
39
  Commands: () => Commands,
40
+ ConflictError: () => ConflictError,
40
41
  ConnectionConfig: () => ConnectionConfig,
41
42
  DEFAULT_MASK_PATTERNS: () => DEFAULT_MASK_PATTERNS,
42
43
  FileType: () => FileType,
@@ -45,6 +46,7 @@ __export(index_exports, {
45
46
  FilesystemEventType: () => FilesystemEventType,
46
47
  GitAuthError: () => GitAuthError,
47
48
  GitUpstreamError: () => GitUpstreamError,
49
+ Governance: () => Governance,
48
50
  InjectionAction: () => InjectionAction,
49
51
  InjectionSensitivity: () => InjectionSensitivity,
50
52
  InvalidArgumentError: () => InvalidArgumentError,
@@ -66,12 +68,16 @@ __export(index_exports, {
66
68
  TemplateError: () => TemplateError,
67
69
  TimeoutError: () => TimeoutError,
68
70
  TransformDirection: () => TransformDirection,
71
+ VolumeFiles: () => VolumeFiles,
72
+ VolumeLocks: () => VolumeLocks,
69
73
  Volumes: () => Volumes,
70
74
  WatchHandle: () => WatchHandle,
71
75
  applyTransformation: () => applyTransformation,
72
76
  codeSecurityConfigToJSON: () => codeSecurityConfigToJSON,
77
+ contentGateConfigToJSON: () => contentGateConfigToJSON,
73
78
  createAuditConfig: () => createAuditConfig,
74
79
  createCodeSecurityConfig: () => createCodeSecurityConfig,
80
+ createContentGateConfig: () => createContentGateConfig,
75
81
  createEnvSecurityConfig: () => createEnvSecurityConfig,
76
82
  createInjectionDefenseConfig: () => createInjectionDefenseConfig,
77
83
  createInvisibleTextConfig: () => createInvisibleTextConfig,
@@ -90,11 +96,17 @@ __export(index_exports, {
90
96
  parseBuildInfo: () => parseBuildInfo,
91
97
  parseCodeSecurityConfig: () => parseCodeSecurityConfig,
92
98
  parseCommandResult: () => parseCommandResult,
99
+ parseContentGateConfig: () => parseContentGateConfig,
93
100
  parseEntryInfo: () => parseEntryInfo,
94
101
  parseEnvSecurityConfig: () => parseEnvSecurityConfig,
102
+ parseFileEntry: () => parseFileEntry,
103
+ parseFileInfo: () => parseFileInfo,
95
104
  parseFilesystemEvent: () => parseFilesystemEvent,
105
+ parseGovernancePack: () => parseGovernancePack,
96
106
  parseInjectionDefenseConfig: () => parseInjectionDefenseConfig,
97
107
  parseInvisibleTextConfig: () => parseInvisibleTextConfig,
108
+ parseLockLease: () => parseLockLease,
109
+ parseLockStatus: () => parseLockStatus,
98
110
  parseNetworkPolicy: () => parseNetworkPolicy,
99
111
  parsePIIConfig: () => parsePIIConfig,
100
112
  parseProcessInfo: () => parseProcessInfo,
@@ -191,6 +203,12 @@ var NotEnoughSpaceError = class extends SandboxError {
191
203
  this.name = "NotEnoughSpaceError";
192
204
  }
193
205
  };
206
+ var ConflictError = class extends SandboxError {
207
+ constructor(message, opts) {
208
+ super(message, opts);
209
+ this.name = "ConflictError";
210
+ }
211
+ };
194
212
  var TemplateError = class extends SandboxError {
195
213
  constructor(message, opts) {
196
214
  super(message, opts);
@@ -236,6 +254,8 @@ var CommandExitError = class extends SandboxError {
236
254
 
237
255
  // src/api/client.ts
238
256
  var _dispatcherPromise;
257
+ var _resolvedDispatcher;
258
+ var _dispatcherResolved = false;
239
259
  function getDispatcher() {
240
260
  if (_dispatcherPromise) return _dispatcherPromise;
241
261
  _dispatcherPromise = (async () => {
@@ -260,7 +280,14 @@ function getDispatcher() {
260
280
  maxConcurrentStreams,
261
281
  keepAliveTimeout: 3e4,
262
282
  keepAliveMaxTimeout: 6e4,
263
- pipelining: 1,
283
+ // NOTE: do NOT set `pipelining` here. undici applies it to H2 sessions
284
+ // too, where `pipelining: 1` caps each connection to ONE in-flight
285
+ // stream — silently defeating the H2 multiplexing that allowH2 enables.
286
+ // Omitted, H2 sessions default to unlimited concurrent streams (capped
287
+ // by maxConcurrentStreams) and H1.1 fallback defaults to 1 (safe, no
288
+ // head-of-line risk on non-idempotent POSTs). This is the single
289
+ // biggest burst-latency lever: with pipelining:1 a 100-concurrent burst
290
+ // queued ~36 requests behind the `connections` cap.
264
291
  allowH2: true,
265
292
  connect: { keepAlive: true, keepAliveInitialDelay: 5e3 }
266
293
  });
@@ -268,13 +295,21 @@ function getDispatcher() {
268
295
  return void 0;
269
296
  }
270
297
  })();
298
+ void _dispatcherPromise.then((d) => {
299
+ _resolvedDispatcher = d;
300
+ _dispatcherResolved = true;
301
+ });
271
302
  return _dispatcherPromise;
272
303
  }
304
+ void getDispatcher();
273
305
  var STATUS_ERROR_MAP = {
306
+ 400: InvalidArgumentError,
274
307
  401: AuthenticationError,
275
308
  403: AuthenticationError,
276
309
  404: NotFoundError,
277
310
  408: TimeoutError,
311
+ 409: ConflictError,
312
+ 413: NotEnoughSpaceError,
278
313
  422: InvalidArgumentError,
279
314
  507: NotEnoughSpaceError
280
315
  };
@@ -286,7 +321,7 @@ var ApiClient = class {
286
321
  constructor(config, opts) {
287
322
  this.config = config ?? new ConnectionConfig();
288
323
  this.maxRetries = opts?.maxRetries ?? 3;
289
- this.retryDelay = opts?.retryDelay ?? 0.5;
324
+ this.retryDelay = opts?.retryDelay ?? 0.1;
290
325
  this.abortController = new AbortController();
291
326
  }
292
327
  /** Send a GET request and return parsed JSON. */
@@ -380,7 +415,7 @@ var ApiClient = class {
380
415
  signals.push(AbortSignal.timeout(timeoutMs));
381
416
  }
382
417
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
383
- const dispatcher = await getDispatcher();
418
+ const dispatcher = _dispatcherResolved ? _resolvedDispatcher : await getDispatcher();
384
419
  const fetchOpts = {
385
420
  method,
386
421
  headers,
@@ -933,6 +968,49 @@ function invisibleTextConfigToJSON(config) {
933
968
  return result;
934
969
  }
935
970
 
971
+ // src/security/customPolicy.ts
972
+ function parseCustomPolicyConfig(data) {
973
+ return {
974
+ enabled: data.enabled ?? false,
975
+ inlineRego: data.inline_rego ?? data.inlineRego,
976
+ inlineModules: data.inline_modules ?? data.inlineModules,
977
+ policyRef: data.policy_ref ?? data.policyRef,
978
+ defaultDeny: data.default_deny ?? data.defaultDeny ?? false
979
+ };
980
+ }
981
+ function customPolicyConfigToJSON(config) {
982
+ return {
983
+ enabled: config.enabled,
984
+ inline_rego: config.inlineRego,
985
+ inline_modules: config.inlineModules,
986
+ policy_ref: config.policyRef,
987
+ default_deny: config.defaultDeny
988
+ };
989
+ }
990
+
991
+ // src/security/contentGate.ts
992
+ function createContentGateConfig(opts) {
993
+ return {
994
+ enabled: opts?.enabled ?? false,
995
+ domains: opts?.domains
996
+ };
997
+ }
998
+ function parseContentGateConfig(data) {
999
+ return {
1000
+ enabled: data.enabled ?? false,
1001
+ domains: data.domains
1002
+ };
1003
+ }
1004
+ function contentGateConfigToJSON(config) {
1005
+ const result = {
1006
+ enabled: config.enabled
1007
+ };
1008
+ if (config.domains !== void 0) {
1009
+ result.domains = config.domains;
1010
+ }
1011
+ return result;
1012
+ }
1013
+
936
1014
  // src/security/policy.ts
937
1015
  function createSecurityPolicy(opts) {
938
1016
  return {
@@ -944,12 +1022,16 @@ function createSecurityPolicy(opts) {
944
1022
  envSecurity: opts?.envSecurity ?? createEnvSecurityConfig(),
945
1023
  toxicity: opts?.toxicity,
946
1024
  codeSecurity: opts?.codeSecurity,
947
- invisibleText: opts?.invisibleText
1025
+ invisibleText: opts?.invisibleText,
1026
+ contentGate: opts?.contentGate,
1027
+ customPolicy: opts?.customPolicy
948
1028
  };
949
1029
  }
950
1030
  function parseSecurityPolicy(data) {
951
1031
  const injDef = data.injection_defense ?? data.injectionDefense;
952
1032
  const auditData = data.audit;
1033
+ const customPolicyData = data.custom_policy ?? data.customPolicy;
1034
+ const contentGateData = data.content_gate ?? data.contentGate;
953
1035
  return {
954
1036
  pii: data.pii ? parsePIIConfig(data.pii) : createPIIConfig(),
955
1037
  injectionDefense: typeof injDef === "boolean" ? injDef : injDef ? parseInjectionDefenseConfig(injDef) : false,
@@ -959,7 +1041,9 @@ function parseSecurityPolicy(data) {
959
1041
  envSecurity: data.env_security ?? data.envSecurity ? parseEnvSecurityConfig(data.env_security ?? data.envSecurity) : createEnvSecurityConfig(),
960
1042
  toxicity: data.toxicity ? parseToxicityConfig(data.toxicity) : void 0,
961
1043
  codeSecurity: data.code_security ?? data.codeSecurity ? parseCodeSecurityConfig(data.code_security ?? data.codeSecurity) : void 0,
962
- invisibleText: data.invisible_text ?? data.invisibleText ? parseInvisibleTextConfig(data.invisible_text ?? data.invisibleText) : void 0
1044
+ invisibleText: data.invisible_text ?? data.invisibleText ? parseInvisibleTextConfig(data.invisible_text ?? data.invisibleText) : void 0,
1045
+ contentGate: contentGateData ? parseContentGateConfig(contentGateData) : void 0,
1046
+ customPolicy: customPolicyData ? parseCustomPolicyConfig(customPolicyData) : void 0
963
1047
  };
964
1048
  }
965
1049
  function securityPolicyToJSON(policy) {
@@ -1009,6 +1093,12 @@ function securityPolicyToJSON(policy) {
1009
1093
  if (policy.invisibleText) {
1010
1094
  result.invisible_text = invisibleTextConfigToJSON(policy.invisibleText);
1011
1095
  }
1096
+ if (policy.contentGate) {
1097
+ result.content_gate = contentGateConfigToJSON(policy.contentGate);
1098
+ }
1099
+ if (policy.customPolicy) {
1100
+ result.custom_policy = customPolicyConfigToJSON(policy.customPolicy);
1101
+ }
1012
1102
  return result;
1013
1103
  }
1014
1104
  function requiresTlsInterception(policy) {
@@ -2027,11 +2117,53 @@ function parseVolumeInfo(data) {
2027
2117
  sizeBytes: Number(data.size_bytes ?? 0),
2028
2118
  contentType: String(data.content_type ?? ""),
2029
2119
  metadata: data.metadata ?? {},
2030
- createdAt: String(data.created_at ?? "")
2120
+ createdAt: String(data.created_at ?? ""),
2121
+ backend: String(data.backend ?? ""),
2122
+ quotaBytes: Number(data.quota_bytes ?? 0),
2123
+ updatedAt: String(data.updated_at ?? "")
2124
+ };
2125
+ }
2126
+ function parseFileEntry(data) {
2127
+ return {
2128
+ name: String(data.name ?? ""),
2129
+ path: String(data.path ?? ""),
2130
+ isDir: Boolean(data.is_dir ?? false),
2131
+ size: Number(data.size ?? 0),
2132
+ modTime: String(data.mod_time ?? ""),
2133
+ mode: Number(data.mode ?? 0)
2134
+ };
2135
+ }
2136
+ function parseFileInfo(data) {
2137
+ return {
2138
+ ...parseFileEntry(data),
2139
+ version: String(data.version ?? "")
2140
+ };
2141
+ }
2142
+ function parseLockLease(data) {
2143
+ return {
2144
+ token: String(data.token ?? ""),
2145
+ ttlSeconds: Number(data.ttl_seconds ?? 0),
2146
+ expiresAt: String(data.expires_at ?? "")
2147
+ };
2148
+ }
2149
+ function parseLockStatus(data) {
2150
+ return {
2151
+ held: Boolean(data.held ?? false),
2152
+ expiresInMs: Number(data.expires_in_ms ?? 0)
2031
2153
  };
2032
2154
  }
2033
2155
  function volumeAttachmentToJSON(att) {
2034
- return { volume_id: att.volumeId, mount_path: att.mountPath };
2156
+ const out = {
2157
+ volume_id: att.volumeId,
2158
+ mount_path: att.mountPath
2159
+ };
2160
+ if (att.mode) {
2161
+ out.mode = att.mode;
2162
+ }
2163
+ if (att.subpath) {
2164
+ out.subpath = att.subpath;
2165
+ }
2166
+ return out;
2035
2167
  }
2036
2168
 
2037
2169
  // src/sandbox/sandbox.ts
@@ -2857,7 +2989,7 @@ var Template = class {
2857
2989
  }
2858
2990
  };
2859
2991
 
2860
- // src/volumes/volumes.ts
2992
+ // src/volumes/files.ts
2861
2993
  var VALID_VOLUME_ID_RE = /^[a-zA-Z0-9_-]+$/;
2862
2994
  function assertValidVolumeId(id) {
2863
2995
  if (!id || !VALID_VOLUME_ID_RE.test(id)) {
@@ -2866,8 +2998,223 @@ function assertValidVolumeId(id) {
2866
2998
  );
2867
2999
  }
2868
3000
  }
3001
+ var VolumeFiles = class {
3002
+ volumeId;
3003
+ opts;
3004
+ constructor(volumeId, opts) {
3005
+ assertValidVolumeId(volumeId);
3006
+ this.volumeId = volumeId;
3007
+ this.opts = opts;
3008
+ }
3009
+ client() {
3010
+ const config = new ConnectionConfig({
3011
+ apiKey: this.opts?.apiKey,
3012
+ domain: this.opts?.domain,
3013
+ apiUrl: this.opts?.apiUrl,
3014
+ requestTimeout: this.opts?.requestTimeout
3015
+ });
3016
+ return getSharedClient(config);
3017
+ }
3018
+ timeout() {
3019
+ return this.opts?.requestTimeout;
3020
+ }
3021
+ /** Write raw bytes to `path`. Optionally conditional on `ifVersion` (CAS). */
3022
+ async write(path, data, opts) {
3023
+ if (!path) {
3024
+ throw new InvalidArgumentError("path is required");
3025
+ }
3026
+ const params = { path };
3027
+ if (opts?.ifVersion) {
3028
+ params.if_version = opts.ifVersion;
3029
+ }
3030
+ const body = data instanceof Uint8Array ? data : new Uint8Array(data);
3031
+ const resp = await this.client().put(`/volumes/${this.volumeId}/files/raw`, {
3032
+ params,
3033
+ body,
3034
+ headers: { "Content-Type": "application/octet-stream" },
3035
+ timeout: opts?.requestTimeout ?? this.timeout()
3036
+ });
3037
+ return String(resp.path ?? path);
3038
+ }
3039
+ /** Read raw bytes from `path`. */
3040
+ async read(path) {
3041
+ if (!path) {
3042
+ throw new InvalidArgumentError("path is required");
3043
+ }
3044
+ return this.client().getBytes(`/volumes/${this.volumeId}/files/raw`, {
3045
+ params: { path },
3046
+ timeout: this.timeout()
3047
+ });
3048
+ }
3049
+ /** List directory entries under `path`. */
3050
+ async list(path) {
3051
+ if (!path) {
3052
+ throw new InvalidArgumentError("path is required");
3053
+ }
3054
+ const resp = await this.client().get(`/volumes/${this.volumeId}/files/list`, {
3055
+ params: { path },
3056
+ timeout: this.timeout()
3057
+ });
3058
+ const rows = resp.entries ?? [];
3059
+ return rows.map(parseFileEntry);
3060
+ }
3061
+ /** Stat `path`, returning the entry plus the CAS `version` token. */
3062
+ async info(path) {
3063
+ if (!path) {
3064
+ throw new InvalidArgumentError("path is required");
3065
+ }
3066
+ const resp = await this.client().get(`/volumes/${this.volumeId}/files/info`, {
3067
+ params: { path },
3068
+ timeout: this.timeout()
3069
+ });
3070
+ return parseFileInfo(resp);
3071
+ }
3072
+ /** Return whether `path` exists. */
3073
+ async exists(path) {
3074
+ if (!path) {
3075
+ throw new InvalidArgumentError("path is required");
3076
+ }
3077
+ const resp = await this.client().get(`/volumes/${this.volumeId}/files/exists`, {
3078
+ params: { path },
3079
+ timeout: this.timeout()
3080
+ });
3081
+ return Boolean(resp.exists ?? false);
3082
+ }
3083
+ /** Remove `path`. Pass `{ recursive: true }` to remove a directory tree. */
3084
+ async remove(path, opts) {
3085
+ if (!path) {
3086
+ throw new InvalidArgumentError("path is required");
3087
+ }
3088
+ const params = {
3089
+ path,
3090
+ recursive: opts?.recursive ? "true" : "false"
3091
+ };
3092
+ await this.client().delete(`/volumes/${this.volumeId}/files`, {
3093
+ params,
3094
+ timeout: opts?.requestTimeout ?? this.timeout()
3095
+ });
3096
+ }
3097
+ /** Rename `oldPath` to `newPath`. */
3098
+ async rename(oldPath, newPath) {
3099
+ if (!oldPath || !newPath) {
3100
+ throw new InvalidArgumentError("oldPath and newPath are required");
3101
+ }
3102
+ const resp = await this.client().patch(`/volumes/${this.volumeId}/files`, {
3103
+ json: { old_path: oldPath, new_path: newPath },
3104
+ timeout: this.timeout()
3105
+ });
3106
+ return {
3107
+ oldPath: String(resp.old_path ?? oldPath),
3108
+ newPath: String(resp.new_path ?? newPath)
3109
+ };
3110
+ }
3111
+ /** Create a directory at `path`. */
3112
+ async mkdir(path) {
3113
+ if (!path) {
3114
+ throw new InvalidArgumentError("path is required");
3115
+ }
3116
+ const resp = await this.client().post(`/volumes/${this.volumeId}/files/mkdir`, {
3117
+ json: { path },
3118
+ timeout: this.timeout()
3119
+ });
3120
+ return String(resp.path ?? path);
3121
+ }
3122
+ };
3123
+
3124
+ // src/volumes/locks.ts
3125
+ var VALID_VOLUME_ID_RE2 = /^[a-zA-Z0-9_-]+$/;
3126
+ function assertValidVolumeId2(id) {
3127
+ if (!id || !VALID_VOLUME_ID_RE2.test(id)) {
3128
+ throw new InvalidArgumentError(
3129
+ `Invalid volume ID: "${id}". Must be alphanumeric with hyphens/underscores only.`
3130
+ );
3131
+ }
3132
+ }
3133
+ var VolumeLocks = class {
3134
+ volumeId;
3135
+ opts;
3136
+ constructor(volumeId, opts) {
3137
+ assertValidVolumeId2(volumeId);
3138
+ this.volumeId = volumeId;
3139
+ this.opts = opts;
3140
+ }
3141
+ client() {
3142
+ const config = new ConnectionConfig({
3143
+ apiKey: this.opts?.apiKey,
3144
+ domain: this.opts?.domain,
3145
+ apiUrl: this.opts?.apiUrl,
3146
+ requestTimeout: this.opts?.requestTimeout
3147
+ });
3148
+ return getSharedClient(config);
3149
+ }
3150
+ timeout() {
3151
+ return this.opts?.requestTimeout;
3152
+ }
3153
+ /** Acquire a lock on `path`. Throws ConflictError (409) if already held. */
3154
+ async acquire(path, ttlSeconds) {
3155
+ if (!path) {
3156
+ throw new InvalidArgumentError("path is required");
3157
+ }
3158
+ const json = { path };
3159
+ if (ttlSeconds !== void 0) {
3160
+ json.ttl_seconds = ttlSeconds;
3161
+ }
3162
+ const resp = await this.client().post(`/volumes/${this.volumeId}/locks`, {
3163
+ json,
3164
+ timeout: this.timeout()
3165
+ });
3166
+ return parseLockLease(resp);
3167
+ }
3168
+ /** Release a lock on `path` held under `token`. Throws ConflictError (409) if not the holder. */
3169
+ async release(path, token) {
3170
+ if (!path || !token) {
3171
+ throw new InvalidArgumentError("path and token are required");
3172
+ }
3173
+ const resp = await this.client().delete(`/volumes/${this.volumeId}/locks`, {
3174
+ json: { path, token },
3175
+ timeout: this.timeout()
3176
+ });
3177
+ return Boolean(resp.released ?? false);
3178
+ }
3179
+ /** Renew a lock on `path` held under `token`. Throws ConflictError (409) if not the holder. */
3180
+ async renew(path, token, ttlSeconds) {
3181
+ if (!path || !token) {
3182
+ throw new InvalidArgumentError("path and token are required");
3183
+ }
3184
+ const json = { path, token };
3185
+ if (ttlSeconds !== void 0) {
3186
+ json.ttl_seconds = ttlSeconds;
3187
+ }
3188
+ const resp = await this.client().post(`/volumes/${this.volumeId}/locks/renew`, {
3189
+ json,
3190
+ timeout: this.timeout()
3191
+ });
3192
+ return { ...parseLockLease(resp), token };
3193
+ }
3194
+ /** Query whether `path` is currently locked. */
3195
+ async status(path) {
3196
+ if (!path) {
3197
+ throw new InvalidArgumentError("path is required");
3198
+ }
3199
+ const resp = await this.client().get(`/volumes/${this.volumeId}/locks`, {
3200
+ params: { path },
3201
+ timeout: this.timeout()
3202
+ });
3203
+ return parseLockStatus(resp);
3204
+ }
3205
+ };
3206
+
3207
+ // src/volumes/volumes.ts
3208
+ var VALID_VOLUME_ID_RE3 = /^[a-zA-Z0-9_-]+$/;
3209
+ function assertValidVolumeId3(id) {
3210
+ if (!id || !VALID_VOLUME_ID_RE3.test(id)) {
3211
+ throw new InvalidArgumentError(
3212
+ `Invalid volume ID: "${id}". Must be alphanumeric with hyphens/underscores only.`
3213
+ );
3214
+ }
3215
+ }
2869
3216
  var Volumes = class {
2870
- /** Create a volume by streaming a tarball to the server. */
3217
+ /** Create a volume by streaming a tarball (gzip tar.gz) to the server. */
2871
3218
  static async create(name, data, opts) {
2872
3219
  if (!name) {
2873
3220
  throw new InvalidArgumentError("volume name is required");
@@ -2888,9 +3235,113 @@ var Volumes = class {
2888
3235
  });
2889
3236
  return parseVolumeInfo(resp);
2890
3237
  }
3238
+ /**
3239
+ * Capture the attached volume's mount path in `sandboxId` into a NEW volume.
3240
+ *
3241
+ * The source volume is left unchanged. If `name` is omitted the server names
3242
+ * the new volume "<source-name>-commit". Returns the new VolumeInfo.
3243
+ */
3244
+ static async commit(sandboxId, volumeId, name, opts) {
3245
+ if (!sandboxId || !VALID_VOLUME_ID_RE3.test(sandboxId)) {
3246
+ throw new InvalidArgumentError(
3247
+ `Invalid sandbox ID: "${sandboxId}". Must be alphanumeric with hyphens/underscores only.`
3248
+ );
3249
+ }
3250
+ assertValidVolumeId3(volumeId);
3251
+ const config = new ConnectionConfig({
3252
+ apiKey: opts?.apiKey,
3253
+ domain: opts?.domain,
3254
+ apiUrl: opts?.apiUrl,
3255
+ requestTimeout: opts?.requestTimeout
3256
+ });
3257
+ const client = getSharedClient(config);
3258
+ const resp = await client.post(`/sandboxes/${sandboxId}/volumes/${volumeId}/commit`, {
3259
+ params: name ? { name } : void 0,
3260
+ timeout: opts?.requestTimeout
3261
+ });
3262
+ return parseVolumeInfo(resp);
3263
+ }
3264
+ /**
3265
+ * Snapshot an arbitrary absolute in-sandbox `path` into a NEW volume.
3266
+ *
3267
+ * Unlike `commit` (which captures an already-attached volume's mount path),
3268
+ * `snapshot` captures any path in the running sandbox. `name` defaults to
3269
+ * "snapshot" on the server. Synthetic paths (/proc, /sys, /dev) are rejected.
3270
+ */
3271
+ static async snapshot(sandboxId, path, name, opts) {
3272
+ if (!sandboxId || !VALID_VOLUME_ID_RE3.test(sandboxId)) {
3273
+ throw new InvalidArgumentError(
3274
+ `Invalid sandbox ID: "${sandboxId}". Must be alphanumeric with hyphens/underscores only.`
3275
+ );
3276
+ }
3277
+ if (!path) {
3278
+ throw new InvalidArgumentError("path is required");
3279
+ }
3280
+ const config = new ConnectionConfig({
3281
+ apiKey: opts?.apiKey,
3282
+ domain: opts?.domain,
3283
+ apiUrl: opts?.apiUrl,
3284
+ requestTimeout: opts?.requestTimeout
3285
+ });
3286
+ const client = getSharedClient(config);
3287
+ const params = { path };
3288
+ if (name) {
3289
+ params.name = name;
3290
+ }
3291
+ const resp = await client.post(`/sandboxes/${sandboxId}/volumes/snapshot`, {
3292
+ params,
3293
+ timeout: opts?.requestTimeout
3294
+ });
3295
+ return parseVolumeInfo(resp);
3296
+ }
3297
+ /**
3298
+ * Create an empty file-granular volume. Requires a file-granular backend
3299
+ * (503 if not configured). Returns the new VolumeInfo.
3300
+ */
3301
+ static async empty(name, opts) {
3302
+ if (!name) {
3303
+ throw new InvalidArgumentError("volume name is required");
3304
+ }
3305
+ const config = new ConnectionConfig({
3306
+ apiKey: opts?.apiKey,
3307
+ domain: opts?.domain,
3308
+ apiUrl: opts?.apiUrl,
3309
+ requestTimeout: opts?.requestTimeout
3310
+ });
3311
+ const client = getSharedClient(config);
3312
+ const resp = await client.post("/volumes/empty", {
3313
+ params: { name },
3314
+ timeout: opts?.requestTimeout
3315
+ });
3316
+ return parseVolumeInfo(resp);
3317
+ }
3318
+ /**
3319
+ * Ingest a gzip tar.gz archive into a NEW file-granular volume. Requires a
3320
+ * file-granular backend (503 if not configured). 413 on quota exceeded.
3321
+ */
3322
+ static async ingest(name, data, opts) {
3323
+ if (!name) {
3324
+ throw new InvalidArgumentError("volume name is required");
3325
+ }
3326
+ const config = new ConnectionConfig({
3327
+ apiKey: opts?.apiKey,
3328
+ domain: opts?.domain,
3329
+ apiUrl: opts?.apiUrl,
3330
+ requestTimeout: opts?.requestTimeout
3331
+ });
3332
+ const client = getSharedClient(config);
3333
+ const body = data instanceof Uint8Array ? data : new Uint8Array(data);
3334
+ const resp = await client.post("/volumes/ingest", {
3335
+ params: { name },
3336
+ body,
3337
+ headers: { "Content-Type": opts?.contentType ?? "application/gzip" },
3338
+ timeout: opts?.requestTimeout
3339
+ });
3340
+ return parseVolumeInfo(resp);
3341
+ }
2891
3342
  /** Fetch metadata for a single volume. */
2892
3343
  static async get(volumeId, opts) {
2893
- assertValidVolumeId(volumeId);
3344
+ assertValidVolumeId3(volumeId);
2894
3345
  const config = new ConnectionConfig({
2895
3346
  apiKey: opts?.apiKey,
2896
3347
  domain: opts?.domain,
@@ -2918,9 +3369,23 @@ var Volumes = class {
2918
3369
  const rows = resp.volumes ?? [];
2919
3370
  return rows.map(parseVolumeInfo);
2920
3371
  }
3372
+ /** Download the volume's contents as raw bytes (the stored archive/blob). */
3373
+ static async download(volumeId, opts) {
3374
+ assertValidVolumeId3(volumeId);
3375
+ const config = new ConnectionConfig({
3376
+ apiKey: opts?.apiKey,
3377
+ domain: opts?.domain,
3378
+ apiUrl: opts?.apiUrl,
3379
+ requestTimeout: opts?.requestTimeout
3380
+ });
3381
+ const client = getSharedClient(config);
3382
+ return client.getBytes(`/volumes/${volumeId}/download`, {
3383
+ timeout: opts?.requestTimeout
3384
+ });
3385
+ }
2921
3386
  /** Delete a volume and its blob. Idempotent on the wire. */
2922
3387
  static async delete(volumeId, opts) {
2923
- assertValidVolumeId(volumeId);
3388
+ assertValidVolumeId3(volumeId);
2924
3389
  const config = new ConnectionConfig({
2925
3390
  apiKey: opts?.apiKey,
2926
3391
  domain: opts?.domain,
@@ -2930,6 +3395,100 @@ var Volumes = class {
2930
3395
  const client = getSharedClient(config);
2931
3396
  await client.delete(`/volumes/${volumeId}`, { timeout: opts?.requestTimeout });
2932
3397
  }
3398
+ /**
3399
+ * File-granular operations (read/write/list/info/exists/remove/rename/mkdir,
3400
+ * plus CAS via `write(..., { ifVersion })`) on `volumeId`. File-granular
3401
+ * volumes only.
3402
+ */
3403
+ static files(volumeId, opts) {
3404
+ return new VolumeFiles(volumeId, opts);
3405
+ }
3406
+ /** Advisory locks (acquire/release/renew/status) over a (volume, path). */
3407
+ static locks(volumeId, opts) {
3408
+ return new VolumeLocks(volumeId, opts);
3409
+ }
3410
+ };
3411
+
3412
+ // src/governance/models.ts
3413
+ function parseGovernanceControl(data) {
3414
+ return {
3415
+ control: String(data.control ?? ""),
3416
+ gate: String(data.gate ?? ""),
3417
+ rule: String(data.rule ?? ""),
3418
+ playbook: String(data.playbook ?? "")
3419
+ };
3420
+ }
3421
+ function parseGovernanceAdvisory(data) {
3422
+ return {
3423
+ control: String(data.control ?? ""),
3424
+ reason: String(data.reason ?? "")
3425
+ };
3426
+ }
3427
+ function parseGovernancePack(data) {
3428
+ const rawEnforces = data.enforces ?? [];
3429
+ const rawAdvisory = data.advisory ?? [];
3430
+ return {
3431
+ name: String(data.name ?? ""),
3432
+ version: String(data.version ?? ""),
3433
+ framework: String(data.framework ?? ""),
3434
+ description: String(data.description ?? ""),
3435
+ gates: data.gates ?? [],
3436
+ enforces: rawEnforces.map(parseGovernanceControl),
3437
+ advisory: rawAdvisory.map(parseGovernanceAdvisory),
3438
+ policyRef: String(data.policy_ref ?? data.policyRef ?? ""),
3439
+ seeded: Boolean(data.seeded ?? false)
3440
+ };
3441
+ }
3442
+
3443
+ // src/governance/governance.ts
3444
+ var VALID_PACK_NAME_RE = /^[a-zA-Z0-9_-]+$/;
3445
+ function assertValidPackName(name) {
3446
+ if (!name || !VALID_PACK_NAME_RE.test(name)) {
3447
+ throw new InvalidArgumentError(
3448
+ `Invalid pack name: "${name}". Must be alphanumeric with hyphens/underscores only.`
3449
+ );
3450
+ }
3451
+ }
3452
+ var Governance = class {
3453
+ /**
3454
+ * List all available governance packs.
3455
+ *
3456
+ * Sends GET /governance/packs and returns the `packs` array.
3457
+ */
3458
+ static async listPacks(opts) {
3459
+ const config = new ConnectionConfig({
3460
+ apiKey: opts?.apiKey,
3461
+ domain: opts?.domain,
3462
+ apiUrl: opts?.apiUrl,
3463
+ requestTimeout: opts?.requestTimeout
3464
+ });
3465
+ const client = getSharedClient(config);
3466
+ const resp = await client.get("/governance/packs", {
3467
+ timeout: opts?.requestTimeout
3468
+ });
3469
+ const rows = resp.packs ?? [];
3470
+ return rows.map(parseGovernancePack);
3471
+ }
3472
+ /**
3473
+ * Fetch a single governance pack by name.
3474
+ *
3475
+ * Sends GET /governance/packs/:name and returns the pack object.
3476
+ * Throws InvalidArgumentError if the name contains unsafe characters.
3477
+ */
3478
+ static async getPack(name, opts) {
3479
+ assertValidPackName(name);
3480
+ const config = new ConnectionConfig({
3481
+ apiKey: opts?.apiKey,
3482
+ domain: opts?.domain,
3483
+ apiUrl: opts?.apiUrl,
3484
+ requestTimeout: opts?.requestTimeout
3485
+ });
3486
+ const client = getSharedClient(config);
3487
+ const resp = await client.get(`/governance/packs/${name}`, {
3488
+ timeout: opts?.requestTimeout
3489
+ });
3490
+ return parseGovernancePack(resp);
3491
+ }
2933
3492
  };
2934
3493
  // Annotate the CommonJS export names for ESM import in node:
2935
3494
  0 && (module.exports = {
@@ -2940,6 +3499,7 @@ var Volumes = class {
2940
3499
  CommandExitError,
2941
3500
  CommandHandle,
2942
3501
  Commands,
3502
+ ConflictError,
2943
3503
  ConnectionConfig,
2944
3504
  DEFAULT_MASK_PATTERNS,
2945
3505
  FileType,
@@ -2948,6 +3508,7 @@ var Volumes = class {
2948
3508
  FilesystemEventType,
2949
3509
  GitAuthError,
2950
3510
  GitUpstreamError,
3511
+ Governance,
2951
3512
  InjectionAction,
2952
3513
  InjectionSensitivity,
2953
3514
  InvalidArgumentError,
@@ -2969,12 +3530,16 @@ var Volumes = class {
2969
3530
  TemplateError,
2970
3531
  TimeoutError,
2971
3532
  TransformDirection,
3533
+ VolumeFiles,
3534
+ VolumeLocks,
2972
3535
  Volumes,
2973
3536
  WatchHandle,
2974
3537
  applyTransformation,
2975
3538
  codeSecurityConfigToJSON,
3539
+ contentGateConfigToJSON,
2976
3540
  createAuditConfig,
2977
3541
  createCodeSecurityConfig,
3542
+ createContentGateConfig,
2978
3543
  createEnvSecurityConfig,
2979
3544
  createInjectionDefenseConfig,
2980
3545
  createInvisibleTextConfig,
@@ -2993,11 +3558,17 @@ var Volumes = class {
2993
3558
  parseBuildInfo,
2994
3559
  parseCodeSecurityConfig,
2995
3560
  parseCommandResult,
3561
+ parseContentGateConfig,
2996
3562
  parseEntryInfo,
2997
3563
  parseEnvSecurityConfig,
3564
+ parseFileEntry,
3565
+ parseFileInfo,
2998
3566
  parseFilesystemEvent,
3567
+ parseGovernancePack,
2999
3568
  parseInjectionDefenseConfig,
3000
3569
  parseInvisibleTextConfig,
3570
+ parseLockLease,
3571
+ parseLockStatus,
3001
3572
  parseNetworkPolicy,
3002
3573
  parsePIIConfig,
3003
3574
  parseProcessInfo,