@odla-ai/harness 0.11.10 → 0.11.12

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.
@@ -3,7 +3,7 @@
3
3
 
4
4
  // src/code-runtime-cli.ts
5
5
  var import_node_os4 = require("os");
6
- var import_promises15 = require("fs/promises");
6
+ var import_promises17 = require("fs/promises");
7
7
 
8
8
  // src/code-runtime-client-validation.ts
9
9
  var import_code = require("@odla-ai/camel/code");
@@ -452,7 +452,7 @@ async function withOverloadRetry(call, wait2, onRetry = () => void 0, retryable
452
452
  }
453
453
 
454
454
  // src/code-runtime-reconciler.ts
455
- var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
455
+ var sleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
456
456
  var POST_ACK_RETRY_TICKS = 40;
457
457
  var CodeRuntimeReconciler = class {
458
458
  constructor(control, engine, onDiagnostic, options = {}) {
@@ -554,12 +554,12 @@ function retryableControlFailure(value) {
554
554
  return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
555
555
  }
556
556
  function wait(ms, signal) {
557
- return new Promise((resolve7) => {
558
- if (signal?.aborted) return resolve7();
559
- const timer = setTimeout(resolve7, ms);
557
+ return new Promise((resolve8) => {
558
+ if (signal?.aborted) return resolve8();
559
+ const timer = setTimeout(resolve8, ms);
560
560
  signal?.addEventListener("abort", () => {
561
561
  clearTimeout(timer);
562
- resolve7();
562
+ resolve8();
563
563
  }, { once: true });
564
564
  });
565
565
  }
@@ -981,12 +981,12 @@ async function restoreCodeWorkspaceCheckpoint(input) {
981
981
  // src/code-verifier.ts
982
982
  var import_node_crypto3 = require("crypto");
983
983
  var import_node_fs2 = require("fs");
984
- var import_promises5 = require("fs/promises");
985
- var import_node_path6 = require("path");
984
+ var import_promises7 = require("fs/promises");
985
+ var import_node_path8 = require("path");
986
986
  var import_code3 = require("@odla-ai/camel/code");
987
987
 
988
988
  // src/recipe-container.ts
989
- var import_node_child_process4 = require("child_process");
989
+ var import_node_child_process5 = require("child_process");
990
990
  var import_node_process2 = require("process");
991
991
  var import_node_crypto = require("crypto");
992
992
 
@@ -1044,7 +1044,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
1044
1044
  throw new TypeError("no supported container engine found");
1045
1045
  }
1046
1046
  function inspectRootlessPodman() {
1047
- return new Promise((resolve7, reject) => {
1047
+ return new Promise((resolve8, reject) => {
1048
1048
  (0, import_node_child_process3.execFile)(
1049
1049
  "podman",
1050
1050
  ["info", "--format", "{{.Host.Security.Rootless}}"],
@@ -1054,7 +1054,7 @@ function inspectRootlessPodman() {
1054
1054
  reject(new TypeError("could not verify that the active Podman service is rootless"));
1055
1055
  return;
1056
1056
  }
1057
- resolve7(stdout.trim() === "true");
1057
+ resolve8(stdout.trim() === "true");
1058
1058
  }
1059
1059
  );
1060
1060
  });
@@ -1072,12 +1072,89 @@ async function verifyContainerEngineBoundary(engine, options = {}) {
1072
1072
  if (!rootless) throw new TypeError("the active Podman service is not rootless; refusing to run the harness");
1073
1073
  }
1074
1074
 
1075
+ // src/code-recipe-dependencies.ts
1076
+ var import_promises4 = require("fs/promises");
1077
+ var import_node_path5 = require("path");
1078
+ var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
1079
+ function assertLentPath(mountAs) {
1080
+ const parts = mountAs.split("/");
1081
+ if ((0, import_node_path5.isAbsolute)(mountAs) || mountAs.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
1082
+ throw new TypeError(`recipe dependencies must mount inside the workspace, not at "${mountAs}"`);
1083
+ }
1084
+ }
1085
+ function assertReservedMount(mountAs) {
1086
+ assertLentPath(mountAs);
1087
+ if (!RESERVED_MOUNTS.has(mountAs.split("/").at(-1))) {
1088
+ throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
1089
+ }
1090
+ }
1091
+ function lentDirectories(dependencies) {
1092
+ return [{ source: dependencies.source, mountAs: dependencies.mountAs ?? "node_modules" }, ...dependencies.nested ?? []];
1093
+ }
1094
+
1095
+ // src/recipe-container-lending.ts
1096
+ var import_node_child_process4 = require("child_process");
1097
+ var import_promises5 = require("fs/promises");
1098
+ var import_node_path6 = require("path");
1099
+ var RUN_ID = /^[A-Za-z0-9_.-]{1,64}$/;
1100
+ function copy(args) {
1101
+ return new Promise((accept, reject) => {
1102
+ const child = (0, import_node_child_process4.spawn)("cp", args, { shell: false, stdio: "ignore" });
1103
+ child.once("error", reject);
1104
+ child.once("exit", (code) => accept(code ?? 1));
1105
+ });
1106
+ }
1107
+ async function cloneTree(source, target, platform = process.platform) {
1108
+ await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(target), { recursive: true });
1109
+ const fast = platform === "darwin" ? ["-Rc", source, target] : platform === "linux" ? ["-R", "--reflink=auto", source, target] : null;
1110
+ if (fast && await copy(fast) === 0) return;
1111
+ await (0, import_promises5.rm)(target, { recursive: true, force: true });
1112
+ if (await copy(["-R", source, target]) !== 0) throw new Error(`could not clone ${source}`);
1113
+ }
1114
+ async function lendDependencies(dependencies, runId) {
1115
+ if (!dependencies?.runsDir) return { dependencies, release: async () => void 0 };
1116
+ if (!RUN_ID.test(runId)) throw new TypeError("run id is not a safe directory name");
1117
+ const treeRoot = (0, import_node_path6.dirname)(dependencies.source);
1118
+ for (const entry of lentDirectories(dependencies)) {
1119
+ if (entry.source !== (0, import_node_path6.join)(treeRoot, entry.mountAs)) {
1120
+ throw new TypeError("every directory of a cloned set must lie under its tree root at its mount path");
1121
+ }
1122
+ }
1123
+ const runDir = (0, import_node_path6.join)(dependencies.runsDir, runId);
1124
+ await cloneTree(treeRoot, runDir);
1125
+ const rebase = (entry) => ({ source: (0, import_node_path6.join)(runDir, entry.mountAs), mountAs: entry.mountAs });
1126
+ return {
1127
+ dependencies: {
1128
+ ...dependencies,
1129
+ source: (0, import_node_path6.join)(runDir, dependencies.mountAs ?? "node_modules"),
1130
+ nested: (dependencies.nested ?? []).map(rebase),
1131
+ writable: true
1132
+ },
1133
+ release: () => (0, import_promises5.rm)(runDir, { recursive: true, force: true })
1134
+ };
1135
+ }
1136
+ async function lendBuildProducts(products, workspaceDir) {
1137
+ const root = (0, import_node_path6.resolve)(workspaceDir);
1138
+ const lent = [];
1139
+ for (const product of products) {
1140
+ assertLentPath(product.mountAs);
1141
+ if (SECRET_WORKSPACE_FILE.test(product.mountAs.split("/").at(-1))) throw new TypeError("a secret is never a build product");
1142
+ const target = (0, import_node_path6.resolve)(root, product.mountAs);
1143
+ if (!target.startsWith(`${root}${import_node_path6.sep}`)) throw new TypeError("build product escapes the workspace");
1144
+ if (await (0, import_promises5.stat)(target).catch(() => null)) continue;
1145
+ if (!await (0, import_promises5.stat)((0, import_node_path6.join)(root, product.within ?? (0, import_node_path6.dirname)(product.mountAs))).catch(() => null)) continue;
1146
+ await cloneTree(product.source, target);
1147
+ lent.push(product.mountAs);
1148
+ }
1149
+ return lent;
1150
+ }
1151
+
1075
1152
  // src/recipe-container.ts
1076
1153
  var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
1077
1154
  var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
1078
1155
  function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`, dependencies = null) {
1079
1156
  assertCodeBuildRecipe(recipe2);
1080
- const mounts = dependencies ? [dependencyMount(engine, dependencies)] : [];
1157
+ const mounts = dependencies ? dependencyMounts(engine, dependencies) : [];
1081
1158
  if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
1082
1159
  const uid = typeof import_node_process2.getuid === "function" ? (0, import_node_process2.getuid)() : 1e3;
1083
1160
  const gid = typeof import_node_process2.getgid === "function" ? (0, import_node_process2.getgid)() : 1e3;
@@ -1103,6 +1180,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
1103
1180
  ...mounts,
1104
1181
  "--workdir=/workspace",
1105
1182
  "--env=CI=1",
1183
+ "--env=HOME=/tmp",
1106
1184
  recipe2.image,
1107
1185
  ...recipe2.command
1108
1186
  ];
@@ -1125,26 +1203,34 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
1125
1203
  ...mounts,
1126
1204
  "--workdir=/workspace",
1127
1205
  "--env=CI=1",
1206
+ "--env=HOME=/tmp",
1128
1207
  recipe2.image,
1129
1208
  ...recipe2.command
1130
1209
  ];
1131
1210
  }
1132
- var LENDABLE = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
1133
- function dependencyMount(engine, dependencies) {
1134
- const mountAs = dependencies.mountAs ?? "node_modules";
1135
- if (!dependencies.source.startsWith("/") || /[,\r\n]/.test(dependencies.source)) {
1136
- throw new TypeError("recipe dependency source must be an absolute path without mount separators");
1137
- }
1138
- if (!LENDABLE.has(mountAs)) throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
1139
- return engine === "container" ? `--mount=type=bind,source=${dependencies.source},target=/workspace/${mountAs},readonly` : `--mount=type=bind,src=${dependencies.source},dst=/workspace/${mountAs},readonly`;
1211
+ function dependencyMounts(engine, dependencies) {
1212
+ const readonly = dependencies.writable ? "" : ",readonly";
1213
+ return lentDirectories(dependencies).map(({ source, mountAs }) => {
1214
+ if (!source.startsWith("/") || /[,\r\n]/.test(source)) {
1215
+ throw new TypeError("recipe dependency source must be an absolute path without mount separators");
1216
+ }
1217
+ assertReservedMount(mountAs);
1218
+ return engine === "container" ? `--mount=type=bind,source=${source},target=/workspace/${mountAs}${readonly}` : `--mount=type=bind,src=${source},dst=/workspace/${mountAs}${readonly}`;
1219
+ });
1140
1220
  }
1141
1221
  function createContainerRecipeExecutor(engine, options = {}) {
1142
1222
  return {
1143
1223
  async run(input) {
1144
1224
  await verifyContainerEngineBoundary(engine);
1145
1225
  const name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`;
1146
- const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, options.dependencies ?? null);
1147
- return execute(engine, args, name, input.recipe, input.signal);
1226
+ const lent = await lendDependencies(options.dependencies ?? null, name);
1227
+ try {
1228
+ if (lent.dependencies?.products?.length) await lendBuildProducts(lent.dependencies.products, input.workspaceDir);
1229
+ const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, lent.dependencies);
1230
+ return await execute(engine, args, name, input.recipe, input.signal);
1231
+ } finally {
1232
+ await lent.release();
1233
+ }
1148
1234
  }
1149
1235
  };
1150
1236
  }
