@odla-ai/harness 0.10.0 → 0.10.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/{chunk-5MDYIJXC.js → chunk-WM34GGTK.js} +543 -56
- package/dist/chunk-WM34GGTK.js.map +1 -0
- package/dist/code-runtime-cli.cjs +548 -62
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +554 -66
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +39 -4
- package/dist/node.d.ts +39 -4
- package/dist/node.js +3 -1
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5MDYIJXC.js.map +0 -1
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
// src/code-runtime-cli.ts
|
|
5
|
-
var
|
|
6
|
-
var
|
|
5
|
+
var import_node_os4 = require("os");
|
|
6
|
+
var import_promises12 = require("fs/promises");
|
|
7
7
|
|
|
8
8
|
// src/code-runtime-client-validation.ts
|
|
9
9
|
var import_code = require("@odla-ai/camel/code");
|
|
@@ -249,6 +249,34 @@ function createCodeRuntimeControlClient(options) {
|
|
|
249
249
|
}
|
|
250
250
|
return value;
|
|
251
251
|
};
|
|
252
|
+
const callRaw = async (path, body) => {
|
|
253
|
+
const timeout = AbortSignal.timeout(modelRequestTimeoutMs);
|
|
254
|
+
const signals = [options.signal, timeout].filter((item) => Boolean(item));
|
|
255
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
256
|
+
let response2;
|
|
257
|
+
try {
|
|
258
|
+
response2 = await request(`${endpoint}${path}`, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
headers: { authorization: `Bearer ${options.token}`, "content-type": "application/json" },
|
|
261
|
+
body: JSON.stringify(body),
|
|
262
|
+
redirect: "error",
|
|
263
|
+
signal
|
|
264
|
+
});
|
|
265
|
+
} catch (cause) {
|
|
266
|
+
if (options.signal?.aborted) throw cause;
|
|
267
|
+
throw new CodeRuntimeControlError("Code source archive is unavailable", 503, "transport_unavailable");
|
|
268
|
+
}
|
|
269
|
+
if (!response2.ok) {
|
|
270
|
+
const value = await response2.json().catch(() => null);
|
|
271
|
+
const problem = record(record(value)?.error);
|
|
272
|
+
throw new CodeRuntimeControlError(
|
|
273
|
+
typeof problem?.message === "string" ? problem.message : `Code runtime request failed (${response2.status})`,
|
|
274
|
+
response2.status,
|
|
275
|
+
typeof problem?.code === "string" ? problem.code : void 0
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return response2;
|
|
279
|
+
};
|
|
252
280
|
return {
|
|
253
281
|
heartbeat: async (version, capabilities) => {
|
|
254
282
|
validateHeartbeat(version, capabilities);
|
|
@@ -260,6 +288,14 @@ function createCodeRuntimeControlClient(options) {
|
|
|
260
288
|
source: async (sessionId) => parseSource(
|
|
261
289
|
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
262
290
|
),
|
|
291
|
+
sourceArchive: async (sessionId, alias) => {
|
|
292
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias)) throw new TypeError("invalid Code source alias");
|
|
293
|
+
const response2 = await callRaw(
|
|
294
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source/archive`,
|
|
295
|
+
{ alias }
|
|
296
|
+
);
|
|
297
|
+
return parseSourceArchiveResponse(response2, alias);
|
|
298
|
+
},
|
|
263
299
|
infer: async (sessionId, inference) => {
|
|
264
300
|
const value = record(await call(
|
|
265
301
|
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
@@ -324,9 +360,70 @@ function createCodeRuntimeControlClient(options) {
|
|
|
324
360
|
}
|
|
325
361
|
};
|
|
326
362
|
}
|
|
363
|
+
var ARCHIVE_LIMIT_MAXIMA = {
|
|
364
|
+
maxCompressedBytes: 64 * 1024 * 1024,
|
|
365
|
+
maxDecompressedBytes: 96 * 1024 * 1024,
|
|
366
|
+
maxEntries: 2e5,
|
|
367
|
+
maxFiles: 1e5,
|
|
368
|
+
maxFileBytes: 16 * 1024 * 1024,
|
|
369
|
+
maxTotalFileBytes: 80 * 1024 * 1024,
|
|
370
|
+
maxPathBytes: 4096,
|
|
371
|
+
maxExtendedHeaderBytes: 32 * 1024
|
|
372
|
+
};
|
|
373
|
+
async function parseSourceArchiveResponse(response2, expectedAlias) {
|
|
374
|
+
const alias = response2.headers.get("x-odla-source-alias") ?? "";
|
|
375
|
+
const repository = response2.headers.get("x-odla-source-repository") ?? "";
|
|
376
|
+
const commitSha = response2.headers.get("x-odla-source-commit") ?? "";
|
|
377
|
+
if (alias !== expectedAlias || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository) || !/^[0-9a-f]{40}$/.test(commitSha)) {
|
|
378
|
+
await response2.body?.cancel().catch(() => void 0);
|
|
379
|
+
throw new CodeRuntimeControlError("invalid Code source archive identity", 502, "invalid_response");
|
|
380
|
+
}
|
|
381
|
+
const limits = Object.fromEntries(Object.entries(ARCHIVE_LIMIT_MAXIMA).map(([key, maximum]) => {
|
|
382
|
+
const header = `x-odla-${key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
|
|
383
|
+
const value = response2.headers.get(header) ?? "";
|
|
384
|
+
if (!/^\d+$/.test(value) || !Number.isSafeInteger(Number(value)) || Number(value) < 1 || Number(value) > maximum) {
|
|
385
|
+
throw new CodeRuntimeControlError("invalid Code source archive limits", 502, "invalid_response");
|
|
386
|
+
}
|
|
387
|
+
return [key, Number(value)];
|
|
388
|
+
}));
|
|
389
|
+
const length = response2.headers.get("content-length");
|
|
390
|
+
if (length && (!/^\d+$/.test(length) || Number(length) > limits.maxCompressedBytes)) {
|
|
391
|
+
await response2.body?.cancel().catch(() => void 0);
|
|
392
|
+
throw new CodeRuntimeControlError("Code source archive exceeds its byte bound", 502, "invalid_response");
|
|
393
|
+
}
|
|
394
|
+
const compressed = await readBoundedResponse(response2.body, limits.maxCompressedBytes);
|
|
395
|
+
return { alias, repository, commitSha, compressed, limits };
|
|
396
|
+
}
|
|
397
|
+
async function readBoundedResponse(body, maximum) {
|
|
398
|
+
if (!body) throw new CodeRuntimeControlError("Code source archive body is missing", 502, "invalid_response");
|
|
399
|
+
const reader = body.getReader();
|
|
400
|
+
const chunks = [];
|
|
401
|
+
let size = 0;
|
|
402
|
+
try {
|
|
403
|
+
while (true) {
|
|
404
|
+
const { done, value } = await reader.read();
|
|
405
|
+
if (done) break;
|
|
406
|
+
size += value.byteLength;
|
|
407
|
+
if (size > maximum) {
|
|
408
|
+
await reader.cancel().catch(() => void 0);
|
|
409
|
+
throw new CodeRuntimeControlError("Code source archive exceeds its byte bound", 502, "invalid_response");
|
|
410
|
+
}
|
|
411
|
+
chunks.push(value);
|
|
412
|
+
}
|
|
413
|
+
} finally {
|
|
414
|
+
reader.releaseLock();
|
|
415
|
+
}
|
|
416
|
+
const result = new Uint8Array(size);
|
|
417
|
+
let offset = 0;
|
|
418
|
+
for (const chunk of chunks) {
|
|
419
|
+
result.set(chunk, offset);
|
|
420
|
+
offset += chunk.byteLength;
|
|
421
|
+
}
|
|
422
|
+
return result;
|
|
423
|
+
}
|
|
327
424
|
|
|
328
425
|
// src/code-runtime.ts
|
|
329
|
-
var CODE_RUNTIME_PROTOCOL_VERSION =
|
|
426
|
+
var CODE_RUNTIME_PROTOCOL_VERSION = 3;
|
|
330
427
|
async function runCodeRuntimeHeartbeatLoop(options) {
|
|
331
428
|
const heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
332
429
|
if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1e3 || heartbeatMs > 3e5) {
|
|
@@ -395,12 +492,12 @@ function retryableControlFailure(value) {
|
|
|
395
492
|
return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
|
|
396
493
|
}
|
|
397
494
|
function wait(ms, signal) {
|
|
398
|
-
return new Promise((
|
|
399
|
-
if (signal?.aborted) return
|
|
400
|
-
const timer = setTimeout(
|
|
495
|
+
return new Promise((resolve7) => {
|
|
496
|
+
if (signal?.aborted) return resolve7();
|
|
497
|
+
const timer = setTimeout(resolve7, ms);
|
|
401
498
|
signal?.addEventListener("abort", () => {
|
|
402
499
|
clearTimeout(timer);
|
|
403
|
-
|
|
500
|
+
resolve7();
|
|
404
501
|
}, { once: true });
|
|
405
502
|
});
|
|
406
503
|
}
|
|
@@ -846,7 +943,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
|
|
|
846
943
|
throw new TypeError("no supported container engine found");
|
|
847
944
|
}
|
|
848
945
|
function inspectRootlessPodman() {
|
|
849
|
-
return new Promise((
|
|
946
|
+
return new Promise((resolve7, reject) => {
|
|
850
947
|
(0, import_node_child_process3.execFile)(
|
|
851
948
|
"podman",
|
|
852
949
|
["info", "--format", "{{.Host.Security.Rootless}}"],
|
|
@@ -856,7 +953,7 @@ function inspectRootlessPodman() {
|
|
|
856
953
|
reject(new TypeError("could not verify that the active Podman service is rootless"));
|
|
857
954
|
return;
|
|
858
955
|
}
|
|
859
|
-
|
|
956
|
+
resolve7(stdout.trim() === "true");
|
|
860
957
|
}
|
|
861
958
|
);
|
|
862
959
|
});
|
|
@@ -1363,7 +1460,8 @@ function codeCommandMetadata(payload, resume) {
|
|
|
1363
1460
|
const repository = trusted?.repository;
|
|
1364
1461
|
const baseCommitSha = trusted?.commitSha;
|
|
1365
1462
|
const sourceTreeDigest = trusted?.treeDigest;
|
|
1366
|
-
|
|
1463
|
+
const hostMaterialized = record2(payload.sourceSet) !== null && sourceTreeDigest === void 0;
|
|
1464
|
+
if (typeof repository !== "string" || !repository.includes("/") || typeof baseCommitSha !== "string" || !/^[0-9a-f]{40}$/.test(baseCommitSha) || !hostMaterialized && (typeof sourceTreeDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(sourceTreeDigest))) {
|
|
1367
1465
|
throw new TypeError(`invalid Code ${resume ? "resume" : "start"} trusted base`);
|
|
1368
1466
|
}
|
|
1369
1467
|
if (!Number.isSafeInteger(maxTokensPerInteraction) || Number(maxTokensPerInteraction) < 4e3 || Number(maxTokensPerInteraction) > 2e5) {
|
|
@@ -1379,7 +1477,7 @@ function codeCommandMetadata(payload, resume) {
|
|
|
1379
1477
|
attestationDigest: typeof attestation === "string" ? attestation : "resume",
|
|
1380
1478
|
repository,
|
|
1381
1479
|
baseCommitSha,
|
|
1382
|
-
sourceTreeDigest
|
|
1480
|
+
sourceTreeDigest: typeof sourceTreeDigest === "string" ? sourceTreeDigest : null
|
|
1383
1481
|
};
|
|
1384
1482
|
}
|
|
1385
1483
|
function codeLocalSource(payload) {
|
|
@@ -1444,19 +1542,320 @@ async function prepareRuntimeLocalSource(input) {
|
|
|
1444
1542
|
}
|
|
1445
1543
|
|
|
1446
1544
|
// src/code-runtime-source.ts
|
|
1545
|
+
var import_promises8 = require("fs/promises");
|
|
1546
|
+
var import_node_os3 = require("os");
|
|
1547
|
+
var import_node_path9 = require("path");
|
|
1548
|
+
|
|
1549
|
+
// src/code-runtime-archive.ts
|
|
1550
|
+
var import_node_zlib = require("zlib");
|
|
1447
1551
|
var import_promises6 = require("fs/promises");
|
|
1448
1552
|
var import_node_os2 = require("os");
|
|
1449
1553
|
var import_node_path7 = require("path");
|
|
1450
1554
|
var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
1451
1555
|
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
1556
|
+
var MAX_NUL_SHARE = 0.1;
|
|
1557
|
+
async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (0, import_node_os2.tmpdir)()) {
|
|
1558
|
+
if (!archive.compressed.byteLength || archive.compressed.byteLength > archive.limits.maxCompressedBytes) {
|
|
1559
|
+
throw new TypeError("Code source archive exceeds its compressed byte bound");
|
|
1560
|
+
}
|
|
1561
|
+
let bytes;
|
|
1562
|
+
try {
|
|
1563
|
+
bytes = archive.compressed[0] === 31 && archive.compressed[1] === 139 ? (0, import_node_zlib.gunzipSync)(archive.compressed, { maxOutputLength: archive.limits.maxDecompressedBytes }) : archive.compressed;
|
|
1564
|
+
} catch {
|
|
1565
|
+
throw new TypeError("Code source archive is not a valid bounded gzip stream");
|
|
1566
|
+
}
|
|
1567
|
+
if (bytes.byteLength > archive.limits.maxDecompressedBytes) {
|
|
1568
|
+
throw new TypeError("Code source archive exceeds its decompressed byte bound");
|
|
1569
|
+
}
|
|
1570
|
+
const entries = parseTar(bytes, archive.limits);
|
|
1571
|
+
const root = await (0, import_promises6.mkdtemp)((0, import_node_path7.join)(tempRoot, "odla-code-archive-"));
|
|
1572
|
+
const sourceDir = (0, import_node_path7.join)(root, "source");
|
|
1573
|
+
await (0, import_promises6.mkdir)(sourceDir);
|
|
1574
|
+
let visible = 0;
|
|
1575
|
+
try {
|
|
1576
|
+
for (const entry of entries) {
|
|
1577
|
+
if (entry.directory || visiblePaths && !visiblePaths.has(entry.path)) continue;
|
|
1578
|
+
if (filteredPath(entry.path)) continue;
|
|
1579
|
+
let content;
|
|
1580
|
+
try {
|
|
1581
|
+
content = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(entry.bytes);
|
|
1582
|
+
} catch {
|
|
1583
|
+
continue;
|
|
1584
|
+
}
|
|
1585
|
+
if (nulShare(content) > MAX_NUL_SHARE) continue;
|
|
1586
|
+
const target = (0, import_node_path7.resolve)(sourceDir, entry.path);
|
|
1587
|
+
if (!target.startsWith(`${(0, import_node_path7.resolve)(sourceDir)}${import_node_path7.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
1588
|
+
await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
|
|
1589
|
+
await (0, import_promises6.writeFile)(target, content, { flag: "wx", mode: 420 });
|
|
1590
|
+
visible += 1;
|
|
1591
|
+
}
|
|
1592
|
+
if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
|
|
1593
|
+
return { sourceDir, cleanup: () => (0, import_promises6.rm)(root, { recursive: true, force: true }) };
|
|
1594
|
+
} catch (cause) {
|
|
1595
|
+
await (0, import_promises6.rm)(root, { recursive: true, force: true });
|
|
1596
|
+
throw cause;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
function parseTar(bytes, limits) {
|
|
1600
|
+
const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false });
|
|
1601
|
+
const entries = [];
|
|
1602
|
+
const archivePaths = /* @__PURE__ */ new Set();
|
|
1603
|
+
let entryCount = 0;
|
|
1604
|
+
let fileCount = 0;
|
|
1605
|
+
let totalFileBytes = 0;
|
|
1606
|
+
let pendingPath;
|
|
1607
|
+
let pendingPaxPath;
|
|
1608
|
+
let offset = 0;
|
|
1609
|
+
let ended = false;
|
|
1610
|
+
while (offset + 512 <= bytes.byteLength) {
|
|
1611
|
+
const header = bytes.subarray(offset, offset + 512);
|
|
1612
|
+
offset += 512;
|
|
1613
|
+
if (zeroBlock(header)) {
|
|
1614
|
+
ended = true;
|
|
1615
|
+
assertZeroTail(bytes, offset);
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
1618
|
+
entryCount += 1;
|
|
1619
|
+
if (entryCount > limits.maxEntries) throw new TypeError("Code source archive has too many entries");
|
|
1620
|
+
validateChecksum(header);
|
|
1621
|
+
const type = header[156] ?? 0;
|
|
1622
|
+
const size = tarNumber(header.subarray(124, 136));
|
|
1623
|
+
if (size > limits.maxDecompressedBytes || offset + aligned(size) > bytes.byteLength) {
|
|
1624
|
+
throw new TypeError("Code source archive entry is invalid or too large");
|
|
1625
|
+
}
|
|
1626
|
+
const payload = bytes.subarray(offset, offset + size);
|
|
1627
|
+
offset += aligned(size);
|
|
1628
|
+
if (type === 120 || type === 103) {
|
|
1629
|
+
if (size > limits.maxExtendedHeaderBytes) throw new TypeError("Code source archive extended header is too large");
|
|
1630
|
+
const pax = parsePax(payload, decoder, limits.maxPathBytes);
|
|
1631
|
+
if (pax.linkpath !== void 0 || pax.size !== void 0 || Object.keys(pax).some((key) => key.toLowerCase().includes("sparse"))) {
|
|
1632
|
+
throw new TypeError("Code source archive contains an unsupported entry");
|
|
1633
|
+
}
|
|
1634
|
+
if (type === 103 && pax.path !== void 0) throw new TypeError("Code source archive path is unsafe");
|
|
1635
|
+
if (type === 120 && pax.path !== void 0) {
|
|
1636
|
+
if (pendingPaxPath !== void 0 || pendingPath !== void 0) throw new TypeError("Code source archive is invalid");
|
|
1637
|
+
pendingPaxPath = pax.path;
|
|
1638
|
+
}
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
if (type === 76) {
|
|
1642
|
+
if (size > limits.maxExtendedHeaderBytes || pendingPath !== void 0 || pendingPaxPath !== void 0) {
|
|
1643
|
+
throw new TypeError("Code source archive long path is invalid");
|
|
1644
|
+
}
|
|
1645
|
+
pendingPath = validateArchivePath(decode(payload, decoder).replace(/\0+$/, "").replace(/\n$/, ""), limits.maxPathBytes);
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
if (type === 75) throw new TypeError("Code source archive contains an unsupported link");
|
|
1649
|
+
const path = validateArchivePath(pendingPaxPath ?? pendingPath ?? tarPath(header, decoder), limits.maxPathBytes);
|
|
1650
|
+
pendingPaxPath = void 0;
|
|
1651
|
+
pendingPath = void 0;
|
|
1652
|
+
const directory = type === 53;
|
|
1653
|
+
const regular = type === 0 || type === 48;
|
|
1654
|
+
if (!directory && !regular || directory && size !== 0 || archivePaths.has(path)) {
|
|
1655
|
+
throw new TypeError("Code source archive contains an invalid or duplicate entry");
|
|
1656
|
+
}
|
|
1657
|
+
archivePaths.add(path);
|
|
1658
|
+
if (regular) {
|
|
1659
|
+
fileCount += 1;
|
|
1660
|
+
totalFileBytes += size;
|
|
1661
|
+
if (fileCount > limits.maxFiles || size > limits.maxFileBytes || totalFileBytes > limits.maxTotalFileBytes) {
|
|
1662
|
+
throw new TypeError("Code source archive exceeds its file bounds");
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
entries.push({ path, directory, ...regular ? { bytes: payload } : {} });
|
|
1666
|
+
}
|
|
1667
|
+
if (!ended || pendingPath !== void 0 || pendingPaxPath !== void 0 || !entries.length || !fileCount) {
|
|
1668
|
+
throw new TypeError("Code source archive is incomplete");
|
|
1669
|
+
}
|
|
1670
|
+
return unwrapRepository(entries, limits.maxPathBytes);
|
|
1671
|
+
}
|
|
1672
|
+
function unwrapRepository(entries, maxPathBytes) {
|
|
1673
|
+
const root = entries[0].path.split("/")[0];
|
|
1674
|
+
if (!root || entries.some((entry) => entry.path !== root && !entry.path.startsWith(`${root}/`))) {
|
|
1675
|
+
throw new TypeError("Code source archive has no single repository root");
|
|
1676
|
+
}
|
|
1677
|
+
const output = [];
|
|
1678
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
1679
|
+
for (const entry of entries) {
|
|
1680
|
+
if (entry.path === root) {
|
|
1681
|
+
if (!entry.directory) throw new TypeError("Code source archive root is not a directory");
|
|
1682
|
+
continue;
|
|
1683
|
+
}
|
|
1684
|
+
const path = entry.path.slice(root.length + 1);
|
|
1685
|
+
validateRepositoryPath(path, maxPathBytes);
|
|
1686
|
+
const parts = path.split("/");
|
|
1687
|
+
for (let index = 1; index < parts.length; index += 1) {
|
|
1688
|
+
if (kinds.get(parts.slice(0, index).join("/")) === "file") throw new TypeError("Code source archive path conflicts");
|
|
1689
|
+
}
|
|
1690
|
+
if (!entry.directory) {
|
|
1691
|
+
for (const existing of kinds.keys()) {
|
|
1692
|
+
if (existing.startsWith(`${path}/`)) throw new TypeError("Code source archive path conflicts");
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (kinds.has(path)) throw new TypeError("Code source archive repeats a path");
|
|
1696
|
+
kinds.set(path, entry.directory ? "directory" : "file");
|
|
1697
|
+
output.push({ ...entry, path });
|
|
1698
|
+
}
|
|
1699
|
+
return output.sort((left, right) => left.path.localeCompare(right.path));
|
|
1700
|
+
}
|
|
1701
|
+
function parsePax(bytes, decoder, maxPathBytes) {
|
|
1702
|
+
const result = {};
|
|
1703
|
+
let offset = 0;
|
|
1704
|
+
while (offset < bytes.byteLength) {
|
|
1705
|
+
const space = bytes.indexOf(32, offset);
|
|
1706
|
+
if (space < 0) throw new TypeError("Code source archive PAX header is invalid");
|
|
1707
|
+
const lengthText = ascii(bytes.subarray(offset, space));
|
|
1708
|
+
if (!/^[1-9][0-9]{0,8}$/.test(lengthText)) throw new TypeError("Code source archive PAX length is invalid");
|
|
1709
|
+
const length = Number(lengthText);
|
|
1710
|
+
const end = offset + length;
|
|
1711
|
+
if (!Number.isSafeInteger(length) || end > bytes.byteLength || bytes[end - 1] !== 10) {
|
|
1712
|
+
throw new TypeError("Code source archive PAX record is invalid");
|
|
1713
|
+
}
|
|
1714
|
+
const record4 = decode(bytes.subarray(space + 1, end - 1), decoder);
|
|
1715
|
+
const equals = record4.indexOf("=");
|
|
1716
|
+
if (equals < 1) throw new TypeError("Code source archive PAX field is invalid");
|
|
1717
|
+
const key = record4.slice(0, equals);
|
|
1718
|
+
if (Object.hasOwn(result, key)) throw new TypeError("Code source archive PAX field repeats");
|
|
1719
|
+
result[key] = record4.slice(equals + 1);
|
|
1720
|
+
offset = end;
|
|
1721
|
+
}
|
|
1722
|
+
if (result.path !== void 0) result.path = validateArchivePath(result.path, maxPathBytes);
|
|
1723
|
+
return result;
|
|
1724
|
+
}
|
|
1725
|
+
var aligned = (size) => Math.ceil(size / 512) * 512;
|
|
1726
|
+
var zeroBlock = (block) => block.every((value) => value === 0);
|
|
1727
|
+
function validateChecksum(header) {
|
|
1728
|
+
const expected = tarNumber(header.subarray(148, 156));
|
|
1729
|
+
let actual = 0;
|
|
1730
|
+
for (let index = 0; index < header.length; index += 1) actual += index >= 148 && index < 156 ? 32 : header[index];
|
|
1731
|
+
if (actual !== expected) throw new TypeError("Code source archive checksum is invalid");
|
|
1732
|
+
}
|
|
1733
|
+
function tarNumber(field) {
|
|
1734
|
+
if ((field[0] ?? 0) & 128) throw new TypeError("Code source archive numeric format is unsupported");
|
|
1735
|
+
const value = ascii(field).replaceAll("\0", "").trim();
|
|
1736
|
+
if (!value) return 0;
|
|
1737
|
+
if (!/^[0-7]+$/.test(value)) throw new TypeError("Code source archive number is invalid");
|
|
1738
|
+
const parsed = Number.parseInt(value, 8);
|
|
1739
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) throw new TypeError("Code source archive number is too large");
|
|
1740
|
+
return parsed;
|
|
1741
|
+
}
|
|
1742
|
+
function tarPath(header, decoder) {
|
|
1743
|
+
const name = decodeNul(header.subarray(0, 100), decoder);
|
|
1744
|
+
const prefix = decodeNul(header.subarray(345, 500), decoder);
|
|
1745
|
+
return prefix ? `${prefix}/${name}` : name;
|
|
1746
|
+
}
|
|
1747
|
+
function decodeNul(value, decoder) {
|
|
1748
|
+
const end = value.indexOf(0);
|
|
1749
|
+
return decode(end < 0 ? value : value.subarray(0, end), decoder);
|
|
1750
|
+
}
|
|
1751
|
+
function decode(value, decoder) {
|
|
1752
|
+
try {
|
|
1753
|
+
return decoder.decode(value);
|
|
1754
|
+
} catch {
|
|
1755
|
+
throw new TypeError("Code source archive text is invalid UTF-8");
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
function ascii(value) {
|
|
1759
|
+
let output = "";
|
|
1760
|
+
for (const byte of value) output += String.fromCharCode(byte);
|
|
1761
|
+
return output;
|
|
1762
|
+
}
|
|
1763
|
+
function validateArchivePath(input, maximum) {
|
|
1764
|
+
const path = input.endsWith("/") ? input.slice(0, -1) : input;
|
|
1765
|
+
validateRepositoryPath(path, maximum);
|
|
1766
|
+
return path;
|
|
1767
|
+
}
|
|
1768
|
+
function validateRepositoryPath(path, maximum) {
|
|
1769
|
+
if (!path || path.startsWith("/") || path.includes("\\") || /[\u0000-\u001f\u007f]/.test(path) || new TextEncoder().encode(path).byteLength > maximum || path.split("/").some((part) => !part || part === "." || part === "..")) {
|
|
1770
|
+
throw new TypeError("Code source archive path is unsafe");
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
function assertZeroTail(bytes, offset) {
|
|
1774
|
+
for (let index = offset; index < bytes.byteLength; index += 1) {
|
|
1775
|
+
if (bytes[index] !== 0) throw new TypeError("Code source archive has data after its end marker");
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
function filteredPath(path) {
|
|
1779
|
+
const parts = path.split("/");
|
|
1780
|
+
return parts.some((part) => RESERVED2.has(part) || SECRET2.test(part));
|
|
1781
|
+
}
|
|
1782
|
+
function nulShare(content) {
|
|
1783
|
+
if (!content.length) return 0;
|
|
1784
|
+
let count = 0;
|
|
1785
|
+
for (let index = 0; index < content.length; index += 1) if (content.charCodeAt(index) === 0) count += 1;
|
|
1786
|
+
return count / content.length;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
// src/code-runtime-selected-source.ts
|
|
1790
|
+
var import_promises7 = require("fs/promises");
|
|
1791
|
+
var import_node_path8 = require("path");
|
|
1792
|
+
function selectedSourceSet(payload) {
|
|
1793
|
+
if (!payload.sourceSet) return null;
|
|
1794
|
+
const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
|
|
1795
|
+
if (!set) throw new TypeError("Code selected source set is invalid");
|
|
1796
|
+
const parse2 = (value, primary2) => {
|
|
1797
|
+
const item = value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
1798
|
+
const alias = item?.alias;
|
|
1799
|
+
const repository = item?.repository;
|
|
1800
|
+
const commitSha = item?.commitSha;
|
|
1801
|
+
const materialization = item?.materialization ?? "registry_snapshot";
|
|
1802
|
+
if (typeof alias !== "string" || alias !== (primary2 ? "primary" : alias) || !/^[a-z][a-z0-9-]{0,39}$/.test(alias) || typeof repository !== "string" || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository) || typeof commitSha !== "string" || !/^[0-9a-f]{40}$/.test(commitSha) || !["registry_snapshot", "local_checkout", "host_archive"].includes(String(materialization))) {
|
|
1803
|
+
throw new TypeError("Code selected source identity is invalid");
|
|
1804
|
+
}
|
|
1805
|
+
const visiblePaths = item?.visiblePaths;
|
|
1806
|
+
if (visiblePaths !== void 0 && (!Array.isArray(visiblePaths) || visiblePaths.length > 2e3 || visiblePaths.some((path) => typeof path !== "string" || path.length > 4096))) {
|
|
1807
|
+
throw new TypeError("Code selected source visibility slice is invalid");
|
|
1808
|
+
}
|
|
1809
|
+
return {
|
|
1810
|
+
alias,
|
|
1811
|
+
repository,
|
|
1812
|
+
commitSha,
|
|
1813
|
+
materialization,
|
|
1814
|
+
...visiblePaths ? { visiblePaths } : {}
|
|
1815
|
+
};
|
|
1816
|
+
};
|
|
1817
|
+
if (!Array.isArray(set.references)) throw new TypeError("Code selected source references are invalid");
|
|
1818
|
+
const primary = parse2(set.primary, true);
|
|
1819
|
+
const references = set.references.map((item) => parse2(item, false));
|
|
1820
|
+
const aliases = /* @__PURE__ */ new Set([primary.alias, ...references.map((item) => item.alias)]);
|
|
1821
|
+
if (aliases.size !== references.length + 1) throw new TypeError("Code selected source aliases repeat");
|
|
1822
|
+
return { primary, references };
|
|
1823
|
+
}
|
|
1824
|
+
async function attachReferenceDirectories(workspace, references) {
|
|
1825
|
+
for (const reference of references) {
|
|
1826
|
+
validateAlias(reference.alias);
|
|
1827
|
+
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
1828
|
+
const target = (0, import_node_path8.join)(root, ".odla-references", reference.alias);
|
|
1829
|
+
await (0, import_promises7.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
|
|
1830
|
+
await (0, import_promises7.cp)(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
|
|
1831
|
+
await makeTreeReadOnly(target);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
function validateAlias(alias) {
|
|
1836
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
|
|
1837
|
+
throw new TypeError("Code reference alias is invalid");
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
async function makeTreeReadOnly(root) {
|
|
1841
|
+
for (const entry of await (0, import_promises7.readdir)(root, { withFileTypes: true })) {
|
|
1842
|
+
const target = (0, import_node_path8.join)(root, entry.name);
|
|
1843
|
+
if (entry.isDirectory()) await makeTreeReadOnly(target);
|
|
1844
|
+
else if (entry.isFile()) await (0, import_promises7.chmod)(target, 292);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
// src/code-runtime-source.ts
|
|
1849
|
+
var RESERVED3 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
1850
|
+
var SECRET3 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
1452
1851
|
var SOURCE_MAX_FILES = 1e5;
|
|
1453
1852
|
var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
|
|
1454
1853
|
var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
|
|
1455
|
-
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0,
|
|
1854
|
+
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os3.tmpdir)()) {
|
|
1456
1855
|
if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
|
|
1457
|
-
const root = await (0,
|
|
1458
|
-
const sourceDir = (0,
|
|
1459
|
-
await (0,
|
|
1856
|
+
const root = await (0, import_promises8.mkdtemp)((0, import_node_path9.join)(tempRoot, "odla-code-source-"));
|
|
1857
|
+
const sourceDir = (0, import_node_path9.join)(root, "source");
|
|
1858
|
+
await (0, import_promises8.mkdir)(sourceDir);
|
|
1460
1859
|
const seen = /* @__PURE__ */ new Set();
|
|
1461
1860
|
let bytes = 0;
|
|
1462
1861
|
try {
|
|
@@ -1466,13 +1865,13 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
1466
1865
|
seen.add(file.path);
|
|
1467
1866
|
bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
|
|
1468
1867
|
if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
|
|
1469
|
-
const target = (0,
|
|
1470
|
-
if (!target.startsWith(`${(0,
|
|
1471
|
-
await (0,
|
|
1472
|
-
await (0,
|
|
1868
|
+
const target = (0, import_node_path9.resolve)(sourceDir, file.path);
|
|
1869
|
+
if (!target.startsWith(`${(0, import_node_path9.resolve)(sourceDir)}${import_node_path9.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
1870
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
1871
|
+
await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
1473
1872
|
}
|
|
1474
1873
|
for (const reference of snapshot.references ?? []) {
|
|
1475
|
-
|
|
1874
|
+
validateAlias2(reference.alias);
|
|
1476
1875
|
if (!reference.files.length || reference.files.length > SOURCE_MAX_FILES) throw new TypeError("Code reference file count is invalid");
|
|
1477
1876
|
for (const file of reference.files) {
|
|
1478
1877
|
validatePath(file.path);
|
|
@@ -1481,19 +1880,19 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
1481
1880
|
seen.add(path);
|
|
1482
1881
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
1483
1882
|
if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
|
|
1484
|
-
const target = (0,
|
|
1485
|
-
if (!target.startsWith(`${(0,
|
|
1486
|
-
await (0,
|
|
1487
|
-
await (0,
|
|
1883
|
+
const target = (0, import_node_path9.resolve)(sourceDir, path);
|
|
1884
|
+
if (!target.startsWith(`${(0, import_node_path9.resolve)(sourceDir)}${import_node_path9.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
1885
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
1886
|
+
await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
1488
1887
|
}
|
|
1489
1888
|
}
|
|
1490
|
-
return { sourceDir, cleanup: () => (0,
|
|
1889
|
+
return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
|
|
1491
1890
|
} catch (cause) {
|
|
1492
|
-
await (0,
|
|
1891
|
+
await (0, import_promises8.rm)(root, { recursive: true, force: true });
|
|
1493
1892
|
throw cause;
|
|
1494
1893
|
}
|
|
1495
1894
|
}
|
|
1496
|
-
function
|
|
1895
|
+
function validateAlias2(alias) {
|
|
1497
1896
|
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
|
|
1498
1897
|
throw new TypeError("Code reference alias is invalid");
|
|
1499
1898
|
}
|
|
@@ -1501,24 +1900,24 @@ function validateAlias(alias) {
|
|
|
1501
1900
|
async function attachCodeRuntimeReferences(workspace, references) {
|
|
1502
1901
|
let bytes = 0;
|
|
1503
1902
|
for (const reference of references) {
|
|
1504
|
-
|
|
1903
|
+
validateAlias2(reference.alias);
|
|
1505
1904
|
for (const file of reference.files) {
|
|
1506
1905
|
validatePath(file.path);
|
|
1507
1906
|
const path = `.odla-references/${reference.alias}/${file.path}`;
|
|
1508
1907
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
1509
1908
|
if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
|
|
1510
1909
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
1511
|
-
const target = (0,
|
|
1512
|
-
if (!target.startsWith(`${(0,
|
|
1513
|
-
await (0,
|
|
1514
|
-
await (0,
|
|
1910
|
+
const target = (0, import_node_path9.resolve)(root, path);
|
|
1911
|
+
if (!target.startsWith(`${(0, import_node_path9.resolve)(root)}${import_node_path9.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
1912
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
1913
|
+
await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
1515
1914
|
}
|
|
1516
1915
|
}
|
|
1517
1916
|
}
|
|
1518
1917
|
}
|
|
1519
1918
|
function validatePath(path) {
|
|
1520
1919
|
const parts = path.split("/");
|
|
1521
|
-
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." ||
|
|
1920
|
+
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." || RESERVED3.has(part) || SECRET3.test(part))) {
|
|
1522
1921
|
throw new TypeError("Code source contains an unsafe path");
|
|
1523
1922
|
}
|
|
1524
1923
|
}
|
|
@@ -1546,12 +1945,12 @@ async function materializeCommandWorkspace(input) {
|
|
|
1546
1945
|
throw new TypeError("Code selected source set is invalid");
|
|
1547
1946
|
}
|
|
1548
1947
|
if (references.length) {
|
|
1549
|
-
const
|
|
1550
|
-
if (
|
|
1948
|
+
const selected2 = await input.control.source(command.sessionId);
|
|
1949
|
+
if (selected2.repository !== metadata.repository || selected2.commitSha !== metadata.baseCommitSha || selected2.treeDigest !== metadata.sourceTreeDigest) {
|
|
1551
1950
|
await prepared.workspace.cleanup();
|
|
1552
1951
|
throw new TypeError("Code local source does not match the selected GitHub primary source");
|
|
1553
1952
|
}
|
|
1554
|
-
await attachCodeRuntimeReferences(prepared.workspace,
|
|
1953
|
+
await attachCodeRuntimeReferences(prepared.workspace, selected2.references ?? []);
|
|
1555
1954
|
}
|
|
1556
1955
|
}
|
|
1557
1956
|
return {
|
|
@@ -1561,6 +1960,63 @@ async function materializeCommandWorkspace(input) {
|
|
|
1561
1960
|
requestedLocal
|
|
1562
1961
|
};
|
|
1563
1962
|
}
|
|
1963
|
+
const selected = selectedSourceSet(command.payload);
|
|
1964
|
+
if (selected?.primary.materialization === "host_archive") {
|
|
1965
|
+
if (!input.control.sourceArchive) {
|
|
1966
|
+
throw new TypeError("Code runtime protocol does not support host source materialization");
|
|
1967
|
+
}
|
|
1968
|
+
const sources = [selected.primary, ...selected.references];
|
|
1969
|
+
const materialized2 = [];
|
|
1970
|
+
try {
|
|
1971
|
+
for (const descriptor2 of sources) {
|
|
1972
|
+
if (descriptor2.materialization !== "host_archive") {
|
|
1973
|
+
throw new TypeError("Code selected source set mixes incompatible materialization modes");
|
|
1974
|
+
}
|
|
1975
|
+
const archive = await input.control.sourceArchive(command.sessionId, descriptor2.alias);
|
|
1976
|
+
if (archive.repository !== descriptor2.repository || archive.commitSha !== descriptor2.commitSha) {
|
|
1977
|
+
throw new TypeError("Code source archive does not match its selected repository and commit");
|
|
1978
|
+
}
|
|
1979
|
+
const source2 = await materializeCodeRuntimeArchive(
|
|
1980
|
+
archive,
|
|
1981
|
+
descriptor2.visiblePaths ? new Set(descriptor2.visiblePaths) : void 0
|
|
1982
|
+
);
|
|
1983
|
+
const treeDigest = await digestStagedWorkspace(source2.sourceDir, {
|
|
1984
|
+
maxFiles: archive.limits.maxFiles,
|
|
1985
|
+
maxBytes: archive.limits.maxTotalFileBytes
|
|
1986
|
+
});
|
|
1987
|
+
materialized2.push({ descriptor: descriptor2, source: source2, treeDigest });
|
|
1988
|
+
}
|
|
1989
|
+
const primary = materialized2[0];
|
|
1990
|
+
const workspace = resume ? (await restoreCodeWorkspaceCheckpoint({
|
|
1991
|
+
trustedBaseDir: primary.source.sourceDir,
|
|
1992
|
+
trustedBaseCommitSha: primary.descriptor.commitSha,
|
|
1993
|
+
checkpoint: codeCheckpointPayload(command.payload)
|
|
1994
|
+
})).workspace : await stageWorkspace(primary.source.sourceDir, {
|
|
1995
|
+
maxFiles: SOURCE_MAX_FILES,
|
|
1996
|
+
maxBytes: SOURCE_SET_MAX_BYTES
|
|
1997
|
+
});
|
|
1998
|
+
try {
|
|
1999
|
+
await attachReferenceDirectories(workspace, materialized2.slice(1).map((item) => ({
|
|
2000
|
+
alias: item.descriptor.alias,
|
|
2001
|
+
sourceDir: item.source.sourceDir
|
|
2002
|
+
})));
|
|
2003
|
+
} catch (cause) {
|
|
2004
|
+
await workspace.cleanup();
|
|
2005
|
+
throw cause;
|
|
2006
|
+
}
|
|
2007
|
+
return {
|
|
2008
|
+
workspace,
|
|
2009
|
+
sourceDigest: primary.treeDigest,
|
|
2010
|
+
requestedLocal: null,
|
|
2011
|
+
sourceDigests: materialized2.map((item) => ({
|
|
2012
|
+
alias: item.descriptor.alias,
|
|
2013
|
+
treeDigest: item.treeDigest
|
|
2014
|
+
}))
|
|
2015
|
+
};
|
|
2016
|
+
} finally {
|
|
2017
|
+
await Promise.allSettled(materialized2.map((item) => item.source.cleanup()));
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
1564
2020
|
const source = await input.control.source(command.sessionId);
|
|
1565
2021
|
const materialized = await materializeCodeRuntimeSource(source);
|
|
1566
2022
|
try {
|
|
@@ -1898,14 +2354,40 @@ async function sessionSkillsFor(options, command) {
|
|
|
1898
2354
|
}
|
|
1899
2355
|
|
|
1900
2356
|
// src/code-runtime-inference.ts
|
|
2357
|
+
var OVERLOAD_RETRY_DELAYS_MS = [2e3, 4e3, 8e3, 16e3];
|
|
2358
|
+
var RETRYABLE_CODES = /* @__PURE__ */ new Set(["control_plane_overloaded", "registry_overloaded", "transport_unavailable"]);
|
|
2359
|
+
function overloadedControlFailure(cause) {
|
|
2360
|
+
return cause instanceof CodeRuntimeControlError && cause.status === 503 && RETRYABLE_CODES.has(cause.code);
|
|
2361
|
+
}
|
|
2362
|
+
async function inferWithBackoff(infer, wait2, onRetry) {
|
|
2363
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
2364
|
+
try {
|
|
2365
|
+
return await infer();
|
|
2366
|
+
} catch (cause) {
|
|
2367
|
+
const delayMs = OVERLOAD_RETRY_DELAYS_MS[attempt];
|
|
2368
|
+
if (delayMs === void 0 || !overloadedControlFailure(cause)) throw cause;
|
|
2369
|
+
await onRetry(cause, delayMs);
|
|
2370
|
+
await wait2(delayMs);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
1901
2374
|
async function handleCodeRuntimeInference(input) {
|
|
1902
2375
|
const { command, request, state } = input;
|
|
1903
2376
|
const startedAt = Date.now();
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2377
|
+
const wait2 = input.wait ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
2378
|
+
const response2 = await inferWithBackoff(
|
|
2379
|
+
() => input.control.infer(command.sessionId, {
|
|
2380
|
+
requestId: request.requestId,
|
|
2381
|
+
interactionId: command.commandId,
|
|
2382
|
+
call: request.call
|
|
2383
|
+
}),
|
|
2384
|
+
wait2,
|
|
2385
|
+
(cause, delayMs) => input.event({
|
|
2386
|
+
type: "diagnostic",
|
|
2387
|
+
level: "error",
|
|
2388
|
+
message: `Code control plane overloaded (${cause.code}); retrying the model call in ${delayMs / 1e3}s`
|
|
2389
|
+
}).catch(() => void 0)
|
|
2390
|
+
);
|
|
1909
2391
|
state.tokens += response2.receipt.inputTokens + response2.receipt.outputTokens;
|
|
1910
2392
|
const { costUsd } = response2.receipt;
|
|
1911
2393
|
if (costUsd === void 0) state.costKnown = false;
|
|
@@ -2368,12 +2850,12 @@ function response(request, ok, content, details) {
|
|
|
2368
2850
|
}
|
|
2369
2851
|
|
|
2370
2852
|
// src/code-tool-reads.ts
|
|
2371
|
-
var
|
|
2853
|
+
var import_promises11 = require("fs/promises");
|
|
2372
2854
|
|
|
2373
2855
|
// src/code-tool-discovery.ts
|
|
2374
2856
|
var import_node_child_process5 = require("child_process");
|
|
2375
|
-
var
|
|
2376
|
-
var
|
|
2857
|
+
var import_promises9 = require("fs/promises");
|
|
2858
|
+
var import_node_path10 = require("path");
|
|
2377
2859
|
var DEFAULT_MAX_FILES = 2e4;
|
|
2378
2860
|
var DEFAULT_MAX_RESULTS = 100;
|
|
2379
2861
|
var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
@@ -2398,13 +2880,13 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
2398
2880
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
2399
2881
|
const paths2 = [];
|
|
2400
2882
|
const walk = async (directory) => {
|
|
2401
|
-
for (const entry of await (0,
|
|
2883
|
+
for (const entry of await (0, import_promises9.readdir)(directory, { withFileTypes: true })) {
|
|
2402
2884
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
2403
2885
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
2404
|
-
const target = (0,
|
|
2886
|
+
const target = (0, import_node_path10.resolve)(directory, entry.name);
|
|
2405
2887
|
if (entry.isDirectory()) await walk(target);
|
|
2406
2888
|
else if (entry.isFile()) {
|
|
2407
|
-
const path = (0,
|
|
2889
|
+
const path = (0, import_node_path10.relative)(root, target).split("\\").join("/");
|
|
2408
2890
|
try {
|
|
2409
2891
|
validateRelativePath(path);
|
|
2410
2892
|
} catch {
|
|
@@ -2415,7 +2897,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
2415
2897
|
}
|
|
2416
2898
|
}
|
|
2417
2899
|
};
|
|
2418
|
-
await walk((0,
|
|
2900
|
+
await walk((0, import_node_path10.resolve)(root));
|
|
2419
2901
|
return paths2.sort();
|
|
2420
2902
|
}
|
|
2421
2903
|
function listWorkspace(paths2, options = {}) {
|
|
@@ -2529,7 +3011,7 @@ async function fallbackSearch(root, scoped, options) {
|
|
|
2529
3011
|
if (matches.length >= options.maxResults) break;
|
|
2530
3012
|
let source;
|
|
2531
3013
|
try {
|
|
2532
|
-
source = await (0,
|
|
3014
|
+
source = await (0, import_promises9.readFile)((0, import_node_path10.resolve)(root, path));
|
|
2533
3015
|
} catch {
|
|
2534
3016
|
continue;
|
|
2535
3017
|
}
|
|
@@ -2547,15 +3029,15 @@ async function fallbackSearch(root, scoped, options) {
|
|
|
2547
3029
|
}
|
|
2548
3030
|
|
|
2549
3031
|
// src/code-tool-graph.ts
|
|
2550
|
-
var
|
|
2551
|
-
var
|
|
3032
|
+
var import_promises10 = require("fs/promises");
|
|
3033
|
+
var import_node_path11 = require("path");
|
|
2552
3034
|
var import_graph = require("@odla-ai/graph");
|
|
2553
3035
|
var import_code4 = require("@odla-ai/graph/code");
|
|
2554
3036
|
var cache = /* @__PURE__ */ new Map();
|
|
2555
3037
|
function workspaceGraphs(workspaceDir, paths2) {
|
|
2556
3038
|
const existing = cache.get(workspaceDir);
|
|
2557
3039
|
if (existing) return existing;
|
|
2558
|
-
const read2 = (path) => (0,
|
|
3040
|
+
const read2 = (path) => (0, import_promises10.readFile)((0, import_node_path11.join)(workspaceDir, path), "utf8");
|
|
2559
3041
|
const built = (async () => ({
|
|
2560
3042
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
2561
3043
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
@@ -2629,11 +3111,11 @@ async function read(context, request, options, policy, registry) {
|
|
|
2629
3111
|
const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
|
|
2630
3112
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
2631
3113
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
2632
|
-
const info = await (0,
|
|
3114
|
+
const info = await (0, import_promises11.stat)(target);
|
|
2633
3115
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
2634
3116
|
throw new TypeError("file is not a bounded regular source file");
|
|
2635
3117
|
}
|
|
2636
|
-
const source = await (0,
|
|
3118
|
+
const source = await (0, import_promises11.readFile)(target);
|
|
2637
3119
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
2638
3120
|
const lines = source.toString("utf8").split("\n");
|
|
2639
3121
|
const content = lines.slice(startLine - 1, endLine).join("\n");
|
|
@@ -3155,8 +3637,8 @@ var runtimeErrorMessage = (value) => value instanceof Error ? value.message : St
|
|
|
3155
3637
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
3156
3638
|
let settle;
|
|
3157
3639
|
let settled = false;
|
|
3158
|
-
const ready = new Promise((
|
|
3159
|
-
settle =
|
|
3640
|
+
const ready = new Promise((resolve7) => {
|
|
3641
|
+
settle = resolve7;
|
|
3160
3642
|
});
|
|
3161
3643
|
const release = (run) => {
|
|
3162
3644
|
if (settled) return;
|
|
@@ -3265,7 +3747,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3265
3747
|
async #start(command, resume) {
|
|
3266
3748
|
if (this.#active.has(command.sessionId)) throw new TypeError("Code session is already active on this runtime");
|
|
3267
3749
|
const metadata = codeCommandMetadata(command.payload, resume);
|
|
3268
|
-
const { workspace, sourceDigest, localTrustedBaseDigest, requestedLocal } = await materializeCommandWorkspace({
|
|
3750
|
+
const { workspace, sourceDigest, sourceDigests, localTrustedBaseDigest, requestedLocal } = await materializeCommandWorkspace({
|
|
3269
3751
|
command,
|
|
3270
3752
|
metadata,
|
|
3271
3753
|
resume,
|
|
@@ -3286,7 +3768,7 @@ var TheseusRuntimeEngine = class {
|
|
|
3286
3768
|
maxTokensPerInteraction: metadata.maxTokensPerInteraction,
|
|
3287
3769
|
baseCommitSha: metadata.baseCommitSha,
|
|
3288
3770
|
repository: metadata.repository,
|
|
3289
|
-
sourceTreeDigest: metadata.sourceTreeDigest,
|
|
3771
|
+
sourceTreeDigest: metadata.sourceTreeDigest ?? sourceDigest,
|
|
3290
3772
|
trustedBaseDigest: requestedLocal ? localTrustedBaseDigest : await digestStagedWorkspace(workspace.baselineDir, {
|
|
3291
3773
|
maxFiles: 2e4,
|
|
3292
3774
|
maxBytes: 512 * 1024 * 1024
|
|
@@ -3312,7 +3794,11 @@ var TheseusRuntimeEngine = class {
|
|
|
3312
3794
|
await this.#failure(command, active, detail);
|
|
3313
3795
|
return null;
|
|
3314
3796
|
});
|
|
3315
|
-
return {
|
|
3797
|
+
return {
|
|
3798
|
+
status: "running",
|
|
3799
|
+
message: resume ? "Theseus resumed from a portable checkpoint" : "Theseus started",
|
|
3800
|
+
...sourceDigests ? { sourceDigests } : {}
|
|
3801
|
+
};
|
|
3316
3802
|
}
|
|
3317
3803
|
/**
|
|
3318
3804
|
* Pursue a goal: attempt, judge with the clean verifier, re-prompt from what
|
|
@@ -3539,7 +4025,7 @@ function parse(argv) {
|
|
|
3539
4025
|
};
|
|
3540
4026
|
}
|
|
3541
4027
|
async function readPolicy(path) {
|
|
3542
|
-
const value = JSON.parse(await (0,
|
|
4028
|
+
const value = JSON.parse(await (0, import_promises12.readFile)(path, "utf8"));
|
|
3543
4029
|
if (!value || Object.keys(value).some((key) => !["recipes", "recipeAuthorization"].includes(key)) || !Array.isArray(value.recipes) || !value.recipes.length || value.recipeAuthorization !== void 0 && value.recipeAuthorization !== "registered_recipe" && value.recipeAuthorization !== "exact_approval") throw new TypeError("invalid Code build policy file");
|
|
3544
4030
|
const recipes = value.recipes;
|
|
3545
4031
|
for (const recipe2 of recipes) assertCodeBuildRecipe(recipe2);
|
|
@@ -3558,8 +4044,8 @@ async function main() {
|
|
|
3558
4044
|
platform: process.platform === "darwin" ? "macos" : "linux",
|
|
3559
4045
|
arch: process.arch,
|
|
3560
4046
|
engines: [engine],
|
|
3561
|
-
cpuCount: (0,
|
|
3562
|
-
memoryBytes: (0,
|
|
4047
|
+
cpuCount: (0, import_node_os4.cpus)().length,
|
|
4048
|
+
memoryBytes: (0, import_node_os4.totalmem)()
|
|
3563
4049
|
};
|
|
3564
4050
|
const controller = new AbortController();
|
|
3565
4051
|
for (const signal of ["SIGINT", "SIGTERM"]) process.once(signal, () => controller.abort(signal));
|