@odla-ai/harness 0.11.8 → 0.11.10

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.
@@ -1075,8 +1075,9 @@ async function verifyContainerEngineBoundary(engine, options = {}) {
1075
1075
  // src/recipe-container.ts
1076
1076
  var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
1077
1077
  var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
1078
- function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`) {
1078
+ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`, dependencies = null) {
1079
1079
  assertCodeBuildRecipe(recipe2);
1080
+ const mounts = dependencies ? [dependencyMount(engine, dependencies)] : [];
1080
1081
  if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
1081
1082
  const uid = typeof import_node_process2.getuid === "function" ? (0, import_node_process2.getuid)() : 1e3;
1082
1083
  const gid = typeof import_node_process2.getgid === "function" ? (0, import_node_process2.getgid)() : 1e3;
@@ -1099,6 +1100,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
1099
1100
  `--user=${uid}:${gid}`,
1100
1101
  "--tmpfs=/tmp",
1101
1102
  `--mount=type=bind,source=${workspaceDir},target=/workspace`,
1103
+ ...mounts,
1102
1104
  "--workdir=/workspace",
1103
1105
  "--env=CI=1",
1104
1106
  recipe2.image,
@@ -1120,18 +1122,28 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
1120
1122
  `--user=${uid}:${gid}`,
1121
1123
  `--tmpfs=/tmp:rw,noexec,nosuid,nodev,size=${limits.tmpfs}`,
1122
1124
  `--mount=type=bind,src=${workspaceDir},dst=/workspace`,
1125
+ ...mounts,
1123
1126
  "--workdir=/workspace",
1124
1127
  "--env=CI=1",
1125
1128
  recipe2.image,
1126
1129
  ...recipe2.command
1127
1130
  ];
1128
1131
  }
1129
- function createContainerRecipeExecutor(engine) {
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`;
1140
+ }
1141
+ function createContainerRecipeExecutor(engine, options = {}) {
1130
1142
  return {
1131
1143
  async run(input) {
1132
1144
  await verifyContainerEngineBoundary(engine);
1133
1145
  const name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`;
1134
- const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name);
1146
+ const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, options.dependencies ?? null);
1135
1147
  return execute(engine, args, name, input.recipe, input.signal);
1136
1148
  }
1137
1149
  };
@@ -1152,6 +1164,9 @@ function parseMemory(value) {
1152
1164
  return Number(match[1]) * scale;
1153
1165
  }
1154
1166
  function execute(engine, args, name, recipe2, signal) {
1167
+ return runContainerCommand(engine, args, name, { timeoutMs: recipe2.timeoutMs, maxOutputBytes: recipe2.maxOutputBytes }, signal);
1168
+ }
1169
+ function runContainerCommand(engine, args, name, recipe2, signal) {
1155
1170
  return new Promise((accept, reject) => {
1156
1171
  const started = Date.now();
1157
1172
  const child = (0, import_node_child_process4.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
@@ -4148,7 +4163,7 @@ var TheseusRuntimeEngine = class {
4148
4163
  this.#checkpoints = new CodeRuntimeCheckpointManager({
4149
4164
  control: options.control,
4150
4165
  recipes: options.recipes,
4151
- recipeExecutor: options.recipeExecutor ?? createContainerRecipeExecutor(options.engine),
4166
+ recipeExecutor: options.recipeExecutor ?? createContainerRecipeExecutor(options.engine, { dependencies: options.recipeDependencies ?? null }),
4152
4167
  fallbackPolicyDigest: this.#buildPolicyDigest,
4153
4168
  event: (command, event, refs) => this.#event(command, event, refs)
4154
4169
  });
@@ -4256,7 +4271,7 @@ var TheseusRuntimeEngine = class {
4256
4271
  active.done = startGoalPursuit({
4257
4272
  spec,
4258
4273
  recipes: active.recipes,
4259
- recipeExecutor: this.options.recipeExecutor ?? createContainerRecipeExecutor(this.options.engine),
4274
+ recipeExecutor: this.options.recipeExecutor ?? createContainerRecipeExecutor(this.options.engine, { dependencies: this.options.recipeDependencies ?? null }),
4260
4275
  workspace: active.workspace,
4261
4276
  baseCommitSha: active.baseCommitSha,
4262
4277
  trustedBaseDigest: active.trustedBaseDigest,