@@ -1169,7 +1255,7 @@ function execute(engine, args, name, recipe2, signal) {
1169
1255
  function runContainerCommand(engine, args, name, recipe2, signal) {
1170
1256
  return new Promise((accept, reject) => {
1171
1257
  const started = Date.now();
1172
- const child = (0, import_node_child_process4.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
1258
+ const child = (0, import_node_child_process5.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
1173
1259
  const stdout = [];
1174
1260
  const stderr = [];
1175
1261
  let bytes = 0;
@@ -1182,7 +1268,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
1182
1268
  timedOut = reason === "timeout";
1183
1269
  outputLimitExceeded = reason === "output";
1184
1270
  const remove = engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
1185
- const killer = (0, import_node_child_process4.spawn)(engine, remove, { shell: false, stdio: "ignore" });
1271
+ const killer = (0, import_node_child_process5.spawn)(engine, remove, { shell: false, stdio: "ignore" });
1186
1272
  killer.unref();
1187
1273
  child.kill("SIGTERM");
1188
1274
  };
@@ -1219,27 +1305,27 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
1219
1305
 
1220
1306
  // src/workspace-digest.ts
1221
1307
  var import_node_crypto2 = require("crypto");
1222
- var import_promises4 = require("fs/promises");
1223
- var import_node_path5 = require("path");
1308
+ var import_promises6 = require("fs/promises");
1309
+ var import_node_path7 = require("path");
1224
1310
  async function digestStagedWorkspace(root, limits) {
1225
1311
  const files = [];
1226
1312
  const walk = async (directory) => {
1227
- const entries = await (0, import_promises4.readdir)(directory, { withFileTypes: true });
1313
+ const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
1228
1314
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
1229
1315
  if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
1230
- const target = (0, import_node_path5.resolve)(directory, entry.name);
1316
+ const target = (0, import_node_path7.resolve)(directory, entry.name);
1231
1317
  if (entry.isDirectory()) await walk(target);
1232
1318
  else if (entry.isFile()) {
1233
- files.push({ path: (0, import_node_path5.relative)(root, target).split("\\").join("/"), target });
1319
+ files.push({ path: (0, import_node_path7.relative)(root, target).split("\\").join("/"), target });
1234
1320
  if (files.length > limits.maxFiles) throw new TypeError("workspace digest exceeds its file bound");
1235
1321
  }
1236
1322
  }
1237
1323
  };
1238
- await walk((0, import_node_path5.resolve)(root));
1324
+ await walk((0, import_node_path7.resolve)(root));
1239
1325
  const hash = (0, import_node_crypto2.createHash)("sha256");
1240
1326
  let bytes = 0;
1241
1327
  for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
1242
- const content = await (0, import_promises4.readFile)(file.target);
1328
+ const content = await (0, import_promises6.readFile)(file.target);
1243
1329
  bytes += Buffer.byteLength(file.path) + content.byteLength;
1244
1330
  if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
1245
1331
  hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content.byteLength}:`);
@@ -1357,8 +1443,8 @@ async function inspectArtifacts(workspaceDir, recipe2) {
1357
1443
  const receipts = [];
1358
1444
  for (const artifact of recipe2.expectedArtifacts ?? []) {
1359
1445
  try {
1360
- const path = (0, import_node_path6.join)(workspaceDir, artifact.path);
1361
- const info = await (0, import_promises5.lstat)(path);
1446
+ const path = (0, import_node_path8.join)(workspaceDir, artifact.path);
1447
+ const info = await (0, import_promises7.lstat)(path);
1362
1448
  if (!info.isFile() || info.isSymbolicLink()) {
1363
1449
  receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
1364
1450
  } else if (info.size > artifact.maximumBytes) {
@@ -1689,15 +1775,15 @@ async function prepareRuntimeLocalSource(input) {
1689
1775
  }
1690
1776
 
1691
1777
  // src/code-runtime-source.ts
1692
- var import_promises8 = require("fs/promises");
1778
+ var import_promises10 = require("fs/promises");
1693
1779
  var import_node_os3 = require("os");
1694
- var import_node_path9 = require("path");
1780
+ var import_node_path11 = require("path");
1695
1781
 
1696
1782
  // src/code-runtime-archive.ts
1697
1783
  var import_node_zlib = require("zlib");
1698
- var import_promises6 = require("fs/promises");
1784
+ var import_promises8 = require("fs/promises");
1699
1785
  var import_node_os2 = require("os");
1700
- var import_node_path7 = require("path");
1786
+ var import_node_path9 = require("path");
1701
1787
  var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
1702
1788
  var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
1703
1789
  var MAX_NUL_SHARE = 0.1;
@@ -1715,9 +1801,9 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
1715
1801
  throw new TypeError("Code source archive exceeds its decompressed byte bound");
1716
1802
  }
1717
1803
  const entries = parseTar(bytes, archive.limits);
1718
- const root = await (0, import_promises6.mkdtemp)((0, import_node_path7.join)(tempRoot, "odla-code-archive-"));
1719
- const sourceDir = (0, import_node_path7.join)(root, "source");
1720
- await (0, import_promises6.mkdir)(sourceDir);
1804
+ const root = await (0, import_promises8.mkdtemp)((0, import_node_path9.join)(tempRoot, "odla-code-archive-"));
1805
+ const sourceDir = (0, import_node_path9.join)(root, "source");
1806
+ await (0, import_promises8.mkdir)(sourceDir);
1721
1807
  let visible = 0;
1722
1808
  try {
1723
1809
  for (const entry of entries) {
@@ -1730,16 +1816,16 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
1730
1816
  continue;
1731
1817
  }
1732
1818
  if (nulShare(content) > MAX_NUL_SHARE) continue;
1733
- const target = (0, import_node_path7.resolve)(sourceDir, entry.path);
1734
- if (!target.startsWith(`${(0, import_node_path7.resolve)(sourceDir)}${import_node_path7.sep}`)) throw new TypeError("Code source path escapes its root");
1735
- await (0, import_promises6.mkdir)((0, import_node_path7.dirname)(target), { recursive: true });
1736
- await (0, import_promises6.writeFile)(target, content, { flag: "wx", mode: 420 });
1819
+ const target = (0, import_node_path9.resolve)(sourceDir, entry.path);
1820
+ if (!target.startsWith(`${(0, import_node_path9.resolve)(sourceDir)}${import_node_path9.sep}`)) throw new TypeError("Code source path escapes its root");
1821
+ await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
1822
+ await (0, import_promises8.writeFile)(target, content, { flag: "wx", mode: 420 });
1737
1823
  visible += 1;
1738
1824
  }
1739
1825
  if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
1740
- return { sourceDir, cleanup: () => (0, import_promises6.rm)(root, { recursive: true, force: true }) };
1826
+ return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
1741
1827
  } catch (cause) {
1742
- await (0, import_promises6.rm)(root, { recursive: true, force: true });
1828
+ await (0, import_promises8.rm)(root, { recursive: true, force: true });
1743
1829
  throw cause;
1744
1830
  }
1745
1831
  }
@@ -1934,8 +2020,8 @@ function nulShare(content) {
1934
2020
  }
1935
2021
 
1936
2022
  // src/code-runtime-selected-source.ts
1937
- var import_promises7 = require("fs/promises");
1938
- var import_node_path8 = require("path");
2023
+ var import_promises9 = require("fs/promises");
2024
+ var import_node_path10 = require("path");
1939
2025
  function selectedSourceSet(payload) {
1940
2026
  if (!payload.sourceSet) return null;
1941
2027
  const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
@@ -1972,9 +2058,9 @@ async function attachReferenceDirectories(workspace, references) {
1972
2058
  for (const reference of references) {
1973
2059
  validateAlias(reference.alias);
1974
2060
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
1975
- const target = (0, import_node_path8.join)(root, ".odla-references", reference.alias);
1976
- await (0, import_promises7.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
1977
- await (0, import_promises7.cp)(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
2061
+ const target = (0, import_node_path10.join)(root, ".odla-references", reference.alias);
2062
+ await (0, import_promises9.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
2063
+ await (0, import_promises9.cp)(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
1978
2064
  await makeTreeReadOnly(target);
1979
2065
  }
1980
2066
  }
@@ -1985,10 +2071,10 @@ function validateAlias(alias) {
1985
2071
  }
1986
2072
  }
1987
2073
  async function makeTreeReadOnly(root) {
1988
- for (const entry of await (0, import_promises7.readdir)(root, { withFileTypes: true })) {
1989
- const target = (0, import_node_path8.join)(root, entry.name);
2074
+ for (const entry of await (0, import_promises9.readdir)(root, { withFileTypes: true })) {
2075
+ const target = (0, import_node_path10.join)(root, entry.name);
1990
2076
  if (entry.isDirectory()) await makeTreeReadOnly(target);
1991
- else if (entry.isFile()) await (0, import_promises7.chmod)(target, 292);
2077
+ else if (entry.isFile()) await (0, import_promises9.chmod)(target, 292);
1992
2078
  }
1993
2079
  }
1994
2080
 
@@ -2000,9 +2086,9 @@ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
2000
2086
  var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
2001
2087
  async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os3.tmpdir)()) {
2002
2088
  if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
2003
- const root = await (0, import_promises8.mkdtemp)((0, import_node_path9.join)(tempRoot, "odla-code-source-"));
2004
- const sourceDir = (0, import_node_path9.join)(root, "source");
2005
- await (0, import_promises8.mkdir)(sourceDir);
2089
+ const root = await (0, import_promises10.mkdtemp)((0, import_node_path11.join)(tempRoot, "odla-code-source-"));
2090
+ const sourceDir = (0, import_node_path11.join)(root, "source");
2091
+ await (0, import_promises10.mkdir)(sourceDir);
2006
2092
  const seen = /* @__PURE__ */ new Set();
2007
2093
  let bytes = 0;
2008
2094
  try {
@@ -2012,10 +2098,10 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
2012
2098
  seen.add(file.path);
2013
2099
  bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
2014
2100
  if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
2015
- const target = (0, import_node_path9.resolve)(sourceDir, file.path);
2016
- if (!target.startsWith(`${(0, import_node_path9.resolve)(sourceDir)}${import_node_path9.sep}`)) throw new TypeError("Code source path escapes its root");
2017
- await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
2018
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 420 });
2101
+ const target = (0, import_node_path11.resolve)(sourceDir, file.path);
2102
+ if (!target.startsWith(`${(0, import_node_path11.resolve)(sourceDir)}${import_node_path11.sep}`)) throw new TypeError("Code source path escapes its root");
2103
+ await (0, import_promises10.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
2104
+ await (0, import_promises10.writeFile)(target, file.content, { flag: "wx", mode: 420 });
2019
2105
  }
2020
2106
  for (const reference of snapshot.references ?? []) {
2021
2107
  validateAlias2(reference.alias);
@@ -2027,15 +2113,15 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
2027
2113
  seen.add(path);
2028
2114
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
2029
2115
  if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
2030
- const target = (0, import_node_path9.resolve)(sourceDir, path);
2031
- if (!target.startsWith(`${(0, import_node_path9.resolve)(sourceDir)}${import_node_path9.sep}`)) throw new TypeError("Code reference path escapes its root");
2032
- await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
2033
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
2116
+ const target = (0, import_node_path11.resolve)(sourceDir, path);
2117
+ if (!target.startsWith(`${(0, import_node_path11.resolve)(sourceDir)}${import_node_path11.sep}`)) throw new TypeError("Code reference path escapes its root");
2118
+ await (0, import_promises10.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
2119
+ await (0, import_promises10.writeFile)(target, file.content, { flag: "wx", mode: 292 });
2034
2120
  }
2035
2121
  }
2036
- return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
2122
+ return { sourceDir, cleanup: () => (0, import_promises10.rm)(root, { recursive: true, force: true }) };
2037
2123
  } catch (cause) {
2038
- await (0, import_promises8.rm)(root, { recursive: true, force: true });
2124
+ await (0, import_promises10.rm)(root, { recursive: true, force: true });
2039
2125
  throw cause;
2040
2126
  }
2041
2127
  }
@@ -2054,10 +2140,10 @@ async function attachCodeRuntimeReferences(workspace, references) {
2054
2140
  bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
2055
2141
  if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
2056
2142
  for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
2057
- const target = (0, import_node_path9.resolve)(root, path);
2058
- if (!target.startsWith(`${(0, import_node_path9.resolve)(root)}${import_node_path9.sep}`)) throw new TypeError("Code reference path escapes its root");
2059
- await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
2060
- await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
2143
+ const target = (0, import_node_path11.resolve)(root, path);
2144
+ if (!target.startsWith(`${(0, import_node_path11.resolve)(root)}${import_node_path11.sep}`)) throw new TypeError("Code reference path escapes its root");
2145
+ await (0, import_promises10.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
2146
+ await (0, import_promises10.writeFile)(target, file.content, { flag: "wx", mode: 292 });
2061
2147
  }
2062
2148
  }
2063
2149
  }
@@ -2503,7 +2589,7 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
2503
2589
  }
2504
2590
 
2505
2591
  // src/code-runtime-session-skills.ts
2506
- var sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
2592
+ var sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
2507
2593
  function createCodeRuntimeSessionSkillLoader(control, options = {}) {
2508
2594
  const wait2 = options.wait ?? sleep2;
2509
2595
  const load = control.collaborationSkills?.bind(control);
@@ -2562,7 +2648,7 @@ var inferWithBackoff = (infer, wait2, onRetry) => withOverloadRetry(infer, wait2
2562
2648
  async function handleCodeRuntimeInference(input) {
2563
2649
  const { command, request, state } = input;
2564
2650
  const startedAt = Date.now();
2565
- const wait2 = input.wait ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
2651
+ const wait2 = input.wait ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
2566
2652
  const response2 = await inferWithBackoff(
2567
2653
  () => input.control.infer(command.sessionId, {
2568
2654
  requestId: request.requestId,
@@ -3073,18 +3159,18 @@ function response(request, ok, content, details) {
3073
3159
  }
3074
3160
 
3075
3161
  // src/code-tool-edit.ts
3076
- var import_promises10 = require("fs/promises");
3162
+ var import_promises12 = require("fs/promises");
3077
3163
 
3078
3164
  // src/code-tool-graph.ts
3079
- var import_promises9 = require("fs/promises");
3080
- var import_node_path10 = require("path");
3165
+ var import_promises11 = require("fs/promises");
3166
+ var import_node_path12 = require("path");
3081
3167
  var import_graph = require("@odla-ai/graph");
3082
3168
  var import_code4 = require("@odla-ai/graph/code");
3083
3169
  var cache = /* @__PURE__ */ new Map();
3084
3170
  function workspaceGraphs(workspaceDir, paths2) {
3085
3171
  const existing = cache.get(workspaceDir);
3086
3172
  if (existing) return existing;
3087
- const read2 = (path) => (0, import_promises9.readFile)((0, import_node_path10.join)(workspaceDir, path), "utf8");
3173
+ const read2 = (path) => (0, import_promises11.readFile)((0, import_node_path12.join)(workspaceDir, path), "utf8");
3088
3174
  const built = (async () => ({
3089
3175
  // No knownTables: a staged workspace may not carry migrations, and a filter
3090
3176
  // that silently drops every table is worse than an unfiltered one. Callers
@@ -3160,7 +3246,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
3160
3246
  }
3161
3247
  if (oldText.includes("\0") || newText.includes("\0")) throw new TypeError("edit text contains NUL bytes; use plain text");
3162
3248
  const target = resolveCodePath(workspaceDir, path);
3163
- const current = await (0, import_promises10.readFile)(target, "utf8");
3249
+ const current = await (0, import_promises12.readFile)(target, "utf8");
3164
3250
  const found = occurrences(current, oldText);
3165
3251
  if (found === 0) {
3166
3252
  throw new TypeError(`oldText was not found in "${path}"; sandbox.read the current lines and copy them exactly, including indentation and blank lines`);
@@ -3169,7 +3255,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
3169
3255
  throw new TypeError(`oldText occurs ${found} times in "${path}"; include more of the surrounding lines so it matches exactly once`);
3170
3256
  }
3171
3257
  const at = current.indexOf(oldText);
3172
- await (0, import_promises10.writeFile)(target, `${current.slice(0, at)}${newText}${current.slice(at + oldText.length)}`, "utf8");
3258
+ await (0, import_promises12.writeFile)(target, `${current.slice(0, at)}${newText}${current.slice(at + oldText.length)}`, "utf8");
3173
3259
  return { deletions: oldText.split("\n").length, additions: newText.split("\n").length };
3174
3260
  }
3175
3261
  async function edit(context, request, options, policy, registry) {
@@ -3194,8 +3280,8 @@ async function edit(context, request, options, policy, registry) {
3194
3280
  }
3195
3281
 
3196
3282
  // src/code-tool-write.ts
3197
- var import_promises11 = require("fs/promises");
3198
- var import_node_path11 = require("path");
3283
+ var import_promises13 = require("fs/promises");
3284
+ var import_node_path13 = require("path");
3199
3285
  var MAX_WRITE_BYTES = 256 * 1024;
3200
3286
  var lines = (text2) => text2 === "" ? [] : text2.replace(/\n$/, "").split("\n");
3201
3287
  function writeAsDiff(path, current, content) {
@@ -3220,8 +3306,8 @@ async function writeCodeFile(workspaceDir, path, content, current) {
3220
3306
  }
3221
3307
  if (content.includes("\0")) throw new TypeError("content contains NUL bytes; use plain text");
3222
3308
  const target = resolveCodePath(workspaceDir, path);
3223
- await (0, import_promises11.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
3224
- await (0, import_promises11.writeFile)(target, content, "utf8");
3309
+ await (0, import_promises13.mkdir)((0, import_node_path13.dirname)(target), { recursive: true });
3310
+ await (0, import_promises13.writeFile)(target, content, "utf8");
3225
3311
  return { created: current === null, deletions: current === null ? 0 : lines(current).length, additions: lines(content).length };
3226
3312
  }
3227
3313
  async function write(context, request, options, policy, registry) {
@@ -3232,7 +3318,7 @@ async function write(context, request, options, policy, registry) {
3232
3318
  throw new TypeError("write targets a read-only reference source");
3233
3319
  }
3234
3320
  const exists = (await registry.files(context.workspaceDir)).includes(path);
3235
- const current = exists ? await (0, import_promises11.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
3321
+ const current = exists ? await (0, import_promises13.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
3236
3322
  const patch2 = writeAsDiff(path, current, content);
3237
3323
  const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
3238
3324
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
@@ -3248,12 +3334,12 @@ async function write(context, request, options, policy, registry) {
3248
3334
  }
3249
3335
 
3250
3336
  // src/code-tool-reads.ts
3251
- var import_promises13 = require("fs/promises");
3337
+ var import_promises15 = require("fs/promises");
3252
3338
 
3253
3339
  // src/code-tool-discovery.ts
3254
- var import_node_child_process5 = require("child_process");
3255
- var import_promises12 = require("fs/promises");
3256
- var import_node_path12 = require("path");
3340
+ var import_node_child_process6 = require("child_process");
3341
+ var import_promises14 = require("fs/promises");
3342
+ var import_node_path14 = require("path");
3257
3343
  var DEFAULT_MAX_FILES = 2e4;
3258
3344
  var DEFAULT_MAX_RESULTS = 100;
3259
3345
  var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
@@ -3278,13 +3364,13 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
3278
3364
  async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
3279
3365
  const paths2 = [];
3280
3366
  const walk = async (directory) => {
3281
- for (const entry of await (0, import_promises12.readdir)(directory, { withFileTypes: true })) {
3367
+ for (const entry of await (0, import_promises14.readdir)(directory, { withFileTypes: true })) {
3282
3368
  if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
3283
3369
  if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
3284
- const target = (0, import_node_path12.resolve)(directory, entry.name);
3370
+ const target = (0, import_node_path14.resolve)(directory, entry.name);
3285
3371
  if (entry.isDirectory()) await walk(target);
3286
3372
  else if (entry.isFile()) {
3287
- const path = (0, import_node_path12.relative)(root, target).split("\\").join("/");
3373
+ const path = (0, import_node_path14.relative)(root, target).split("\\").join("/");
3288
3374
  try {
3289
3375
  validateRelativePath(path);
3290
3376
  } catch {
@@ -3295,7 +3381,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
3295
3381
  }
3296
3382
  }
3297
3383
  };
3298
- await walk((0, import_node_path12.resolve)(root));
3384
+ await walk((0, import_node_path14.resolve)(root));
3299
3385
  return paths2.sort();
3300
3386
  }
3301
3387
  function listWorkspace(paths2, options = {}) {
@@ -3357,7 +3443,7 @@ function nativeSearchBatch(root, paths2, options, remaining) {
3357
3443
  options.query,
3358
3444
  ...paths2
3359
3445
  ];
3360
- const child = (0, import_node_child_process5.spawn)("rg", args, {
3446
+ const child = (0, import_node_child_process6.spawn)("rg", args, {
3361
3447
  cwd: root,
3362
3448
  stdio: ["ignore", "pipe", "ignore"],
3363
3449
  ...options.signal ? { signal: options.signal } : {}
@@ -3409,7 +3495,7 @@ async function fallbackSearch(root, scoped, options) {
3409
3495
  if (matches.length >= options.maxResults) break;
3410
3496
  let source;
3411
3497
  try {
3412
- source = await (0, import_promises12.readFile)((0, import_node_path12.resolve)(root, path));
3498
+ source = await (0, import_promises14.readFile)((0, import_node_path14.resolve)(root, path));
3413
3499
  } catch {
3414
3500
  continue;
3415
3501
  }
@@ -3448,11 +3534,11 @@ async function read(context, request, options, policy, registry) {
3448
3534
  const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
3449
3535
  if (!allowed) return response(request, false, "tool denied by CaMeL policy");
3450
3536
  const target = resolveCodePath(context.workspaceDir, path);
3451
- const info = await (0, import_promises13.stat)(target);
3537
+ const info = await (0, import_promises15.stat)(target);
3452
3538
  if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
3453
3539
  throw new TypeError("file is not a bounded regular source file");
3454
3540
  }
3455
- const source = await (0, import_promises13.readFile)(target);
3541
+ const source = await (0, import_promises15.readFile)(target);
3456
3542
  if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
3457
3543
  const lines2 = source.toString("utf8").split("\n");
3458
3544
  const content = lines2.slice(startLine - 1, endLine).join("\n");
@@ -3977,8 +4063,8 @@ var runtimeErrorMessage = (value) => value instanceof Error ? value.message : St
3977
4063
  function codeRuntimeAcknowledgementGate(signal) {
3978
4064
  let settle;
3979
4065
  let settled = false;
3980
- const ready = new Promise((resolve7) => {
3981
- settle = resolve7;
4066
+ const ready = new Promise((resolve8) => {
4067
+ settle = resolve8;
3982
4068
  });
3983
4069
  const release = (run) => {
3984
4070
  if (settled) return;
@@ -4044,8 +4130,8 @@ function observeCodeRuntimeSessionSkills(command, skills, emit) {
4044
4130
  }
4045
4131
 
4046
4132
  // src/code-repository-recipes.ts
4047
- var import_promises14 = require("fs/promises");
4048
- var import_node_path13 = require("path");
4133
+ var import_promises16 = require("fs/promises");
4134
+ var import_node_path15 = require("path");
4049
4135
  var REPOSITORY_RECIPES_FILE = "odla.recipes.json";
4050
4136
  var MAX_RECIPES = 16;
4051
4137
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -4106,7 +4192,7 @@ function parseRepositoryRecipes(text2, envelope) {
4106
4192
  async function readRepositoryRecipes(baselineDir, envelope) {
4107
4193
  let text2;
4108
4194
  try {
4109
- text2 = await (0, import_promises14.readFile)((0, import_node_path13.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
4195
+ text2 = await (0, import_promises16.readFile)((0, import_node_path15.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
4110
4196
  } catch (cause) {
4111
4197
  if (cause.code === "ENOENT") return null;
4112
4198
  throw cause;
@@ -4486,7 +4572,7 @@ function parse(argv) {
4486
4572
  };
4487
4573
  }
4488
4574
  async function readPolicy(path) {
4489
- const value = JSON.parse(await (0, import_promises15.readFile)(path, "utf8"));
4575
+ const value = JSON.parse(await (0, import_promises17.readFile)(path, "utf8"));
4490
4576
  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");
4491
4577
  const recipes = value.recipes;
4492
4578
  for (const recipe2 of recipes) assertCodeBuildRecipe(recipe2);