@declaw/sdk 1.0.0 → 1.0.3
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 +40 -0
- package/dist/index.cjs +240 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +107 -33
- package/dist/index.d.ts +107 -33
- package/dist/index.js +239 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -162,6 +162,13 @@ var ApiClient = class {
|
|
|
162
162
|
async stream(path, opts) {
|
|
163
163
|
return this.requestWithRetry("POST", path, opts, true);
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Send a GET request and return the raw Response for SSE streaming.
|
|
167
|
+
* Does NOT parse the response body. Used by PTY stream consumers.
|
|
168
|
+
*/
|
|
169
|
+
async streamGet(path, opts) {
|
|
170
|
+
return this.requestWithRetry("GET", path, opts, true);
|
|
171
|
+
}
|
|
165
172
|
/**
|
|
166
173
|
* Abort all in-flight requests and release resources.
|
|
167
174
|
*/
|
|
@@ -441,18 +448,12 @@ function parseInjectionDefenseConfig(data) {
|
|
|
441
448
|
// src/security/audit.ts
|
|
442
449
|
function createAuditConfig(opts) {
|
|
443
450
|
return {
|
|
444
|
-
enabled: opts?.enabled ??
|
|
445
|
-
logRequestBody: opts?.logRequestBody ?? true,
|
|
446
|
-
logResponseBody: opts?.logResponseBody ?? false,
|
|
447
|
-
retentionHours: opts?.retentionHours ?? 24
|
|
451
|
+
enabled: opts?.enabled ?? true
|
|
448
452
|
};
|
|
449
453
|
}
|
|
450
454
|
function parseAuditConfig(data) {
|
|
451
455
|
return {
|
|
452
|
-
enabled: data.enabled ??
|
|
453
|
-
logRequestBody: data.log_request_body ?? data.logRequestBody ?? true,
|
|
454
|
-
logResponseBody: data.log_response_body ?? data.logResponseBody ?? false,
|
|
455
|
-
retentionHours: data.retention_hours ?? data.retentionHours ?? 24
|
|
456
|
+
enabled: data.enabled ?? true
|
|
456
457
|
};
|
|
457
458
|
}
|
|
458
459
|
function parseAuditEntry(data) {
|
|
@@ -749,7 +750,7 @@ function createSecurityPolicy(opts) {
|
|
|
749
750
|
injectionDefense: opts?.injectionDefense ?? false,
|
|
750
751
|
transformations: opts?.transformations ?? [],
|
|
751
752
|
network: opts?.network,
|
|
752
|
-
audit: opts?.audit ??
|
|
753
|
+
audit: opts?.audit ?? true,
|
|
753
754
|
envSecurity: opts?.envSecurity ?? createEnvSecurityConfig(),
|
|
754
755
|
toxicity: opts?.toxicity,
|
|
755
756
|
codeSecurity: opts?.codeSecurity,
|
|
@@ -764,7 +765,7 @@ function parseSecurityPolicy(data) {
|
|
|
764
765
|
injectionDefense: typeof injDef === "boolean" ? injDef : injDef ? parseInjectionDefenseConfig(injDef) : false,
|
|
765
766
|
transformations: Array.isArray(data.transformations) ? data.transformations.map((t) => parseTransformationRule(t)) : [],
|
|
766
767
|
network: data.network ? parseNetworkPolicy(data.network) : void 0,
|
|
767
|
-
audit: typeof auditData === "boolean" ? auditData : auditData ? parseAuditConfig(auditData) :
|
|
768
|
+
audit: typeof auditData === "boolean" ? auditData : auditData ? parseAuditConfig(auditData) : true,
|
|
768
769
|
envSecurity: data.env_security ?? data.envSecurity ? parseEnvSecurityConfig(data.env_security ?? data.envSecurity) : createEnvSecurityConfig(),
|
|
769
770
|
toxicity: data.toxicity ? parseToxicityConfig(data.toxicity) : void 0,
|
|
770
771
|
codeSecurity: data.code_security ?? data.codeSecurity ? parseCodeSecurityConfig(data.code_security ?? data.codeSecurity) : void 0,
|
|
@@ -793,10 +794,7 @@ function securityPolicyToJSON(policy) {
|
|
|
793
794
|
}
|
|
794
795
|
const auditConfig = typeof policy.audit === "boolean" ? createAuditConfig({ enabled: policy.audit }) : policy.audit;
|
|
795
796
|
const audit = {
|
|
796
|
-
enabled: auditConfig.enabled
|
|
797
|
-
log_request_body: auditConfig.logRequestBody,
|
|
798
|
-
log_response_body: auditConfig.logResponseBody,
|
|
799
|
-
retention_hours: auditConfig.retentionHours
|
|
797
|
+
enabled: auditConfig.enabled
|
|
800
798
|
};
|
|
801
799
|
const envSec = {
|
|
802
800
|
mask_patterns: policy.envSecurity.maskPatterns,
|
|
@@ -1263,6 +1261,11 @@ var Filesystem = class {
|
|
|
1263
1261
|
/**
|
|
1264
1262
|
* Write data to a file.
|
|
1265
1263
|
*
|
|
1264
|
+
* When `data` is a `Uint8Array`, the SDK streams the raw bytes to the
|
|
1265
|
+
* binary-safe `PUT /files/raw` endpoint (500 MiB cap). When it's a string,
|
|
1266
|
+
* the SDK uses the JSON `POST /files` endpoint. Callers do not need to
|
|
1267
|
+
* pick the transport.
|
|
1268
|
+
*
|
|
1266
1269
|
* @param path - Absolute path to the file.
|
|
1267
1270
|
* @param data - String or Uint8Array content to write.
|
|
1268
1271
|
* @param opts - Optional user and request timeout.
|
|
@@ -1270,11 +1273,22 @@ var Filesystem = class {
|
|
|
1270
1273
|
*/
|
|
1271
1274
|
async write(path, data, opts) {
|
|
1272
1275
|
const user = opts?.user ?? DEFAULT_USER;
|
|
1273
|
-
|
|
1276
|
+
if (data instanceof Uint8Array) {
|
|
1277
|
+
const result2 = await this.client.put(
|
|
1278
|
+
`/sandboxes/${this.sandboxId}/files/raw`,
|
|
1279
|
+
{
|
|
1280
|
+
params: { path, username: user },
|
|
1281
|
+
body: data,
|
|
1282
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
1283
|
+
timeout: opts?.requestTimeout
|
|
1284
|
+
}
|
|
1285
|
+
);
|
|
1286
|
+
return parseWriteInfo(result2);
|
|
1287
|
+
}
|
|
1274
1288
|
const result = await this.client.post(
|
|
1275
1289
|
`/sandboxes/${this.sandboxId}/files`,
|
|
1276
1290
|
{
|
|
1277
|
-
json: { path, data
|
|
1291
|
+
json: { path, data, username: user },
|
|
1278
1292
|
timeout: opts?.requestTimeout
|
|
1279
1293
|
}
|
|
1280
1294
|
);
|
|
@@ -1283,24 +1297,58 @@ var Filesystem = class {
|
|
|
1283
1297
|
/**
|
|
1284
1298
|
* Write multiple files in a single batch request.
|
|
1285
1299
|
*
|
|
1300
|
+
* The batch endpoint is JSON-only and cannot carry binary. Entries are
|
|
1301
|
+
* partitioned: string entries go through `POST /files/batch` in one call,
|
|
1302
|
+
* `Uint8Array` entries are streamed individually to `PUT /files/raw`.
|
|
1303
|
+
* Results are merged back in input order.
|
|
1304
|
+
*
|
|
1286
1305
|
* @param files - Array of files to write.
|
|
1287
1306
|
* @param opts - Optional user and request timeout.
|
|
1288
|
-
* @returns Array of write info for each file.
|
|
1307
|
+
* @returns Array of write info for each file, in input order.
|
|
1289
1308
|
*/
|
|
1290
1309
|
async writeFiles(files, opts) {
|
|
1291
1310
|
const user = opts?.user ?? DEFAULT_USER;
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
{
|
|
1299
|
-
|
|
1300
|
-
timeout: opts?.requestTimeout
|
|
1311
|
+
const results = new Array(files.length);
|
|
1312
|
+
const strIndices = [];
|
|
1313
|
+
const bytesIndices = [];
|
|
1314
|
+
for (let i = 0; i < files.length; i++) {
|
|
1315
|
+
if (files[i].data instanceof Uint8Array) {
|
|
1316
|
+
bytesIndices.push(i);
|
|
1317
|
+
} else {
|
|
1318
|
+
strIndices.push(i);
|
|
1301
1319
|
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1320
|
+
}
|
|
1321
|
+
if (strIndices.length > 0) {
|
|
1322
|
+
const batchFiles = strIndices.map((i) => ({
|
|
1323
|
+
path: files[i].path,
|
|
1324
|
+
data: files[i].data
|
|
1325
|
+
}));
|
|
1326
|
+
const result = await this.client.post(
|
|
1327
|
+
`/sandboxes/${this.sandboxId}/files/batch`,
|
|
1328
|
+
{
|
|
1329
|
+
json: { files: batchFiles, username: user },
|
|
1330
|
+
timeout: opts?.requestTimeout
|
|
1331
|
+
}
|
|
1332
|
+
);
|
|
1333
|
+
const parsed = (result ?? []).map(parseWriteInfo);
|
|
1334
|
+
for (let k = 0; k < strIndices.length; k++) {
|
|
1335
|
+
results[strIndices[k]] = parsed[k];
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
for (const i of bytesIndices) {
|
|
1339
|
+
const entry = files[i];
|
|
1340
|
+
const result = await this.client.put(
|
|
1341
|
+
`/sandboxes/${this.sandboxId}/files/raw`,
|
|
1342
|
+
{
|
|
1343
|
+
params: { path: entry.path, username: user },
|
|
1344
|
+
body: entry.data,
|
|
1345
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
1346
|
+
timeout: opts?.requestTimeout
|
|
1347
|
+
}
|
|
1348
|
+
);
|
|
1349
|
+
results[i] = parseWriteInfo(result);
|
|
1350
|
+
}
|
|
1351
|
+
return results.filter((r) => r !== void 0);
|
|
1304
1352
|
}
|
|
1305
1353
|
/**
|
|
1306
1354
|
* List entries in a directory.
|
|
@@ -1439,52 +1487,188 @@ var Filesystem = class {
|
|
|
1439
1487
|
};
|
|
1440
1488
|
|
|
1441
1489
|
// src/sandbox/pty/pty.ts
|
|
1442
|
-
var
|
|
1490
|
+
var PtyHandle = class {
|
|
1491
|
+
pid;
|
|
1443
1492
|
sandboxId;
|
|
1444
1493
|
client;
|
|
1445
|
-
|
|
1494
|
+
exitPromise;
|
|
1495
|
+
resolveExit;
|
|
1496
|
+
aborter = new AbortController();
|
|
1497
|
+
constructor(pid, sandboxId, client, onData) {
|
|
1498
|
+
this.pid = pid;
|
|
1446
1499
|
this.sandboxId = sandboxId;
|
|
1447
1500
|
this.client = client;
|
|
1501
|
+
this.exitPromise = new Promise((resolve) => {
|
|
1502
|
+
this.resolveExit = resolve;
|
|
1503
|
+
});
|
|
1504
|
+
if (onData) {
|
|
1505
|
+
void this.consumeStream(onData);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
/** Forward keystrokes to the PTY. */
|
|
1509
|
+
async sendInput(data, requestTimeout) {
|
|
1510
|
+
const strData = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
1511
|
+
await this.client.post(
|
|
1512
|
+
`/sandboxes/${this.sandboxId}/pty/${this.pid}/stdin`,
|
|
1513
|
+
{ json: { data: strData }, timeout: requestTimeout }
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
/** Update the terminal size (TIOCSWINSZ inside the VM). */
|
|
1517
|
+
async resize(size, requestTimeout) {
|
|
1518
|
+
await this.client.patch(
|
|
1519
|
+
`/sandboxes/${this.sandboxId}/pty/${this.pid}`,
|
|
1520
|
+
{
|
|
1521
|
+
json: { size: { cols: size.cols, rows: size.rows } },
|
|
1522
|
+
timeout: requestTimeout
|
|
1523
|
+
}
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
/** SIGKILL the remote process and close any open streams. */
|
|
1527
|
+
async kill(requestTimeout) {
|
|
1528
|
+
this.aborter.abort();
|
|
1529
|
+
const data = await this.client.delete(
|
|
1530
|
+
`/sandboxes/${this.sandboxId}/pty/${this.pid}`,
|
|
1531
|
+
{ timeout: requestTimeout }
|
|
1532
|
+
);
|
|
1533
|
+
return data.killed === true;
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Stop consuming output without killing the process. The PTY keeps
|
|
1537
|
+
* running server-side and a fresh `stream()` call reattaches.
|
|
1538
|
+
*/
|
|
1539
|
+
disconnect() {
|
|
1540
|
+
this.aborter.abort();
|
|
1541
|
+
}
|
|
1542
|
+
/** Resolves with the remote exit result when the PTY process exits. */
|
|
1543
|
+
wait() {
|
|
1544
|
+
return this.exitPromise;
|
|
1448
1545
|
}
|
|
1449
1546
|
/**
|
|
1450
|
-
*
|
|
1547
|
+
* Async iterator over raw output chunks. Use when you want to drive
|
|
1548
|
+
* the stream yourself:
|
|
1549
|
+
*
|
|
1550
|
+
* for await (const chunk of handle.stream()) { ... }
|
|
1451
1551
|
*
|
|
1452
|
-
*
|
|
1453
|
-
*
|
|
1552
|
+
* Don't mix this with `onData` on the same handle — they both try to
|
|
1553
|
+
* consume the same underlying SSE connection.
|
|
1454
1554
|
*/
|
|
1555
|
+
async *stream() {
|
|
1556
|
+
const response = await this.client.streamGet(
|
|
1557
|
+
`/sandboxes/${this.sandboxId}/pty/${this.pid}/stream`,
|
|
1558
|
+
{ timeout: void 0 }
|
|
1559
|
+
);
|
|
1560
|
+
if (!response.body) {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
const reader = response.body.getReader();
|
|
1564
|
+
const decoder = new TextDecoder();
|
|
1565
|
+
let buffer = "";
|
|
1566
|
+
try {
|
|
1567
|
+
while (true) {
|
|
1568
|
+
if (this.aborter.signal.aborted) return;
|
|
1569
|
+
const { value, done } = await reader.read();
|
|
1570
|
+
if (done) break;
|
|
1571
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1572
|
+
let idx;
|
|
1573
|
+
while ((idx = buffer.indexOf("\n\n")) >= 0) {
|
|
1574
|
+
const frame = buffer.slice(0, idx);
|
|
1575
|
+
buffer = buffer.slice(idx + 2);
|
|
1576
|
+
const parsed = parseSSEFrame(frame);
|
|
1577
|
+
if (!parsed) continue;
|
|
1578
|
+
if (parsed.event === "exit") {
|
|
1579
|
+
this.resolveExit({ exitCode: parsed.exitCode ?? -1 });
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (parsed.bytes) {
|
|
1583
|
+
yield parsed.bytes;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
} finally {
|
|
1588
|
+
reader.releaseLock();
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
async consumeStream(onData) {
|
|
1592
|
+
try {
|
|
1593
|
+
for await (const chunk of this.stream()) {
|
|
1594
|
+
onData(chunk);
|
|
1595
|
+
}
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
if (!this.aborter.signal.aborted) {
|
|
1598
|
+
throw err;
|
|
1599
|
+
}
|
|
1600
|
+
} finally {
|
|
1601
|
+
this.resolveExit({ exitCode: -1 });
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
function parseSSEFrame(frame) {
|
|
1606
|
+
let event = "message";
|
|
1607
|
+
let data = "";
|
|
1608
|
+
for (const line of frame.split("\n")) {
|
|
1609
|
+
if (line.startsWith("event:")) event = line.slice(6).trim();
|
|
1610
|
+
else if (line.startsWith("data:")) data += line.slice(5).trim();
|
|
1611
|
+
}
|
|
1612
|
+
if (!event) return null;
|
|
1613
|
+
if (event === "exit") {
|
|
1614
|
+
try {
|
|
1615
|
+
const parsed = JSON.parse(data);
|
|
1616
|
+
return { event, exitCode: parsed.exit_code ?? -1 };
|
|
1617
|
+
} catch {
|
|
1618
|
+
return { event, exitCode: -1 };
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
if (event === "data") {
|
|
1622
|
+
try {
|
|
1623
|
+
const parsed = JSON.parse(data);
|
|
1624
|
+
if (typeof parsed.data === "string") {
|
|
1625
|
+
const bin = atob(parsed.data);
|
|
1626
|
+
const bytes = new Uint8Array(bin.length);
|
|
1627
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
1628
|
+
return { event, bytes };
|
|
1629
|
+
}
|
|
1630
|
+
} catch {
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
return null;
|
|
1635
|
+
}
|
|
1636
|
+
var Pty = class {
|
|
1637
|
+
sandboxId;
|
|
1638
|
+
client;
|
|
1639
|
+
constructor(sandboxId, client) {
|
|
1640
|
+
this.sandboxId = sandboxId;
|
|
1641
|
+
this.client = client;
|
|
1642
|
+
}
|
|
1455
1643
|
async create(opts) {
|
|
1456
1644
|
const size = opts?.size ?? { cols: 80, rows: 24 };
|
|
1457
1645
|
const user = opts?.user ?? "user";
|
|
1458
1646
|
const body = {
|
|
1459
1647
|
size: { cols: size.cols, rows: size.rows },
|
|
1460
|
-
user
|
|
1648
|
+
user,
|
|
1649
|
+
timeout: opts?.timeout ?? 3600
|
|
1461
1650
|
};
|
|
1462
|
-
if (opts?.cwd !== void 0)
|
|
1463
|
-
|
|
1464
|
-
}
|
|
1465
|
-
if (opts?.envs !== void 0) {
|
|
1466
|
-
body.envs = opts.envs;
|
|
1467
|
-
}
|
|
1468
|
-
if (opts?.timeout !== void 0) {
|
|
1469
|
-
body.timeout = opts.timeout;
|
|
1470
|
-
}
|
|
1651
|
+
if (opts?.cwd !== void 0) body.cwd = opts.cwd;
|
|
1652
|
+
if (opts?.envs !== void 0) body.envs = opts.envs;
|
|
1471
1653
|
const data = await this.client.post(
|
|
1472
1654
|
`/sandboxes/${this.sandboxId}/pty`,
|
|
1473
|
-
{
|
|
1474
|
-
json: body,
|
|
1475
|
-
timeout: opts?.requestTimeout
|
|
1476
|
-
}
|
|
1655
|
+
{ json: body, timeout: opts?.requestTimeout }
|
|
1477
1656
|
);
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
return new CommandHandle(pid, this.sandboxId, this.client);
|
|
1657
|
+
const pid = data.pid;
|
|
1658
|
+
return new PtyHandle(pid, this.sandboxId, this.client, opts?.onData);
|
|
1481
1659
|
}
|
|
1482
1660
|
/**
|
|
1483
|
-
*
|
|
1661
|
+
* Reattach to an already-running PTY by its pid.
|
|
1484
1662
|
*
|
|
1485
|
-
*
|
|
1486
|
-
*
|
|
1663
|
+
* Returns a fresh `PtyHandle` that streams the live output of the
|
|
1664
|
+
* existing session. Multiple clients can subscribe to the same pid
|
|
1665
|
+
* concurrently — each receives output from the moment it connects
|
|
1666
|
+
* (no scrollback replay).
|
|
1487
1667
|
*/
|
|
1668
|
+
connect(pid, opts) {
|
|
1669
|
+
return new PtyHandle(pid, this.sandboxId, this.client, opts?.onData);
|
|
1670
|
+
}
|
|
1671
|
+
// --- Low-level API by pid (kept for callers that already hold one). ---
|
|
1488
1672
|
async kill(pid, requestTimeout) {
|
|
1489
1673
|
const data = await this.client.delete(
|
|
1490
1674
|
`/sandboxes/${this.sandboxId}/pty/${pid}`,
|
|
@@ -1492,27 +1676,13 @@ var Pty = class {
|
|
|
1492
1676
|
);
|
|
1493
1677
|
return data.killed === true;
|
|
1494
1678
|
}
|
|
1495
|
-
/**
|
|
1496
|
-
* Send input to a PTY session.
|
|
1497
|
-
*
|
|
1498
|
-
* Sends POST /sandboxes/:id/pty/:pid/stdin.
|
|
1499
|
-
* If data is a Uint8Array, it is decoded to a string using TextDecoder.
|
|
1500
|
-
*/
|
|
1501
1679
|
async sendStdin(pid, data, requestTimeout) {
|
|
1502
1680
|
const strData = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
1503
1681
|
await this.client.post(
|
|
1504
1682
|
`/sandboxes/${this.sandboxId}/pty/${pid}/stdin`,
|
|
1505
|
-
{
|
|
1506
|
-
json: { data: strData },
|
|
1507
|
-
timeout: requestTimeout
|
|
1508
|
-
}
|
|
1683
|
+
{ json: { data: strData }, timeout: requestTimeout }
|
|
1509
1684
|
);
|
|
1510
1685
|
}
|
|
1511
|
-
/**
|
|
1512
|
-
* Resize a PTY session.
|
|
1513
|
-
*
|
|
1514
|
-
* Sends PATCH /sandboxes/:id/pty/:pid.
|
|
1515
|
-
*/
|
|
1516
1686
|
async resize(pid, size, requestTimeout) {
|
|
1517
1687
|
await this.client.patch(
|
|
1518
1688
|
`/sandboxes/${this.sandboxId}/pty/${pid}`,
|
|
@@ -2296,6 +2466,7 @@ export {
|
|
|
2296
2466
|
NotFoundError,
|
|
2297
2467
|
PIIType,
|
|
2298
2468
|
Pty,
|
|
2469
|
+
PtyHandle,
|
|
2299
2470
|
RedactionAction,
|
|
2300
2471
|
Sandbox,
|
|
2301
2472
|
SandboxError,
|