@odla-ai/harness 0.11.10 → 0.11.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZM6AITC2.js → chunk-3ON6UAOV.js} +3 -1
- package/dist/{chunk-4IGSN53G.js → chunk-6QILNLBY.js} +199 -75
- package/dist/chunk-6QILNLBY.js.map +1 -0
- package/dist/{chunk-GNNAQ2JX.js → chunk-EXSEQEZ6.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/code-runtime-cli.cjs +188 -102
- package/dist/code-runtime-cli.cjs.map +1 -1
- package/dist/code-runtime-cli.js +2 -2
- package/dist/node.cjs +326 -168
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +62 -10
- package/dist/node.d.ts +62 -10
- package/dist/node.js +116 -76
- 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,
|
|
@@ -110,7 +119,8 @@ __export(node_exports, {
|
|
|
110
119
|
verifyCodeCandidate: () => verifyCodeCandidate,
|
|
111
120
|
verifyContainerEngineBoundary: () => verifyContainerEngineBoundary,
|
|
112
121
|
withProofRecipes: () => withProofRecipes,
|
|
113
|
-
withRecipeDependencies: () => withRecipeDependencies
|
|
122
|
+
withRecipeDependencies: () => withRecipeDependencies,
|
|
123
|
+
workspaceDirs: () => workspaceDirs
|
|
114
124
|
});
|
|
115
125
|
module.exports = __toCommonJS(node_exports);
|
|
116
126
|
|
|
@@ -250,7 +260,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
|
|
|
250
260
|
throw new TypeError("no supported container engine found");
|
|
251
261
|
}
|
|
252
262
|
function inspectRootlessPodman() {
|
|
253
|
-
return new Promise((
|
|
263
|
+
return new Promise((resolve9, reject) => {
|
|
254
264
|
(0, import_node_child_process.execFile)(
|
|
255
265
|
"podman",
|
|
256
266
|
["info", "--format", "{{.Host.Security.Rootless}}"],
|
|
@@ -260,7 +270,7 @@ function inspectRootlessPodman() {
|
|
|
260
270
|
reject(new TypeError("could not verify that the active Podman service is rootless"));
|
|
261
271
|
return;
|
|
262
272
|
}
|
|
263
|
-
|
|
273
|
+
resolve9(stdout.trim() === "true");
|
|
264
274
|
}
|
|
265
275
|
);
|
|
266
276
|
});
|
|
@@ -1361,7 +1371,7 @@ async function withOverloadRetry(call, wait2, onRetry = () => void 0, retryable
|
|
|
1361
1371
|
}
|
|
1362
1372
|
|
|
1363
1373
|
// src/code-runtime-reconciler.ts
|
|
1364
|
-
var sleep = (ms) => new Promise((
|
|
1374
|
+
var sleep = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
1365
1375
|
var POST_ACK_RETRY_TICKS = 40;
|
|
1366
1376
|
var CodeRuntimeReconciler = class {
|
|
1367
1377
|
constructor(control, engine, onDiagnostic, options = {}) {
|
|
@@ -1463,12 +1473,12 @@ function retryableControlFailure(value) {
|
|
|
1463
1473
|
return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
|
|
1464
1474
|
}
|
|
1465
1475
|
function wait(ms, signal) {
|
|
1466
|
-
return new Promise((
|
|
1467
|
-
if (signal?.aborted) return
|
|
1468
|
-
const timer = setTimeout(
|
|
1476
|
+
return new Promise((resolve9) => {
|
|
1477
|
+
if (signal?.aborted) return resolve9();
|
|
1478
|
+
const timer = setTimeout(resolve9, ms);
|
|
1469
1479
|
signal?.addEventListener("abort", () => {
|
|
1470
1480
|
clearTimeout(timer);
|
|
1471
|
-
|
|
1481
|
+
resolve9();
|
|
1472
1482
|
}, { once: true });
|
|
1473
1483
|
});
|
|
1474
1484
|
}
|
|
@@ -1700,19 +1710,127 @@ function isCheckpointEffectCompleted(checkpoint, effectId, actionDigest) {
|
|
|
1700
1710
|
// src/code-verifier.ts
|
|
1701
1711
|
var import_node_crypto3 = require("crypto");
|
|
1702
1712
|
var import_node_fs2 = require("fs");
|
|
1703
|
-
var
|
|
1704
|
-
var
|
|
1713
|
+
var import_promises9 = require("fs/promises");
|
|
1714
|
+
var import_node_path9 = require("path");
|
|
1705
1715
|
var import_code3 = require("@odla-ai/camel/code");
|
|
1706
1716
|
|
|
1707
1717
|
// src/recipe-container.ts
|
|
1708
|
-
var
|
|
1718
|
+
var import_node_child_process6 = require("child_process");
|
|
1709
1719
|
var import_node_process2 = require("process");
|
|
1710
1720
|
var import_node_crypto2 = require("crypto");
|
|
1721
|
+
|
|
1722
|
+
// src/code-recipe-dependencies.ts
|
|
1723
|
+
var import_promises7 = require("fs/promises");
|
|
1724
|
+
var import_node_path7 = require("path");
|
|
1725
|
+
var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
1726
|
+
function assertLentPath(mountAs) {
|
|
1727
|
+
const parts = mountAs.split("/");
|
|
1728
|
+
if ((0, import_node_path7.isAbsolute)(mountAs) || mountAs.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
|
|
1729
|
+
throw new TypeError(`recipe dependencies must mount inside the workspace, not at "${mountAs}"`);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
function assertReservedMount(mountAs) {
|
|
1733
|
+
assertLentPath(mountAs);
|
|
1734
|
+
if (!RESERVED_MOUNTS.has(mountAs.split("/").at(-1))) {
|
|
1735
|
+
throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
function lentDirectories(dependencies) {
|
|
1739
|
+
return [{ source: dependencies.source, mountAs: dependencies.mountAs ?? "node_modules" }, ...dependencies.nested ?? []];
|
|
1740
|
+
}
|
|
1741
|
+
function withRecipeDependencies(executor, dependencies) {
|
|
1742
|
+
const lent = lentDirectories(dependencies);
|
|
1743
|
+
for (const entry of lent) {
|
|
1744
|
+
if (!(0, import_node_path7.isAbsolute)(entry.source)) {
|
|
1745
|
+
throw new TypeError("recipe dependency source must be an absolute path");
|
|
1746
|
+
}
|
|
1747
|
+
assertReservedMount(entry.mountAs);
|
|
1748
|
+
}
|
|
1749
|
+
return {
|
|
1750
|
+
run: async (input) => {
|
|
1751
|
+
const linked = [];
|
|
1752
|
+
try {
|
|
1753
|
+
for (const entry of lent) {
|
|
1754
|
+
const target = (0, import_node_path7.join)(input.workspaceDir, entry.mountAs);
|
|
1755
|
+
if (await (0, import_promises7.lstat)(target).catch(() => null)) continue;
|
|
1756
|
+
await (0, import_promises7.symlink)(entry.source, target, "dir").then(() => linked.push(target), () => void 0);
|
|
1757
|
+
}
|
|
1758
|
+
return await executor.run(input);
|
|
1759
|
+
} finally {
|
|
1760
|
+
for (const target of linked) await (0, import_promises7.rm)(target, { force: true, recursive: false }).catch(() => void 0);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
async function installedDependencies(repoRoot) {
|
|
1766
|
+
const source = (0, import_node_path7.join)(repoRoot, "node_modules");
|
|
1767
|
+
const info = await (0, import_promises7.lstat)(source).catch(() => null);
|
|
1768
|
+
return info?.isDirectory() ? { source } : null;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// src/recipe-container-lending.ts
|
|
1772
|
+
var import_node_child_process5 = require("child_process");
|
|
1773
|
+
var import_promises8 = require("fs/promises");
|
|
1774
|
+
var import_node_path8 = require("path");
|
|
1775
|
+
var RUN_ID = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
1776
|
+
function copy(args) {
|
|
1777
|
+
return new Promise((accept, reject) => {
|
|
1778
|
+
const child = (0, import_node_child_process5.spawn)("cp", args, { shell: false, stdio: "ignore" });
|
|
1779
|
+
child.once("error", reject);
|
|
1780
|
+
child.once("exit", (code) => accept(code ?? 1));
|
|
1781
|
+
});
|
|
1782
|
+
}
|
|
1783
|
+
async function cloneTree(source, target, platform = process.platform) {
|
|
1784
|
+
await (0, import_promises8.mkdir)((0, import_node_path8.dirname)(target), { recursive: true });
|
|
1785
|
+
const fast = platform === "darwin" ? ["-Rc", source, target] : platform === "linux" ? ["-R", "--reflink=auto", source, target] : null;
|
|
1786
|
+
if (fast && await copy(fast) === 0) return;
|
|
1787
|
+
await (0, import_promises8.rm)(target, { recursive: true, force: true });
|
|
1788
|
+
if (await copy(["-R", source, target]) !== 0) throw new Error(`could not clone ${source}`);
|
|
1789
|
+
}
|
|
1790
|
+
async function lendDependencies(dependencies, runId) {
|
|
1791
|
+
if (!dependencies?.runsDir) return { dependencies, release: async () => void 0 };
|
|
1792
|
+
if (!RUN_ID.test(runId)) throw new TypeError("run id is not a safe directory name");
|
|
1793
|
+
const treeRoot = (0, import_node_path8.dirname)(dependencies.source);
|
|
1794
|
+
for (const entry of lentDirectories(dependencies)) {
|
|
1795
|
+
if (entry.source !== (0, import_node_path8.join)(treeRoot, entry.mountAs)) {
|
|
1796
|
+
throw new TypeError("every directory of a cloned set must lie under its tree root at its mount path");
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
const runDir = (0, import_node_path8.join)(dependencies.runsDir, runId);
|
|
1800
|
+
await cloneTree(treeRoot, runDir);
|
|
1801
|
+
const rebase = (entry) => ({ source: (0, import_node_path8.join)(runDir, entry.mountAs), mountAs: entry.mountAs });
|
|
1802
|
+
return {
|
|
1803
|
+
dependencies: {
|
|
1804
|
+
...dependencies,
|
|
1805
|
+
source: (0, import_node_path8.join)(runDir, dependencies.mountAs ?? "node_modules"),
|
|
1806
|
+
nested: (dependencies.nested ?? []).map(rebase),
|
|
1807
|
+
writable: true
|
|
1808
|
+
},
|
|
1809
|
+
release: () => (0, import_promises8.rm)(runDir, { recursive: true, force: true })
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
async function lendBuildProducts(products, workspaceDir) {
|
|
1813
|
+
const root = (0, import_node_path8.resolve)(workspaceDir);
|
|
1814
|
+
const lent = [];
|
|
1815
|
+
for (const product of products) {
|
|
1816
|
+
assertLentPath(product.mountAs);
|
|
1817
|
+
if (SECRET_WORKSPACE_FILE.test(product.mountAs.split("/").at(-1))) throw new TypeError("a secret is never a build product");
|
|
1818
|
+
const target = (0, import_node_path8.resolve)(root, product.mountAs);
|
|
1819
|
+
if (!target.startsWith(`${root}${import_node_path8.sep}`)) throw new TypeError("build product escapes the workspace");
|
|
1820
|
+
if (await (0, import_promises8.stat)(target).catch(() => null)) continue;
|
|
1821
|
+
if (!await (0, import_promises8.stat)((0, import_node_path8.join)(root, product.within ?? (0, import_node_path8.dirname)(product.mountAs))).catch(() => null)) continue;
|
|
1822
|
+
await cloneTree(product.source, target);
|
|
1823
|
+
lent.push(product.mountAs);
|
|
1824
|
+
}
|
|
1825
|
+
return lent;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// src/recipe-container.ts
|
|
1711
1829
|
var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
1712
1830
|
var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
|
|
1713
1831
|
function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}`, dependencies = null) {
|
|
1714
1832
|
assertCodeBuildRecipe(recipe2);
|
|
1715
|
-
const mounts = dependencies ?
|
|
1833
|
+
const mounts = dependencies ? dependencyMounts(engine, dependencies) : [];
|
|
1716
1834
|
if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
|
|
1717
1835
|
const uid = typeof import_node_process2.getuid === "function" ? (0, import_node_process2.getuid)() : 1e3;
|
|
1718
1836
|
const gid = typeof import_node_process2.getgid === "function" ? (0, import_node_process2.getgid)() : 1e3;
|
|
@@ -1738,6 +1856,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1738
1856
|
...mounts,
|
|
1739
1857
|
"--workdir=/workspace",
|
|
1740
1858
|
"--env=CI=1",
|
|
1859
|
+
"--env=HOME=/tmp",
|
|
1741
1860
|
recipe2.image,
|
|
1742
1861
|
...recipe2.command
|
|
1743
1862
|
];
|
|
@@ -1760,26 +1879,34 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1760
1879
|
...mounts,
|
|
1761
1880
|
"--workdir=/workspace",
|
|
1762
1881
|
"--env=CI=1",
|
|
1882
|
+
"--env=HOME=/tmp",
|
|
1763
1883
|
recipe2.image,
|
|
1764
1884
|
...recipe2.command
|
|
1765
1885
|
];
|
|
1766
1886
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1887
|
+
function dependencyMounts(engine, dependencies) {
|
|
1888
|
+
const readonly = dependencies.writable ? "" : ",readonly";
|
|
1889
|
+
return lentDirectories(dependencies).map(({ source, mountAs }) => {
|
|
1890
|
+
if (!source.startsWith("/") || /[,\r\n]/.test(source)) {
|
|
1891
|
+
throw new TypeError("recipe dependency source must be an absolute path without mount separators");
|
|
1892
|
+
}
|
|
1893
|
+
assertReservedMount(mountAs);
|
|
1894
|
+
return engine === "container" ? `--mount=type=bind,source=${source},target=/workspace/${mountAs}${readonly}` : `--mount=type=bind,src=${source},dst=/workspace/${mountAs}${readonly}`;
|
|
1895
|
+
});
|
|
1775
1896
|
}
|
|
1776
1897
|
function createContainerRecipeExecutor(engine, options = {}) {
|
|
1777
1898
|
return {
|
|
1778
1899
|
async run(input) {
|
|
1779
1900
|
await verifyContainerEngineBoundary(engine);
|
|
1780
1901
|
const name = `odla-recipe-${(0, import_node_crypto2.randomUUID)().slice(0, 12)}`;
|
|
1781
|
-
const
|
|
1782
|
-
|
|
1902
|
+
const lent = await lendDependencies(options.dependencies ?? null, name);
|
|
1903
|
+
try {
|
|
1904
|
+
if (lent.dependencies?.products?.length) await lendBuildProducts(lent.dependencies.products, input.workspaceDir);
|
|
1905
|
+
const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, lent.dependencies);
|
|
1906
|
+
return await execute(engine, args, name, input.recipe, input.signal);
|
|
1907
|
+
} finally {
|
|
1908
|
+
await lent.release();
|
|
1909
|
+
}
|
|
1783
1910
|
}
|
|
1784
1911
|
};
|
|
1785
1912
|
}
|
|
@@ -1804,7 +1931,7 @@ function execute(engine, args, name, recipe2, signal) {
|
|
|
1804
1931
|
function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
1805
1932
|
return new Promise((accept, reject) => {
|
|
1806
1933
|
const started = Date.now();
|
|
1807
|
-
const child = (0,
|
|
1934
|
+
const child = (0, import_node_child_process6.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
1808
1935
|
const stdout = [];
|
|
1809
1936
|
const stderr = [];
|
|
1810
1937
|
let bytes = 0;
|
|
@@ -1817,7 +1944,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
|
1817
1944
|
timedOut = reason === "timeout";
|
|
1818
1945
|
outputLimitExceeded = reason === "output";
|
|
1819
1946
|
const remove = engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
|
|
1820
|
-
const killer = (0,
|
|
1947
|
+
const killer = (0, import_node_child_process6.spawn)(engine, remove, { shell: false, stdio: "ignore" });
|
|
1821
1948
|
killer.unref();
|
|
1822
1949
|
child.kill("SIGTERM");
|
|
1823
1950
|
};
|
|
@@ -1961,8 +2088,8 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
1961
2088
|
const receipts = [];
|
|
1962
2089
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
1963
2090
|
try {
|
|
1964
|
-
const path = (0,
|
|
1965
|
-
const info = await (0,
|
|
2091
|
+
const path = (0, import_node_path9.join)(workspaceDir, artifact.path);
|
|
2092
|
+
const info = await (0, import_promises9.lstat)(path);
|
|
1966
2093
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
1967
2094
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
1968
2095
|
} else if (info.size > artifact.maximumBytes) {
|
|
@@ -2293,15 +2420,15 @@ async function prepareRuntimeLocalSource(input) {
|
|
|
2293
2420
|
}
|
|
2294
2421
|
|
|
2295
2422
|
// src/code-runtime-source.ts
|
|
2296
|
-
var
|
|
2423
|
+
var import_promises12 = require("fs/promises");
|
|
2297
2424
|
var import_node_os4 = require("os");
|
|
2298
|
-
var
|
|
2425
|
+
var import_node_path12 = require("path");
|
|
2299
2426
|
|
|
2300
2427
|
// src/code-runtime-archive.ts
|
|
2301
2428
|
var import_node_zlib = require("zlib");
|
|
2302
|
-
var
|
|
2429
|
+
var import_promises10 = require("fs/promises");
|
|
2303
2430
|
var import_node_os3 = require("os");
|
|
2304
|
-
var
|
|
2431
|
+
var import_node_path10 = require("path");
|
|
2305
2432
|
var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
2306
2433
|
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
2307
2434
|
var MAX_NUL_SHARE = 0.1;
|
|
@@ -2319,9 +2446,9 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
|
|
|
2319
2446
|
throw new TypeError("Code source archive exceeds its decompressed byte bound");
|
|
2320
2447
|
}
|
|
2321
2448
|
const entries = parseTar(bytes, archive.limits);
|
|
2322
|
-
const root = await (0,
|
|
2323
|
-
const sourceDir = (0,
|
|
2324
|
-
await (0,
|
|
2449
|
+
const root = await (0, import_promises10.mkdtemp)((0, import_node_path10.join)(tempRoot, "odla-code-archive-"));
|
|
2450
|
+
const sourceDir = (0, import_node_path10.join)(root, "source");
|
|
2451
|
+
await (0, import_promises10.mkdir)(sourceDir);
|
|
2325
2452
|
let visible = 0;
|
|
2326
2453
|
try {
|
|
2327
2454
|
for (const entry of entries) {
|
|
@@ -2334,16 +2461,16 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
|
|
|
2334
2461
|
continue;
|
|
2335
2462
|
}
|
|
2336
2463
|
if (nulShare(content) > MAX_NUL_SHARE) continue;
|
|
2337
|
-
const target = (0,
|
|
2338
|
-
if (!target.startsWith(`${(0,
|
|
2339
|
-
await (0,
|
|
2340
|
-
await (0,
|
|
2464
|
+
const target = (0, import_node_path10.resolve)(sourceDir, entry.path);
|
|
2465
|
+
if (!target.startsWith(`${(0, import_node_path10.resolve)(sourceDir)}${import_node_path10.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
2466
|
+
await (0, import_promises10.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
|
|
2467
|
+
await (0, import_promises10.writeFile)(target, content, { flag: "wx", mode: 420 });
|
|
2341
2468
|
visible += 1;
|
|
2342
2469
|
}
|
|
2343
2470
|
if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
|
|
2344
|
-
return { sourceDir, cleanup: () => (0,
|
|
2471
|
+
return { sourceDir, cleanup: () => (0, import_promises10.rm)(root, { recursive: true, force: true }) };
|
|
2345
2472
|
} catch (cause) {
|
|
2346
|
-
await (0,
|
|
2473
|
+
await (0, import_promises10.rm)(root, { recursive: true, force: true });
|
|
2347
2474
|
throw cause;
|
|
2348
2475
|
}
|
|
2349
2476
|
}
|
|
@@ -2538,8 +2665,8 @@ function nulShare(content) {
|
|
|
2538
2665
|
}
|
|
2539
2666
|
|
|
2540
2667
|
// src/code-runtime-selected-source.ts
|
|
2541
|
-
var
|
|
2542
|
-
var
|
|
2668
|
+
var import_promises11 = require("fs/promises");
|
|
2669
|
+
var import_node_path11 = require("path");
|
|
2543
2670
|
function selectedSourceSet(payload) {
|
|
2544
2671
|
if (!payload.sourceSet) return null;
|
|
2545
2672
|
const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
|
|
@@ -2576,9 +2703,9 @@ async function attachReferenceDirectories(workspace, references) {
|
|
|
2576
2703
|
for (const reference of references) {
|
|
2577
2704
|
validateAlias(reference.alias);
|
|
2578
2705
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
2579
|
-
const target = (0,
|
|
2580
|
-
await (0,
|
|
2581
|
-
await (0,
|
|
2706
|
+
const target = (0, import_node_path11.join)(root, ".odla-references", reference.alias);
|
|
2707
|
+
await (0, import_promises11.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
2708
|
+
await (0, import_promises11.cp)(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
|
|
2582
2709
|
await makeTreeReadOnly(target);
|
|
2583
2710
|
}
|
|
2584
2711
|
}
|
|
@@ -2589,10 +2716,10 @@ function validateAlias(alias) {
|
|
|
2589
2716
|
}
|
|
2590
2717
|
}
|
|
2591
2718
|
async function makeTreeReadOnly(root) {
|
|
2592
|
-
for (const entry of await (0,
|
|
2593
|
-
const target = (0,
|
|
2719
|
+
for (const entry of await (0, import_promises11.readdir)(root, { withFileTypes: true })) {
|
|
2720
|
+
const target = (0, import_node_path11.join)(root, entry.name);
|
|
2594
2721
|
if (entry.isDirectory()) await makeTreeReadOnly(target);
|
|
2595
|
-
else if (entry.isFile()) await (0,
|
|
2722
|
+
else if (entry.isFile()) await (0, import_promises11.chmod)(target, 292);
|
|
2596
2723
|
}
|
|
2597
2724
|
}
|
|
2598
2725
|
|
|
@@ -2604,9 +2731,9 @@ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
|
|
|
2604
2731
|
var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
|
|
2605
2732
|
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os4.tmpdir)()) {
|
|
2606
2733
|
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,
|
|
2734
|
+
const root = await (0, import_promises12.mkdtemp)((0, import_node_path12.join)(tempRoot, "odla-code-source-"));
|
|
2735
|
+
const sourceDir = (0, import_node_path12.join)(root, "source");
|
|
2736
|
+
await (0, import_promises12.mkdir)(sourceDir);
|
|
2610
2737
|
const seen = /* @__PURE__ */ new Set();
|
|
2611
2738
|
let bytes = 0;
|
|
2612
2739
|
try {
|
|
@@ -2616,10 +2743,10 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
2616
2743
|
seen.add(file.path);
|
|
2617
2744
|
bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
|
|
2618
2745
|
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,
|
|
2746
|
+
const target = (0, import_node_path12.resolve)(sourceDir, file.path);
|
|
2747
|
+
if (!target.startsWith(`${(0, import_node_path12.resolve)(sourceDir)}${import_node_path12.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
2748
|
+
await (0, import_promises12.mkdir)((0, import_node_path12.dirname)(target), { recursive: true });
|
|
2749
|
+
await (0, import_promises12.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
2623
2750
|
}
|
|
2624
2751
|
for (const reference of snapshot.references ?? []) {
|
|
2625
2752
|
validateAlias2(reference.alias);
|
|
@@ -2631,15 +2758,15 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
2631
2758
|
seen.add(path);
|
|
2632
2759
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
2633
2760
|
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,
|
|
2761
|
+
const target = (0, import_node_path12.resolve)(sourceDir, path);
|
|
2762
|
+
if (!target.startsWith(`${(0, import_node_path12.resolve)(sourceDir)}${import_node_path12.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
2763
|
+
await (0, import_promises12.mkdir)((0, import_node_path12.dirname)(target), { recursive: true });
|
|
2764
|
+
await (0, import_promises12.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
2638
2765
|
}
|
|
2639
2766
|
}
|
|
2640
|
-
return { sourceDir, cleanup: () => (0,
|
|
2767
|
+
return { sourceDir, cleanup: () => (0, import_promises12.rm)(root, { recursive: true, force: true }) };
|
|
2641
2768
|
} catch (cause) {
|
|
2642
|
-
await (0,
|
|
2769
|
+
await (0, import_promises12.rm)(root, { recursive: true, force: true });
|
|
2643
2770
|
throw cause;
|
|
2644
2771
|
}
|
|
2645
2772
|
}
|
|
@@ -2658,10 +2785,10 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
2658
2785
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
2659
2786
|
if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
|
|
2660
2787
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
2661
|
-
const target = (0,
|
|
2662
|
-
if (!target.startsWith(`${(0,
|
|
2663
|
-
await (0,
|
|
2664
|
-
await (0,
|
|
2788
|
+
const target = (0, import_node_path12.resolve)(root, path);
|
|
2789
|
+
if (!target.startsWith(`${(0, import_node_path12.resolve)(root)}${import_node_path12.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
2790
|
+
await (0, import_promises12.mkdir)((0, import_node_path12.dirname)(target), { recursive: true });
|
|
2791
|
+
await (0, import_promises12.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
2665
2792
|
}
|
|
2666
2793
|
}
|
|
2667
2794
|
}
|
|
@@ -3107,7 +3234,7 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
|
|
|
3107
3234
|
}
|
|
3108
3235
|
|
|
3109
3236
|
// src/code-runtime-session-skills.ts
|
|
3110
|
-
var sleep2 = (ms) => new Promise((
|
|
3237
|
+
var sleep2 = (ms) => new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
3111
3238
|
function createCodeRuntimeSessionSkillLoader(control, options = {}) {
|
|
3112
3239
|
const wait2 = options.wait ?? sleep2;
|
|
3113
3240
|
const load = control.collaborationSkills?.bind(control);
|
|
@@ -3166,7 +3293,7 @@ var inferWithBackoff = (infer, wait2, onRetry) => withOverloadRetry(infer, wait2
|
|
|
3166
3293
|
async function handleCodeRuntimeInference(input) {
|
|
3167
3294
|
const { command, request, state } = input;
|
|
3168
3295
|
const startedAt = Date.now();
|
|
3169
|
-
const wait2 = input.wait ?? ((ms) => new Promise((
|
|
3296
|
+
const wait2 = input.wait ?? ((ms) => new Promise((resolve9) => setTimeout(resolve9, ms)));
|
|
3170
3297
|
const response2 = await inferWithBackoff(
|
|
3171
3298
|
() => input.control.infer(command.sessionId, {
|
|
3172
3299
|
requestId: request.requestId,
|
|
@@ -3677,18 +3804,18 @@ function response(request, ok, content, details) {
|
|
|
3677
3804
|
}
|
|
3678
3805
|
|
|
3679
3806
|
// src/code-tool-edit.ts
|
|
3680
|
-
var
|
|
3807
|
+
var import_promises14 = require("fs/promises");
|
|
3681
3808
|
|
|
3682
3809
|
// src/code-tool-graph.ts
|
|
3683
|
-
var
|
|
3684
|
-
var
|
|
3810
|
+
var import_promises13 = require("fs/promises");
|
|
3811
|
+
var import_node_path13 = require("path");
|
|
3685
3812
|
var import_graph = require("@odla-ai/graph");
|
|
3686
3813
|
var import_code4 = require("@odla-ai/graph/code");
|
|
3687
3814
|
var cache = /* @__PURE__ */ new Map();
|
|
3688
3815
|
function workspaceGraphs(workspaceDir, paths2) {
|
|
3689
3816
|
const existing = cache.get(workspaceDir);
|
|
3690
3817
|
if (existing) return existing;
|
|
3691
|
-
const read2 = (path) => (0,
|
|
3818
|
+
const read2 = (path) => (0, import_promises13.readFile)((0, import_node_path13.join)(workspaceDir, path), "utf8");
|
|
3692
3819
|
const built = (async () => ({
|
|
3693
3820
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
3694
3821
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
@@ -3764,7 +3891,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
|
|
|
3764
3891
|
}
|
|
3765
3892
|
if (oldText.includes("\0") || newText.includes("\0")) throw new TypeError("edit text contains NUL bytes; use plain text");
|
|
3766
3893
|
const target = resolveCodePath(workspaceDir, path);
|
|
3767
|
-
const current = await (0,
|
|
3894
|
+
const current = await (0, import_promises14.readFile)(target, "utf8");
|
|
3768
3895
|
const found = occurrences(current, oldText);
|
|
3769
3896
|
if (found === 0) {
|
|
3770
3897
|
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 +3900,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
|
|
|
3773
3900
|
throw new TypeError(`oldText occurs ${found} times in "${path}"; include more of the surrounding lines so it matches exactly once`);
|
|
3774
3901
|
}
|
|
3775
3902
|
const at = current.indexOf(oldText);
|
|
3776
|
-
await (0,
|
|
3903
|
+
await (0, import_promises14.writeFile)(target, `${current.slice(0, at)}${newText}${current.slice(at + oldText.length)}`, "utf8");
|
|
3777
3904
|
return { deletions: oldText.split("\n").length, additions: newText.split("\n").length };
|
|
3778
3905
|
}
|
|
3779
3906
|
async function edit(context, request, options, policy, registry) {
|
|
@@ -3798,8 +3925,8 @@ async function edit(context, request, options, policy, registry) {
|
|
|
3798
3925
|
}
|
|
3799
3926
|
|
|
3800
3927
|
// src/code-tool-write.ts
|
|
3801
|
-
var
|
|
3802
|
-
var
|
|
3928
|
+
var import_promises15 = require("fs/promises");
|
|
3929
|
+
var import_node_path14 = require("path");
|
|
3803
3930
|
var MAX_WRITE_BYTES = 256 * 1024;
|
|
3804
3931
|
var lines = (text2) => text2 === "" ? [] : text2.replace(/\n$/, "").split("\n");
|
|
3805
3932
|
function writeAsDiff(path, current, content) {
|
|
@@ -3824,8 +3951,8 @@ async function writeCodeFile(workspaceDir, path, content, current) {
|
|
|
3824
3951
|
}
|
|
3825
3952
|
if (content.includes("\0")) throw new TypeError("content contains NUL bytes; use plain text");
|
|
3826
3953
|
const target = resolveCodePath(workspaceDir, path);
|
|
3827
|
-
await (0,
|
|
3828
|
-
await (0,
|
|
3954
|
+
await (0, import_promises15.mkdir)((0, import_node_path14.dirname)(target), { recursive: true });
|
|
3955
|
+
await (0, import_promises15.writeFile)(target, content, "utf8");
|
|
3829
3956
|
return { created: current === null, deletions: current === null ? 0 : lines(current).length, additions: lines(content).length };
|
|
3830
3957
|
}
|
|
3831
3958
|
async function write(context, request, options, policy, registry) {
|
|
@@ -3836,7 +3963,7 @@ async function write(context, request, options, policy, registry) {
|
|
|
3836
3963
|
throw new TypeError("write targets a read-only reference source");
|
|
3837
3964
|
}
|
|
3838
3965
|
const exists2 = (await registry.files(context.workspaceDir)).includes(path);
|
|
3839
|
-
const current = exists2 ? await (0,
|
|
3966
|
+
const current = exists2 ? await (0, import_promises15.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
|
|
3840
3967
|
const patch2 = writeAsDiff(path, current, content);
|
|
3841
3968
|
const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
|
|
3842
3969
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
@@ -3852,12 +3979,12 @@ async function write(context, request, options, policy, registry) {
|
|
|
3852
3979
|
}
|
|
3853
3980
|
|
|
3854
3981
|
// src/code-tool-reads.ts
|
|
3855
|
-
var
|
|
3982
|
+
var import_promises17 = require("fs/promises");
|
|
3856
3983
|
|
|
3857
3984
|
// src/code-tool-discovery.ts
|
|
3858
|
-
var
|
|
3859
|
-
var
|
|
3860
|
-
var
|
|
3985
|
+
var import_node_child_process7 = require("child_process");
|
|
3986
|
+
var import_promises16 = require("fs/promises");
|
|
3987
|
+
var import_node_path15 = require("path");
|
|
3861
3988
|
var DEFAULT_MAX_FILES = 2e4;
|
|
3862
3989
|
var DEFAULT_MAX_RESULTS = 100;
|
|
3863
3990
|
var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
@@ -3882,13 +4009,13 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
3882
4009
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
3883
4010
|
const paths2 = [];
|
|
3884
4011
|
const walk = async (directory) => {
|
|
3885
|
-
for (const entry of await (0,
|
|
4012
|
+
for (const entry of await (0, import_promises16.readdir)(directory, { withFileTypes: true })) {
|
|
3886
4013
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
3887
4014
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
3888
|
-
const target = (0,
|
|
4015
|
+
const target = (0, import_node_path15.resolve)(directory, entry.name);
|
|
3889
4016
|
if (entry.isDirectory()) await walk(target);
|
|
3890
4017
|
else if (entry.isFile()) {
|
|
3891
|
-
const path = (0,
|
|
4018
|
+
const path = (0, import_node_path15.relative)(root, target).split("\\").join("/");
|
|
3892
4019
|
try {
|
|
3893
4020
|
validateRelativePath(path);
|
|
3894
4021
|
} catch {
|
|
@@ -3899,7 +4026,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
3899
4026
|
}
|
|
3900
4027
|
}
|
|
3901
4028
|
};
|
|
3902
|
-
await walk((0,
|
|
4029
|
+
await walk((0, import_node_path15.resolve)(root));
|
|
3903
4030
|
return paths2.sort();
|
|
3904
4031
|
}
|
|
3905
4032
|
function listWorkspace(paths2, options = {}) {
|
|
@@ -3961,7 +4088,7 @@ function nativeSearchBatch(root, paths2, options, remaining) {
|
|
|
3961
4088
|
options.query,
|
|
3962
4089
|
...paths2
|
|
3963
4090
|
];
|
|
3964
|
-
const child = (0,
|
|
4091
|
+
const child = (0, import_node_child_process7.spawn)("rg", args, {
|
|
3965
4092
|
cwd: root,
|
|
3966
4093
|
stdio: ["ignore", "pipe", "ignore"],
|
|
3967
4094
|
...options.signal ? { signal: options.signal } : {}
|
|
@@ -4013,7 +4140,7 @@ async function fallbackSearch(root, scoped, options) {
|
|
|
4013
4140
|
if (matches.length >= options.maxResults) break;
|
|
4014
4141
|
let source;
|
|
4015
4142
|
try {
|
|
4016
|
-
source = await (0,
|
|
4143
|
+
source = await (0, import_promises16.readFile)((0, import_node_path15.resolve)(root, path));
|
|
4017
4144
|
} catch {
|
|
4018
4145
|
continue;
|
|
4019
4146
|
}
|
|
@@ -4052,11 +4179,11 @@ async function read(context, request, options, policy, registry) {
|
|
|
4052
4179
|
const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
|
|
4053
4180
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
4054
4181
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
4055
|
-
const info = await (0,
|
|
4182
|
+
const info = await (0, import_promises17.stat)(target);
|
|
4056
4183
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
4057
4184
|
throw new TypeError("file is not a bounded regular source file");
|
|
4058
4185
|
}
|
|
4059
|
-
const source = await (0,
|
|
4186
|
+
const source = await (0, import_promises17.readFile)(target);
|
|
4060
4187
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
4061
4188
|
const lines2 = source.toString("utf8").split("\n");
|
|
4062
4189
|
const content = lines2.slice(startLine - 1, endLine).join("\n");
|
|
@@ -4604,8 +4731,8 @@ var runtimeErrorMessage = (value) => value instanceof Error ? value.message : St
|
|
|
4604
4731
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
4605
4732
|
let settle;
|
|
4606
4733
|
let settled = false;
|
|
4607
|
-
const ready = new Promise((
|
|
4608
|
-
settle =
|
|
4734
|
+
const ready = new Promise((resolve9) => {
|
|
4735
|
+
settle = resolve9;
|
|
4609
4736
|
});
|
|
4610
4737
|
const release = (run) => {
|
|
4611
4738
|
if (settled) return;
|
|
@@ -4671,8 +4798,8 @@ function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
|
4671
4798
|
}
|
|
4672
4799
|
|
|
4673
4800
|
// src/code-repository-recipes.ts
|
|
4674
|
-
var
|
|
4675
|
-
var
|
|
4801
|
+
var import_promises18 = require("fs/promises");
|
|
4802
|
+
var import_node_path16 = require("path");
|
|
4676
4803
|
var REPOSITORY_RECIPES_FILE = "odla.recipes.json";
|
|
4677
4804
|
var MAX_RECIPES = 16;
|
|
4678
4805
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -4733,7 +4860,7 @@ function parseRepositoryRecipes(text2, envelope) {
|
|
|
4733
4860
|
async function readRepositoryRecipes(baselineDir, envelope) {
|
|
4734
4861
|
let text2;
|
|
4735
4862
|
try {
|
|
4736
|
-
text2 = await (0,
|
|
4863
|
+
text2 = await (0, import_promises18.readFile)((0, import_node_path16.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
|
|
4737
4864
|
} catch (cause) {
|
|
4738
4865
|
if (cause.code === "ENOENT") return null;
|
|
4739
4866
|
throw cause;
|
|
@@ -5334,82 +5461,58 @@ function chooseStrategy(signals = {}) {
|
|
|
5334
5461
|
}
|
|
5335
5462
|
var feedbackIsActionable = actionable;
|
|
5336
5463
|
|
|
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
5464
|
// src/code-recipe-dependency-cache.ts
|
|
5373
5465
|
var import_node_crypto5 = require("crypto");
|
|
5374
|
-
var
|
|
5466
|
+
var import_promises19 = require("fs/promises");
|
|
5375
5467
|
var import_node_process3 = require("process");
|
|
5376
|
-
var
|
|
5468
|
+
var import_node_path17 = require("path");
|
|
5377
5469
|
var DEPENDENCY_INSTALL_TIMEOUT_MS = 20 * 6e4;
|
|
5378
5470
|
var OUTPUT_CAP = 4 * 1024 * 1024;
|
|
5379
|
-
var
|
|
5380
|
-
async
|
|
5381
|
-
|
|
5471
|
+
var EXACT_VERSION = /^\d+\.\d+\.\d+$/;
|
|
5472
|
+
var exists = async (path) => Boolean(await (0, import_promises19.stat)(path).catch(() => null));
|
|
5473
|
+
async function pinnedNpmVersion(repoRoot) {
|
|
5474
|
+
const manifest = await (0, import_promises19.readFile)((0, import_node_path17.join)(repoRoot, "package.json"), "utf8").catch(() => null);
|
|
5475
|
+
if (!manifest) return null;
|
|
5476
|
+
const engines = JSON.parse(manifest).engines;
|
|
5477
|
+
return typeof engines?.npm === "string" && EXACT_VERSION.test(engines.npm) ? engines.npm : null;
|
|
5478
|
+
}
|
|
5479
|
+
async function dependencyCacheKey(repoRoot, image, arch = process.arch, npmVersion = null) {
|
|
5480
|
+
const lock = await (0, import_promises19.readFile)((0, import_node_path17.join)(repoRoot, "package-lock.json")).catch(() => null);
|
|
5382
5481
|
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);
|
|
5482
|
+
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);
|
|
5483
|
+
}
|
|
5484
|
+
async function workspaceDirs(repoRoot) {
|
|
5485
|
+
const root = JSON.parse(await (0, import_promises19.readFile)((0, import_node_path17.join)(repoRoot, "package.json"), "utf8"));
|
|
5486
|
+
const globs = Array.isArray(root.workspaces) ? root.workspaces : root.workspaces?.packages ?? [];
|
|
5487
|
+
const dirs = [];
|
|
5488
|
+
for (const glob of globs) {
|
|
5489
|
+
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];
|
|
5490
|
+
for (const dir of found) if (await exists((0, import_node_path17.join)(repoRoot, dir, "package.json"))) dirs.push(dir);
|
|
5491
|
+
}
|
|
5492
|
+
return dirs;
|
|
5384
5493
|
}
|
|
5385
5494
|
async function stageManifests(repoRoot, stageDir) {
|
|
5386
|
-
await (0,
|
|
5495
|
+
await (0, import_promises19.mkdir)(stageDir, { recursive: true });
|
|
5387
5496
|
const staged = [];
|
|
5388
5497
|
for (const name of ["package.json", "package-lock.json", ".npmrc"]) {
|
|
5389
|
-
if (!await exists((0,
|
|
5390
|
-
await (0,
|
|
5498
|
+
if (!await exists((0, import_node_path17.join)(repoRoot, name))) continue;
|
|
5499
|
+
await (0, import_promises19.copyFile)((0, import_node_path17.join)(repoRoot, name), (0, import_node_path17.join)(stageDir, name));
|
|
5391
5500
|
staged.push(name);
|
|
5392
5501
|
}
|
|
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
|
-
}
|
|
5502
|
+
for (const dir of await workspaceDirs(repoRoot)) {
|
|
5503
|
+
await (0, import_promises19.mkdir)((0, import_node_path17.join)(stageDir, dir), { recursive: true });
|
|
5504
|
+
await (0, import_promises19.copyFile)((0, import_node_path17.join)(repoRoot, dir, "package.json"), (0, import_node_path17.join)(stageDir, dir, "package.json"));
|
|
5505
|
+
staged.push(`${dir}/package.json`);
|
|
5404
5506
|
}
|
|
5405
5507
|
return staged;
|
|
5406
5508
|
}
|
|
5407
|
-
function dependencyInstallArgs(engine, image, stageDir, name) {
|
|
5509
|
+
function dependencyInstallArgs(engine, image, stageDir, name, npmVersion = null) {
|
|
5408
5510
|
if (/[,\r\n]/.test(stageDir)) throw new TypeError("stage path contains unsupported mount characters");
|
|
5409
5511
|
const uid = typeof import_node_process3.getuid === "function" ? (0, import_node_process3.getuid)() : 1e3;
|
|
5410
5512
|
const gid = typeof import_node_process3.getgid === "function" ? (0, import_node_process3.getgid)() : 1e3;
|
|
5411
5513
|
const mount = engine === "container" ? `--mount=type=bind,source=${stageDir},target=/workspace` : `--mount=type=bind,src=${stageDir},dst=/workspace`;
|
|
5412
|
-
const
|
|
5514
|
+
const install = ["ci", "--no-audit", "--no-fund", "--ignore-scripts", "--engine-strict=false"];
|
|
5515
|
+
const npm = npmVersion ? ["npx", "--yes", `npm@${npmVersion}`, ...install] : ["npm", ...install];
|
|
5413
5516
|
return [
|
|
5414
5517
|
"run",
|
|
5415
5518
|
"--rm",
|
|
@@ -5428,34 +5531,79 @@ function dependencyInstallArgs(engine, image, stageDir, name) {
|
|
|
5428
5531
|
...npm
|
|
5429
5532
|
];
|
|
5430
5533
|
}
|
|
5534
|
+
function npmFailureSummary(stderr) {
|
|
5535
|
+
const lines2 = stderr.split("\n").map((line) => line.trim()).filter((line) => line && line !== "npm error");
|
|
5536
|
+
const at = lines2.findIndex((line) => /^npm error code /.test(line));
|
|
5537
|
+
return (at >= 0 ? lines2.slice(at, at + 3) : lines2.slice(-3)).join(" | ").slice(0, 400);
|
|
5538
|
+
}
|
|
5539
|
+
async function nestedTrees(treeRoot, dirs) {
|
|
5540
|
+
const nested = [];
|
|
5541
|
+
for (const dir of dirs) {
|
|
5542
|
+
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` });
|
|
5543
|
+
}
|
|
5544
|
+
return nested;
|
|
5545
|
+
}
|
|
5431
5546
|
async function prepareContainerDependencies(input) {
|
|
5432
|
-
const
|
|
5547
|
+
const npmVersion = await pinnedNpmVersion(input.repoRoot);
|
|
5548
|
+
const key = await dependencyCacheKey(input.repoRoot, input.image, process.arch, npmVersion);
|
|
5433
5549
|
if (!key) return null;
|
|
5434
|
-
const dir = (0,
|
|
5435
|
-
const tree = (0,
|
|
5436
|
-
const stage = (0,
|
|
5437
|
-
|
|
5550
|
+
const dir = (0, import_node_path17.join)(input.cacheRoot, key);
|
|
5551
|
+
const tree = (0, import_node_path17.join)(dir, "tree");
|
|
5552
|
+
const stage = (0, import_node_path17.join)(dir, "stage");
|
|
5553
|
+
const runsDir = (0, import_node_path17.join)(dir, "runs");
|
|
5554
|
+
await (0, import_promises19.rm)(runsDir, { recursive: true, force: true });
|
|
5555
|
+
const dirs = await workspaceDirs(input.repoRoot);
|
|
5556
|
+
const lend = async () => ({ source: (0, import_node_path17.join)(tree, "node_modules"), nested: await nestedTrees(tree, dirs), runsDir });
|
|
5557
|
+
if (await exists((0, import_node_path17.join)(tree, "node_modules"))) return lend();
|
|
5438
5558
|
const run = input.run ?? runContainerCommand;
|
|
5439
|
-
await (0,
|
|
5559
|
+
await (0, import_promises19.rm)(stage, { recursive: true, force: true });
|
|
5440
5560
|
const staged = await stageManifests(input.repoRoot, stage);
|
|
5441
|
-
input.log?.(`installing ${key.slice(0, 12)}: ${staged.length} manifests, npm ci in ${input.image}`);
|
|
5561
|
+
input.log?.(`installing ${key.slice(0, 12)}: ${staged.length} manifests, npm${npmVersion ? `@${npmVersion}` : ""} ci in ${input.image}`);
|
|
5442
5562
|
const name = `odla-deps-${key.slice(0, 12)}`;
|
|
5443
5563
|
const result = await run(
|
|
5444
5564
|
input.engine,
|
|
5445
|
-
dependencyInstallArgs(input.engine, input.image, stage, name),
|
|
5565
|
+
dependencyInstallArgs(input.engine, input.image, stage, name, npmVersion),
|
|
5446
5566
|
name,
|
|
5447
5567
|
{ timeoutMs: DEPENDENCY_INSTALL_TIMEOUT_MS, maxOutputBytes: OUTPUT_CAP }
|
|
5448
5568
|
);
|
|
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,
|
|
5569
|
+
if (result.exitCode !== 0 || result.timedOut || !await exists((0, import_node_path17.join)(stage, "node_modules"))) {
|
|
5570
|
+
input.log?.(`install failed (exit ${result.exitCode}${result.timedOut ? ", timed out" : ""}): ${npmFailureSummary(result.stderr)}`);
|
|
5571
|
+
await (0, import_promises19.rm)(stage, { recursive: true, force: true });
|
|
5452
5572
|
return null;
|
|
5453
5573
|
}
|
|
5454
|
-
await (0,
|
|
5455
|
-
await (0,
|
|
5456
|
-
await (0,
|
|
5457
|
-
|
|
5458
|
-
|
|
5574
|
+
await (0, import_promises19.rm)(tree, { recursive: true, force: true });
|
|
5575
|
+
await (0, import_promises19.rename)(stage, tree);
|
|
5576
|
+
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));
|
|
5577
|
+
const lent = await lend();
|
|
5578
|
+
input.log?.(`installed ${key.slice(0, 12)} in ${Math.round(result.durationMs / 1e3)}s, ${lent.nested?.length ?? 0} nested tree(s)`);
|
|
5579
|
+
return lent;
|
|
5580
|
+
}
|
|
5581
|
+
|
|
5582
|
+
// src/code-recipe-build-products.ts
|
|
5583
|
+
var import_node_child_process8 = require("child_process");
|
|
5584
|
+
var import_promises20 = require("fs/promises");
|
|
5585
|
+
var import_node_path18 = require("path");
|
|
5586
|
+
function gitIgnoredFiles(repoRoot) {
|
|
5587
|
+
return new Promise((accept) => {
|
|
5588
|
+
const child = (0, import_node_child_process8.spawn)("git", ["-C", repoRoot, "ls-files", "--others", "--ignored", "--exclude-standard", "-z"], { shell: false, stdio: ["ignore", "pipe", "ignore"] });
|
|
5589
|
+
const chunks = [];
|
|
5590
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
5591
|
+
child.once("error", () => accept([]));
|
|
5592
|
+
child.once("exit", (code) => accept(code === 0 ? Buffer.concat(chunks).toString("utf8").split("\0").filter(Boolean) : []));
|
|
5593
|
+
});
|
|
5594
|
+
}
|
|
5595
|
+
async function hostBuildProducts(repoRoot) {
|
|
5596
|
+
const dirs = await workspaceDirs(repoRoot);
|
|
5597
|
+
const within = (path) => dirs.find((dir) => path.startsWith(`${dir}/`)) ?? ".";
|
|
5598
|
+
const products = [];
|
|
5599
|
+
for (const dir of dirs) {
|
|
5600
|
+
const source = (0, import_node_path18.join)(repoRoot, dir, "dist");
|
|
5601
|
+
if ((await (0, import_promises20.stat)(source).catch(() => null))?.isDirectory()) products.push({ source, mountAs: `${dir}/dist`, within: dir });
|
|
5602
|
+
}
|
|
5603
|
+
for (const path of await gitIgnoredFiles(repoRoot)) {
|
|
5604
|
+
if (allowedWorkspacePath(path)) products.push({ source: (0, import_node_path18.join)(repoRoot, path), mountAs: path, within: within(path) });
|
|
5605
|
+
}
|
|
5606
|
+
return products;
|
|
5459
5607
|
}
|
|
5460
5608
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5461
5609
|
0 && (module.exports = {
|
|
@@ -5479,11 +5627,14 @@ async function prepareContainerDependencies(input) {
|
|
|
5479
5627
|
applyPatchDialectToDiff,
|
|
5480
5628
|
assertCodeBuildRecipe,
|
|
5481
5629
|
assertDisjointPlan,
|
|
5630
|
+
assertLentPath,
|
|
5482
5631
|
assertPinnedImage,
|
|
5632
|
+
assertReservedMount,
|
|
5483
5633
|
attachCodeRuntimeReferences,
|
|
5484
5634
|
buildContainerRunArgs,
|
|
5485
5635
|
buildRecipeContainerArgs,
|
|
5486
5636
|
chooseStrategy,
|
|
5637
|
+
cloneTree,
|
|
5487
5638
|
codeSkill,
|
|
5488
5639
|
createCodeRuntimeControlClient,
|
|
5489
5640
|
createCodeRuntimeInference,
|
|
@@ -5500,17 +5651,23 @@ async function prepareContainerDependencies(input) {
|
|
|
5500
5651
|
feedbackIsActionable,
|
|
5501
5652
|
hasContextFreeHunk,
|
|
5502
5653
|
hazardFromAttempt,
|
|
5654
|
+
hostBuildProducts,
|
|
5503
5655
|
installedDependencies,
|
|
5504
5656
|
integrateSubGoals,
|
|
5505
5657
|
isCheckpointEffectCompleted,
|
|
5658
|
+
lendBuildProducts,
|
|
5659
|
+
lendDependencies,
|
|
5660
|
+
lentDirectories,
|
|
5506
5661
|
materializeCodeRuntimeArchive,
|
|
5507
5662
|
materializeCodeRuntimeSource,
|
|
5508
5663
|
materializeCommandWorkspace,
|
|
5509
5664
|
materializeGitTree,
|
|
5665
|
+
npmFailureSummary,
|
|
5510
5666
|
outcomeCloses,
|
|
5511
5667
|
outcomeMemory,
|
|
5512
5668
|
parseRepositoryRecipes,
|
|
5513
5669
|
patchPaths,
|
|
5670
|
+
pinnedNpmVersion,
|
|
5514
5671
|
planReachCollisions,
|
|
5515
5672
|
prepareContainerDependencies,
|
|
5516
5673
|
prepareRuntimeCheckpoint,
|
|
@@ -5549,6 +5706,7 @@ async function prepareContainerDependencies(input) {
|
|
|
5549
5706
|
verifyCodeCandidate,
|
|
5550
5707
|
verifyContainerEngineBoundary,
|
|
5551
5708
|
withProofRecipes,
|
|
5552
|
-
withRecipeDependencies
|
|
5709
|
+
withRecipeDependencies,
|
|
5710
|
+
workspaceDirs
|
|
5553
5711
|
});
|
|
5554
5712
|
//# sourceMappingURL=node.cjs.map
|