@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// src/code-runtime-cli.ts
|
|
5
5
|
var import_node_os4 = require("os");
|
|
6
|
-
var
|
|
6
|
+
var import_promises17 = require("fs/promises");
|
|
7
7
|
|
|
8
8
|
// src/code-runtime-client-validation.ts
|
|
9
9
|
var import_code = require("@odla-ai/camel/code");
|
|
@@ -452,7 +452,7 @@ async function withOverloadRetry(call, wait2, onRetry = () => void 0, retryable
|
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
// src/code-runtime-reconciler.ts
|
|
455
|
-
var sleep = (ms) => new Promise((
|
|
455
|
+
var sleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
456
456
|
var POST_ACK_RETRY_TICKS = 40;
|
|
457
457
|
var CodeRuntimeReconciler = class {
|
|
458
458
|
constructor(control, engine, onDiagnostic, options = {}) {
|
|
@@ -554,12 +554,12 @@ function retryableControlFailure(value) {
|
|
|
554
554
|
return failure.status === 408 || failure.status === 425 || failure.status === 429 || failure.status >= 500;
|
|
555
555
|
}
|
|
556
556
|
function wait(ms, signal) {
|
|
557
|
-
return new Promise((
|
|
558
|
-
if (signal?.aborted) return
|
|
559
|
-
const timer = setTimeout(
|
|
557
|
+
return new Promise((resolve8) => {
|
|
558
|
+
if (signal?.aborted) return resolve8();
|
|
559
|
+
const timer = setTimeout(resolve8, ms);
|
|
560
560
|
signal?.addEventListener("abort", () => {
|
|
561
561
|
clearTimeout(timer);
|
|
562
|
-
|
|
562
|
+
resolve8();
|
|
563
563
|
}, { once: true });
|
|
564
564
|
});
|
|
565
565
|
}
|
|
@@ -981,12 +981,12 @@ async function restoreCodeWorkspaceCheckpoint(input) {
|
|
|
981
981
|
// src/code-verifier.ts
|
|
982
982
|
var import_node_crypto3 = require("crypto");
|
|
983
983
|
var import_node_fs2 = require("fs");
|
|
984
|
-
var
|
|
985
|
-
var
|
|
984
|
+
var import_promises7 = require("fs/promises");
|
|
985
|
+
var import_node_path8 = require("path");
|
|
986
986
|
var import_code3 = require("@odla-ai/camel/code");
|
|
987
987
|
|
|
988
988
|
// src/recipe-container.ts
|
|
989
|
-
var
|
|
989
|
+
var import_node_child_process5 = require("child_process");
|
|
990
990
|
var import_node_process2 = require("process");
|
|
991
991
|
var import_node_crypto = require("crypto");
|
|
992
992
|
|
|
@@ -1044,7 +1044,7 @@ async function selectContainerEngine(requested = "auto", options = {}) {
|
|
|
1044
1044
|
throw new TypeError("no supported container engine found");
|
|
1045
1045
|
}
|
|
1046
1046
|
function inspectRootlessPodman() {
|
|
1047
|
-
return new Promise((
|
|
1047
|
+
return new Promise((resolve8, reject) => {
|
|
1048
1048
|
(0, import_node_child_process3.execFile)(
|
|
1049
1049
|
"podman",
|
|
1050
1050
|
["info", "--format", "{{.Host.Security.Rootless}}"],
|
|
@@ -1054,7 +1054,7 @@ function inspectRootlessPodman() {
|
|
|
1054
1054
|
reject(new TypeError("could not verify that the active Podman service is rootless"));
|
|
1055
1055
|
return;
|
|
1056
1056
|
}
|
|
1057
|
-
|
|
1057
|
+
resolve8(stdout.trim() === "true");
|
|
1058
1058
|
}
|
|
1059
1059
|
);
|
|
1060
1060
|
});
|
|
@@ -1072,12 +1072,89 @@ async function verifyContainerEngineBoundary(engine, options = {}) {
|
|
|
1072
1072
|
if (!rootless) throw new TypeError("the active Podman service is not rootless; refusing to run the harness");
|
|
1073
1073
|
}
|
|
1074
1074
|
|
|
1075
|
+
// src/code-recipe-dependencies.ts
|
|
1076
|
+
var import_promises4 = require("fs/promises");
|
|
1077
|
+
var import_node_path5 = require("path");
|
|
1078
|
+
var RESERVED_MOUNTS = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage"]);
|
|
1079
|
+
function assertLentPath(mountAs) {
|
|
1080
|
+
const parts = mountAs.split("/");
|
|
1081
|
+
if ((0, import_node_path5.isAbsolute)(mountAs) || mountAs.includes("\\") || parts.some((part) => !part || part === "." || part === "..")) {
|
|
1082
|
+
throw new TypeError(`recipe dependencies must mount inside the workspace, not at "${mountAs}"`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
function assertReservedMount(mountAs) {
|
|
1086
|
+
assertLentPath(mountAs);
|
|
1087
|
+
if (!RESERVED_MOUNTS.has(mountAs.split("/").at(-1))) {
|
|
1088
|
+
throw new TypeError(`recipe dependencies must mount at a reserved name, not "${mountAs}"`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function lentDirectories(dependencies) {
|
|
1092
|
+
return [{ source: dependencies.source, mountAs: dependencies.mountAs ?? "node_modules" }, ...dependencies.nested ?? []];
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// src/recipe-container-lending.ts
|
|
1096
|
+
var import_node_child_process4 = require("child_process");
|
|
1097
|
+
var import_promises5 = require("fs/promises");
|
|
1098
|
+
var import_node_path6 = require("path");
|
|
1099
|
+
var RUN_ID = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
1100
|
+
function copy(args) {
|
|
1101
|
+
return new Promise((accept, reject) => {
|
|
1102
|
+
const child = (0, import_node_child_process4.spawn)("cp", args, { shell: false, stdio: "ignore" });
|
|
1103
|
+
child.once("error", reject);
|
|
1104
|
+
child.once("exit", (code) => accept(code ?? 1));
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
async function cloneTree(source, target, platform = process.platform) {
|
|
1108
|
+
await (0, import_promises5.mkdir)((0, import_node_path6.dirname)(target), { recursive: true });
|
|
1109
|
+
const fast = platform === "darwin" ? ["-Rc", source, target] : platform === "linux" ? ["-R", "--reflink=auto", source, target] : null;
|
|
1110
|
+
if (fast && await copy(fast) === 0) return;
|
|
1111
|
+
await (0, import_promises5.rm)(target, { recursive: true, force: true });
|
|
1112
|
+
if (await copy(["-R", source, target]) !== 0) throw new Error(`could not clone ${source}`);
|
|
1113
|
+
}
|
|
1114
|
+
async function lendDependencies(dependencies, runId) {
|
|
1115
|
+
if (!dependencies?.runsDir) return { dependencies, release: async () => void 0 };
|
|
1116
|
+
if (!RUN_ID.test(runId)) throw new TypeError("run id is not a safe directory name");
|
|
1117
|
+
const treeRoot = (0, import_node_path6.dirname)(dependencies.source);
|
|
1118
|
+
for (const entry of lentDirectories(dependencies)) {
|
|
1119
|
+
if (entry.source !== (0, import_node_path6.join)(treeRoot, entry.mountAs)) {
|
|
1120
|
+
throw new TypeError("every directory of a cloned set must lie under its tree root at its mount path");
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
const runDir = (0, import_node_path6.join)(dependencies.runsDir, runId);
|
|
1124
|
+
await cloneTree(treeRoot, runDir);
|
|
1125
|
+
const rebase = (entry) => ({ source: (0, import_node_path6.join)(runDir, entry.mountAs), mountAs: entry.mountAs });
|
|
1126
|
+
return {
|
|
1127
|
+
dependencies: {
|
|
1128
|
+
...dependencies,
|
|
1129
|
+
source: (0, import_node_path6.join)(runDir, dependencies.mountAs ?? "node_modules"),
|
|
1130
|
+
nested: (dependencies.nested ?? []).map(rebase),
|
|
1131
|
+
writable: true
|
|
1132
|
+
},
|
|
1133
|
+
release: () => (0, import_promises5.rm)(runDir, { recursive: true, force: true })
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
async function lendBuildProducts(products, workspaceDir) {
|
|
1137
|
+
const root = (0, import_node_path6.resolve)(workspaceDir);
|
|
1138
|
+
const lent = [];
|
|
1139
|
+
for (const product of products) {
|
|
1140
|
+
assertLentPath(product.mountAs);
|
|
1141
|
+
if (SECRET_WORKSPACE_FILE.test(product.mountAs.split("/").at(-1))) throw new TypeError("a secret is never a build product");
|
|
1142
|
+
const target = (0, import_node_path6.resolve)(root, product.mountAs);
|
|
1143
|
+
if (!target.startsWith(`${root}${import_node_path6.sep}`)) throw new TypeError("build product escapes the workspace");
|
|
1144
|
+
if (await (0, import_promises5.stat)(target).catch(() => null)) continue;
|
|
1145
|
+
if (!await (0, import_promises5.stat)((0, import_node_path6.join)(root, product.within ?? (0, import_node_path6.dirname)(product.mountAs))).catch(() => null)) continue;
|
|
1146
|
+
await cloneTree(product.source, target);
|
|
1147
|
+
lent.push(product.mountAs);
|
|
1148
|
+
}
|
|
1149
|
+
return lent;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1075
1152
|
// src/recipe-container.ts
|
|
1076
1153
|
var ARTIFACT_PATH = /^[A-Za-z0-9_@+.,-]+(?:\/[A-Za-z0-9_@+.,-]+)*$/;
|
|
1077
1154
|
var PRIVATE_ARTIFACT_PART = /^(?:\.git|\.odla|\.wrangler|\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json)$/i;
|
|
1078
1155
|
function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`, dependencies = null) {
|
|
1079
1156
|
assertCodeBuildRecipe(recipe2);
|
|
1080
|
-
const mounts = dependencies ?
|
|
1157
|
+
const mounts = dependencies ? dependencyMounts(engine, dependencies) : [];
|
|
1081
1158
|
if (/[,\r\n]/.test(workspaceDir)) throw new TypeError("workspace path contains unsupported mount characters");
|
|
1082
1159
|
const uid = typeof import_node_process2.getuid === "function" ? (0, import_node_process2.getuid)() : 1e3;
|
|
1083
1160
|
const gid = typeof import_node_process2.getgid === "function" ? (0, import_node_process2.getgid)() : 1e3;
|
|
@@ -1103,6 +1180,7 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1103
1180
|
...mounts,
|
|
1104
1181
|
"--workdir=/workspace",
|
|
1105
1182
|
"--env=CI=1",
|
|
1183
|
+
"--env=HOME=/tmp",
|
|
1106
1184
|
recipe2.image,
|
|
1107
1185
|
...recipe2.command
|
|
1108
1186
|
];
|
|
@@ -1125,26 +1203,34 @@ function buildRecipeContainerArgs(engine, workspaceDir, recipe2, name = `odla-re
|
|
|
1125
1203
|
...mounts,
|
|
1126
1204
|
"--workdir=/workspace",
|
|
1127
1205
|
"--env=CI=1",
|
|
1206
|
+
"--env=HOME=/tmp",
|
|
1128
1207
|
recipe2.image,
|
|
1129
1208
|
...recipe2.command
|
|
1130
1209
|
];
|
|
1131
1210
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1211
|
+
function dependencyMounts(engine, dependencies) {
|
|
1212
|
+
const readonly = dependencies.writable ? "" : ",readonly";
|
|
1213
|
+
return lentDirectories(dependencies).map(({ source, mountAs }) => {
|
|
1214
|
+
if (!source.startsWith("/") || /[,\r\n]/.test(source)) {
|
|
1215
|
+
throw new TypeError("recipe dependency source must be an absolute path without mount separators");
|
|
1216
|
+
}
|
|
1217
|
+
assertReservedMount(mountAs);
|
|
1218
|
+
return engine === "container" ? `--mount=type=bind,source=${source},target=/workspace/${mountAs}${readonly}` : `--mount=type=bind,src=${source},dst=/workspace/${mountAs}${readonly}`;
|
|
1219
|
+
});
|
|
1140
1220
|
}
|
|
1141
1221
|
function createContainerRecipeExecutor(engine, options = {}) {
|
|
1142
1222
|
return {
|
|
1143
1223
|
async run(input) {
|
|
1144
1224
|
await verifyContainerEngineBoundary(engine);
|
|
1145
1225
|
const name = `odla-recipe-${(0, import_node_crypto.randomUUID)().slice(0, 12)}`;
|
|
1146
|
-
const
|
|
1147
|
-
|
|
1226
|
+
const lent = await lendDependencies(options.dependencies ?? null, name);
|
|
1227
|
+
try {
|
|
1228
|
+
if (lent.dependencies?.products?.length) await lendBuildProducts(lent.dependencies.products, input.workspaceDir);
|
|
1229
|
+
const args = buildRecipeContainerArgs(engine, input.workspaceDir, input.recipe, name, lent.dependencies);
|
|
1230
|
+
return await execute(engine, args, name, input.recipe, input.signal);
|
|
1231
|
+
} finally {
|
|
1232
|
+
await lent.release();
|
|
1233
|
+
}
|
|
1148
1234
|
}
|
|
1149
1235
|
};
|
|
1150
1236
|
}
|
|
@@ -1166,10 +1252,14 @@ function parseMemory(value) {
|
|
|
1166
1252
|
function execute(engine, args, name, recipe2, signal) {
|
|
1167
1253
|
return runContainerCommand(engine, args, name, { timeoutMs: recipe2.timeoutMs, maxOutputBytes: recipe2.maxOutputBytes }, signal);
|
|
1168
1254
|
}
|
|
1255
|
+
var CONTAINER_PROGRESS = /^\[\d+\/\d+\] .*$/gm;
|
|
1256
|
+
function recipeOutput(engine, stderr) {
|
|
1257
|
+
return engine === "container" ? stderr.replace(CONTAINER_PROGRESS, "").replace(/^\n+/, "") : stderr;
|
|
1258
|
+
}
|
|
1169
1259
|
function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
1170
1260
|
return new Promise((accept, reject) => {
|
|
1171
1261
|
const started = Date.now();
|
|
1172
|
-
const child = (0,
|
|
1262
|
+
const child = (0, import_node_child_process5.spawn)(engine, args, { shell: false, stdio: ["ignore", "pipe", "pipe"] });
|
|
1173
1263
|
const stdout = [];
|
|
1174
1264
|
const stderr = [];
|
|
1175
1265
|
let bytes = 0;
|
|
@@ -1182,7 +1272,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
|
1182
1272
|
timedOut = reason === "timeout";
|
|
1183
1273
|
outputLimitExceeded = reason === "output";
|
|
1184
1274
|
const remove = engine === "container" ? ["delete", "--force", name] : ["rm", "-f", name];
|
|
1185
|
-
const killer = (0,
|
|
1275
|
+
const killer = (0, import_node_child_process5.spawn)(engine, remove, { shell: false, stdio: "ignore" });
|
|
1186
1276
|
killer.unref();
|
|
1187
1277
|
child.kill("SIGTERM");
|
|
1188
1278
|
};
|
|
@@ -1208,7 +1298,7 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
|
1208
1298
|
accept({
|
|
1209
1299
|
exitCode: code ?? 1,
|
|
1210
1300
|
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
1211
|
-
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
1301
|
+
stderr: recipeOutput(engine, Buffer.concat(stderr).toString("utf8")),
|
|
1212
1302
|
durationMs: Date.now() - started,
|
|
1213
1303
|
outputLimitExceeded,
|
|
1214
1304
|
timedOut
|
|
@@ -1219,27 +1309,27 @@ function runContainerCommand(engine, args, name, recipe2, signal) {
|
|
|
1219
1309
|
|
|
1220
1310
|
// src/workspace-digest.ts
|
|
1221
1311
|
var import_node_crypto2 = require("crypto");
|
|
1222
|
-
var
|
|
1223
|
-
var
|
|
1312
|
+
var import_promises6 = require("fs/promises");
|
|
1313
|
+
var import_node_path7 = require("path");
|
|
1224
1314
|
async function digestStagedWorkspace(root, limits) {
|
|
1225
1315
|
const files = [];
|
|
1226
1316
|
const walk = async (directory) => {
|
|
1227
|
-
const entries = await (0,
|
|
1317
|
+
const entries = await (0, import_promises6.readdir)(directory, { withFileTypes: true });
|
|
1228
1318
|
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
1229
1319
|
if (entry.isSymbolicLink()) throw new TypeError("workspace digest refuses symbolic links");
|
|
1230
|
-
const target = (0,
|
|
1320
|
+
const target = (0, import_node_path7.resolve)(directory, entry.name);
|
|
1231
1321
|
if (entry.isDirectory()) await walk(target);
|
|
1232
1322
|
else if (entry.isFile()) {
|
|
1233
|
-
files.push({ path: (0,
|
|
1323
|
+
files.push({ path: (0, import_node_path7.relative)(root, target).split("\\").join("/"), target });
|
|
1234
1324
|
if (files.length > limits.maxFiles) throw new TypeError("workspace digest exceeds its file bound");
|
|
1235
1325
|
}
|
|
1236
1326
|
}
|
|
1237
1327
|
};
|
|
1238
|
-
await walk((0,
|
|
1328
|
+
await walk((0, import_node_path7.resolve)(root));
|
|
1239
1329
|
const hash = (0, import_node_crypto2.createHash)("sha256");
|
|
1240
1330
|
let bytes = 0;
|
|
1241
1331
|
for (const file of files.sort((left, right) => left.path.localeCompare(right.path))) {
|
|
1242
|
-
const content = await (0,
|
|
1332
|
+
const content = await (0, import_promises6.readFile)(file.target);
|
|
1243
1333
|
bytes += Buffer.byteLength(file.path) + content.byteLength;
|
|
1244
1334
|
if (bytes > limits.maxBytes) throw new TypeError("workspace digest exceeds its byte bound");
|
|
1245
1335
|
hash.update(`${Buffer.byteLength(file.path)}:${file.path}:${content.byteLength}:`);
|
|
@@ -1357,8 +1447,8 @@ async function inspectArtifacts(workspaceDir, recipe2) {
|
|
|
1357
1447
|
const receipts = [];
|
|
1358
1448
|
for (const artifact of recipe2.expectedArtifacts ?? []) {
|
|
1359
1449
|
try {
|
|
1360
|
-
const path = (0,
|
|
1361
|
-
const info = await (0,
|
|
1450
|
+
const path = (0, import_node_path8.join)(workspaceDir, artifact.path);
|
|
1451
|
+
const info = await (0, import_promises7.lstat)(path);
|
|
1362
1452
|
if (!info.isFile() || info.isSymbolicLink()) {
|
|
1363
1453
|
receipts.push({ artifactId: artifact.id, status: "invalid", bytes: null, digest: null });
|
|
1364
1454
|
} else if (info.size > artifact.maximumBytes) {
|
|
@@ -1689,15 +1779,15 @@ async function prepareRuntimeLocalSource(input) {
|
|
|
1689
1779
|
}
|
|
1690
1780
|
|
|
1691
1781
|
// src/code-runtime-source.ts
|
|
1692
|
-
var
|
|
1782
|
+
var import_promises10 = require("fs/promises");
|
|
1693
1783
|
var import_node_os3 = require("os");
|
|
1694
|
-
var
|
|
1784
|
+
var import_node_path11 = require("path");
|
|
1695
1785
|
|
|
1696
1786
|
// src/code-runtime-archive.ts
|
|
1697
1787
|
var import_node_zlib = require("zlib");
|
|
1698
|
-
var
|
|
1788
|
+
var import_promises8 = require("fs/promises");
|
|
1699
1789
|
var import_node_os2 = require("os");
|
|
1700
|
-
var
|
|
1790
|
+
var import_node_path9 = require("path");
|
|
1701
1791
|
var RESERVED2 = /* @__PURE__ */ new Set([".git", ".odla", ".wrangler", "node_modules", "dist", "coverage"]);
|
|
1702
1792
|
var SECRET2 = /^(?:\.env(?:\..+)?|\.dev\.vars|credentials(?:\..+)?\.json|dev-token(?:\..+)?\.json)$/i;
|
|
1703
1793
|
var MAX_NUL_SHARE = 0.1;
|
|
@@ -1715,9 +1805,9 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
|
|
|
1715
1805
|
throw new TypeError("Code source archive exceeds its decompressed byte bound");
|
|
1716
1806
|
}
|
|
1717
1807
|
const entries = parseTar(bytes, archive.limits);
|
|
1718
|
-
const root = await (0,
|
|
1719
|
-
const sourceDir = (0,
|
|
1720
|
-
await (0,
|
|
1808
|
+
const root = await (0, import_promises8.mkdtemp)((0, import_node_path9.join)(tempRoot, "odla-code-archive-"));
|
|
1809
|
+
const sourceDir = (0, import_node_path9.join)(root, "source");
|
|
1810
|
+
await (0, import_promises8.mkdir)(sourceDir);
|
|
1721
1811
|
let visible = 0;
|
|
1722
1812
|
try {
|
|
1723
1813
|
for (const entry of entries) {
|
|
@@ -1730,16 +1820,16 @@ async function materializeCodeRuntimeArchive(archive, visiblePaths, tempRoot = (
|
|
|
1730
1820
|
continue;
|
|
1731
1821
|
}
|
|
1732
1822
|
if (nulShare(content) > MAX_NUL_SHARE) continue;
|
|
1733
|
-
const target = (0,
|
|
1734
|
-
if (!target.startsWith(`${(0,
|
|
1735
|
-
await (0,
|
|
1736
|
-
await (0,
|
|
1823
|
+
const target = (0, import_node_path9.resolve)(sourceDir, entry.path);
|
|
1824
|
+
if (!target.startsWith(`${(0, import_node_path9.resolve)(sourceDir)}${import_node_path9.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
1825
|
+
await (0, import_promises8.mkdir)((0, import_node_path9.dirname)(target), { recursive: true });
|
|
1826
|
+
await (0, import_promises8.writeFile)(target, content, { flag: "wx", mode: 420 });
|
|
1737
1827
|
visible += 1;
|
|
1738
1828
|
}
|
|
1739
1829
|
if (!visible && !visiblePaths) throw new TypeError("GitHub commit has no Code-visible text source");
|
|
1740
|
-
return { sourceDir, cleanup: () => (0,
|
|
1830
|
+
return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
|
|
1741
1831
|
} catch (cause) {
|
|
1742
|
-
await (0,
|
|
1832
|
+
await (0, import_promises8.rm)(root, { recursive: true, force: true });
|
|
1743
1833
|
throw cause;
|
|
1744
1834
|
}
|
|
1745
1835
|
}
|
|
@@ -1934,8 +2024,8 @@ function nulShare(content) {
|
|
|
1934
2024
|
}
|
|
1935
2025
|
|
|
1936
2026
|
// src/code-runtime-selected-source.ts
|
|
1937
|
-
var
|
|
1938
|
-
var
|
|
2027
|
+
var import_promises9 = require("fs/promises");
|
|
2028
|
+
var import_node_path10 = require("path");
|
|
1939
2029
|
function selectedSourceSet(payload) {
|
|
1940
2030
|
if (!payload.sourceSet) return null;
|
|
1941
2031
|
const set = payload.sourceSet && typeof payload.sourceSet === "object" && !Array.isArray(payload.sourceSet) ? payload.sourceSet : null;
|
|
@@ -1972,9 +2062,9 @@ async function attachReferenceDirectories(workspace, references) {
|
|
|
1972
2062
|
for (const reference of references) {
|
|
1973
2063
|
validateAlias(reference.alias);
|
|
1974
2064
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
1975
|
-
const target = (0,
|
|
1976
|
-
await (0,
|
|
1977
|
-
await (0,
|
|
2065
|
+
const target = (0, import_node_path10.join)(root, ".odla-references", reference.alias);
|
|
2066
|
+
await (0, import_promises9.mkdir)((0, import_node_path10.dirname)(target), { recursive: true });
|
|
2067
|
+
await (0, import_promises9.cp)(reference.sourceDir, target, { recursive: true, errorOnExist: true, force: false });
|
|
1978
2068
|
await makeTreeReadOnly(target);
|
|
1979
2069
|
}
|
|
1980
2070
|
}
|
|
@@ -1985,10 +2075,10 @@ function validateAlias(alias) {
|
|
|
1985
2075
|
}
|
|
1986
2076
|
}
|
|
1987
2077
|
async function makeTreeReadOnly(root) {
|
|
1988
|
-
for (const entry of await (0,
|
|
1989
|
-
const target = (0,
|
|
2078
|
+
for (const entry of await (0, import_promises9.readdir)(root, { withFileTypes: true })) {
|
|
2079
|
+
const target = (0, import_node_path10.join)(root, entry.name);
|
|
1990
2080
|
if (entry.isDirectory()) await makeTreeReadOnly(target);
|
|
1991
|
-
else if (entry.isFile()) await (0,
|
|
2081
|
+
else if (entry.isFile()) await (0, import_promises9.chmod)(target, 292);
|
|
1992
2082
|
}
|
|
1993
2083
|
}
|
|
1994
2084
|
|
|
@@ -2000,9 +2090,9 @@ var SOURCE_MAX_BYTES = 80 * 1024 * 1024;
|
|
|
2000
2090
|
var SOURCE_SET_MAX_BYTES = 480 * 1024 * 1024;
|
|
2001
2091
|
async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node_os3.tmpdir)()) {
|
|
2002
2092
|
if (!snapshot.files.length || snapshot.files.length > SOURCE_MAX_FILES) throw new TypeError("Code source file count is invalid");
|
|
2003
|
-
const root = await (0,
|
|
2004
|
-
const sourceDir = (0,
|
|
2005
|
-
await (0,
|
|
2093
|
+
const root = await (0, import_promises10.mkdtemp)((0, import_node_path11.join)(tempRoot, "odla-code-source-"));
|
|
2094
|
+
const sourceDir = (0, import_node_path11.join)(root, "source");
|
|
2095
|
+
await (0, import_promises10.mkdir)(sourceDir);
|
|
2006
2096
|
const seen = /* @__PURE__ */ new Set();
|
|
2007
2097
|
let bytes = 0;
|
|
2008
2098
|
try {
|
|
@@ -2012,10 +2102,10 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
2012
2102
|
seen.add(file.path);
|
|
2013
2103
|
bytes += Buffer.byteLength(file.path) + Buffer.byteLength(file.content);
|
|
2014
2104
|
if (bytes > SOURCE_MAX_BYTES) throw new TypeError("Code source exceeds its byte bound");
|
|
2015
|
-
const target = (0,
|
|
2016
|
-
if (!target.startsWith(`${(0,
|
|
2017
|
-
await (0,
|
|
2018
|
-
await (0,
|
|
2105
|
+
const target = (0, import_node_path11.resolve)(sourceDir, file.path);
|
|
2106
|
+
if (!target.startsWith(`${(0, import_node_path11.resolve)(sourceDir)}${import_node_path11.sep}`)) throw new TypeError("Code source path escapes its root");
|
|
2107
|
+
await (0, import_promises10.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
2108
|
+
await (0, import_promises10.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
2019
2109
|
}
|
|
2020
2110
|
for (const reference of snapshot.references ?? []) {
|
|
2021
2111
|
validateAlias2(reference.alias);
|
|
@@ -2027,15 +2117,15 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_node
|
|
|
2027
2117
|
seen.add(path);
|
|
2028
2118
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
2029
2119
|
if (bytes > SOURCE_SET_MAX_BYTES) throw new TypeError("Code source set exceeds its byte bound");
|
|
2030
|
-
const target = (0,
|
|
2031
|
-
if (!target.startsWith(`${(0,
|
|
2032
|
-
await (0,
|
|
2033
|
-
await (0,
|
|
2120
|
+
const target = (0, import_node_path11.resolve)(sourceDir, path);
|
|
2121
|
+
if (!target.startsWith(`${(0, import_node_path11.resolve)(sourceDir)}${import_node_path11.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
2122
|
+
await (0, import_promises10.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
2123
|
+
await (0, import_promises10.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
2034
2124
|
}
|
|
2035
2125
|
}
|
|
2036
|
-
return { sourceDir, cleanup: () => (0,
|
|
2126
|
+
return { sourceDir, cleanup: () => (0, import_promises10.rm)(root, { recursive: true, force: true }) };
|
|
2037
2127
|
} catch (cause) {
|
|
2038
|
-
await (0,
|
|
2128
|
+
await (0, import_promises10.rm)(root, { recursive: true, force: true });
|
|
2039
2129
|
throw cause;
|
|
2040
2130
|
}
|
|
2041
2131
|
}
|
|
@@ -2054,10 +2144,10 @@ async function attachCodeRuntimeReferences(workspace, references) {
|
|
|
2054
2144
|
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
2055
2145
|
if (bytes > SOURCE_SET_MAX_BYTES - SOURCE_MAX_BYTES) throw new TypeError("Code reference set exceeds its byte bound");
|
|
2056
2146
|
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
2057
|
-
const target = (0,
|
|
2058
|
-
if (!target.startsWith(`${(0,
|
|
2059
|
-
await (0,
|
|
2060
|
-
await (0,
|
|
2147
|
+
const target = (0, import_node_path11.resolve)(root, path);
|
|
2148
|
+
if (!target.startsWith(`${(0, import_node_path11.resolve)(root)}${import_node_path11.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
2149
|
+
await (0, import_promises10.mkdir)((0, import_node_path11.dirname)(target), { recursive: true });
|
|
2150
|
+
await (0, import_promises10.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
2061
2151
|
}
|
|
2062
2152
|
}
|
|
2063
2153
|
}
|
|
@@ -2503,7 +2593,7 @@ Finish with a concise, non-empty answer to the owner. Do not call tools or promi
|
|
|
2503
2593
|
}
|
|
2504
2594
|
|
|
2505
2595
|
// src/code-runtime-session-skills.ts
|
|
2506
|
-
var sleep2 = (ms) => new Promise((
|
|
2596
|
+
var sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
2507
2597
|
function createCodeRuntimeSessionSkillLoader(control, options = {}) {
|
|
2508
2598
|
const wait2 = options.wait ?? sleep2;
|
|
2509
2599
|
const load = control.collaborationSkills?.bind(control);
|
|
@@ -2562,7 +2652,7 @@ var inferWithBackoff = (infer, wait2, onRetry) => withOverloadRetry(infer, wait2
|
|
|
2562
2652
|
async function handleCodeRuntimeInference(input) {
|
|
2563
2653
|
const { command, request, state } = input;
|
|
2564
2654
|
const startedAt = Date.now();
|
|
2565
|
-
const wait2 = input.wait ?? ((ms) => new Promise((
|
|
2655
|
+
const wait2 = input.wait ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
2566
2656
|
const response2 = await inferWithBackoff(
|
|
2567
2657
|
() => input.control.infer(command.sessionId, {
|
|
2568
2658
|
requestId: request.requestId,
|
|
@@ -2760,7 +2850,10 @@ function toolFailureReason(response2) {
|
|
|
2760
2850
|
if (response2.ok) return void 0;
|
|
2761
2851
|
const supplied = response2.details?.failureReason;
|
|
2762
2852
|
const reason = typeof supplied === "string" && supplied || DEFAULT_FAILURE_REASON[response2.content] || response2.content || "tool request failed; inspect the tool input and workspace state";
|
|
2763
|
-
|
|
2853
|
+
if (reason.length <= MAX_FAILURE_REASON) return reason;
|
|
2854
|
+
const head = reason.slice(0, 60);
|
|
2855
|
+
const gap = " \u2026 ";
|
|
2856
|
+
return `${head}${gap}${reason.slice(-(MAX_FAILURE_REASON - head.length - gap.length))}`;
|
|
2764
2857
|
}
|
|
2765
2858
|
var DEFAULT_FAILURE_REASON = {
|
|
2766
2859
|
"tool denied by CaMeL policy": "tool denied by CaMeL policy",
|
|
@@ -3073,18 +3166,18 @@ function response(request, ok, content, details) {
|
|
|
3073
3166
|
}
|
|
3074
3167
|
|
|
3075
3168
|
// src/code-tool-edit.ts
|
|
3076
|
-
var
|
|
3169
|
+
var import_promises12 = require("fs/promises");
|
|
3077
3170
|
|
|
3078
3171
|
// src/code-tool-graph.ts
|
|
3079
|
-
var
|
|
3080
|
-
var
|
|
3172
|
+
var import_promises11 = require("fs/promises");
|
|
3173
|
+
var import_node_path12 = require("path");
|
|
3081
3174
|
var import_graph = require("@odla-ai/graph");
|
|
3082
3175
|
var import_code4 = require("@odla-ai/graph/code");
|
|
3083
3176
|
var cache = /* @__PURE__ */ new Map();
|
|
3084
3177
|
function workspaceGraphs(workspaceDir, paths2) {
|
|
3085
3178
|
const existing = cache.get(workspaceDir);
|
|
3086
3179
|
if (existing) return existing;
|
|
3087
|
-
const read2 = (path) => (0,
|
|
3180
|
+
const read2 = (path) => (0, import_promises11.readFile)((0, import_node_path12.join)(workspaceDir, path), "utf8");
|
|
3088
3181
|
const built = (async () => ({
|
|
3089
3182
|
// No knownTables: a staged workspace may not carry migrations, and a filter
|
|
3090
3183
|
// that silently drops every table is worse than an unfiltered one. Callers
|
|
@@ -3160,7 +3253,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
|
|
|
3160
3253
|
}
|
|
3161
3254
|
if (oldText.includes("\0") || newText.includes("\0")) throw new TypeError("edit text contains NUL bytes; use plain text");
|
|
3162
3255
|
const target = resolveCodePath(workspaceDir, path);
|
|
3163
|
-
const current = await (0,
|
|
3256
|
+
const current = await (0, import_promises12.readFile)(target, "utf8");
|
|
3164
3257
|
const found = occurrences(current, oldText);
|
|
3165
3258
|
if (found === 0) {
|
|
3166
3259
|
throw new TypeError(`oldText was not found in "${path}"; sandbox.read the current lines and copy them exactly, including indentation and blank lines`);
|
|
@@ -3169,7 +3262,7 @@ async function editCodeFile(workspaceDir, path, oldText, newText) {
|
|
|
3169
3262
|
throw new TypeError(`oldText occurs ${found} times in "${path}"; include more of the surrounding lines so it matches exactly once`);
|
|
3170
3263
|
}
|
|
3171
3264
|
const at = current.indexOf(oldText);
|
|
3172
|
-
await (0,
|
|
3265
|
+
await (0, import_promises12.writeFile)(target, `${current.slice(0, at)}${newText}${current.slice(at + oldText.length)}`, "utf8");
|
|
3173
3266
|
return { deletions: oldText.split("\n").length, additions: newText.split("\n").length };
|
|
3174
3267
|
}
|
|
3175
3268
|
async function edit(context, request, options, policy, registry) {
|
|
@@ -3194,8 +3287,8 @@ async function edit(context, request, options, policy, registry) {
|
|
|
3194
3287
|
}
|
|
3195
3288
|
|
|
3196
3289
|
// src/code-tool-write.ts
|
|
3197
|
-
var
|
|
3198
|
-
var
|
|
3290
|
+
var import_promises13 = require("fs/promises");
|
|
3291
|
+
var import_node_path13 = require("path");
|
|
3199
3292
|
var MAX_WRITE_BYTES = 256 * 1024;
|
|
3200
3293
|
var lines = (text2) => text2 === "" ? [] : text2.replace(/\n$/, "").split("\n");
|
|
3201
3294
|
function writeAsDiff(path, current, content) {
|
|
@@ -3220,8 +3313,8 @@ async function writeCodeFile(workspaceDir, path, content, current) {
|
|
|
3220
3313
|
}
|
|
3221
3314
|
if (content.includes("\0")) throw new TypeError("content contains NUL bytes; use plain text");
|
|
3222
3315
|
const target = resolveCodePath(workspaceDir, path);
|
|
3223
|
-
await (0,
|
|
3224
|
-
await (0,
|
|
3316
|
+
await (0, import_promises13.mkdir)((0, import_node_path13.dirname)(target), { recursive: true });
|
|
3317
|
+
await (0, import_promises13.writeFile)(target, content, "utf8");
|
|
3225
3318
|
return { created: current === null, deletions: current === null ? 0 : lines(current).length, additions: lines(content).length };
|
|
3226
3319
|
}
|
|
3227
3320
|
async function write(context, request, options, policy, registry) {
|
|
@@ -3232,7 +3325,7 @@ async function write(context, request, options, policy, registry) {
|
|
|
3232
3325
|
throw new TypeError("write targets a read-only reference source");
|
|
3233
3326
|
}
|
|
3234
3327
|
const exists = (await registry.files(context.workspaceDir)).includes(path);
|
|
3235
|
-
const current = exists ? await (0,
|
|
3328
|
+
const current = exists ? await (0, import_promises13.readFile)(resolveCodePath(context.workspaceDir, path), "utf8") : null;
|
|
3236
3329
|
const patch2 = writeAsDiff(path, current, content);
|
|
3237
3330
|
const allowed = await policy.write(policyContext(context, request, options, { patch: patch2 }));
|
|
3238
3331
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
@@ -3248,12 +3341,12 @@ async function write(context, request, options, policy, registry) {
|
|
|
3248
3341
|
}
|
|
3249
3342
|
|
|
3250
3343
|
// src/code-tool-reads.ts
|
|
3251
|
-
var
|
|
3344
|
+
var import_promises15 = require("fs/promises");
|
|
3252
3345
|
|
|
3253
3346
|
// src/code-tool-discovery.ts
|
|
3254
|
-
var
|
|
3255
|
-
var
|
|
3256
|
-
var
|
|
3347
|
+
var import_node_child_process6 = require("child_process");
|
|
3348
|
+
var import_promises14 = require("fs/promises");
|
|
3349
|
+
var import_node_path14 = require("path");
|
|
3257
3350
|
var DEFAULT_MAX_FILES = 2e4;
|
|
3258
3351
|
var DEFAULT_MAX_RESULTS = 100;
|
|
3259
3352
|
var DEFAULT_MAX_FILE_BYTES = 512 * 1024;
|
|
@@ -3278,13 +3371,13 @@ function createWorkspaceFileRegistry(limit = DEFAULT_MAX_FILES, enumerate = regi
|
|
|
3278
3371
|
async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
3279
3372
|
const paths2 = [];
|
|
3280
3373
|
const walk = async (directory) => {
|
|
3281
|
-
for (const entry of await (0,
|
|
3374
|
+
for (const entry of await (0, import_promises14.readdir)(directory, { withFileTypes: true })) {
|
|
3282
3375
|
if (SKIP_WORKSPACE_DIRS.has(entry.name)) continue;
|
|
3283
3376
|
if (entry.isSymbolicLink()) throw new TypeError("workspace contains a symbolic link");
|
|
3284
|
-
const target = (0,
|
|
3377
|
+
const target = (0, import_node_path14.resolve)(directory, entry.name);
|
|
3285
3378
|
if (entry.isDirectory()) await walk(target);
|
|
3286
3379
|
else if (entry.isFile()) {
|
|
3287
|
-
const path = (0,
|
|
3380
|
+
const path = (0, import_node_path14.relative)(root, target).split("\\").join("/");
|
|
3288
3381
|
try {
|
|
3289
3382
|
validateRelativePath(path);
|
|
3290
3383
|
} catch {
|
|
@@ -3295,7 +3388,7 @@ async function registeredFiles(root, limit = DEFAULT_MAX_FILES) {
|
|
|
3295
3388
|
}
|
|
3296
3389
|
}
|
|
3297
3390
|
};
|
|
3298
|
-
await walk((0,
|
|
3391
|
+
await walk((0, import_node_path14.resolve)(root));
|
|
3299
3392
|
return paths2.sort();
|
|
3300
3393
|
}
|
|
3301
3394
|
function listWorkspace(paths2, options = {}) {
|
|
@@ -3357,7 +3450,7 @@ function nativeSearchBatch(root, paths2, options, remaining) {
|
|
|
3357
3450
|
options.query,
|
|
3358
3451
|
...paths2
|
|
3359
3452
|
];
|
|
3360
|
-
const child = (0,
|
|
3453
|
+
const child = (0, import_node_child_process6.spawn)("rg", args, {
|
|
3361
3454
|
cwd: root,
|
|
3362
3455
|
stdio: ["ignore", "pipe", "ignore"],
|
|
3363
3456
|
...options.signal ? { signal: options.signal } : {}
|
|
@@ -3409,7 +3502,7 @@ async function fallbackSearch(root, scoped, options) {
|
|
|
3409
3502
|
if (matches.length >= options.maxResults) break;
|
|
3410
3503
|
let source;
|
|
3411
3504
|
try {
|
|
3412
|
-
source = await (0,
|
|
3505
|
+
source = await (0, import_promises14.readFile)((0, import_node_path14.resolve)(root, path));
|
|
3413
3506
|
} catch {
|
|
3414
3507
|
continue;
|
|
3415
3508
|
}
|
|
@@ -3448,11 +3541,11 @@ async function read(context, request, options, policy, registry) {
|
|
|
3448
3541
|
const allowed = await policy.read(policyContext(context, request, options, { paths: paths2, path, startLine, endLine }));
|
|
3449
3542
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
3450
3543
|
const target = resolveCodePath(context.workspaceDir, path);
|
|
3451
|
-
const info = await (0,
|
|
3544
|
+
const info = await (0, import_promises15.stat)(target);
|
|
3452
3545
|
if (!info.isFile() || info.size > Math.max(options.maxReadBytes ?? 128 * 1024, 2 * 1024 * 1024)) {
|
|
3453
3546
|
throw new TypeError("file is not a bounded regular source file");
|
|
3454
3547
|
}
|
|
3455
|
-
const source = await (0,
|
|
3548
|
+
const source = await (0, import_promises15.readFile)(target);
|
|
3456
3549
|
if (source.includes(0)) throw new TypeError("binary files are not readable through this tool");
|
|
3457
3550
|
const lines2 = source.toString("utf8").split("\n");
|
|
3458
3551
|
const content = lines2.slice(startLine - 1, endLine).join("\n");
|
|
@@ -3977,8 +4070,8 @@ var runtimeErrorMessage = (value) => value instanceof Error ? value.message : St
|
|
|
3977
4070
|
function codeRuntimeAcknowledgementGate(signal) {
|
|
3978
4071
|
let settle;
|
|
3979
4072
|
let settled = false;
|
|
3980
|
-
const ready = new Promise((
|
|
3981
|
-
settle =
|
|
4073
|
+
const ready = new Promise((resolve8) => {
|
|
4074
|
+
settle = resolve8;
|
|
3982
4075
|
});
|
|
3983
4076
|
const release = (run) => {
|
|
3984
4077
|
if (settled) return;
|
|
@@ -4044,8 +4137,8 @@ function observeCodeRuntimeSessionSkills(command, skills, emit) {
|
|
|
4044
4137
|
}
|
|
4045
4138
|
|
|
4046
4139
|
// src/code-repository-recipes.ts
|
|
4047
|
-
var
|
|
4048
|
-
var
|
|
4140
|
+
var import_promises16 = require("fs/promises");
|
|
4141
|
+
var import_node_path15 = require("path");
|
|
4049
4142
|
var REPOSITORY_RECIPES_FILE = "odla.recipes.json";
|
|
4050
4143
|
var MAX_RECIPES = 16;
|
|
4051
4144
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -4106,7 +4199,7 @@ function parseRepositoryRecipes(text2, envelope) {
|
|
|
4106
4199
|
async function readRepositoryRecipes(baselineDir, envelope) {
|
|
4107
4200
|
let text2;
|
|
4108
4201
|
try {
|
|
4109
|
-
text2 = await (0,
|
|
4202
|
+
text2 = await (0, import_promises16.readFile)((0, import_node_path15.join)(baselineDir, REPOSITORY_RECIPES_FILE), "utf8");
|
|
4110
4203
|
} catch (cause) {
|
|
4111
4204
|
if (cause.code === "ENOENT") return null;
|
|
4112
4205
|
throw cause;
|
|
@@ -4486,7 +4579,7 @@ function parse(argv) {
|
|
|
4486
4579
|
};
|
|
4487
4580
|
}
|
|
4488
4581
|
async function readPolicy(path) {
|
|
4489
|
-
const value = JSON.parse(await (0,
|
|
4582
|
+
const value = JSON.parse(await (0, import_promises17.readFile)(path, "utf8"));
|
|
4490
4583
|
if (!value || Object.keys(value).some((key) => !["recipes", "recipeAuthorization"].includes(key)) || !Array.isArray(value.recipes) || !value.recipes.length || value.recipeAuthorization !== void 0 && value.recipeAuthorization !== "registered_recipe" && value.recipeAuthorization !== "exact_approval") throw new TypeError("invalid Code build policy file");
|
|
4491
4584
|
const recipes = value.recipes;
|
|
4492
4585
|
for (const recipe2 of recipes) assertCodeBuildRecipe(recipe2);
|