@declaw/sdk 1.2.3 → 1.4.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 +34 -0
- package/dist/index.cjs +419 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +226 -7
- package/dist/index.d.ts +226 -7
- package/dist/index.js +411 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,13 +33,51 @@ var ConnectionConfig = class {
|
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
// src/api/idempotency.ts
|
|
37
|
+
var CODE_IDEMPOTENCY_IN_PROGRESS = "idempotency_in_progress";
|
|
38
|
+
var CODE_IDEMPOTENCY_KEY_REUSED = "idempotency_key_reused";
|
|
39
|
+
var CODE_TEMPLATE_NOT_READY = "template_not_ready";
|
|
40
|
+
var MAX_RETRY_AFTER_MS = 6e4;
|
|
41
|
+
function newIdempotencyKey() {
|
|
42
|
+
const c = globalThis.crypto;
|
|
43
|
+
if (c?.randomUUID) {
|
|
44
|
+
return c.randomUUID();
|
|
45
|
+
}
|
|
46
|
+
if (c?.getRandomValues) {
|
|
47
|
+
const b = c.getRandomValues(new Uint8Array(16));
|
|
48
|
+
b[6] = b[6] & 15 | 64;
|
|
49
|
+
b[8] = b[8] & 63 | 128;
|
|
50
|
+
const hex = Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
51
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
52
|
+
}
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
function retryAfterMs(response) {
|
|
56
|
+
const raw = response.headers.get("Retry-After");
|
|
57
|
+
if (raw === null) return void 0;
|
|
58
|
+
const secs = Number(raw);
|
|
59
|
+
if (!Number.isFinite(secs) || secs < 0) return void 0;
|
|
60
|
+
return Math.min(secs * 1e3, MAX_RETRY_AFTER_MS);
|
|
61
|
+
}
|
|
62
|
+
|
|
36
63
|
// src/errors.ts
|
|
37
64
|
var SandboxError = class extends Error {
|
|
38
65
|
sandboxId;
|
|
66
|
+
/**
|
|
67
|
+
* Machine-readable error code from the API's `code` field, when present.
|
|
68
|
+
*
|
|
69
|
+
* Branch on this, never on `message`. Messages are prose and change; codes are
|
|
70
|
+
* contract. It matters most where one status means several unrelated things:
|
|
71
|
+
* a 409 from `POST /sandboxes` is either `idempotency_in_progress` (the
|
|
72
|
+
* original create is still running — retry the identical request) or
|
|
73
|
+
* `template_not_ready` (rebuild the template; retrying cannot help).
|
|
74
|
+
*/
|
|
75
|
+
code;
|
|
39
76
|
constructor(message, opts) {
|
|
40
77
|
super(message);
|
|
41
78
|
this.name = "SandboxError";
|
|
42
79
|
this.sandboxId = opts?.sandboxId;
|
|
80
|
+
this.code = opts?.code;
|
|
43
81
|
}
|
|
44
82
|
};
|
|
45
83
|
var TimeoutError = class extends SandboxError {
|
|
@@ -297,6 +335,15 @@ var ApiClient = class {
|
|
|
297
335
|
await this.delay(attempt);
|
|
298
336
|
continue;
|
|
299
337
|
}
|
|
338
|
+
if (response.status === 409 && attempt < this.maxRetries - 1) {
|
|
339
|
+
const parsed = await this.readErrorBody(response);
|
|
340
|
+
if (parsed.code === CODE_IDEMPOTENCY_IN_PROGRESS) {
|
|
341
|
+
const after = retryAfterMs(response);
|
|
342
|
+
await (after !== void 0 ? new Promise((r) => setTimeout(r, after)) : this.delay(attempt));
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
throw this.errorFrom(response, parsed);
|
|
346
|
+
}
|
|
300
347
|
if (!response.ok) {
|
|
301
348
|
throw await this.buildError(response);
|
|
302
349
|
}
|
|
@@ -322,20 +369,36 @@ var ApiClient = class {
|
|
|
322
369
|
`Request failed after ${this.maxRetries} retries: ${lastError?.message ?? "unknown error"}`
|
|
323
370
|
);
|
|
324
371
|
}
|
|
325
|
-
|
|
326
|
-
|
|
372
|
+
/**
|
|
373
|
+
* Read an error body ONCE.
|
|
374
|
+
*
|
|
375
|
+
* `Response` bodies are single-read streams, so the 409 path cannot inspect
|
|
376
|
+
* the code and then hand the response to a separate error builder — the
|
|
377
|
+
* second read yields nothing and the error loses its message. Everything that
|
|
378
|
+
* needs the body goes through here, and the parsed result is passed around
|
|
379
|
+
* instead of the response.
|
|
380
|
+
*/
|
|
381
|
+
async readErrorBody(response) {
|
|
327
382
|
try {
|
|
328
|
-
const body = await response.
|
|
383
|
+
const body = JSON.parse(await response.text());
|
|
329
384
|
const bodyMsg = body.message ?? body.error ?? response.statusText;
|
|
330
|
-
|
|
385
|
+
return {
|
|
386
|
+
message: `HTTP ${response.status}: ${bodyMsg}`,
|
|
387
|
+
code: typeof body.code === "string" ? body.code : void 0
|
|
388
|
+
};
|
|
331
389
|
} catch {
|
|
332
|
-
message
|
|
390
|
+
return { message: `HTTP ${response.status}: ${response.statusText}` };
|
|
333
391
|
}
|
|
392
|
+
}
|
|
393
|
+
errorFrom(response, parsed) {
|
|
334
394
|
const ErrorClass = STATUS_ERROR_MAP[response.status];
|
|
335
395
|
if (ErrorClass) {
|
|
336
|
-
return new ErrorClass(message);
|
|
396
|
+
return new ErrorClass(parsed.message, { code: parsed.code });
|
|
337
397
|
}
|
|
338
|
-
return new SandboxError(message);
|
|
398
|
+
return new SandboxError(parsed.message, { code: parsed.code });
|
|
399
|
+
}
|
|
400
|
+
async buildError(response) {
|
|
401
|
+
return this.errorFrom(response, await this.readErrorBody(response));
|
|
339
402
|
}
|
|
340
403
|
async parseResponseBody(response) {
|
|
341
404
|
const contentLength = response.headers.get("content-length");
|
|
@@ -509,7 +572,7 @@ function createInjectionDefenseConfig(opts) {
|
|
|
509
572
|
enabled: opts?.enabled ?? false,
|
|
510
573
|
sensitivity: opts?.sensitivity ?? "medium" /* Medium */,
|
|
511
574
|
action: opts?.action ?? "log_only" /* LogOnly */,
|
|
512
|
-
threshold: opts?.threshold ?? 0.
|
|
575
|
+
threshold: opts?.threshold ?? 0.95,
|
|
513
576
|
domains: opts?.domains,
|
|
514
577
|
judge: opts?.judge,
|
|
515
578
|
injectionMode: opts?.injectionMode
|
|
@@ -536,7 +599,7 @@ function parseInjectionDefenseConfig(data) {
|
|
|
536
599
|
enabled: data.enabled ?? false,
|
|
537
600
|
sensitivity: data.sensitivity ?? "medium" /* Medium */,
|
|
538
601
|
action: data.action ?? "log_only" /* LogOnly */,
|
|
539
|
-
threshold: data.threshold ?? 0.
|
|
602
|
+
threshold: data.threshold ?? 0.95,
|
|
540
603
|
domains: data.domains,
|
|
541
604
|
judge: data.judge ? { enabled: data.judge.enabled ?? false, always: data.judge.always, policy: data.judge.policy } : void 0,
|
|
542
605
|
injectionMode: data.injection_mode ?? data.injectionMode
|
|
@@ -914,14 +977,14 @@ function fullInjectionDefensePolicy(opts) {
|
|
|
914
977
|
injectionDefense: createInjectionDefenseConfig({
|
|
915
978
|
enabled: true,
|
|
916
979
|
action: opts?.action ?? "block",
|
|
917
|
-
threshold: opts?.threshold ?? 0.
|
|
980
|
+
threshold: opts?.threshold ?? 0.95,
|
|
918
981
|
domains: opts?.domains,
|
|
919
982
|
injectionMode: opts?.mode ?? "balanced",
|
|
920
983
|
judge: { enabled: true, always: opts?.alwaysJudge ?? false, policy: opts?.agentPolicy ?? "" }
|
|
921
984
|
}),
|
|
922
985
|
customPolicy: createCustomPolicyConfig({
|
|
923
986
|
enabled: true,
|
|
924
|
-
policyRef: "prompt-injection@
|
|
987
|
+
policyRef: "prompt-injection@v3",
|
|
925
988
|
defaultDeny: false
|
|
926
989
|
})
|
|
927
990
|
});
|
|
@@ -962,7 +1025,7 @@ function securityPolicyToJSON(policy) {
|
|
|
962
1025
|
action: injDefConfig.action,
|
|
963
1026
|
threshold: injDefConfig.threshold
|
|
964
1027
|
};
|
|
965
|
-
if (injDefConfig.domains !== void 0) {
|
|
1028
|
+
if (injDefConfig.domains !== void 0 && injDefConfig.domains.length > 0) {
|
|
966
1029
|
injDef.domains = injDefConfig.domains;
|
|
967
1030
|
}
|
|
968
1031
|
if (injDefConfig.injectionMode !== void 0) {
|
|
@@ -2074,6 +2137,311 @@ function volumeAttachmentToJSON(att) {
|
|
|
2074
2137
|
return out;
|
|
2075
2138
|
}
|
|
2076
2139
|
|
|
2140
|
+
// src/vault/models.ts
|
|
2141
|
+
function parseVaultScope(data) {
|
|
2142
|
+
const scope = {
|
|
2143
|
+
domainRegex: String(data.domain_regex ?? "")
|
|
2144
|
+
};
|
|
2145
|
+
if (data.injection_type !== void 0 && data.injection_type !== null) {
|
|
2146
|
+
scope.injectionType = String(data.injection_type);
|
|
2147
|
+
}
|
|
2148
|
+
if (data.header_name !== void 0 && data.header_name !== null) {
|
|
2149
|
+
scope.headerName = String(data.header_name);
|
|
2150
|
+
}
|
|
2151
|
+
if (data.value_prefix !== void 0 && data.value_prefix !== null) {
|
|
2152
|
+
scope.valuePrefix = String(data.value_prefix);
|
|
2153
|
+
}
|
|
2154
|
+
if (data.basic_username !== void 0 && data.basic_username !== null) {
|
|
2155
|
+
scope.basicUsername = String(data.basic_username);
|
|
2156
|
+
}
|
|
2157
|
+
if (data.extra_headers !== void 0 && data.extra_headers !== null) {
|
|
2158
|
+
scope.extraHeaders = data.extra_headers;
|
|
2159
|
+
}
|
|
2160
|
+
if (data.query_params !== void 0 && data.query_params !== null) {
|
|
2161
|
+
scope.queryParams = data.query_params;
|
|
2162
|
+
}
|
|
2163
|
+
return scope;
|
|
2164
|
+
}
|
|
2165
|
+
function parseVaultSecret(data) {
|
|
2166
|
+
const secret = {
|
|
2167
|
+
secretId: String(data.secret_id ?? ""),
|
|
2168
|
+
name: String(data.name ?? ""),
|
|
2169
|
+
createdAt: String(data.created_at ?? ""),
|
|
2170
|
+
updatedAt: String(data.updated_at ?? "")
|
|
2171
|
+
};
|
|
2172
|
+
const rawScopes = data.scopes;
|
|
2173
|
+
if (rawScopes && rawScopes.length > 0) {
|
|
2174
|
+
secret.scopes = rawScopes.map(parseVaultScope);
|
|
2175
|
+
}
|
|
2176
|
+
if (data.rotated_at !== void 0 && data.rotated_at !== null) {
|
|
2177
|
+
secret.rotatedAt = String(data.rotated_at);
|
|
2178
|
+
}
|
|
2179
|
+
if (data.rotation_interval_days !== void 0 && data.rotation_interval_days !== null) {
|
|
2180
|
+
secret.rotationIntervalDays = Number(data.rotation_interval_days);
|
|
2181
|
+
}
|
|
2182
|
+
if (data.rotation_due !== void 0 && data.rotation_due !== null) {
|
|
2183
|
+
secret.rotationDue = Boolean(data.rotation_due);
|
|
2184
|
+
}
|
|
2185
|
+
return secret;
|
|
2186
|
+
}
|
|
2187
|
+
function parseVaultPreset(data) {
|
|
2188
|
+
const rawScopes = data.scopes ?? [];
|
|
2189
|
+
const preset = {
|
|
2190
|
+
key: String(data.key ?? ""),
|
|
2191
|
+
name: String(data.name ?? ""),
|
|
2192
|
+
category: String(data.category ?? ""),
|
|
2193
|
+
keyHint: String(data.key_hint ?? ""),
|
|
2194
|
+
scopes: rawScopes.map(parseVaultScope)
|
|
2195
|
+
};
|
|
2196
|
+
if (data.docs_url !== void 0 && data.docs_url !== null) {
|
|
2197
|
+
preset.docsUrl = String(data.docs_url);
|
|
2198
|
+
}
|
|
2199
|
+
return preset;
|
|
2200
|
+
}
|
|
2201
|
+
function vaultScopeToJSON(scope) {
|
|
2202
|
+
const out = {
|
|
2203
|
+
domain_regex: scope.domainRegex
|
|
2204
|
+
};
|
|
2205
|
+
if (scope.injectionType !== void 0) {
|
|
2206
|
+
out.injection_type = scope.injectionType;
|
|
2207
|
+
}
|
|
2208
|
+
if (scope.headerName !== void 0) {
|
|
2209
|
+
out.header_name = scope.headerName;
|
|
2210
|
+
}
|
|
2211
|
+
if (scope.valuePrefix !== void 0) {
|
|
2212
|
+
out.value_prefix = scope.valuePrefix;
|
|
2213
|
+
}
|
|
2214
|
+
if (scope.basicUsername !== void 0) {
|
|
2215
|
+
out.basic_username = scope.basicUsername;
|
|
2216
|
+
}
|
|
2217
|
+
if (scope.extraHeaders !== void 0) {
|
|
2218
|
+
out.extra_headers = scope.extraHeaders;
|
|
2219
|
+
}
|
|
2220
|
+
if (scope.queryParams !== void 0) {
|
|
2221
|
+
out.query_params = scope.queryParams;
|
|
2222
|
+
}
|
|
2223
|
+
return out;
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// src/vault/vault.ts
|
|
2227
|
+
var DEFAULT_TEAM_NAME = "default";
|
|
2228
|
+
var DEFAULT_ENV_NAME = "prod";
|
|
2229
|
+
var defaultTeamCache = /* @__PURE__ */ new Map();
|
|
2230
|
+
function cacheKey(config) {
|
|
2231
|
+
return `${config.apiUrl}\0${config.apiKey}`;
|
|
2232
|
+
}
|
|
2233
|
+
async function resolveDefaultTeamId(config, create, timeout) {
|
|
2234
|
+
const key = cacheKey(config);
|
|
2235
|
+
const cached = defaultTeamCache.get(key);
|
|
2236
|
+
if (cached !== void 0) return cached;
|
|
2237
|
+
const client = getSharedClient(config);
|
|
2238
|
+
const resp = await client.get("/teams", { timeout });
|
|
2239
|
+
const rows = resp.teams ?? [];
|
|
2240
|
+
let best = null;
|
|
2241
|
+
for (const t of rows) {
|
|
2242
|
+
if (t.name === DEFAULT_TEAM_NAME) {
|
|
2243
|
+
if (best === null || t.created_at < best.created_at) {
|
|
2244
|
+
best = t;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
if (best !== null) {
|
|
2249
|
+
defaultTeamCache.set(key, best.team_id);
|
|
2250
|
+
return best.team_id;
|
|
2251
|
+
}
|
|
2252
|
+
if (!create) return null;
|
|
2253
|
+
const created = await client.post("/teams", {
|
|
2254
|
+
json: { name: DEFAULT_TEAM_NAME },
|
|
2255
|
+
timeout
|
|
2256
|
+
});
|
|
2257
|
+
defaultTeamCache.set(key, created.team_id);
|
|
2258
|
+
return created.team_id;
|
|
2259
|
+
}
|
|
2260
|
+
async function ensureDefaultEnv(config, teamId, timeout) {
|
|
2261
|
+
const client = getSharedClient(config);
|
|
2262
|
+
const resp = await client.get(
|
|
2263
|
+
`/teams/${encodeURIComponent(teamId)}/environments`,
|
|
2264
|
+
{ timeout }
|
|
2265
|
+
);
|
|
2266
|
+
const rows = resp.environments ?? [];
|
|
2267
|
+
if (rows.some((e) => e.name === DEFAULT_ENV_NAME)) return;
|
|
2268
|
+
try {
|
|
2269
|
+
await client.post(`/teams/${encodeURIComponent(teamId)}/environments`, {
|
|
2270
|
+
json: { name: DEFAULT_ENV_NAME },
|
|
2271
|
+
timeout
|
|
2272
|
+
});
|
|
2273
|
+
} catch {
|
|
2274
|
+
const resp2 = await client.get(
|
|
2275
|
+
`/teams/${encodeURIComponent(teamId)}/environments`,
|
|
2276
|
+
{ timeout }
|
|
2277
|
+
);
|
|
2278
|
+
const rows2 = resp2.environments ?? [];
|
|
2279
|
+
if (rows2.some((e) => e.name === DEFAULT_ENV_NAME)) return;
|
|
2280
|
+
throw new Error(`Failed to ensure default environment "${DEFAULT_ENV_NAME}" on team ${teamId}`);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
async function expandVaultRefs(config, refs, timeout) {
|
|
2284
|
+
if (Object.keys(refs).length === 0) return refs;
|
|
2285
|
+
const needsTeam = Object.values(refs).some((v) => !v.startsWith("vault://"));
|
|
2286
|
+
if (!needsTeam) return refs;
|
|
2287
|
+
const teamId = await resolveDefaultTeamId(config, false, timeout);
|
|
2288
|
+
if (teamId === null) {
|
|
2289
|
+
throw new Error("vault_refs given but no vault secrets exist for this account");
|
|
2290
|
+
}
|
|
2291
|
+
const out = {};
|
|
2292
|
+
for (const [envVar, ref] of Object.entries(refs)) {
|
|
2293
|
+
out[envVar] = ref.startsWith("vault://") ? ref : `vault://${teamId}/${DEFAULT_ENV_NAME}/${ref}`;
|
|
2294
|
+
}
|
|
2295
|
+
return out;
|
|
2296
|
+
}
|
|
2297
|
+
function buildConfig(opts) {
|
|
2298
|
+
return new ConnectionConfig({
|
|
2299
|
+
apiKey: opts?.apiKey,
|
|
2300
|
+
domain: opts?.domain,
|
|
2301
|
+
apiUrl: opts?.apiUrl,
|
|
2302
|
+
requestTimeout: opts?.requestTimeout
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
var Vault = class _Vault {
|
|
2306
|
+
// -------------------------------------------------------------------------
|
|
2307
|
+
// Secrets
|
|
2308
|
+
// -------------------------------------------------------------------------
|
|
2309
|
+
/**
|
|
2310
|
+
* Store a secret's value (server-side, in OpenBao) plus its injection
|
|
2311
|
+
* scopes, under the auto-provisioned default team + "prod" environment.
|
|
2312
|
+
* Returns metadata only — the value is never echoed.
|
|
2313
|
+
*
|
|
2314
|
+
* POST /teams/{teamId}/vault/secrets
|
|
2315
|
+
*/
|
|
2316
|
+
static async createSecret(input, opts) {
|
|
2317
|
+
const config = buildConfig(opts);
|
|
2318
|
+
const teamId = await resolveDefaultTeamId(config, true, opts?.requestTimeout);
|
|
2319
|
+
if (teamId === null) throw new Error("Failed to resolve or create default team");
|
|
2320
|
+
await ensureDefaultEnv(config, teamId, opts?.requestTimeout);
|
|
2321
|
+
const body = {
|
|
2322
|
+
environment: DEFAULT_ENV_NAME,
|
|
2323
|
+
value: input.value
|
|
2324
|
+
};
|
|
2325
|
+
if (input.name) body.name = input.name;
|
|
2326
|
+
if (input.provider) body.provider = input.provider;
|
|
2327
|
+
if (input.scopes && input.scopes.length > 0) {
|
|
2328
|
+
body.scopes = input.scopes.map(vaultScopeToJSON);
|
|
2329
|
+
}
|
|
2330
|
+
if (input.rotationIntervalDays && input.rotationIntervalDays > 0) {
|
|
2331
|
+
body.rotation_interval_days = input.rotationIntervalDays;
|
|
2332
|
+
}
|
|
2333
|
+
const client = getSharedClient(config);
|
|
2334
|
+
const resp = await client.post(
|
|
2335
|
+
`/teams/${encodeURIComponent(teamId)}/vault/secrets`,
|
|
2336
|
+
{ json: body, timeout: opts?.requestTimeout }
|
|
2337
|
+
);
|
|
2338
|
+
return parseVaultSecret(resp);
|
|
2339
|
+
}
|
|
2340
|
+
/**
|
|
2341
|
+
* List secret metadata for the default team. Returns an empty array if no
|
|
2342
|
+
* default team has been provisioned yet.
|
|
2343
|
+
*
|
|
2344
|
+
* GET /teams/{teamId}/vault/secrets -> {secrets}
|
|
2345
|
+
*/
|
|
2346
|
+
static async listSecrets(opts) {
|
|
2347
|
+
const config = buildConfig(opts);
|
|
2348
|
+
const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
|
|
2349
|
+
if (teamId === null) return [];
|
|
2350
|
+
const client = getSharedClient(config);
|
|
2351
|
+
const resp = await client.get(
|
|
2352
|
+
`/teams/${encodeURIComponent(teamId)}/vault/secrets`,
|
|
2353
|
+
{ timeout: opts?.requestTimeout }
|
|
2354
|
+
);
|
|
2355
|
+
const rows = resp.secrets ?? [];
|
|
2356
|
+
return rows.map(parseVaultSecret);
|
|
2357
|
+
}
|
|
2358
|
+
/**
|
|
2359
|
+
* Replace a secret's value by name (server-side); scopes are unchanged.
|
|
2360
|
+
*
|
|
2361
|
+
* POST /teams/{teamId}/vault/secrets/{secretId}/rotate {value}
|
|
2362
|
+
*/
|
|
2363
|
+
static async rotateSecret(name, value, opts) {
|
|
2364
|
+
const config = buildConfig(opts);
|
|
2365
|
+
const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
|
|
2366
|
+
if (teamId === null) throw new Error(`vault secret "${name}" not found`);
|
|
2367
|
+
const secretId = await _Vault._resolveSecretId(config, teamId, name, opts?.requestTimeout);
|
|
2368
|
+
const client = getSharedClient(config);
|
|
2369
|
+
await client.post(
|
|
2370
|
+
`/teams/${encodeURIComponent(teamId)}/vault/secrets/${encodeURIComponent(secretId)}/rotate`,
|
|
2371
|
+
{ json: { value }, timeout: opts?.requestTimeout }
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Delete a secret by name — metadata and stored value.
|
|
2376
|
+
*
|
|
2377
|
+
* DELETE /teams/{teamId}/vault/secrets/{secretId}
|
|
2378
|
+
*/
|
|
2379
|
+
static async deleteSecret(name, opts) {
|
|
2380
|
+
const config = buildConfig(opts);
|
|
2381
|
+
const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
|
|
2382
|
+
if (teamId === null) throw new Error(`vault secret "${name}" not found`);
|
|
2383
|
+
const secretId = await _Vault._resolveSecretId(config, teamId, name, opts?.requestTimeout);
|
|
2384
|
+
const client = getSharedClient(config);
|
|
2385
|
+
await client.delete(
|
|
2386
|
+
`/teams/${encodeURIComponent(teamId)}/vault/secrets/${encodeURIComponent(secretId)}`,
|
|
2387
|
+
{ timeout: opts?.requestTimeout }
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Replace a secret's injection scopes by name; the value is unchanged. Use
|
|
2392
|
+
* this to change a secret's destination(s) or injection format in place
|
|
2393
|
+
* instead of delete + recreate. At least one scope is required.
|
|
2394
|
+
*
|
|
2395
|
+
* POST /teams/{teamId}/vault/secrets/{secretId}/scopes
|
|
2396
|
+
*/
|
|
2397
|
+
static async updateScopes(name, scopes, opts) {
|
|
2398
|
+
if (!scopes || scopes.length === 0) {
|
|
2399
|
+
throw new Error("at least one scope is required");
|
|
2400
|
+
}
|
|
2401
|
+
const config = buildConfig(opts);
|
|
2402
|
+
const teamId = await resolveDefaultTeamId(config, false, opts?.requestTimeout);
|
|
2403
|
+
if (teamId === null) throw new Error(`vault secret "${name}" not found`);
|
|
2404
|
+
const secretId = await _Vault._resolveSecretId(config, teamId, name, opts?.requestTimeout);
|
|
2405
|
+
const client = getSharedClient(config);
|
|
2406
|
+
await client.post(
|
|
2407
|
+
`/teams/${encodeURIComponent(teamId)}/vault/secrets/${encodeURIComponent(secretId)}/scopes`,
|
|
2408
|
+
{ json: { scopes: scopes.map(vaultScopeToJSON) }, timeout: opts?.requestTimeout }
|
|
2409
|
+
);
|
|
2410
|
+
}
|
|
2411
|
+
// -------------------------------------------------------------------------
|
|
2412
|
+
// Presets
|
|
2413
|
+
// -------------------------------------------------------------------------
|
|
2414
|
+
/**
|
|
2415
|
+
* List built-in provider preset catalog (templates only, no secret material).
|
|
2416
|
+
*
|
|
2417
|
+
* GET /vault/presets -> {presets}
|
|
2418
|
+
*/
|
|
2419
|
+
static async listPresets(opts) {
|
|
2420
|
+
const client = getSharedClient(buildConfig(opts));
|
|
2421
|
+
const resp = await client.get("/vault/presets", {
|
|
2422
|
+
timeout: opts?.requestTimeout
|
|
2423
|
+
});
|
|
2424
|
+
const rows = resp.presets ?? [];
|
|
2425
|
+
return rows.map(parseVaultPreset);
|
|
2426
|
+
}
|
|
2427
|
+
// -------------------------------------------------------------------------
|
|
2428
|
+
// Internal helpers
|
|
2429
|
+
// -------------------------------------------------------------------------
|
|
2430
|
+
/** Maps a secret name to its id within the given team. */
|
|
2431
|
+
static async _resolveSecretId(config, teamId, name, timeout) {
|
|
2432
|
+
const client = getSharedClient(config);
|
|
2433
|
+
const resp = await client.get(
|
|
2434
|
+
`/teams/${encodeURIComponent(teamId)}/vault/secrets`,
|
|
2435
|
+
{ timeout }
|
|
2436
|
+
);
|
|
2437
|
+
const rows = resp.secrets ?? [];
|
|
2438
|
+
for (const s of rows) {
|
|
2439
|
+
if (s.name === name) return String(s.secret_id ?? "");
|
|
2440
|
+
}
|
|
2441
|
+
throw new Error(`vault secret "${name}" not found`);
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
|
|
2077
2445
|
// src/sandbox/sandbox.ts
|
|
2078
2446
|
var DEFAULT_TEMPLATE = "base";
|
|
2079
2447
|
var DEFAULT_TIMEOUT = 300;
|
|
@@ -2219,6 +2587,9 @@ var Sandbox = class _Sandbox {
|
|
|
2219
2587
|
if (opts?.envs) {
|
|
2220
2588
|
body.envs = opts.envs;
|
|
2221
2589
|
}
|
|
2590
|
+
if (opts?.vaultRefs) {
|
|
2591
|
+
body.vault_refs = await expandVaultRefs(config, opts.vaultRefs, opts.requestTimeout);
|
|
2592
|
+
}
|
|
2222
2593
|
if (opts?.network) {
|
|
2223
2594
|
body.network = networkOptsToJSON(opts.network);
|
|
2224
2595
|
} else if (opts?.allowInternetAccess === false) {
|
|
@@ -2236,9 +2607,11 @@ var Sandbox = class _Sandbox {
|
|
|
2236
2607
|
if (opts?.volumes && opts.volumes.length > 0) {
|
|
2237
2608
|
body.volumes = opts.volumes.map(volumeAttachmentToJSON);
|
|
2238
2609
|
}
|
|
2610
|
+
const idempotencyKey = newIdempotencyKey();
|
|
2239
2611
|
const data = await client.post("/sandboxes", {
|
|
2240
2612
|
json: body,
|
|
2241
|
-
timeout: opts?.requestTimeout
|
|
2613
|
+
timeout: opts?.requestTimeout,
|
|
2614
|
+
...idempotencyKey ? { headers: { "Idempotency-Key": idempotencyKey } } : {}
|
|
2242
2615
|
});
|
|
2243
2616
|
const sandboxId = data.sandbox_id;
|
|
2244
2617
|
assertValidId(sandboxId, "sandbox ID (from server)");
|
|
@@ -3122,7 +3495,16 @@ function assertValidVolumeId3(id) {
|
|
|
3122
3495
|
}
|
|
3123
3496
|
}
|
|
3124
3497
|
var Volumes = class {
|
|
3125
|
-
/**
|
|
3498
|
+
/**
|
|
3499
|
+
* Create a volume named `name`, optionally populated with `data`.
|
|
3500
|
+
*
|
|
3501
|
+
* `POST /volumes` is the canonical create endpoint. With `data` (a gzip tar.gz)
|
|
3502
|
+
* the body is ingested into a new file-granular volume (or a legacy tarball
|
|
3503
|
+
* blob if no file-granular backend is configured). Creating an empty volume
|
|
3504
|
+
* (no `data`, or an empty buffer) requires a file-granular backend.
|
|
3505
|
+
* `empty(name)` / `ingest(name, data)` remain available for explicit,
|
|
3506
|
+
* backend-specific control.
|
|
3507
|
+
*/
|
|
3126
3508
|
static async create(name, data, opts) {
|
|
3127
3509
|
if (!name) {
|
|
3128
3510
|
throw new InvalidArgumentError("volume name is required");
|
|
@@ -3134,6 +3516,13 @@ var Volumes = class {
|
|
|
3134
3516
|
requestTimeout: opts?.requestTimeout
|
|
3135
3517
|
});
|
|
3136
3518
|
const client = getSharedClient(config);
|
|
3519
|
+
if (data === void 0 || data.byteLength === 0) {
|
|
3520
|
+
const resp2 = await client.post("/volumes", {
|
|
3521
|
+
params: { name },
|
|
3522
|
+
timeout: opts?.requestTimeout
|
|
3523
|
+
});
|
|
3524
|
+
return parseVolumeInfo(resp2);
|
|
3525
|
+
}
|
|
3137
3526
|
const body = data instanceof Uint8Array ? data : new Uint8Array(data);
|
|
3138
3527
|
const resp = await client.post("/volumes", {
|
|
3139
3528
|
params: { name },
|
|
@@ -3403,6 +3792,9 @@ export {
|
|
|
3403
3792
|
ApiClient,
|
|
3404
3793
|
AuthenticationError,
|
|
3405
3794
|
BuildError,
|
|
3795
|
+
CODE_IDEMPOTENCY_IN_PROGRESS,
|
|
3796
|
+
CODE_IDEMPOTENCY_KEY_REUSED,
|
|
3797
|
+
CODE_TEMPLATE_NOT_READY,
|
|
3406
3798
|
CommandExitError,
|
|
3407
3799
|
CommandHandle,
|
|
3408
3800
|
Commands,
|
|
@@ -3437,6 +3829,7 @@ export {
|
|
|
3437
3829
|
TemplateError,
|
|
3438
3830
|
TimeoutError,
|
|
3439
3831
|
TransformDirection,
|
|
3832
|
+
Vault,
|
|
3440
3833
|
VolumeFiles,
|
|
3441
3834
|
VolumeLocks,
|
|
3442
3835
|
Volumes,
|
|
@@ -3488,6 +3881,9 @@ export {
|
|
|
3488
3881
|
parseSnapshotInfo,
|
|
3489
3882
|
parseTemplateBuildStatus,
|
|
3490
3883
|
parseToxicityConfig,
|
|
3884
|
+
parseVaultPreset,
|
|
3885
|
+
parseVaultScope,
|
|
3886
|
+
parseVaultSecret,
|
|
3491
3887
|
parseVolumeInfo,
|
|
3492
3888
|
parseWriteInfo,
|
|
3493
3889
|
requiresTlsInterception,
|
|
@@ -3495,6 +3891,7 @@ export {
|
|
|
3495
3891
|
securityPolicyToJSON,
|
|
3496
3892
|
toxicityConfigToJSON,
|
|
3497
3893
|
validateNetworkEntry,
|
|
3894
|
+
vaultScopeToJSON,
|
|
3498
3895
|
volumeAttachmentToJSON
|
|
3499
3896
|
};
|
|
3500
3897
|
//# sourceMappingURL=index.js.map
|