@odla-ai/harness 0.11.11 → 0.11.13
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-ZM6AITC2.js → chunk-3ON6UAOV.js} +3 -1
- package/dist/{chunk-4IGSN53G.js → chunk-B2PQAORN.js} +206 -77
- package/dist/chunk-B2PQAORN.js.map +1 -0
- package/dist/{chunk-GNNAQ2JX.js → chunk-EXSEQEZ6.js} +2 -2
- package/dist/{chunk-INL642J5.js → chunk-OXWLPB7P.js} +5 -2
- package/dist/{chunk-INL642J5.js.map → chunk-OXWLPB7P.js.map} +1 -1
- package/dist/cli.js +2 -2
- package/dist/code-runtime-cli.cjs +197 -104
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +3 -3
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/node.cjs +337 -170
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +66 -10
- package/dist/node.d.ts +66 -10
- package/dist/node.js +119 -77
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-4IGSN53G.js.map +0 -1
- /package/dist/{chunk-ZM6AITC2.js.map → chunk-3ON6UAOV.js.map} +0 -0
- /package/dist/{chunk-GNNAQ2JX.js.map → chunk-EXSEQEZ6.js.map} +0 -0
package/dist/node.cjs
CHANGED
|
@@ -40,11 +40,14 @@ __export(node_exports, {
|
|
|
40
40
|
applyPatchDialectToDiff: () => applyPatchDialectToDiff,
|
|
41
41
|
assertCodeBuildRecipe: () => assertCodeBuildRecipe,
|
|
42
42
|
assertDisjointPlan: () => assertDisjointPlan,
|
|
43
|
+
assertLentPath: () => assertLentPath,
|
|
43
44
|
assertPinnedImage: () => assertPinnedImage,
|
|
45
|
+
assertReservedMount: () => assertReservedMount,
|
|
44
46
|
attachCodeRuntimeReferences: () => attachCodeRuntimeReferences,
|
|
45
47
|
buildContainerRunArgs: () => buildContainerRunArgs,
|
|
46
48
|
buildRecipeContainerArgs: () => buildRecipeContainerArgs,
|
|
47
49
|
chooseStrategy: () => chooseStrategy,
|
|
50
|
+
cloneTree: () => cloneTree,
|
|
48
51
|
codeSkill: () => codeSkill,
|
|
49
52
|
createCodeRuntimeControlClient: () => createCodeRuntimeControlClient,
|
|
50
53
|
createCodeRuntimeInference: () => createCodeRuntimeInference,
|
|
@@ -61,17 +64,23 @@ __export(node_exports, {
|
|
|
61
64
|
feedbackIsActionable: () => feedbackIsActionable,
|
|
62
65
|
hasContextFreeHunk: () => hasContextFreeHunk,
|
|
63
66
|
hazardFromAttempt: () => hazardFromAttempt,
|
|
67
|
+
hostBuildProducts: () => hostBuildProducts,
|
|
64
68
|
installedDependencies: () => installedDependencies,
|
|
65
69
|
integrateSubGoals: () => integrateSubGoals,
|
|
66
70
|
isCheckpointEffectCompleted: () => isCheckpointEffectCompleted,
|
|
71
|
+
lendBuildProducts: () => lendBuildProducts,
|
|
72
|
+
lendDependencies: () => lendDependencies,
|
|
73
|
+
lentDirectories: () => lentDirectories,
|
|
67
74
|
materializeCodeRuntimeArchive: () => materializeCodeRuntimeArchive,
|
|
68
75
|
materializeCodeRuntimeSource: () => materializeCodeRuntimeSource,
|
|
69
76
|
materializeCommandWorkspace: () => materializeCommandWorkspace,
|
|
70
77
|
materializeGitTree: () => materializeGitTree,
|
|
78
|
+
npmFailureSummary: () => npmFailureSummary,
|
|
71
79
|
outcomeCloses: () => outcomeCloses,
|
|
72
80
|
outcomeMemory: () => outcomeMemory,
|
|
73
81
|
parseRepositoryRecipes: () => parseRepositoryRecipes,
|
|
74
82
|
patchPaths: () => patchPaths,
|
|
83
|
+
pinnedNpmVersion: () => pinnedNpmVersion,
|
|
75
84
|
planReachCollisions: () => planReachCollisions,
|
|
76
85
|
prepareContainerDependencies: () => prepareContainerDependencies,
|
|
77
86
|
prepareRuntimeCheckpoint: () => prepareRuntimeCheckpoint,
|
|
@@ -79,6 +88,7 @@ __export(node_exports, {
|
|
|
79
88
|
readOnlyNotice: () => readOnlyNotice,
|
|
80
89
|
readRepositoryRecipes: () => readRepositoryRecipes,
|
|
81
90
|
recallAbout: () => recallAbout,
|
|
91
|
+
recipeOutput: () => recipeOutput,
|
|
82
92
|
registeredFiles: () => registeredFiles,
|
|
83
93
|
renderMemories: () => renderMemories,
|
|
84
94
|
renderOutcome: () => renderOutcome,
|
|
@@ -110,7 +120,8 @@ __export(node_exports, {
|
|
|
110
120
|
verifyCodeCandidate: () => verifyCodeCandidate,
|
|
111
121
|
verifyContainerEngineBoundary: () => verifyContainerEngineBoundary,
|
|
112
122
|
withProofRecipes: () => withProofRecipes,
|
|
113
|
-
withRecipeDependencies: () => withRecipeDependencies
|
|
123
|
+
withRecipeDependencies: () => withRecipeDependencies,
|
|
124
|
+
workspaceDirs: () => workspaceDirs
|
|
114
125
|
});
|
|
115
126
|
module.exports = __toCommonJS(node_exports);
|
|
116
127
|
|
|
@@ -250,7 +261,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
|
|
|
250
261
|
throw new TypeError("no supported container engine found");
|
|
251
262
|
}
|
|
252
263
|
function inspectRootlessPodman() {
|
|
253
|
-
return new Promise((
|
|
264
|
+
return new Promise((resolve9, reject) => {
|
|
254
265
|
(0, import_node_child_process.execFile)(
|
|
255
266
|
"podman",
|
|
256
267
|
["info", "--format", "{{.Host.Security.Rootless}}"],
|
|
@@ -260,7 +271,7 @@ function inspectRootlessPodman() {
|
|
|
260
271
|
reject(new TypeError("could not verify that the active Podman service is rootless"));
|
|
261
272
|
return;
|
|
262
273
|
}
|
|
263
|
-
|
|
274
|
+
resolve9(stdout.trim() === "true");
|
|
264
275
|
}
|
|
265
276
|
);
|
|
266
277
|
});
|
|
@@ -1361,7 +1372,7 @@ async function withOverloadRetry(call, wait2, onRetry = () => void 0, retryable
|
|
|
1361
1372
|
}
|
|
1362
1373
|
|
|
1363
1374
|
// src/code-runtime-reconciler.ts
|
|
1364
|
-
var sleep = (ms) => new Promise((
|
|
1375
|
+
var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
1365
1376
|
var POST_ACK_RETRY_TICKS = 40;
|
|
1366
1377
|
var CodeRuntimeReconciler = class {
|
|
1367
1378
|
constructor(control, engine, onDiagnostic, options = {}) {
|
|
@@ -1463,12 +1474,12 @@ function retryableControlFailure(value) {
|
|
|
1463
1474
|
return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
|
|
1464
1475
|
}
|
|
1465
1476
|
function wait(ms, signal) {
|
|
1466
|
-
return new Promise((
|
|
1467
|
-
if (signal?.aborted) return
|
|
1468
|
-
const timer = setTimeout(
|
|
1477
|
+
return new Promise((resolve9) => {
|
|
1478
|
+
if (signal?.aborted) return resolve9();
|
|
1479
|
+
const timer = setTimeout(resolve9, ms);
|
|
1469
1480
|
signal?.addEventListener("abort", () => {
|
|
1470
1481
|
clearTimeout(timer);
|
|
1471
|
-
|
|
1482
|
+
resolve9();
|
|
1472
1483
|
}, { once: true });
|
|
1473
1484
|
});
|
|
1474
1485
|
}
|
|
@@ -1700,19 +1711,127 @@ function isCheckpointEffectCompleted(checkpoint, effectId, actionDigest) {
|
|
|
1700
1711
|
// src/code-verifier.ts
|
|
1701
1712
|
var import_node_crypto3 = require("crypto");
|
|
1702
1713
|
var import_node_fs2 = require("fs");
|
|
1703
|
-
var
|
|
1704
|
-
var
|
|
1714
|
+
var import_promises9 = require("fs/promises");
|
|
1715
|
+
var import_node_path9 = require("path");
|
|
1705
1716
|
var import_code3 = require("@odla-ai/camel/code");
|
|
1706
1717
|
|
|
1707
1718
|
// src/recipe-container.ts
|
|
1708
|
-
var
|
|
1719
|
+
var import_node_child_process6 = require("child_process");
|
|
1709
1720
|
var import_node_process2 = require("process");
|
|
1710
1721
|
var import_node_crypto2 = require("crypto");
|
|
1722
|
+
|
|
1723
|
+
// src/code-recipe-dependencies.ts
|
|
1724
|
+
var import_promises7 = require("fs/promises");
|
|
1725
|
+
var import_node_path7 = require("path");
|
|
1726
|
+
var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
1727
|
+
function assertLentPath(mountAs) {
|
|
1728
|
+
const parts = mountAs.split("/");
|
|
1729
|
+
if ((0, import_node_path7.isAbsolute)(mountAs) || mountAs.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
|
|
1730
|
+
throw new TypeError(`recipe dependencies must mount inside the workspace, not at "${mountAs}"`);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
function assertReservedMount(mountAs) {
|
|
1734
|
+
assertLentPath(mountAs);
|
|
1735
|
+
if (!RESERVED_MOUNTS.has(mountAs.split("/").at(-1))) {
|
|
1736
|
+
throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
function lentDirectories(dependencies) {
|
|
1740
|
+
return [{ source: dependencies.source, mountAs: dependencies.mountAs ?? "node_modules" }, ...dependencies.nested ?? []];
|
|
1741
|
+
}
|
|
1742
|
+
function withRecipeDependencies(executor, dependencies) {
|
|
1743
|
+
const lent = lentDirectories(dependencies);
|
|
1744
|
+
for (const entry of lent) {
|
|
1745
|
+
if (!(0, import_node_path7.isAbsolute)(entry.source)) {
|
|
1746
|
+
throw new TypeError("recipe dependency source must be an absolute path");
|
|
1747
|
+
}
|
|
1748
|
+
assertReservedMount(entry.mountAs);
|
|
1749
|
+
}
|
|
1750
|
+
return {
|
|
1751
|
+
run: async (input) => {
|
|
1752
|
+
const linked = [];
|
|
1753
|
+
try {
|
|
1754
|
+
for (const entry of lent) {
|
|
1755
|
+
const target = (0, import_node_path7.join)(input.workspaceDir, entry.mountAs);
|
|
1756
|
+
if (await (0, import_promises7.lstat)(target).catch(() => null)) continue;
|
|
1757
|
+
await (0, import_promises7.symlink)(entry.source, target, "dir").then(() => linked.push(target), () => void 0);
|
|
1758
|
+
}
|
|
1759
|
+
return await executor.run(input);
|
|
1760
|
+
} finally {
|
|
1761
|
+
for (const target of linked) await (0, import_promises7.rm)(target, { force: true, recursive: false }).catch(() => void 0);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
async function installedDependencies(repoRoot) {
|
|
1767
|
+
const source = (0, import_node_path7.join)(repoRoot, "node_modules");
|
|
1768
|
+
const info = await (0, import_promises7.lstat)(source).catch(() => null);
|
|
1769
|
+
return info?.isDirectory() ? { source } : null;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
// src/recipe-container-lending.ts
|
|
1773
|
+
var import_node_child_process5 = require("child_process");
|
|
1774
|
+
var import_promises8 = require("fs/promises");
|
|
1775
|
+
var import_node_path8 = require("path");
|
|
1776
|
+
var RUN_ID = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
1777
|
+
function copy(args) {
|
|
1778
|
+
return new Promise((accept, reject) => {
|
|
1779
|
+
const child = (0, import_node_child_process5.spawn)("cp", args, { shell: false, stdio: "ignore" });
|
|
1780
|
+
child.once("error", reject);
|
|
1781
|
+
child.once("exit", (code) => accept(code ?? 1));
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
async function cloneTree(source, target, platform = process.platform) {
|
|
1785
|
+
await (0, import_promises8.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
|
|
1786
|
+
const fast = platform === "darwin" ? ["-Rc", source, target] : platform === "linux" ? ["-R", "--reflink=auto", source, target] : null;
|
|
1787
|
+
if (fast && await copy(fast) === 0) return;
|
|
1788
|
+
await (0, import_promises8.rm)(target, { recursive: true, force: true });
|
|
1789
|
+
if (await copy(["-R", source, target]) !== 0) throw new Error(`could not clone ${source}`);
|
|
1790
|
+
}
|
|
1791
|
+
async function lendDependencies(dependencies, runId) {
|
|
1792
|
+
if (!dependencies?.runsDir) return { dependencies, release: async () => void 0 };
|
|
1793
|
+
if (!RUN_ID.test(runId)) throw new TypeError("run id is not a safe directory name");
|
|
1794
|
+
const treeRoot = (0, import_node_path8.dirname)(dependencies.source);
|
|
1795
|
+
for (const entry of lentDirectories(dependencies)) {
|
|
1796
|
+
if (entry.source !== (0, import_node_path8.join)(treeRoot, entry.mountAs)) {
|
|
1797
|
+
throw new TypeError("every directory of a cloned set must lie under its tree root at its mount path");
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
const runDir = (0, import_node_path8.join)(dependencies.runsDir, runId);
|
|
1801
|
+
await cloneTree(treeRoot, runDir);
|
|
1802
|
+
const rebase = (entry) => ({ source: (0, import_node_path8.join)(runDir, entry.mountAs), mountAs: entry.mountAs });
|
|
1803
|
+
return {
|
|
1804
|
+
dependencies: {
|
|
1805
|
+
...dependencies,
|
|
1806
|
+
source: (0, import_node_path8.join)(runDir, dependencies.mountAs ?? "node_modules"),
|
|
1807
|
+
nested: (dependencies.nested ?? []).map(rebase),
|
|
1808
|
+
writable: true
|
|
1809
|
+
},
|
|
1810
|
+
release: () => (0, import_promises8.rm)(runDir, { recursive: true, force: true })
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
async function lendBuildProducts(products, workspaceDir) {
|
|
1814
|
+
const root = (0, import_node_path8.resolve)(workspaceDir);
|
|
1815
|
+
const lent = [];
|
|
1816
|
+
for (const product of products) {
|
|
1817
|
+
assertLentPath(product.mountAs);
|
|
1818
|
+
if (SECRET_WORKSPACE_FILE.test(product.mountAs.split("/").at(-1))) throw new TypeError("a secret is never a build product");
|
|
1819
|
+
const target = (0, import_node_path8.resolve)(root, product.mountAs);
|
|
1820
|
+
if (!target.startsWith(`${root}${import_node_path8.sep}`)) throw new TypeError("build product escapes the workspace");
|
|
1821
|
+
if (await (0, import_promises8.stat)(target).catch(() => null)) continue;
|
|
1822
|
+
if (!await (0, import_promises8.stat)((0, import_node_path8.join)(root, product.within ?? (0, import_node_path8.dirname)(product.mountAs))).catch(() => null)) continue;
|
|
1823
|
+
await cloneTree(product.source, target);
|
|
1824
|
+
lent.push(product.mountAs);
|
|
1825
|
+
}
|
|
1826
|
+
return lent;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
// src/recipe-container.ts
|
|
1711
1830
|
var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
1712
1831
|
var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
|
|
1713
1832
|
function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}`, dependencies = null) {
|
|
1714
1833
|
assertCodeBuildRecipe(recipe2);
|
|
1715
|
-
const mounts = dependencies ?
|
|
1834
|
+
const mounts = dependencies ? dependencyMounts(engine, dependencies) : [];
|
|
1716
1835
|
if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
|
|
1717
1836
|
const uid = typeof import_node_process2.getuid === "function" ? (0, import_node_process2.getuid)() : 1e3;
|
|
1718
1837
|
const gid = typeof import_node_process2.getgid === "function" ? (0, import_node_process2.getgid)() : 1e3;
|
|
@@ -1738,6 +1857,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1738
1857
|
...mounts,
|
|
1739
1858
|
"--workdir=/workspace",
|
|
1740
1859
|
"--env=CI=1",
|
|
1860
|
+
"--env=HOME=/tmp",
|
|
1741
1861
|
recipe2.image,
|
|
1742
1862
|
...recipe2.command
|
|
1743
1863
|
];
|
|
@@ -1760,26 +1880,34 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1760
1880
|
...mounts,
|
|
1761
1881
|
"--workdir=/workspace",
|
|
1762
1882
|
"--env=CI=1",
|
|
1883
|
+
"--env=HOME=/tmp",
|
|
1763
1884
|
recipe2.image,
|
|
1764
1885
|
...recipe2.command
|
|
1765
1886
|
];
|
|
1766
1887
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1888
|
+
function dependencyMounts(engine, dependencies) {
|
|
1889
|
+
const readonly = dependencies.writable ? "" : ",readonly";
|
|
1890
|
+
return lentDirectories(dependencies).map(({ source, mountAs }) => {
|
|
1891
|
+
if (!source.startsWith("/") || /[,\r\n]/.test(source)) {
|
|
1892
|
+
throw new TypeError("recipe dependency source must be an absolute path without mount separators");
|
|
1893
|
+
}
|
|
1894
|
+
assertReservedMount(mountAs);
|
|
1895
|
+
return engine === "container" ? `--mount=type=bind,source=${source},target=/workspace/${mountAs}${readonly}` : `--mount=type=bind,src=${source},dst=/workspace/${mountAs}${readonly}`;
|
|
1896
|
+
});
|
|
1775
1897
|
}
|
|
1776
1898
|
function createContainerRecipeExecutor(engine, options = {}) {
|
|
1777
1899
|
return {
|
|
1778
1900
|
async run(input) {
|
|
1779
1901
|
await verifyContainerEngineBoundary(engine);
|
|
1780
1902
|
const name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}`;
|
|
1781
|
-
const
|
|
1782
|
-
|
|
1903
|
+
const lent = await lendDependencies(options.dependencies ?? null, name);
|
|
1904
|
+
try {
|
|
1905
|
+
if (lent.dependencies?.products?.length) await lendBuildProducts(lent.dependencies.products, input.workspaceDir);
|
|
1906
|
+
const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, lent.dependencies);
|
|
1907
|
+
return await execute(engine, args, name, input.recipe, input.signal);
|
|
1908
|
+
} finally {
|
|
1909
|
+
await lent.release();
|
|
1910
|
+
}
|
|
1783
1911
|
}
|
|
1784
1912
|
};
|
|
1785
1913
|
}
|
|
@@ -1801,10 +1929,14 @@ function parseMemory(value) {
|
|
|
1801
1929
|
function execute(engine, args, name, recipe2, signal) {
|
|
1802
1930
|
return runContainerCommand(engine, args, name, { timeoutMs: recipe2.timeoutMs, maxOutputBytes: recipe2.maxOutputBytes }, signal);
|
|
1803
1931
|
}
|
|
1932
|
+
var CONTAINER_PROGRESS = /^\[\d+\/\d+\] .*$/gm;
|
|
1933
|
+
function recipeOutput(engine, stderr) {
|
|
1934
|
+
return engine === "container" ? stderr.replace(CONTAINER_PROGRESS, "").replace(/^\n+/, "") : stderr;
|
|
1935
|
+
}
|
|
1804
1936
|
function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
1805
1937
|
return new Promise((accept, reject) => {
|
|
1806
1938
|
const started = Date.now();
|
|
1807
|
-
const child = (0,
|
|
1939
|
+
const child = (0, import_node_child_process6.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
1808
1940
|
const stdout = [];
|
|
1809
1941
|
const stderr = [];
|
|
1810
1942
|
let bytes = 0;
|
|
@@ -1817,7 +1949,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
|
1817
1949
|
timedOut = reason === "timeout";
|
|
1818
1950
|
outputLimitExceeded = reason === "output";
|
|
1819
1951
|
const remove = engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
|
|
1820
|
-
const killer = (0,
|
|
1952
|
+
const killer = (0, import_node_child_process6.spawn)(engine, remove, { shell: false, stdio: "ignore" });
|
|
1821
1953
|
killer.unref();
|
|
1822
1954
|
child.kill("SIGTERM");
|
|
1823
1955
|
};
|
|
@@ -1843,7 +1975,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
|
1843
1975
|
accept({
|
|
1844
1976
|
exitCode: code ?? 1,
|
|
1845
1977
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
1846
|
-
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
1978
|
+
stderr: recipeOutput(engine, Buffer.concat(stderr).toString("utf8")),
|
|
1847
1979
|
durationMs: Date.now() - started,
|
|
1848
1980
|
outputLimitExceeded,
|
|
1849
1981
|
timedOut
|
|
@@ -1961,8 +2093,8 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
1961
2093
|
const receipts = [];
|
|
1962
2094
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
1963
2095
|
try {
|
|
1964
|
-
const path = (0,
|
|
1965
|
-
const info = await (0,
|
|
2096
|
+
const path = (0, import_node_path9.join)(workspaceDir, artifact.path);
|
|
2097
|
+
const info = await (0, import_promises9.lstat)(path);
|
|
1966
2098
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
1967
2099
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
1968
2100
|
} else if (info.size > artifact.maximumBytes) {
|
|
@@ -2293,15 +2425,15 @@ async function prepareRuntimeLocalSource(input) {
|
|
|
2293
2425
|
}
|
|
2294
2426
|
|
|
2295
2427
|
// src/code-runtime-source.ts
|
|
2296
|
-
var
|
|
2428
|
+
var import_promises12 = require("fs/promises");
|
|
2297
2429
|
var import_node_os4 = require("os");
|
|
2298
|
-
var
|
|
2430
|
+
var import_node_path12 = require("path");
|
|
2299
2431
|
|
|
2300
2432
|
// src/code-runtime-archive.ts
|
|
2301
2433
|
var import_node_zlib = require("zlib");
|
|
2302
|
-
var
|
|
2434
|
+
var import_promises10 = require("fs/promises");
|
|
2303
2435
|
var import_node_os3 = require("os");
|
|
2304
|
-
var
|
|
2436
|
+
var import_node_path10 = require("path");
|
|
2305
2437
|
var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
2306
2438
|
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
2307
2439
|
var MAX_NUL_SHARE = 0.1;
|
|
@@ -2319,9 +2451,9 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
|
|
|
2319
2451
|
throw new TypeError("Code source archive exceeds its decompressed byte bound");
|
|
2320
2452
|
}
|
|
2321
2453
|
const entries = parseTar(bytes, archive.limits);
|
|
2322
|
-
const root = await (0,
|
|
2323
|
-
const sourceDir = (0,
|
|
2324
|
-
await (0,
|
|
2454
|
+
const root = await (0, import_promises10.mkdtemp)((0, import_node_path10.join)(tempRoot, "odla-code-archive-"));
|
|
2455
|
+
const sourceDir = (0, import_node_path10.join)(root, "source");
|
|
2456
|
+
await (0, import_promises10.mkdir)(sourceDir);
|
|
2325
2457
|
let visible = 0;
|
|
2326
2458
|
try {
|
|
2327
2459
|
for (const entry of entries) {
|
|
@@ -2334,16 +2466,16 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
|
|
|
2334
2466
|
continue;
|
|
2335
2467
|
}
|
|
2336
2468
|
if (nulShare(content) > MAX_NUL_SHARE) continue;
|
|
2337
|
-
const target = (0,
|
|
2338
|
-
if (!target.startsWith(`${(0,
|
|
2339
|
-
await (0,
|
|
2340
|
-
await (0,
|
|
2469
|
+
const target = (0, import_node_path10.resolve)(sourceDir, entry.path);
|
|
2470
|
+
if (!target.startsWith(`${(0, import_node_path10.resolve)(sourceDir)}${import_node_path10.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
2471
|
+
await (0, import_promises10.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
|
|
2472
|
+
await (0, import_promises10.writeFile)(target, content, { flag: "wx", mode: 420 });
|
|
2341
2473
|
visible += 1;
|
|
2342
2474
|
}
|
|
2343
2475
|
if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
|
|
2344
|
-
return { sourceDir, cleanup: () => (0,
|
|
2476
|
+
return { sourceDir, cleanup: () => (0, import_promises10.rm)(root, { recursive: true, force: true }) };
|
|
2345
2477
|
} catch (cause) {
|
|
2346
|
-
await (0,
|
|
2478
|
+
await (0, import_promises10.rm)(root, { recursive: true, force: true });
|
|
2347
2479
|
throw cause;
|
|
2348
2480
|
}
|
|
2349
2481
|
}
|
|
@@ -2538,8 +2670,8 @@ function nulShare(content) {
|
|
|
2538
2670
|
}
|
|
2539
2671
|
|
|
2540
2672
|
// src/code-runtime-selected-source.ts
|
|
2541
|
-
var
|
|
2542
|
-
var
|
|
2673
|
+
var import_promises11 = require("fs/promises");
|
|
2674
|
+
var import_node_path11 = require("path");
|
|
2543
2675
|
function selectedSourceSet(payload) {
|
|
2544
2676
|
if (!payload.sourceSet) return null;
|
|
2545
2677
|
const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
|
|
@@ -2576,9 +2708,9 @@ async function attachReferenceDirectories(workspace, references) {
|
|
|
2576
2708
|
for (const reference of references) {
|
|
2577
2709
|
validateAlias(reference.alias);
|
|
2578
2710
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
2579
|
-
const target = (0,
|
|
2580
|
-
await (0,
|
|
2581
|
-
await (0,
|
|
2711
|
+
const target = (0, import_node_path11.join)(root, ".odla-references", reference.alias);
|
|
2712
|
+
await (0, import_promises11.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
2713
|
+
await (0, import_promises11.cp)(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
|
|
2582
2714
|
await makeTreeReadOnly(target);
|
|
2583
2715
|
}
|
|
2584
2716
|
}
|
|
@@ -2589,10 +2721,10 @@ function validateAlias(alias) {
|
|
|
2589
2721
|
}
|
|
2590
2722
|
}
|
|
2591
2723
|
async function makeTreeReadOnly(root) {
|
|
2592
|
-
for (const entry of await (0,
|
|
2593
|
-
const target = (0,
|
|
2724
|
+
for (const entry of await (0, import_promises11.readdir)(root, { withFileTypes: true })) {
|
|
2725
|
+
const target = (0, import_node_path11.join)(root, entry.name);
|
|
2594
2726
|
if (entry.isDirectory()) await makeTreeReadOnly(target);
|
|
2595
|
-
else if (entry.isFile()) await (0,
|
|
2727
|
+
else if (entry.isFile()) await (0, import_promises11.chmod)(target, 292);
|
|
2596
2728
|
}
|
|
2597
2729
|
}
|
|
2598
2730
|
|
|
@@ -2604,9 +2736,9 @@ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
|
|
|
2604
2736
|
var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
|
|
2605
2737
|
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os4.tmpdir)()) {
|
|
2606
2738
|
if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
|
|
2607
|
-
const root = await (0,
|
|
2608
|
-
const sourceDir = (0,
|
|
2609
|
-
await (0,
|
|
2739
|
+
const root = await (0, import_promises12.mkdtemp)((0, import_node_path12.join)(tempRoot, "odla-code-source-"));
|
|
2740
|
+
const sourceDir = (0, import_node_path12.join)(root, "source");
|
|
2741
|
+
await (0, import_promises12.mkdir)(sourceDir);
|
|
2610
2742
|
const seen = /* @__PURE__ */ new Set();
|
|
2611
2743
|
let bytes = 0;
|
|
2612
2744
|
try {
|
|
@@ -2616,10 +2748,10 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
2616
2748
|
seen.add(file.path);
|
|
2617
2749
|
bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
|
|
2618
2750
|
if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
|
|
2619
|
-
const target = (0,
|
|
2620
|
-
if (!target.startsWith(`${(0,
|
|
2621
|
-
await (0,
|
|
2622
|
-
await (0,
|
|
2751
|
+
const target = (0, import_node_path12.resolve)(sourceDir, file.path);
|
|
2752
|
+
if (!target.startsWith(`${(0, import_node_path12.resolve)(sourceDir)}${import_node_path12.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
2753
|
+
await (0, import_promises12.mkdir)((0, import_node_path12.dirname)(target), { recursive: true });
|
|
2754
|
+
await (0, import_promises12.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
2623
2755
|
}
|
|
2624
2756
|
for (const reference of snapshot.references ?? []) {
|
|
2625
2757
|
validateAlias2(reference.alias);
|
|
@@ -2631,15 +2763,15 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
2631
2763
|
seen.add(path);
|
|
2632
2764
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
2633
2765
|
if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
|
|
2634
|
-
const target = (0,
|
|
2635
|
-
if (!target.startsWith(`${(0,
|
|
2636
|
-
await (0,
|
|
2637
|
-
await (0,
|
|
2766
|
+
const target = (0, import_node_path12.resolve)(sourceDir, path);
|
|
2767
|
+
if (!target.startsWith(`${(0, import_node_path12.resolve)(sourceDir)}${import_node_path12.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
2768
|
+
await (0, import_promises12.mkdir)((0, import_node_path12.dirname)(target), { recursive: true });
|
|
2769
|
+
await (0, import_promises12.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
2638
2770
|
}
|
|
2639
2771
|
}
|
|
2640
|
-
return { sourceDir, cleanup: () => (0,
|
|
2772
|
+
return { sourceDir, cleanup: () => (0, import_promises12.rm)(root, { recursive: true, force: true }) };
|
|
2641
2773
|
} catch (cause) {
|
|
2642
|
-
await (0,
|
|
2774
|
+
await (0, import_promises12.rm)(root, { recursive: true, force: true });
|
|
2643
2775
|
throw cause;
|
|
2644
2776
|
}
|
|
2645
2777
|
}
|
|
@@ -2658,10 +2790,10 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
2658
2790
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
2659
2791
|
if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
|
|
2660
2792
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
2661
|
-
const target = (0,
|
|
2662
|
-
if (!target.startsWith(`${(0,
|
|
2663
|
-
await (0,
|
|
2664
|
-
await (0,
|
|
2793
|
+
const target = (0, import_node_path12.resolve)(root, path);
|
|
2794
|
+
if (!target.startsWith(`${(0, import_node_path12.resolve)(root)}${import_node_path12.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
2795
|
+
await (0, import_promises12.mkdir)((0, import_node_path12.dirname)(target), { recursive: true });
|
|
2796
|
+
await (0, import_promises12.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
2665
2797
|
}
|
|
2666
2798
|
}
|
|
2667
2799
|
}
|
|
@@ -3107,7 +3239,7 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
|
|
|
3107
3239
|
}
|
|
3108
3240
|
|
|
3109
3241
|
// src/code-runtime-session-skills.ts
|
|
3110
|
-
var sleep2 = (ms) => new Promise((
|
|
3242
|
+
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
3111
3243
|
function createCodeRuntimeSessionSkillLoader(control, options = {}) {
|
|
3112
3244
|
const wait2 = options.wait ?? sleep2;
|
|
3113
3245
|
const load = control.collaborationSkills?.bind(control);
|
|
@@ -3166,7 +3298,7 @@ var inferWithBackoff = (infer, wait2, onRetry) => withOverloadRetry(infer, wait2
|
|
|
3166
3298
|
async function handleCodeRuntimeInference(input) {
|
|
3167
3299
|
const { command, request, state } = input;
|
|
3168
3300
|
const startedAt = Date.now();
|
|
3169
|
-
const wait2 = input.wait ?? ((ms) => new Promise((
|
|
3301
|
+
const wait2 = input.wait ?? ((ms) => new Promise((resolve9) => setTimeout(resolve9, ms)));
|
|
3170
3302
|
const response2 = await inferWithBackoff(
|
|
3171
3303
|
() => input.control.infer(command.sessionId, {
|
|
3172
3304
|
requestId: request.requestId,
|
|
@@ -3364,7 +3496,10 @@ function toolFailureReason(response2) {
|
|
|
3364
3496
|
if (response2.ok) return void 0;
|
|
3365
3497
|
const supplied = response2.details?.failureReason;
|
|
3366
3498
|
const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
|
|
3367
|
-
|
|
3499
|
+
if (reason.length <= MAX_FAILURE_REASON) return reason;
|
|
3500
|
+
const head = reason.slice(0, 60);
|
|
3501
|
+
const gap = " \u2026 ";
|
|
3502
|
+
return `${head}${gap}${reason.slice(-(MAX_FAILURE_REASON - head.length - gap.length))}`;
|
|
3368
3503
|
}
|
|
3369
3504
|
var DEFAULT_FAILURE_REASON = {
|
|
3370
3505
|
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
@@ -3677,18 +3812,18 @@ function response(request, ok, content, details) {
|
|
|
3677
3812
|
}
|
|
3678
3813
|
|
|
3679
3814
|
// src/code-tool-edit.ts
|
|
3680
|
-
var
|
|
3815
|
+
var import_promises14 = require("fs/promises");
|
|
3681
3816
|
|
|
3682
3817
|
// src/code-tool-graph.ts
|
|
3683
|
-
var
|
|
3684
|
-
var
|
|
3818
|
+
var import_promises13 = require("fs/promises");
|
|
3819
|
+
var import_node_path13 = require("path");
|
|
3685
3820
|
var import_graph = require("@odla-ai/graph");
|
|
3686
3821
|
var import_code4 = require("@odla-ai/graph/code");
|
|
3687
3822
|
var cache = /* @__PURE__ */ new Map();
|
|
3688
3823
|
function workspaceGraphs(workspaceDir, paths2) {
|
|
3689
3824
|
const existing = cache.get(workspaceDir);
|
|
3690
3825
|
if (existing) return existing;
|
|
3691
|
-
const read2 = (path) => (0,
|
|
3826
|
+
const read2 = (path) => (0, import_promises13.readFile)((0, import_node_path13.join)(workspaceDir, path), "utf8");
|
|
3692
3827
|
const built = (async () => ({
|
|
3693
3828
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
3694
3829
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
@@ -3764,7 +3899,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
|
|
|
3764
3899
|
}
|
|
3765
3900
|
if (oldText.includes("\0") || newText.includes("\0")) throw new TypeError("edit text contains NUL bytes; use plain text");
|
|
3766
3901
|
const target = resolveCodePath(workspaceDir, path);
|
|
3767
|
-
const current = await (0,
|
|
3902
|
+
const current = await (0, import_promises14.readFile)(target, "utf8");
|
|
3768
3903
|
const found = occurrences(current, oldText);
|
|
3769
3904
|
if (found === 0) {
|
|
3770
3905
|
throw new TypeError(`oldText was not found in "${path}"; sandbox.read the current lines and copy them exactly, including indentation and blank lines`);
|
|
@@ -3773,7 +3908,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
|
|
|
3773
3908
|
throw new TypeError(`oldText occurs ${found} times in "${path}"; include more of the surrounding lines so it matches exactly once`);
|
|
3774
3909
|
}
|
|
3775
3910
|
const at = current.indexOf(oldText);
|
|
3776
|
-
await (0,
|
|
3911
|
+
await (0, import_promises14.writeFile)(target, `${current.slice(0, at)}${newText}${current.slice(at + oldText.length)}`, "utf8");
|
|
3777
3912
|
return { deletions: oldText.split("\n").length, additions: newText.split("\n").length };
|
|
3778
3913
|
}
|
|
3779
3914
|
async function edit(context, request, options, policy, registry) {
|
|
@@ -3798,8 +3933,8 @@ async function edit(context, request, options, policy, registry) {
|
|
|
3798
3933
|
}
|
|
3799
3934
|
|
|
3800
3935
|
// src/code-tool-write.ts
|
|
3801
|
-
var
|
|
3802
|
-
var
|
|
3936
|
+
var import_promises15 = require("fs/promises");
|
|
3937
|
+
var import_node_path14 = require("path");
|
|
3803
3938
|
var MAX_WRITE_BYTES = 256 * 1024;
|
|
3804
3939
|
var lines = (text2) => text2 === "" ? [] : text2.replace(/\n$/, "").split("\n");
|
|
3805
3940
|
function writeAsDiff(path, current, content) {
|
|
@@ -3824,8 +3959,8 @@ async function writeCodeFile(workspaceDir, path, content, current) {
|
|
|
3824
3959
|
}
|
|
3825
3960
|
if (content.includes("\0")) throw new TypeError("content contains NUL bytes; use plain text");
|
|
3826
3961
|
const target = resolveCodePath(workspaceDir, path);
|
|
3827
|
-
await (0,
|
|
3828
|
-
await (0,
|
|
3962
|
+
await (0, import_promises15.mkdir)((0, import_node_path14.dirname)(target), { recursive: true });
|
|
3963
|
+
await (0, import_promises15.writeFile)(target, content, "utf8");
|
|
3829
3964
|
return { created: current === null, deletions: current === null ? 0 : lines(current).length, additions: lines(content).length };
|
|
3830
3965
|
}
|
|
3831
3966
|
async function write(context, request, options, policy, registry) {
|
|
@@ -3836,7 +3971,7 @@ async function write(context, request, options, policy, registry) {
|
|
|
3836
3971
|
throw new TypeError("write targets a read-only reference source");
|
|
3837
3972
|
}
|
|
3838
3973
|
const exists2 = (await registry.files(context.workspaceDir)).includes(path);
|
|
3839
|
-
const current = exists2 ? await (0,
|
|
3974
|
+
const current = exists2 ? await (0, import_promises15.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
|
|
3840
3975
|
const patch2 = writeAsDiff(path, current, content);
|
|
3841
3976
|
const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
|
|
3842
3977
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
@@ -3852,12 +3987,12 @@ async function write(context, request, options, policy, registry) {
|
|
|
3852
3987
|
}
|
|
3853
3988
|
|
|
3854
3989
|
// src/code-tool-reads.ts
|
|
3855
|
-
var
|
|
3990
|
+
var import_promises17 = require("fs/promises");
|
|
3856
3991
|
|
|
3857
3992
|
// src/code-tool-discovery.ts
|
|
3858
|
-
var
|
|
3859
|
-
var
|
|
3860
|
-
var
|
|
3993
|
+
var import_node_child_process7 = require("child_process");
|
|
3994
|
+
var import_promises16 = require("fs/promises");
|
|
3995
|
+
var import_node_path15 = require("path");
|
|
3861
3996
|
var DEFAULT_MAX_FILES = 2e4;
|
|
3862
3997
|
var DEFAULT_MAX_RESULTS = 100;
|
|
3863
3998
|
var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
@@ -3882,13 +4017,13 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
3882
4017
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
3883
4018
|
const paths2 = [];
|
|
3884
4019
|
const walk = async (directory) => {
|
|
3885
|
-
for (const entry of await (0,
|
|
4020
|
+
for (const entry of await (0, import_promises16.readdir)(directory, { withFileTypes: true })) {
|
|
3886
4021
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
3887
4022
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
3888
|
-
const target = (0,
|
|
4023
|
+
const target = (0, import_node_path15.resolve)(directory, entry.name);
|
|
3889
4024
|
if (entry.isDirectory()) await walk(target);
|
|
3890
4025
|
else if (entry.isFile()) {
|
|
3891
|
-
const path = (0,
|
|
4026
|
+
const path = (0, import_node_path15.relative)(root, target).split("\\").join("/");
|
|
3892
4027
|
try {
|
|
3893
4028
|
validateRelativePath(path);
|
|
3894
4029
|
} catch {
|
|
@@ -3899,7 +4034,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
3899
4034
|
}
|
|
3900
4035
|
}
|
|
3901
4036
|
};
|
|
3902
|
-
await walk((0,
|
|
4037
|
+
await walk((0, import_node_path15.resolve)(root));
|
|
3903
4038
|
return paths2.sort();
|
|
3904
4039
|
}
|
|
3905
4040
|
function listWorkspace(paths2, options = {}) {
|
|
@@ -3961,7 +4096,7 @@ function nativeSearchBatch(root, paths2, options, remaining) {
|
|
|
3961
4096
|
options.query,
|
|
3962
4097
|
...paths2
|
|
3963
4098
|
];
|
|
3964
|
-
const child = (0,
|
|
4099
|
+
const child = (0, import_node_child_process7.spawn)("rg", args, {
|
|
3965
4100
|
cwd: root,
|
|
3966
4101
|
stdio: ["ignore", "pipe", "ignore"],
|
|
3967
4102
|
...options.signal ? { signal: options.signal } : {}
|
|
@@ -4013,7 +4148,7 @@ async function fallbackSearch(root, scoped, options) {
|
|
|
4013
4148
|
if (matches.length >= options.maxResults) break;
|
|
4014
4149
|
let source;
|
|
4015
4150
|
try {
|
|
4016
|
-
source = await (0,
|
|
4151
|
+
source = await (0, import_promises16.readFile)((0, import_node_path15.resolve)(root, path));
|
|
4017
4152
|
} catch {
|
|
4018
4153
|
continue;
|
|
4019
4154
|
}
|
|
@@ -4052,11 +4187,11 @@ async function read(context, request, options, policy, registry) {
|
|
|
4052
4187
|
const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
|
|
4053
4188
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
4054
4189
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
4055
|
-
const info = await (0,
|
|
4190
|
+
const info = await (0, import_promises17.stat)(target);
|
|
4056
4191
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
4057
4192
|
throw new TypeError("file is not a bounded regular source file");
|
|
4058
4193
|
}
|
|
4059
|
-
const source = await (0,
|
|
4194
|
+
const source = await (0, import_promises17.readFile)(target);
|
|
4060
4195
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
4061
4196
|
const lines2 = source.toString("utf8").split("\n");
|
|
4062
4197
|
const content = lines2.slice(startLine - 1, endLine).join("\n");
|
|
@@ -4604,8 +4739,8 @@ var runtimeErrorMessage = (value) => value instanceof Error ? value.message : St
|
|
|
4604
4739
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
4605
4740
|
let settle;
|
|
4606
4741
|
let settled = false;
|
|
4607
|
-
const ready = new Promise((
|
|
4608
|
-
settle =
|
|
4742
|
+
const ready = new Promise((resolve9) => {
|
|
4743
|
+
settle = resolve9;
|
|
4609
4744
|
});
|
|
4610
4745
|
const release = (run) => {
|
|
4611
4746
|
if (settled) return;
|
|
@@ -4671,8 +4806,8 @@ function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
|
4671
4806
|
}
|
|
4672
4807
|
|
|
4673
4808
|
// src/code-repository-recipes.ts
|
|
4674
|
-
var
|
|
4675
|
-
var
|
|
4809
|
+
var import_promises18 = require("fs/promises");
|
|
4810
|
+
var import_node_path16 = require("path");
|
|
4676
4811
|
var REPOSITORY_RECIPES_FILE = "odla.recipes.json";
|
|
4677
4812
|
var MAX_RECIPES = 16;
|
|
4678
4813
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -4733,7 +4868,7 @@ function parseRepositoryRecipes(text2, envelope) {
|
|
|
4733
4868
|
async function readRepositoryRecipes(baselineDir, envelope) {
|
|
4734
4869
|
let text2;
|
|
4735
4870
|
try {
|
|
4736
|
-
text2 = await (0,
|
|
4871
|
+
text2 = await (0, import_promises18.readFile)((0, import_node_path16.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
|
|
4737
4872
|
} catch (cause) {
|
|
4738
4873
|
if (cause.code === "ENOENT") return null;
|
|
4739
4874
|
throw cause;
|
|
@@ -5334,82 +5469,58 @@ function chooseStrategy(signals = {}) {
|
|
|
5334
5469
|
}
|
|
5335
5470
|
var feedbackIsActionable = actionable;
|
|
5336
5471
|
|
|
5337
|
-
// src/code-recipe-dependencies.ts
|
|
5338
|
-
var import_promises17 = require("fs/promises");
|
|
5339
|
-
var import_node_path15 = require("path");
|
|
5340
|
-
var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
5341
|
-
function withRecipeDependencies(executor, dependencies) {
|
|
5342
|
-
const mountAs = dependencies.mountAs ?? "node_modules";
|
|
5343
|
-
if (!(0, import_node_path15.isAbsolute)(dependencies.source)) {
|
|
5344
|
-
throw new TypeError("recipe dependency source must be an absolute path");
|
|
5345
|
-
}
|
|
5346
|
-
if (!RESERVED_MOUNTS.has(mountAs)) {
|
|
5347
|
-
throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
|
|
5348
|
-
}
|
|
5349
|
-
return {
|
|
5350
|
-
run: async (input) => {
|
|
5351
|
-
const target = (0, import_node_path15.join)(input.workspaceDir, mountAs);
|
|
5352
|
-
let linked = false;
|
|
5353
|
-
try {
|
|
5354
|
-
const existing = await (0, import_promises17.lstat)(target).catch(() => null);
|
|
5355
|
-
if (!existing) {
|
|
5356
|
-
await (0, import_promises17.symlink)(dependencies.source, target, "dir");
|
|
5357
|
-
linked = true;
|
|
5358
|
-
}
|
|
5359
|
-
return await executor.run(input);
|
|
5360
|
-
} finally {
|
|
5361
|
-
if (linked) await (0, import_promises17.rm)(target, { force: true, recursive: false }).catch(() => void 0);
|
|
5362
|
-
}
|
|
5363
|
-
}
|
|
5364
|
-
};
|
|
5365
|
-
}
|
|
5366
|
-
async function installedDependencies(repoRoot) {
|
|
5367
|
-
const source = (0, import_node_path15.join)(repoRoot, "node_modules");
|
|
5368
|
-
const info = await (0, import_promises17.lstat)(source).catch(() => null);
|
|
5369
|
-
return info?.isDirectory() ? { source } : null;
|
|
5370
|
-
}
|
|
5371
|
-
|
|
5372
5472
|
// src/code-recipe-dependency-cache.ts
|
|
5373
5473
|
var import_node_crypto5 = require("crypto");
|
|
5374
|
-
var
|
|
5474
|
+
var import_promises19 = require("fs/promises");
|
|
5375
5475
|
var import_node_process3 = require("process");
|
|
5376
|
-
var
|
|
5476
|
+
var import_node_path17 = require("path");
|
|
5377
5477
|
var DEPENDENCY_INSTALL_TIMEOUT_MS = 20 * 6e4;
|
|
5378
5478
|
var OUTPUT_CAP = 4 * 1024 * 1024;
|
|
5379
|
-
var
|
|
5380
|
-
async
|
|
5381
|
-
|
|
5479
|
+
var EXACT_VERSION = /^\d+\.\d+\.\d+$/;
|
|
5480
|
+
var exists = async (path) => Boolean(await (0, import_promises19.stat)(path).catch(() => null));
|
|
5481
|
+
async function pinnedNpmVersion(repoRoot) {
|
|
5482
|
+
const manifest = await (0, import_promises19.readFile)((0, import_node_path17.join)(repoRoot, "package.json"), "utf8").catch(() => null);
|
|
5483
|
+
if (!manifest) return null;
|
|
5484
|
+
const engines = JSON.parse(manifest).engines;
|
|
5485
|
+
return typeof engines?.npm === "string" && EXACT_VERSION.test(engines.npm) ? engines.npm : null;
|
|
5486
|
+
}
|
|
5487
|
+
async function dependencyCacheKey(repoRoot, image, arch = process.arch, npmVersion = null) {
|
|
5488
|
+
const lock = await (0, import_promises19.readFile)((0, import_node_path17.join)(repoRoot, "package-lock.json")).catch(() => null);
|
|
5382
5489
|
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);
|
|
5490
|
+
return (0, import_node_crypto5.createHash)("sha256").update(lock).update("\n").update(image).update("\n").update(arch).update("\n").update(npmVersion ?? "image").digest("hex").slice(0, 32);
|
|
5491
|
+
}
|
|
5492
|
+
async function workspaceDirs(repoRoot) {
|
|
5493
|
+
const root = JSON.parse(await (0, import_promises19.readFile)((0, import_node_path17.join)(repoRoot, "package.json"), "utf8"));
|
|
5494
|
+
const globs = Array.isArray(root.workspaces) ? root.workspaces : root.workspaces?.packages ?? [];
|
|
5495
|
+
const dirs = [];
|
|
5496
|
+
for (const glob of globs) {
|
|
5497
|
+
const found = glob.endsWith("/*") ? (await (0, import_promises19.readdir)((0, import_node_path17.join)(repoRoot, glob.slice(0, -2)), { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map((entry) => `${glob.slice(0, -2)}/${entry.name}`) : [glob];
|
|
5498
|
+
for (const dir of found) if (await exists((0, import_node_path17.join)(repoRoot, dir, "package.json"))) dirs.push(dir);
|
|
5499
|
+
}
|
|
5500
|
+
return dirs;
|
|
5384
5501
|
}
|
|
5385
5502
|
async function stageManifests(repoRoot, stageDir) {
|
|
5386
|
-
await (0,
|
|
5503
|
+
await (0, import_promises19.mkdir)(stageDir, { recursive: true });
|
|
5387
5504
|
const staged = [];
|
|
5388
5505
|
for (const name of ["package.json", "package-lock.json", ".npmrc"]) {
|
|
5389
|
-
if (!await exists((0,
|
|
5390
|
-
await (0,
|
|
5506
|
+
if (!await exists((0, import_node_path17.join)(repoRoot, name))) continue;
|
|
5507
|
+
await (0, import_promises19.copyFile)((0, import_node_path17.join)(repoRoot, name), (0, import_node_path17.join)(stageDir, name));
|
|
5391
5508
|
staged.push(name);
|
|
5392
5509
|
}
|
|
5393
|
-
const
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
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
|
-
}
|
|
5510
|
+
for (const dir of await workspaceDirs(repoRoot)) {
|
|
5511
|
+
await (0, import_promises19.mkdir)((0, import_node_path17.join)(stageDir, dir), { recursive: true });
|
|
5512
|
+
await (0, import_promises19.copyFile)((0, import_node_path17.join)(repoRoot, dir, "package.json"), (0, import_node_path17.join)(stageDir, dir, "package.json"));
|
|
5513
|
+
staged.push(`${dir}/package.json`);
|
|
5404
5514
|
}
|
|
5405
5515
|
return staged;
|
|
5406
5516
|
}
|
|
5407
|
-
function dependencyInstallArgs(engine, image, stageDir, name) {
|
|
5517
|
+
function dependencyInstallArgs(engine, image, stageDir, name, npmVersion = null) {
|
|
5408
5518
|
if (/[,\r\n]/.test(stageDir)) throw new TypeError("stage path contains unsupported mount characters");
|
|
5409
5519
|
const uid = typeof import_node_process3.getuid === "function" ? (0, import_node_process3.getuid)() : 1e3;
|
|
5410
5520
|
const gid = typeof import_node_process3.getgid === "function" ? (0, import_node_process3.getgid)() : 1e3;
|
|
5411
5521
|
const mount = engine === "container" ? `--mount=type=bind,source=${stageDir},target=/workspace` : `--mount=type=bind,src=${stageDir},dst=/workspace`;
|
|
5412
|
-
const
|
|
5522
|
+
const install = ["ci", "--no-audit", "--no-fund", "--ignore-scripts", "--engine-strict=false"];
|
|
5523
|
+
const npm = npmVersion ? ["npx", "--yes", `npm@${npmVersion}`, ...install] : ["npm", ...install];
|
|
5413
5524
|
return [
|
|
5414
5525
|
"run",
|
|
5415
5526
|
"--rm",
|
|
@@ -5428,34 +5539,79 @@ function dependencyInstallArgs(engine, image, stageDir, name) {
|
|
|
5428
5539
|
...npm
|
|
5429
5540
|
];
|
|
5430
5541
|
}
|
|
5542
|
+
function npmFailureSummary(stderr) {
|
|
5543
|
+
const lines2 = stderr.split("\n").map((line) => line.trim()).filter((line) => line && line !== "npm error");
|
|
5544
|
+
const at = lines2.findIndex((line) => /^npm error code /.test(line));
|
|
5545
|
+
return (at >= 0 ? lines2.slice(at, at + 3) : lines2.slice(-3)).join(" | ").slice(0, 400);
|
|
5546
|
+
}
|
|
5547
|
+
async function nestedTrees(treeRoot, dirs) {
|
|
5548
|
+
const nested = [];
|
|
5549
|
+
for (const dir of dirs) {
|
|
5550
|
+
if (await exists((0, import_node_path17.join)(treeRoot, dir, "node_modules"))) nested.push({ source: (0, import_node_path17.join)(treeRoot, dir, "node_modules"), mountAs: `${dir}/node_modules` });
|
|
5551
|
+
}
|
|
5552
|
+
return nested;
|
|
5553
|
+
}
|
|
5431
5554
|
async function prepareContainerDependencies(input) {
|
|
5432
|
-
const
|
|
5555
|
+
const npmVersion = await pinnedNpmVersion(input.repoRoot);
|
|
5556
|
+
const key = await dependencyCacheKey(input.repoRoot, input.image, process.arch, npmVersion);
|
|
5433
5557
|
if (!key) return null;
|
|
5434
|
-
const dir = (0,
|
|
5435
|
-
const tree = (0,
|
|
5436
|
-
const stage = (0,
|
|
5437
|
-
|
|
5558
|
+
const dir = (0, import_node_path17.join)(input.cacheRoot, key);
|
|
5559
|
+
const tree = (0, import_node_path17.join)(dir, "tree");
|
|
5560
|
+
const stage = (0, import_node_path17.join)(dir, "stage");
|
|
5561
|
+
const runsDir = (0, import_node_path17.join)(dir, "runs");
|
|
5562
|
+
await (0, import_promises19.rm)(runsDir, { recursive: true, force: true });
|
|
5563
|
+
const dirs = await workspaceDirs(input.repoRoot);
|
|
5564
|
+
const lend = async () => ({ source: (0, import_node_path17.join)(tree, "node_modules"), nested: await nestedTrees(tree, dirs), runsDir });
|
|
5565
|
+
if (await exists((0, import_node_path17.join)(tree, "node_modules"))) return lend();
|
|
5438
5566
|
const run = input.run ?? runContainerCommand;
|
|
5439
|
-
await (0,
|
|
5567
|
+
await (0, import_promises19.rm)(stage, { recursive: true, force: true });
|
|
5440
5568
|
const staged = await stageManifests(input.repoRoot, stage);
|
|
5441
|
-
input.log?.(`installing ${key.slice(0, 12)}: ${staged.length} manifests, npm ci in ${input.image}`);
|
|
5569
|
+
input.log?.(`installing ${key.slice(0, 12)}: ${staged.length} manifests, npm${npmVersion ? `@${npmVersion}` : ""} ci in ${input.image}`);
|
|
5442
5570
|
const name = `odla-deps-${key.slice(0, 12)}`;
|
|
5443
5571
|
const result = await run(
|
|
5444
5572
|
input.engine,
|
|
5445
|
-
dependencyInstallArgs(input.engine, input.image, stage, name),
|
|
5573
|
+
dependencyInstallArgs(input.engine, input.image, stage, name, npmVersion),
|
|
5446
5574
|
name,
|
|
5447
5575
|
{ timeoutMs: DEPENDENCY_INSTALL_TIMEOUT_MS, maxOutputBytes: OUTPUT_CAP }
|
|
5448
5576
|
);
|
|
5449
|
-
if (result.exitCode !== 0 || result.timedOut || !await exists((0,
|
|
5450
|
-
input.log?.(`install failed (exit ${result.exitCode}${result.timedOut ? ", timed out" : ""}): ${result.stderr
|
|
5451
|
-
await (0,
|
|
5577
|
+
if (result.exitCode !== 0 || result.timedOut || !await exists((0, import_node_path17.join)(stage, "node_modules"))) {
|
|
5578
|
+
input.log?.(`install failed (exit ${result.exitCode}${result.timedOut ? ", timed out" : ""}): ${npmFailureSummary(result.stderr)}`);
|
|
5579
|
+
await (0, import_promises19.rm)(stage, { recursive: true, force: true });
|
|
5452
5580
|
return null;
|
|
5453
5581
|
}
|
|
5454
|
-
await (0,
|
|
5455
|
-
await (0,
|
|
5456
|
-
await (0,
|
|
5457
|
-
|
|
5458
|
-
|
|
5582
|
+
await (0, import_promises19.rm)(tree, { recursive: true, force: true });
|
|
5583
|
+
await (0, import_promises19.rename)(stage, tree);
|
|
5584
|
+
await (0, import_promises19.writeFile)((0, import_node_path17.join)(dir, "manifest.json"), JSON.stringify({ image: input.image, key, npmVersion, createdAt: Date.now() }, null, 2));
|
|
5585
|
+
const lent = await lend();
|
|
5586
|
+
input.log?.(`installed ${key.slice(0, 12)} in ${Math.round(result.durationMs / 1e3)}s, ${lent.nested?.length ?? 0} nested tree(s)`);
|
|
5587
|
+
return lent;
|
|
5588
|
+
}
|
|
5589
|
+
|
|
5590
|
+
// src/code-recipe-build-products.ts
|
|
5591
|
+
var import_node_child_process8 = require("child_process");
|
|
5592
|
+
var import_promises20 = require("fs/promises");
|
|
5593
|
+
var import_node_path18 = require("path");
|
|
5594
|
+
function gitIgnoredFiles(repoRoot) {
|
|
5595
|
+
return new Promise((accept) => {
|
|
5596
|
+
const child = (0, import_node_child_process8.spawn)("git", ["-C", repoRoot, "ls-files", "--others", "--ignored", "--exclude-standard", "-z"], { shell: false, stdio: ["ignore", "pipe", "ignore"] });
|
|
5597
|
+
const chunks = [];
|
|
5598
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
5599
|
+
child.once("error", () => accept([]));
|
|
5600
|
+
child.once("exit", (code) => accept(code === 0 ? Buffer.concat(chunks).toString("utf8").split("\0").filter(Boolean) : []));
|
|
5601
|
+
});
|
|
5602
|
+
}
|
|
5603
|
+
async function hostBuildProducts(repoRoot) {
|
|
5604
|
+
const dirs = await workspaceDirs(repoRoot);
|
|
5605
|
+
const within = (path) => dirs.find((dir) => path.startsWith(`${dir}/`)) ?? ".";
|
|
5606
|
+
const products = [];
|
|
5607
|
+
for (const dir of dirs) {
|
|
5608
|
+
const source = (0, import_node_path18.join)(repoRoot, dir, "dist");
|
|
5609
|
+
if ((await (0, import_promises20.stat)(source).catch(() => null))?.isDirectory()) products.push({ source, mountAs: `${dir}/dist`, within: dir });
|
|
5610
|
+
}
|
|
5611
|
+
for (const path of await gitIgnoredFiles(repoRoot)) {
|
|
5612
|
+
if (allowedWorkspacePath(path)) products.push({ source: (0, import_node_path18.join)(repoRoot, path), mountAs: path, within: within(path) });
|
|
5613
|
+
}
|
|
5614
|
+
return products;
|
|
5459
5615
|
}
|
|
5460
5616
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5461
5617
|
0 && (module.exports = {
|
|
@@ -5479,11 +5635,14 @@ async function prepareContainerDependencies(input) {
|
|
|
5479
5635
|
applyPatchDialectToDiff,
|
|
5480
5636
|
assertCodeBuildRecipe,
|
|
5481
5637
|
assertDisjointPlan,
|
|
5638
|
+
assertLentPath,
|
|
5482
5639
|
assertPinnedImage,
|
|
5640
|
+
assertReservedMount,
|
|
5483
5641
|
attachCodeRuntimeReferences,
|
|
5484
5642
|
buildContainerRunArgs,
|
|
5485
5643
|
buildRecipeContainerArgs,
|
|
5486
5644
|
chooseStrategy,
|
|
5645
|
+
cloneTree,
|
|
5487
5646
|
codeSkill,
|
|
5488
5647
|
createCodeRuntimeControlClient,
|
|
5489
5648
|
createCodeRuntimeInference,
|
|
@@ -5500,17 +5659,23 @@ async function prepareContainerDependencies(input) {
|
|
|
5500
5659
|
feedbackIsActionable,
|
|
5501
5660
|
hasContextFreeHunk,
|
|
5502
5661
|
hazardFromAttempt,
|
|
5662
|
+
hostBuildProducts,
|
|
5503
5663
|
installedDependencies,
|
|
5504
5664
|
integrateSubGoals,
|
|
5505
5665
|
isCheckpointEffectCompleted,
|
|
5666
|
+
lendBuildProducts,
|
|
5667
|
+
lendDependencies,
|
|
5668
|
+
lentDirectories,
|
|
5506
5669
|
materializeCodeRuntimeArchive,
|
|
5507
5670
|
materializeCodeRuntimeSource,
|
|
5508
5671
|
materializeCommandWorkspace,
|
|
5509
5672
|
materializeGitTree,
|
|
5673
|
+
npmFailureSummary,
|
|
5510
5674
|
outcomeCloses,
|
|
5511
5675
|
outcomeMemory,
|
|
5512
5676
|
parseRepositoryRecipes,
|
|
5513
5677
|
patchPaths,
|
|
5678
|
+
pinnedNpmVersion,
|
|
5514
5679
|
planReachCollisions,
|
|
5515
5680
|
prepareContainerDependencies,
|
|
5516
5681
|
prepareRuntimeCheckpoint,
|
|
@@ -5518,6 +5683,7 @@ async function prepareContainerDependencies(input) {
|
|
|
5518
5683
|
readOnlyNotice,
|
|
5519
5684
|
readRepositoryRecipes,
|
|
5520
5685
|
recallAbout,
|
|
5686
|
+
recipeOutput,
|
|
5521
5687
|
registeredFiles,
|
|
5522
5688
|
renderMemories,
|
|
5523
5689
|
renderOutcome,
|
|
@@ -5549,6 +5715,7 @@ async function prepareContainerDependencies(input) {
|
|
|
5549
5715
|
verifyCodeCandidate,
|
|
5550
5716
|
verifyContainerEngineBoundary,
|
|
5551
5717
|
withProofRecipes,
|
|
5552
|
-
withRecipeDependencies
|
|
5718
|
+
withRecipeDependencies,
|
|
5719
|
+
workspaceDirs
|
|
5553
5720
|
});
|
|
5554
5721
|
//# sourceMappingURL=node.cjs.map
|