@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.
- package/dist/{chunk-NUDKRYL4.js → chunk-4IGSN53G.js} +22 -6
- package/dist/chunk-4IGSN53G.js.map +1 -0
- package/dist/code-runtime-cli.cjs +20 -5
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +125 -9
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +65 -30
- package/dist/node.d.ts +65 -30
- package/dist/node.js +97 -1
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-NUDKRYL4.js.map +0 -1
package/dist/code-runtime-cli.js
CHANGED
package/dist/node.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(node_exports, {
|
|
|
25
25
|
CodeRuntimeCheckpointManager: () => CodeRuntimeCheckpointManager,
|
|
26
26
|
CodeRuntimeControlError: () => CodeRuntimeControlError,
|
|
27
27
|
CodeRuntimeReconciler: () => CodeRuntimeReconciler,
|
|
28
|
+
DEPENDENCY_INSTALL_TIMEOUT_MS: () => DEPENDENCY_INSTALL_TIMEOUT_MS,
|
|
28
29
|
DecompositionError: () => DecompositionError,
|
|
29
30
|
MAX_MEMORY_BODY: () => MAX_MEMORY_BODY,
|
|
30
31
|
MEASURED_PREMIUM: () => MEASURED_PREMIUM,
|
|
@@ -51,6 +52,8 @@ __export(node_exports, {
|
|
|
51
52
|
createCodeToolBroker: () => createCodeToolBroker,
|
|
52
53
|
createCodeWorkspaceCheckpoint: () => createCodeWorkspaceCheckpoint,
|
|
53
54
|
createContainerRecipeExecutor: () => createContainerRecipeExecutor,
|
|
55
|
+
dependencyCacheKey: () => dependencyCacheKey,
|
|
56
|
+
dependencyInstallArgs: () => dependencyInstallArgs,
|
|
54
57
|
describeCodeRecipes: () => describeCodeRecipes,
|
|
55
58
|
describeGateFailure: () => describeGateFailure,
|
|
56
59
|
describePatchFailure: () => describePatchFailure,
|
|
@@ -70,6 +73,7 @@ __export(node_exports, {
|
|
|
70
73
|
parseRepositoryRecipes: () => parseRepositoryRecipes,
|
|
71
74
|
patchPaths: () => patchPaths,
|
|
72
75
|
planReachCollisions: () => planReachCollisions,
|
|
76
|
+
prepareContainerDependencies: () => prepareContainerDependencies,
|
|
73
77
|
prepareRuntimeCheckpoint: () => prepareRuntimeCheckpoint,
|
|
74
78
|
racedAttempt: () => racedAttempt,
|
|
75
79
|
readOnlyNotice: () => readOnlyNotice,
|
|
@@ -85,6 +89,7 @@ __export(node_exports, {
|
|
|
85
89
|
runCodeAgentAttempt: () => runCodeAgentAttempt,
|
|
86
90
|
runCodeRuntimeHeartbeatLoop: () => runCodeRuntimeHeartbeatLoop,
|
|
87
91
|
runContainerAttempt: () => runContainerAttempt,
|
|
92
|
+
runContainerCommand: () => runContainerCommand,
|
|
88
93
|
runGoal: () => runGoal,
|
|
89
94
|
runHarnessRunner: () => runHarnessRunner,
|
|
90
95
|
runLeasedAttempt: () => runLeasedAttempt,
|
|
@@ -94,6 +99,7 @@ __export(node_exports, {
|
|
|
94
99
|
selectWinner: () => selectWinner,
|
|
95
100
|
sessionRecipesFor: () => sessionRecipesFor,
|
|
96
101
|
sessionSkillsFor: () => sessionSkillsFor,
|
|
102
|
+
stageManifests: () => stageManifests,
|
|
97
103
|
stageWorkspace: () => stageWorkspace,
|
|
98
104
|
stageWorkspacePair: () => stageWorkspacePair,
|
|
99
105
|
straySubGoalFiles: () => straySubGoalFiles,
|
|
@@ -1704,8 +1710,9 @@ var import_node_process2 = require("process");
|
|
|
1704
1710
|
var import_node_crypto2 = require("crypto");
|
|
1705
1711
|
var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
1706
1712
|
var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
|
|
1707
|
-
function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}
|
|
1713
|
+
function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}`, dependencies = null) {
|
|
1708
1714
|
assertCodeBuildRecipe(recipe2);
|
|
1715
|
+
const mounts = dependencies ? [dependencyMount(engine, dependencies)] : [];
|
|
1709
1716
|
if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
|
|
1710
1717
|
const uid = typeof import_node_process2.getuid === "function" ? (0, import_node_process2.getuid)() : 1e3;
|
|
1711
1718
|
const gid = typeof import_node_process2.getgid === "function" ? (0, import_node_process2.getgid)() : 1e3;
|
|
@@ -1728,6 +1735,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1728
1735
|
`--user=${uid}:${gid}`,
|
|
1729
1736
|
"--tmpfs=/tmp",
|
|
1730
1737
|
`--mount=type=bind,source=${workspaceDir},target=/workspace`,
|
|
1738
|
+
...mounts,
|
|
1731
1739
|
"--workdir=/workspace",
|
|
1732
1740
|
"--env=CI=1",
|
|
1733
1741
|
recipe2.image,
|
|
@@ -1749,18 +1757,28 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1749
1757
|
`--user=${uid}:${gid}`,
|
|
1750
1758
|
`--tmpfs=/tmp:rw,noexec,nosuid,nodev,size=${limits.tmpfs}`,
|
|
1751
1759
|
`--mount=type=bind,src=${workspaceDir},dst=/workspace`,
|
|
1760
|
+
...mounts,
|
|
1752
1761
|
"--workdir=/workspace",
|
|
1753
1762
|
"--env=CI=1",
|
|
1754
1763
|
recipe2.image,
|
|
1755
1764
|
...recipe2.command
|
|
1756
1765
|
];
|
|
1757
1766
|
}
|
|
1758
|
-
|
|
1767
|
+
var LENDABLE = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
1768
|
+
function dependencyMount(engine, dependencies) {
|
|
1769
|
+
const mountAs = dependencies.mountAs ?? "node_modules";
|
|
1770
|
+
if (!dependencies.source.startsWith("/") || /[,\r\n]/.test(dependencies.source)) {
|
|
1771
|
+
throw new TypeError("recipe dependency source must be an absolute path without mount separators");
|
|
1772
|
+
}
|
|
1773
|
+
if (!LENDABLE.has(mountAs)) throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
|
|
1774
|
+
return engine === "container" ? `--mount=type=bind,source=${dependencies.source},target=/workspace/${mountAs},readonly` : `--mount=type=bind,src=${dependencies.source},dst=/workspace/${mountAs},readonly`;
|
|
1775
|
+
}
|
|
1776
|
+
function createContainerRecipeExecutor(engine, options = {}) {
|
|
1759
1777
|
return {
|
|
1760
1778
|
async run(input) {
|
|
1761
1779
|
await verifyContainerEngineBoundary(engine);
|
|
1762
1780
|
const name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}`;
|
|
1763
|
-
const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name);
|
|
1781
|
+
const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, options.dependencies ?? null);
|
|
1764
1782
|
return execute(engine, args, name, input.recipe, input.signal);
|
|
1765
1783
|
}
|
|
1766
1784
|
};
|
|
@@ -1781,6 +1799,9 @@ function parseMemory(value) {
|
|
|
1781
1799
|
return Number(match[1]) * scale;
|
|
1782
1800
|
}
|
|
1783
1801
|
function execute(engine, args, name, recipe2, signal) {
|
|
1802
|
+
return runContainerCommand(engine, args, name, { timeoutMs: recipe2.timeoutMs, maxOutputBytes: recipe2.maxOutputBytes }, signal);
|
|
1803
|
+
}
|
|
1804
|
+
function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
1784
1805
|
return new Promise((accept, reject) => {
|
|
1785
1806
|
const started = Date.now();
|
|
1786
1807
|
const child = (0, import_node_child_process5.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -2881,7 +2902,7 @@ function codeSkill(opts) {
|
|
|
2881
2902
|
},
|
|
2882
2903
|
handler: (input, ctx) => call("sandbox.edit", input, ctx.signal)
|
|
2883
2904
|
};
|
|
2884
|
-
const
|
|
2905
|
+
const writeFile7 = {
|
|
2885
2906
|
name: "odla_write_file",
|
|
2886
2907
|
description: "Create a file or replace its entire contents with exactly the text given. Use this for a NEW file (a test, a module, a fixture) and for a rewrite where most lines change; use odla_edit_file for a small change inside an existing file. Paths are repository-relative; parent directories are created. The content must be the complete file.",
|
|
2887
2908
|
inputSchema: {
|
|
@@ -2983,7 +3004,7 @@ function codeSkill(opts) {
|
|
|
2983
3004
|
];
|
|
2984
3005
|
const runsRecipes = opts.recipes ?? !opts.readOnly;
|
|
2985
3006
|
const effects = [...opts.readOnly ? [] : [applyPatch], ...runsRecipes ? [runRecipe] : []];
|
|
2986
|
-
const tools = opts.surface === "v3" ? [...orientation, searchFiles, read2, ...opts.readOnly ? [] : [editFile,
|
|
3007
|
+
const tools = opts.surface === "v3" ? [...orientation, searchFiles, read2, ...opts.readOnly ? [] : [editFile, writeFile7], ...effects] : opts.surface === "v2" ? [listFiles, searchFiles, read2, ...effects] : [read2, ...effects];
|
|
2987
3008
|
return { name: "code", tools };
|
|
2988
3009
|
}
|
|
2989
3010
|
|
|
@@ -3814,8 +3835,8 @@ async function write(context, request, options, policy, registry) {
|
|
|
3814
3835
|
if (options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`))) {
|
|
3815
3836
|
throw new TypeError("write targets a read-only reference source");
|
|
3816
3837
|
}
|
|
3817
|
-
const
|
|
3818
|
-
const current =
|
|
3838
|
+
const exists2 = (await registry.files(context.workspaceDir)).includes(path);
|
|
3839
|
+
const current = exists2 ? await (0, import_promises13.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
|
|
3819
3840
|
const patch2 = writeAsDiff(path, current, content);
|
|
3820
3841
|
const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
|
|
3821
3842
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
@@ -4769,7 +4790,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4769
4790
|
this.#checkpoints = new CodeRuntimeCheckpointManager({
|
|
4770
4791
|
control: options.control,
|
|
4771
4792
|
recipes: options.recipes,
|
|
4772
|
-
recipeExecutor: options.recipeExecutor ?? createContainerRecipeExecutor(options.engine),
|
|
4793
|
+
recipeExecutor: options.recipeExecutor ?? createContainerRecipeExecutor(options.engine, { dependencies: options.recipeDependencies ?? null }),
|
|
4773
4794
|
fallbackPolicyDigest: this.#buildPolicyDigest,
|
|
4774
4795
|
event: (command, event, refs) => this.#event(command, event, refs)
|
|
4775
4796
|
});
|
|
@@ -4877,7 +4898,7 @@ var TheseusRuntimeEngine = class {
|
|
|
4877
4898
|
active.done = startGoalPursuit({
|
|
4878
4899
|
spec,
|
|
4879
4900
|
recipes: active.recipes,
|
|
4880
|
-
recipeExecutor: this.options.recipeExecutor ?? createContainerRecipeExecutor(this.options.engine),
|
|
4901
|
+
recipeExecutor: this.options.recipeExecutor ?? createContainerRecipeExecutor(this.options.engine, { dependencies: this.options.recipeDependencies ?? null }),
|
|
4881
4902
|
workspace: active.workspace,
|
|
4882
4903
|
baseCommitSha: active.baseCommitSha,
|
|
4883
4904
|
trustedBaseDigest: active.trustedBaseDigest,
|
|
@@ -5347,6 +5368,95 @@ async function installedDependencies(repoRoot) {
|
|
|
5347
5368
|
const info = await (0, import_promises17.lstat)(source).catch(() => null);
|
|
5348
5369
|
return info?.isDirectory() ? { source } : null;
|
|
5349
5370
|
}
|
|
5371
|
+
|
|
5372
|
+
// src/code-recipe-dependency-cache.ts
|
|
5373
|
+
var import_node_crypto5 = require("crypto");
|
|
5374
|
+
var import_promises18 = require("fs/promises");
|
|
5375
|
+
var import_node_process3 = require("process");
|
|
5376
|
+
var import_node_path16 = require("path");
|
|
5377
|
+
var DEPENDENCY_INSTALL_TIMEOUT_MS = 20 * 6e4;
|
|
5378
|
+
var OUTPUT_CAP = 4 * 1024 * 1024;
|
|
5379
|
+
var exists = async (path) => Boolean(await (0, import_promises18.stat)(path).catch(() => null));
|
|
5380
|
+
async function dependencyCacheKey(repoRoot, image, arch = process.arch) {
|
|
5381
|
+
const lock = await (0, import_promises18.readFile)((0, import_node_path16.join)(repoRoot, "package-lock.json")).catch(() => null);
|
|
5382
|
+
if (!lock) return null;
|
|
5383
|
+
return (0, import_node_crypto5.createHash)("sha256").update(lock).update("\n").update(image).update("\n").update(arch).digest("hex").slice(0, 32);
|
|
5384
|
+
}
|
|
5385
|
+
async function stageManifests(repoRoot, stageDir) {
|
|
5386
|
+
await (0, import_promises18.mkdir)(stageDir, { recursive: true });
|
|
5387
|
+
const staged = [];
|
|
5388
|
+
for (const name of ["package.json", "package-lock.json", ".npmrc"]) {
|
|
5389
|
+
if (!await exists((0, import_node_path16.join)(repoRoot, name))) continue;
|
|
5390
|
+
await (0, import_promises18.copyFile)((0, import_node_path16.join)(repoRoot, name), (0, import_node_path16.join)(stageDir, name));
|
|
5391
|
+
staged.push(name);
|
|
5392
|
+
}
|
|
5393
|
+
const root = JSON.parse(await (0, import_promises18.readFile)((0, import_node_path16.join)(repoRoot, "package.json"), "utf8"));
|
|
5394
|
+
const globs = Array.isArray(root.workspaces) ? root.workspaces : root.workspaces?.packages ?? [];
|
|
5395
|
+
for (const glob of globs) {
|
|
5396
|
+
const dirs = glob.endsWith("/*") ? (await (0, import_promises18.readdir)((0, import_node_path16.join)(repoRoot, glob.slice(0, -2)), { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map((entry) => `${glob.slice(0, -2)}/${entry.name}`) : [glob];
|
|
5397
|
+
for (const dir of dirs) {
|
|
5398
|
+
const manifest = (0, import_node_path16.join)(repoRoot, dir, "package.json");
|
|
5399
|
+
if (!await exists(manifest)) continue;
|
|
5400
|
+
await (0, import_promises18.mkdir)((0, import_node_path16.join)(stageDir, dir), { recursive: true });
|
|
5401
|
+
await (0, import_promises18.copyFile)(manifest, (0, import_node_path16.join)(stageDir, dir, "package.json"));
|
|
5402
|
+
staged.push(`${dir}/package.json`);
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
return staged;
|
|
5406
|
+
}
|
|
5407
|
+
function dependencyInstallArgs(engine, image, stageDir, name) {
|
|
5408
|
+
if (/[,\r\n]/.test(stageDir)) throw new TypeError("stage path contains unsupported mount characters");
|
|
5409
|
+
const uid = typeof import_node_process3.getuid === "function" ? (0, import_node_process3.getuid)() : 1e3;
|
|
5410
|
+
const gid = typeof import_node_process3.getgid === "function" ? (0, import_node_process3.getgid)() : 1e3;
|
|
5411
|
+
const mount = engine === "container" ? `--mount=type=bind,source=${stageDir},target=/workspace` : `--mount=type=bind,src=${stageDir},dst=/workspace`;
|
|
5412
|
+
const npm = ["npm", "ci", "--no-audit", "--no-fund", "--ignore-scripts"];
|
|
5413
|
+
return [
|
|
5414
|
+
"run",
|
|
5415
|
+
"--rm",
|
|
5416
|
+
`--name=${name}`,
|
|
5417
|
+
"--cap-drop=ALL",
|
|
5418
|
+
"--memory=2g",
|
|
5419
|
+
"--cpus=2",
|
|
5420
|
+
`--user=${uid}:${gid}`,
|
|
5421
|
+
"--tmpfs=/tmp",
|
|
5422
|
+
mount,
|
|
5423
|
+
"--workdir=/workspace",
|
|
5424
|
+
"--env=CI=1",
|
|
5425
|
+
"--env=npm_config_cache=/tmp/npm-cache",
|
|
5426
|
+
"--env=HOME=/tmp",
|
|
5427
|
+
image,
|
|
5428
|
+
...npm
|
|
5429
|
+
];
|
|
5430
|
+
}
|
|
5431
|
+
async function prepareContainerDependencies(input) {
|
|
5432
|
+
const key = await dependencyCacheKey(input.repoRoot, input.image);
|
|
5433
|
+
if (!key) return null;
|
|
5434
|
+
const dir = (0, import_node_path16.join)(input.cacheRoot, key);
|
|
5435
|
+
const tree = (0, import_node_path16.join)(dir, "node_modules");
|
|
5436
|
+
const stage = (0, import_node_path16.join)(dir, "stage");
|
|
5437
|
+
if (await exists(tree)) return { source: tree };
|
|
5438
|
+
const run = input.run ?? runContainerCommand;
|
|
5439
|
+
await (0, import_promises18.rm)(stage, { recursive: true, force: true });
|
|
5440
|
+
const staged = await stageManifests(input.repoRoot, stage);
|
|
5441
|
+
input.log?.(`installing ${key.slice(0, 12)}: ${staged.length} manifests, npm ci in ${input.image}`);
|
|
5442
|
+
const name = `odla-deps-${key.slice(0, 12)}`;
|
|
5443
|
+
const result = await run(
|
|
5444
|
+
input.engine,
|
|
5445
|
+
dependencyInstallArgs(input.engine, input.image, stage, name),
|
|
5446
|
+
name,
|
|
5447
|
+
{ timeoutMs: DEPENDENCY_INSTALL_TIMEOUT_MS, maxOutputBytes: OUTPUT_CAP }
|
|
5448
|
+
);
|
|
5449
|
+
if (result.exitCode !== 0 || result.timedOut || !await exists((0, import_node_path16.join)(stage, "node_modules"))) {
|
|
5450
|
+
input.log?.(`install failed (exit ${result.exitCode}${result.timedOut ? ", timed out" : ""}): ${result.stderr.trim().split("\n").slice(-3).join(" | ").slice(0, 400)}`);
|
|
5451
|
+
await (0, import_promises18.rm)(stage, { recursive: true, force: true });
|
|
5452
|
+
return null;
|
|
5453
|
+
}
|
|
5454
|
+
await (0, import_promises18.rename)((0, import_node_path16.join)(stage, "node_modules"), tree);
|
|
5455
|
+
await (0, import_promises18.rm)(stage, { recursive: true, force: true });
|
|
5456
|
+
await (0, import_promises18.writeFile)((0, import_node_path16.join)(dir, "manifest.json"), JSON.stringify({ image: input.image, key, createdAt: Date.now() }, null, 2));
|
|
5457
|
+
input.log?.(`installed ${key.slice(0, 12)} in ${Math.round(result.durationMs / 1e3)}s`);
|
|
5458
|
+
return { source: tree };
|
|
5459
|
+
}
|
|
5350
5460
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5351
5461
|
0 && (module.exports = {
|
|
5352
5462
|
CODE_PATCH_PATH,
|
|
@@ -5354,6 +5464,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5354
5464
|
CodeRuntimeCheckpointManager,
|
|
5355
5465
|
CodeRuntimeControlError,
|
|
5356
5466
|
CodeRuntimeReconciler,
|
|
5467
|
+
DEPENDENCY_INSTALL_TIMEOUT_MS,
|
|
5357
5468
|
DecompositionError,
|
|
5358
5469
|
MAX_MEMORY_BODY,
|
|
5359
5470
|
MEASURED_PREMIUM,
|
|
@@ -5380,6 +5491,8 @@ async function installedDependencies(repoRoot) {
|
|
|
5380
5491
|
createCodeToolBroker,
|
|
5381
5492
|
createCodeWorkspaceCheckpoint,
|
|
5382
5493
|
createContainerRecipeExecutor,
|
|
5494
|
+
dependencyCacheKey,
|
|
5495
|
+
dependencyInstallArgs,
|
|
5383
5496
|
describeCodeRecipes,
|
|
5384
5497
|
describeGateFailure,
|
|
5385
5498
|
describePatchFailure,
|
|
@@ -5399,6 +5512,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5399
5512
|
parseRepositoryRecipes,
|
|
5400
5513
|
patchPaths,
|
|
5401
5514
|
planReachCollisions,
|
|
5515
|
+
prepareContainerDependencies,
|
|
5402
5516
|
prepareRuntimeCheckpoint,
|
|
5403
5517
|
racedAttempt,
|
|
5404
5518
|
readOnlyNotice,
|
|
@@ -5414,6 +5528,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5414
5528
|
runCodeAgentAttempt,
|
|
5415
5529
|
runCodeRuntimeHeartbeatLoop,
|
|
5416
5530
|
runContainerAttempt,
|
|
5531
|
+
runContainerCommand,
|
|
5417
5532
|
runGoal,
|
|
5418
5533
|
runHarnessRunner,
|
|
5419
5534
|
runLeasedAttempt,
|
|
@@ -5423,6 +5538,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5423
5538
|
selectWinner,
|
|
5424
5539
|
sessionRecipesFor,
|
|
5425
5540
|
sessionSkillsFor,
|
|
5541
|
+
stageManifests,
|
|
5426
5542
|
stageWorkspace,
|
|
5427
5543
|
stageWorkspacePair,
|
|
5428
5544
|
straySubGoalFiles,
|