@declaw/sdk 1.2.2 → 1.3.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/index.js CHANGED
@@ -137,7 +137,7 @@ function getDispatcher() {
137
137
  }
138
138
  const undici = await import("undici");
139
139
  const connOverride = parseInt(process.env.DECLAW_SDK_CONNECTIONS || "", 10);
140
- const connections = Number.isFinite(connOverride) && connOverride > 0 ? connOverride : 512;
140
+ const connections = Number.isFinite(connOverride) && connOverride > 0 ? connOverride : 64;
141
141
  const streamsOverride = parseInt(process.env.DECLAW_SDK_MAX_CONCURRENT_STREAMS || "", 10);
142
142
  const maxConcurrentStreams = Number.isFinite(streamsOverride) && streamsOverride > 0 ? streamsOverride : 1e3;
143
143
  return new undici.Agent({
@@ -154,7 +154,9 @@ function getDispatcher() {
154
154
  // stream — silently defeating the H2 multiplexing that allowH2 enables.
155
155
  // Omitted, H2 sessions default to unlimited concurrent streams (capped
156
156
  // by maxConcurrentStreams) and H1.1 fallback defaults to 1 (safe, no
157
- // head-of-line risk on non-idempotent POSTs).
157
+ // head-of-line risk on non-idempotent POSTs). This is the single
158
+ // biggest burst-latency lever: with pipelining:1 a 100-concurrent burst
159
+ // queued ~36 requests behind the `connections` cap.
158
160
  allowH2: true,
159
161
  connect: { keepAlive: true, keepAliveInitialDelay: 5e3 }
160
162
  });
@@ -507,7 +509,7 @@ function createInjectionDefenseConfig(opts) {
507
509
  enabled: opts?.enabled ?? false,
508
510
  sensitivity: opts?.sensitivity ?? "medium" /* Medium */,
509
511
  action: opts?.action ?? "log_only" /* LogOnly */,
510
- threshold: opts?.threshold ?? 0.8,
512
+ threshold: opts?.threshold ?? 0.95,
511
513
  domains: opts?.domains,
512
514
  judge: opts?.judge,
513
515
  injectionMode: opts?.injectionMode
@@ -534,7 +536,7 @@ function parseInjectionDefenseConfig(data) {
534
536
  enabled: data.enabled ?? false,
535
537
  sensitivity: data.sensitivity ?? "medium" /* Medium */,
536
538
  action: data.action ?? "log_only" /* LogOnly */,
537
- threshold: data.threshold ?? 0.8,
539
+ threshold: data.threshold ?? 0.95,
538
540
  domains: data.domains,
539
541
  judge: data.judge ? { enabled: data.judge.enabled ?? false, always: data.judge.always, policy: data.judge.policy } : void 0,
540
542
  injectionMode: data.injection_mode ?? data.injectionMode
@@ -912,14 +914,14 @@ function fullInjectionDefensePolicy(opts) {
912
914
  injectionDefense: createInjectionDefenseConfig({
913
915
  enabled: true,
914
916
  action: opts?.action ?? "block",
915
- threshold: opts?.threshold ?? 0.8,
917
+ threshold: opts?.threshold ?? 0.95,
916
918
  domains: opts?.domains,
917
919
  injectionMode: opts?.mode ?? "balanced",
918
920
  judge: { enabled: true, always: opts?.alwaysJudge ?? false, policy: opts?.agentPolicy ?? "" }
919
921
  }),
920
922
  customPolicy: createCustomPolicyConfig({
921
923
  enabled: true,
922
- policyRef: "prompt-injection@v2",
924
+ policyRef: "prompt-injection@v3",
923
925
  defaultDeny: false
924
926
  })
925
927
  });
@@ -960,7 +962,7 @@ function securityPolicyToJSON(policy) {
960
962
  action: injDefConfig.action,
961
963
  threshold: injDefConfig.threshold
962
964
  };
963
- if (injDefConfig.domains !== void 0) {
965
+ if (injDefConfig.domains !== void 0 && injDefConfig.domains.length > 0) {
964
966
  injDef.domains = injDefConfig.domains;
965
967
  }
966
968
  if (injDefConfig.injectionMode !== void 0) {
@@ -2072,6 +2074,311 @@ function volumeAttachmentToJSON(att) {
2072
2074
  return out;
2073
2075
  }
2074
2076
 
2077
+ // src/vault/models.ts
2078
+ function parseVaultScope(data) {
2079
+ const scope = {
2080
+ domainRegex: String(data.domain_regex ?? "")
2081
+ };
2082
+ if (data.injection_type !== void 0 && data.injection_type !== null) {
2083
+ scope.injectionType = String(data.injection_type);
2084
+ }
2085
+ if (data.header_name !== void 0 && data.header_name !== null) {
2086
+ scope.headerName = String(data.header_name);
2087
+ }
2088
+ if (data.value_prefix !== void 0 && data.value_prefix !== null) {
2089
+ scope.valuePrefix = String(data.value_prefix);
2090
+ }
2091
+ if (data.basic_username !== void 0 && data.basic_username !== null) {
2092
+ scope.basicUsername = String(data.basic_username);
2093
+ }
2094
+ if (data.extra_headers !== void 0 && data.extra_headers !== null) {
2095
+ scope.extraHeaders = data.extra_headers;
2096
+ }
2097
+ if (data.query_params !== void 0 && data.query_params !== null) {
2098
+ scope.queryParams = data.query_params;
2099
+ }
2100
+ return scope;
2101
+ }
2102
+ function parseVaultSecret(data) {
2103
+ const secret = {
2104
+ secretId: String(data.secret_id ?? ""),
2105
+ name: String(data.name ?? ""),
2106
+ createdAt: String(data.created_at ?? ""),
2107
+ updatedAt: String(data.updated_at ?? "")
2108
+ };
2109
+ const rawScopes = data.scopes;
2110
+ if (rawScopes && rawScopes.length > 0) {
2111
+ secret.scopes = rawScopes.map(parseVaultScope);
2112
+ }
2113
+ if (data.rotated_at !== void 0 && data.rotated_at !== null) {
2114
+ secret.rotatedAt = String(data.rotated_at);
2115
+ }
2116
+ if (data.rotation_interval_days !== void 0 && data.rotation_interval_days !== null) {
2117
+ secret.rotationIntervalDays = Number(data.rotation_interval_days);
2118
+ }
2119
+ if (data.rotation_due !== void 0 && data.rotation_due !== null) {
2120
+ secret.rotationDue = Boolean(data.rotation_due);
2121
+ }
2122
+ return secret;
2123
+ }
2124
+ function parseVaultPreset(data) {
2125
+ const rawScopes = data.scopes ?? [];
2126
+ const preset = {
2127
+ key: String(data.key ?? ""),
2128
+ name: String(data.name ?? ""),
2129
+ category: String(data.category ?? ""),
2130
+ keyHint: String(data.key_hint ?? ""),
2131
+ scopes: rawScopes.map(parseVaultScope)
2132
+ };
2133
+ if (data.docs_url !== void 0 && data.docs_url !== null) {
2134
+ preset.docsUrl = String(data.docs_url);
2135
+ }
2136
+ return preset;
2137
+ }
2138
+ function vaultScopeToJSON(scope) {
2139
+ const out = {
2140
+ domain_regex: scope.domainRegex
2141
+ };
2142
+ if (scope.injectionType !== void 0) {
2143
+ out.injection_type = scope.injectionType;
2144
+ }
2145
+ if (scope.headerName !== void 0) {
2146
+ out.header_name = scope.headerName;
2147
+ }
2148
+ if (scope.valuePrefix !== void 0) {
2149
+ out.value_prefix = scope.valuePrefix;
2150
+ }
2151
+ if (scope.basicUsername !== void 0) {
2152
+ out.basic_username = scope.basicUsername;
2153
+ }
2154
+ if (scope.extraHeaders !== void 0) {
2155
+ out.extra_headers = scope.extraHeaders;
2156
+ }
2157
+ if (scope.queryParams !== void 0) {
2158
+ out.query_params = scope.queryParams;
2159
+ }
2160
+ return out;
2161
+ }
2162
+
2163
+ // src/vault/vault.ts
2164
+ var DEFAULT_TEAM_NAME = "default";
2165
+ var DEFAULT_ENV_NAME = "prod";
2166
+ var defaultTeamCache = /* @__PURE__ */ new Map();
2167
+ function cacheKey(config) {
2168
+ return `${config.apiUrl}\0${config.apiKey}`;
2169
+ }
2170
+ async function resolveDefaultTeamId(config, create, timeout) {
2171
+ const key = cacheKey(config);
2172
+ const cached = defaultTeamCache.get(key);
2173
+ if (cached !== void 0) return cached;
2174
+ const client = getSharedClient(config);
2175
+ const resp = await client.get("/teams", { timeout });
2176
+ const rows = resp.teams ?? [];
2177
+ let best = null;
2178
+ for (const t of rows) {
2179
+ if (t.name === DEFAULT_TEAM_NAME) {
2180
+ if (best === null || t.created_at < best.created_at) {
2181
+ best = t;
2182
+ }
2183
+ }
2184
+ }
2185
+ if (best !== null) {
2186
+ defaultTeamCache.set(key, best.team_id);
2187
+ return best.team_id;
2188
+ }
2189
+ if (!create) return null;
2190
+ const created = await client.post("/teams", {
2191
+ json: { name: DEFAULT_TEAM_NAME },
2192
+ timeout
2193
+ });
2194
+ defaultTeamCache.set(key, created.team_id);
2195
+ return created.team_id;
2196
+ }
2197
+ async function ensureDefaultEnv(config, teamId, timeout) {
2198
+ const client = getSharedClient(config);
2199
+ const resp = await client.get(
2200
+ `/teams/${encodeURIComponent(teamId)}/environments`,
2201
+ { timeout }
2202
+ );
2203
+ const rows = resp.environments ?? [];
2204
+ if (rows.some((e) => e.name === DEFAULT_ENV_NAME)) return;
2205
+ try {
2206
+ await client.post(`/teams/${encodeURIComponent(teamId)}/environments`, {
2207
+ json: { name: DEFAULT_ENV_NAME },
2208
+ timeout
2209
+ });
2210
+ } catch {
2211
+ const resp2 = await client.get(
2212
+ `/teams/${encodeURIComponent(teamId)}/environments`,
2213
+ { timeout }
2214
+ );
2215
+ const rows2 = resp2.environments ?? [];
2216
+ if (rows2.some((e) => e.name === DEFAULT_ENV_NAME)) return;
2217
+ throw new Error(`Failed to ensure default environment "${DEFAULT_ENV_NAME}" on team ${teamId}`);
2218
+ }
2219
+ }
2220
+ async function expandVaultRefs(config, refs, timeout) {
2221
+ if (Object.keys(refs).length === 0) return refs;
2222
+ const needsTeam = Object.values(refs).some((v) => !v.startsWith("vault://"));
2223
+ if (!needsTeam) return refs;
2224
+ const teamId = await resolveDefaultTeamId(config, false, timeout);
2225
+ if (teamId === null) {
2226
+ throw new Error("vault_refs given but no vault secrets exist for this account");
2227
+ }
2228
+ const out = {};
2229
+ for (const [envVar, ref] of Object.entries(refs)) {
2230
+ out[envVar] = ref.startsWith("vault://") ? ref : `vault://${teamId}/${DEFAULT_ENV_NAME}/${ref}`;
2231
+ }
2232
+ return out;
2233
+ }
2234
+ function buildConfig(opts) {
2235
+ return new ConnectionConfig({
2236
+ apiKey: opts?.apiKey,
2237
+ domain: opts?.domain,
2238
+ apiUrl: opts?.apiUrl,
2239
+ requestTimeout: opts?.requestTimeout
2240
+ });
2241
+ }
2242
+ var Vault = class _Vault {
2243
+ // -------------------------------------------------------------------------
2244
+ // Secrets
2245
+ // -------------------------------------------------------------------------
2246
+ /**
2247
+ * Store a secret's value (server-side, in OpenBao) plus its injection
2248
+ * scopes, under the auto-provisioned default team + "prod" environment.
2249
+ * Returns metadata only — the value is never echoed.
2250
+ *
2251
+ * POST /teams/{teamId}/vault/secrets
2252
+ */
2253
+ static async createSecret(input, opts) {
2254
+ const config = buildConfig(opts);
2255
+ const teamId = await resolveDefaultTeamId(config, true, opts?.requestTimeout);
2256
+ if (teamId === null) throw new Error("Failed to resolve or create default team");
2257
+ await ensureDefaultEnv(config, teamId, opts?.requestTimeout);
2258
+ const body = {
2259
+ environment: DEFAULT_ENV_NAME,
2260
+ value: input.value
2261
+ };
2262
+ if (input.name) body.name = input.name;
2263
+ if (input.provider) body.provider = input.provider;
2264
+ if (input.scopes && input.scopes.length > 0) {
2265
+ body.scopes = input.scopes.map(vaultScopeToJSON);
2266
+ }
2267
+ if (input.rotationIntervalDays && input.rotationIntervalDays > 0) {
2268
+ body.rotation_interval_days = input.rotationIntervalDays;
2269
+ }
2270
+ const client = getSharedClient(config);
2271
+ const resp = await client.post(
2272
+ `/teams/${encodeURIComponent(teamId)}/vault/secrets`,
2273
+ { json: body, timeout: opts?.requestTimeout }
2274
+ );
2275
+ return parseVaultSecret(resp);
2276
+ }
2277
+ /**
2278
+ * List secret metadata for the default team. Returns an empty array if no
2279
+ * default team has been provisioned yet.
2280
+ *
2281
+ * GET /teams/{teamId}/vault/secrets -> {secrets}
2282
+ */
2283
+ static async listSecrets(opts) {
2284
+ const config = buildConfig(opts);
2285
+ const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
2286
+ if (teamId === null) return [];
2287
+ const client = getSharedClient(config);
2288
+ const resp = await client.get(
2289
+ `/teams/${encodeURIComponent(teamId)}/vault/secrets`,
2290
+ { timeout: opts?.requestTimeout }
2291
+ );
2292
+ const rows = resp.secrets ?? [];
2293
+ return rows.map(parseVaultSecret);
2294
+ }
2295
+ /**
2296
+ * Replace a secret's value by name (server-side); scopes are unchanged.
2297
+ *
2298
+ * POST /teams/{teamId}/vault/secrets/{secretId}/rotate {value}
2299
+ */
2300
+ static async rotateSecret(name, value, opts) {
2301
+ const config = buildConfig(opts);
2302
+ const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
2303
+ if (teamId === null) throw new Error(`vault secret "${name}" not found`);
2304
+ const secretId = await _Vault._resolveSecretId(config, teamId, name, opts?.requestTimeout);
2305
+ const client = getSharedClient(config);
2306
+ await client.post(
2307
+ `/teams/${encodeURIComponent(teamId)}/vault/secrets/${encodeURIComponent(secretId)}/rotate`,
2308
+ { json: { value }, timeout: opts?.requestTimeout }
2309
+ );
2310
+ }
2311
+ /**
2312
+ * Delete a secret by name — metadata and stored value.
2313
+ *
2314
+ * DELETE /teams/{teamId}/vault/secrets/{secretId}
2315
+ */
2316
+ static async deleteSecret(name, opts) {
2317
+ const config = buildConfig(opts);
2318
+ const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
2319
+ if (teamId === null) throw new Error(`vault secret "${name}" not found`);
2320
+ const secretId = await _Vault._resolveSecretId(config, teamId, name, opts?.requestTimeout);
2321
+ const client = getSharedClient(config);
2322
+ await client.delete(
2323
+ `/teams/${encodeURIComponent(teamId)}/vault/secrets/${encodeURIComponent(secretId)}`,
2324
+ { timeout: opts?.requestTimeout }
2325
+ );
2326
+ }
2327
+ /**
2328
+ * Replace a secret's injection scopes by name; the value is unchanged. Use
2329
+ * this to change a secret's destination(s) or injection format in place
2330
+ * instead of delete + recreate. At least one scope is required.
2331
+ *
2332
+ * POST /teams/{teamId}/vault/secrets/{secretId}/scopes
2333
+ */
2334
+ static async updateScopes(name, scopes, opts) {
2335
+ if (!scopes || scopes.length === 0) {
2336
+ throw new Error("at least one scope is required");
2337
+ }
2338
+ const config = buildConfig(opts);
2339
+ const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
2340
+ if (teamId === null) throw new Error(`vault secret "${name}" not found`);
2341
+ const secretId = await _Vault._resolveSecretId(config, teamId, name, opts?.requestTimeout);
2342
+ const client = getSharedClient(config);
2343
+ await client.post(
2344
+ `/teams/${encodeURIComponent(teamId)}/vault/secrets/${encodeURIComponent(secretId)}/scopes`,
2345
+ { json: { scopes: scopes.map(vaultScopeToJSON) }, timeout: opts?.requestTimeout }
2346
+ );
2347
+ }
2348
+ // -------------------------------------------------------------------------
2349
+ // Presets
2350
+ // -------------------------------------------------------------------------
2351
+ /**
2352
+ * List built-in provider preset catalog (templates only, no secret material).
2353
+ *
2354
+ * GET /vault/presets -> {presets}
2355
+ */
2356
+ static async listPresets(opts) {
2357
+ const client = getSharedClient(buildConfig(opts));
2358
+ const resp = await client.get("/vault/presets", {
2359
+ timeout: opts?.requestTimeout
2360
+ });
2361
+ const rows = resp.presets ?? [];
2362
+ return rows.map(parseVaultPreset);
2363
+ }
2364
+ // -------------------------------------------------------------------------
2365
+ // Internal helpers
2366
+ // -------------------------------------------------------------------------
2367
+ /** Maps a secret name to its id within the given team. */
2368
+ static async _resolveSecretId(config, teamId, name, timeout) {
2369
+ const client = getSharedClient(config);
2370
+ const resp = await client.get(
2371
+ `/teams/${encodeURIComponent(teamId)}/vault/secrets`,
2372
+ { timeout }
2373
+ );
2374
+ const rows = resp.secrets ?? [];
2375
+ for (const s of rows) {
2376
+ if (s.name === name) return String(s.secret_id ?? "");
2377
+ }
2378
+ throw new Error(`vault secret "${name}" not found`);
2379
+ }
2380
+ };
2381
+
2075
2382
  // src/sandbox/sandbox.ts
2076
2383
  var DEFAULT_TEMPLATE = "base";
2077
2384
  var DEFAULT_TIMEOUT = 300;
@@ -2217,6 +2524,9 @@ var Sandbox = class _Sandbox {
2217
2524
  if (opts?.envs) {
2218
2525
  body.envs = opts.envs;
2219
2526
  }
2527
+ if (opts?.vaultRefs) {
2528
+ body.vault_refs = await expandVaultRefs(config, opts.vaultRefs, opts.requestTimeout);
2529
+ }
2220
2530
  if (opts?.network) {
2221
2531
  body.network = networkOptsToJSON(opts.network);
2222
2532
  } else if (opts?.allowInternetAccess === false) {
@@ -3120,7 +3430,16 @@ function assertValidVolumeId3(id) {
3120
3430
  }
3121
3431
  }
3122
3432
  var Volumes = class {
3123
- /** Create a volume by streaming a tarball (gzip tar.gz) to the server. */
3433
+ /**
3434
+ * Create a volume named `name`, optionally populated with `data`.
3435
+ *
3436
+ * `POST /volumes` is the canonical create endpoint. With `data` (a gzip tar.gz)
3437
+ * the body is ingested into a new file-granular volume (or a legacy tarball
3438
+ * blob if no file-granular backend is configured). Creating an empty volume
3439
+ * (no `data`, or an empty buffer) requires a file-granular backend.
3440
+ * `empty(name)` / `ingest(name, data)` remain available for explicit,
3441
+ * backend-specific control.
3442
+ */
3124
3443
  static async create(name, data, opts) {
3125
3444
  if (!name) {
3126
3445
  throw new InvalidArgumentError("volume name is required");
@@ -3132,6 +3451,13 @@ var Volumes = class {
3132
3451
  requestTimeout: opts?.requestTimeout
3133
3452
  });
3134
3453
  const client = getSharedClient(config);
3454
+ if (data === void 0 || data.byteLength === 0) {
3455
+ const resp2 = await client.post("/volumes", {
3456
+ params: { name },
3457
+ timeout: opts?.requestTimeout
3458
+ });
3459
+ return parseVolumeInfo(resp2);
3460
+ }
3135
3461
  const body = data instanceof Uint8Array ? data : new Uint8Array(data);
3136
3462
  const resp = await client.post("/volumes", {
3137
3463
  params: { name },
@@ -3435,6 +3761,7 @@ export {
3435
3761
  TemplateError,
3436
3762
  TimeoutError,
3437
3763
  TransformDirection,
3764
+ Vault,
3438
3765
  VolumeFiles,
3439
3766
  VolumeLocks,
3440
3767
  Volumes,
@@ -3486,6 +3813,9 @@ export {
3486
3813
  parseSnapshotInfo,
3487
3814
  parseTemplateBuildStatus,
3488
3815
  parseToxicityConfig,
3816
+ parseVaultPreset,
3817
+ parseVaultScope,
3818
+ parseVaultSecret,
3489
3819
  parseVolumeInfo,
3490
3820
  parseWriteInfo,
3491
3821
  requiresTlsInterception,
@@ -3493,6 +3823,7 @@ export {
3493
3823
  securityPolicyToJSON,
3494
3824
  toxicityConfigToJSON,
3495
3825
  validateNetworkEntry,
3826
+ vaultScopeToJSON,
3496
3827
  volumeAttachmentToJSON
3497
3828
  };
3498
3829
  //# sourceMappingURL=index.js.map