@odla-ai/harness 0.11.9 → 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-X5KT7NHM.js → chunk-4IGSN53G.js} +5 -1
- package/dist/chunk-4IGSN53G.js.map +1 -0
- package/dist/code-runtime-cli.cjs +3 -0
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +1 -1
- package/dist/node.cjs +108 -4
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +31 -1
- package/dist/node.d.ts +31 -1
- package/dist/node.js +97 -1
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-X5KT7NHM.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,
|
|
@@ -1793,6 +1799,9 @@ function parseMemory(value) {
|
|
|
1793
1799
|
return Number(match[1]) * scale;
|
|
1794
1800
|
}
|
|
1795
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) {
|
|
1796
1805
|
return new Promise((accept, reject) => {
|
|
1797
1806
|
const started = Date.now();
|
|
1798
1807
|
const child = (0, import_node_child_process5.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -2893,7 +2902,7 @@ function codeSkill(opts) {
|
|
|
2893
2902
|
},
|
|
2894
2903
|
handler: (input, ctx) => call("sandbox.edit", input, ctx.signal)
|
|
2895
2904
|
};
|
|
2896
|
-
const
|
|
2905
|
+
const writeFile7 = {
|
|
2897
2906
|
name: "odla_write_file",
|
|
2898
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.",
|
|
2899
2908
|
inputSchema: {
|
|
@@ -2995,7 +3004,7 @@ function codeSkill(opts) {
|
|
|
2995
3004
|
];
|
|
2996
3005
|
const runsRecipes = opts.recipes ?? !opts.readOnly;
|
|
2997
3006
|
const effects = [...opts.readOnly ? [] : [applyPatch], ...runsRecipes ? [runRecipe] : []];
|
|
2998
|
-
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];
|
|
2999
3008
|
return { name: "code", tools };
|
|
3000
3009
|
}
|
|
3001
3010
|
|
|
@@ -3826,8 +3835,8 @@ async function write(context, request, options, policy, registry) {
|
|
|
3826
3835
|
if (options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`))) {
|
|
3827
3836
|
throw new TypeError("write targets a read-only reference source");
|
|
3828
3837
|
}
|
|
3829
|
-
const
|
|
3830
|
-
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;
|
|
3831
3840
|
const patch2 = writeAsDiff(path, current, content);
|
|
3832
3841
|
const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
|
|
3833
3842
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
@@ -5359,6 +5368,95 @@ async function installedDependencies(repoRoot) {
|
|
|
5359
5368
|
const info = await (0, import_promises17.lstat)(source).catch(() => null);
|
|
5360
5369
|
return info?.isDirectory() ? { source } : null;
|
|
5361
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
|
+
}
|
|
5362
5460
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5363
5461
|
0 && (module.exports = {
|
|
5364
5462
|
CODE_PATCH_PATH,
|
|
@@ -5366,6 +5464,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5366
5464
|
CodeRuntimeCheckpointManager,
|
|
5367
5465
|
CodeRuntimeControlError,
|
|
5368
5466
|
CodeRuntimeReconciler,
|
|
5467
|
+
DEPENDENCY_INSTALL_TIMEOUT_MS,
|
|
5369
5468
|
DecompositionError,
|
|
5370
5469
|
MAX_MEMORY_BODY,
|
|
5371
5470
|
MEASURED_PREMIUM,
|
|
@@ -5392,6 +5491,8 @@ async function installedDependencies(repoRoot) {
|
|
|
5392
5491
|
createCodeToolBroker,
|
|
5393
5492
|
createCodeWorkspaceCheckpoint,
|
|
5394
5493
|
createContainerRecipeExecutor,
|
|
5494
|
+
dependencyCacheKey,
|
|
5495
|
+
dependencyInstallArgs,
|
|
5395
5496
|
describeCodeRecipes,
|
|
5396
5497
|
describeGateFailure,
|
|
5397
5498
|
describePatchFailure,
|
|
@@ -5411,6 +5512,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5411
5512
|
parseRepositoryRecipes,
|
|
5412
5513
|
patchPaths,
|
|
5413
5514
|
planReachCollisions,
|
|
5515
|
+
prepareContainerDependencies,
|
|
5414
5516
|
prepareRuntimeCheckpoint,
|
|
5415
5517
|
racedAttempt,
|
|
5416
5518
|
readOnlyNotice,
|
|
@@ -5426,6 +5528,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5426
5528
|
runCodeAgentAttempt,
|
|
5427
5529
|
runCodeRuntimeHeartbeatLoop,
|
|
5428
5530
|
runContainerAttempt,
|
|
5531
|
+
runContainerCommand,
|
|
5429
5532
|
runGoal,
|
|
5430
5533
|
runHarnessRunner,
|
|
5431
5534
|
runLeasedAttempt,
|
|
@@ -5435,6 +5538,7 @@ async function installedDependencies(repoRoot) {
|
|
|
5435
5538
|
selectWinner,
|
|
5436
5539
|
sessionRecipesFor,
|
|
5437
5540
|
sessionSkillsFor,
|
|
5541
|
+
stageManifests,
|
|
5438
5542
|
stageWorkspace,
|
|
5439
5543
|
stageWorkspacePair,
|
|
5440
5544
|
straySubGoalFiles,
|