@odla-ai/cli 0.15.0 → 0.16.0
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/README.md +2 -2
- package/dist/bin.cjs +80 -8
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-E3CCYDIT.js → chunk-S3OYXHDG.js} +81 -9
- package/dist/chunk-S3OYXHDG.js.map +1 -0
- package/dist/index.cjs +80 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/odla-migrate/references/phase-2-db.md +2 -2
- package/dist/chunk-E3CCYDIT.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2593,7 +2593,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
2593
2593
|
}
|
|
2594
2594
|
}
|
|
2595
2595
|
|
|
2596
|
-
// ../harness/dist/chunk-
|
|
2596
|
+
// ../harness/dist/chunk-4EM6NMET.js
|
|
2597
2597
|
var import_crypto = require("crypto");
|
|
2598
2598
|
var import_promises5 = require("fs/promises");
|
|
2599
2599
|
var import_path5 = require("path");
|
|
@@ -2930,7 +2930,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
2930
2930
|
}
|
|
2931
2931
|
}
|
|
2932
2932
|
|
|
2933
|
-
// ../harness/dist/chunk-
|
|
2933
|
+
// ../harness/dist/chunk-4EM6NMET.js
|
|
2934
2934
|
var import_child_process4 = require("child_process");
|
|
2935
2935
|
var import_promises6 = require("fs/promises");
|
|
2936
2936
|
var import_path6 = require("path");
|
|
@@ -3223,7 +3223,7 @@ function looksLikeDestination(value) {
|
|
|
3223
3223
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
|
|
3224
3224
|
}
|
|
3225
3225
|
|
|
3226
|
-
// ../harness/dist/chunk-
|
|
3226
|
+
// ../harness/dist/chunk-4EM6NMET.js
|
|
3227
3227
|
var import_crypto4 = require("crypto");
|
|
3228
3228
|
async function digestStagedWorkspace(root, limits) {
|
|
3229
3229
|
const files = [];
|
|
@@ -3471,10 +3471,28 @@ async function parseSource(value) {
|
|
|
3471
3471
|
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
|
|
3472
3472
|
return { path: file.path, content: file.content };
|
|
3473
3473
|
});
|
|
3474
|
+
const referencesValue = snapshot.references === void 0 ? [] : snapshot.references;
|
|
3475
|
+
if (!Array.isArray(referencesValue) || referencesValue.length > 5) throw invalid("reference sources");
|
|
3476
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
3477
|
+
const references = [];
|
|
3478
|
+
for (const item of referencesValue) {
|
|
3479
|
+
const reference = record3(item);
|
|
3480
|
+
if (!reference || typeof reference.alias !== "string" || !/^[a-z][a-z0-9-]{0,39}$/.test(reference.alias) || aliases.has(reference.alias) || reference.alias === "primary" || typeof reference.repository !== "string" || typeof reference.commitSha !== "string" || typeof reference.treeDigest !== "string" || !Array.isArray(reference.files)) throw invalid("reference source");
|
|
3481
|
+
aliases.add(reference.alias);
|
|
3482
|
+
const referenceFiles = reference.files.map((entry) => {
|
|
3483
|
+
const file = record3(entry);
|
|
3484
|
+
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
|
|
3485
|
+
return { path: file.path, content: file.content };
|
|
3486
|
+
});
|
|
3487
|
+
const source2 = { repository: reference.repository, commitSha: reference.commitSha, files: referenceFiles };
|
|
3488
|
+
const referenceDigest = await digestCodeRepositorySnapshot(source2, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
|
|
3489
|
+
if (referenceDigest !== reference.treeDigest) throw invalid("reference source digest");
|
|
3490
|
+
references.push({ alias: reference.alias, ...source2, treeDigest: referenceDigest });
|
|
3491
|
+
}
|
|
3474
3492
|
const source = { repository: snapshot.repository, commitSha: snapshot.commitSha, files };
|
|
3475
3493
|
const digest = await digestCodeRepositorySnapshot(source, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
|
|
3476
3494
|
if (digest !== snapshot.treeDigest) throw invalid("source digest");
|
|
3477
|
-
return { ...source, treeDigest: digest };
|
|
3495
|
+
return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
|
|
3478
3496
|
}
|
|
3479
3497
|
function parseReview(value) {
|
|
3480
3498
|
const review = record3(record3(value)?.review);
|
|
@@ -4005,7 +4023,7 @@ var CodeRuntimeCheckpointManager = class {
|
|
|
4005
4023
|
await this.options.event(command, {
|
|
4006
4024
|
type: "message",
|
|
4007
4025
|
actor: "system",
|
|
4008
|
-
body: `Candidate ${candidate.candidateId} is ready for
|
|
4026
|
+
body: command.payload.sourceSet ? `Candidate ${candidate.candidateId} was verified and delivered to the session PR branch` : `Candidate ${candidate.candidateId} is ready for legacy owner publication approval`
|
|
4009
4027
|
}, pending.refs);
|
|
4010
4028
|
this.#pending.delete(command.commandId);
|
|
4011
4029
|
return true;
|
|
@@ -4032,12 +4050,51 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = (0, import_os3.
|
|
|
4032
4050
|
await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
4033
4051
|
await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 420 });
|
|
4034
4052
|
}
|
|
4053
|
+
for (const reference of snapshot.references ?? []) {
|
|
4054
|
+
validateAlias(reference.alias);
|
|
4055
|
+
if (!reference.files.length || reference.files.length > 1e4) throw new TypeError("Code reference file count is invalid");
|
|
4056
|
+
for (const file of reference.files) {
|
|
4057
|
+
validatePath(file.path);
|
|
4058
|
+
const path = `.odla-references/${reference.alias}/${file.path}`;
|
|
4059
|
+
if (seen.has(path)) throw new TypeError("Code reference repeats a path");
|
|
4060
|
+
seen.add(path);
|
|
4061
|
+
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
4062
|
+
if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
|
|
4063
|
+
const target = (0, import_path8.resolve)(sourceDir, path);
|
|
4064
|
+
if (!target.startsWith(`${(0, import_path8.resolve)(sourceDir)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
4065
|
+
await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
4066
|
+
await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4035
4069
|
return { sourceDir, cleanup: () => (0, import_promises8.rm)(root, { recursive: true, force: true }) };
|
|
4036
4070
|
} catch (cause) {
|
|
4037
4071
|
await (0, import_promises8.rm)(root, { recursive: true, force: true });
|
|
4038
4072
|
throw cause;
|
|
4039
4073
|
}
|
|
4040
4074
|
}
|
|
4075
|
+
function validateAlias(alias) {
|
|
4076
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
|
|
4077
|
+
throw new TypeError("Code reference alias is invalid");
|
|
4078
|
+
}
|
|
4079
|
+
}
|
|
4080
|
+
async function attachCodeRuntimeReferences(workspace, references) {
|
|
4081
|
+
let bytes = 0;
|
|
4082
|
+
for (const reference of references) {
|
|
4083
|
+
validateAlias(reference.alias);
|
|
4084
|
+
for (const file of reference.files) {
|
|
4085
|
+
validatePath(file.path);
|
|
4086
|
+
const path = `.odla-references/${reference.alias}/${file.path}`;
|
|
4087
|
+
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
4088
|
+
if (bytes > 64 * 1024 * 1024) throw new TypeError("Code reference set exceeds its byte bound");
|
|
4089
|
+
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
4090
|
+
const target = (0, import_path8.resolve)(root, path);
|
|
4091
|
+
if (!target.startsWith(`${(0, import_path8.resolve)(root)}${import_path8.sep}`)) throw new TypeError("Code reference path escapes its root");
|
|
4092
|
+
await (0, import_promises8.mkdir)((0, import_path8.dirname)(target), { recursive: true });
|
|
4093
|
+
await (0, import_promises8.writeFile)(target, file.content, { flag: "wx", mode: 292 });
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4041
4098
|
function validatePath(path) {
|
|
4042
4099
|
const parts = path.split("/");
|
|
4043
4100
|
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." || RESERVED2.has(part) || SECRET2.test(part))) {
|
|
@@ -4236,6 +4293,9 @@ async function patch(context, request, options, policy) {
|
|
|
4236
4293
|
exactKeys(request.input, ["patch"]);
|
|
4237
4294
|
const value = stringField(request.input, "patch");
|
|
4238
4295
|
const paths = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
|
|
4296
|
+
if (paths.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
|
|
4297
|
+
throw new TypeError("patch targets a read-only reference source");
|
|
4298
|
+
}
|
|
4239
4299
|
const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
|
|
4240
4300
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
4241
4301
|
await applyCodePatch(context.workspaceDir, value, paths);
|
|
@@ -4321,6 +4381,9 @@ function validateOptions(options) {
|
|
|
4321
4381
|
throw new TypeError("Code tool broker requires a reader and unique registered recipes");
|
|
4322
4382
|
}
|
|
4323
4383
|
for (const recipe2 of options.recipes) assertCodeBuildRecipe(recipe2);
|
|
4384
|
+
if (options.readOnlyPrefixes?.some((prefix) => !/^[A-Za-z0-9_.-]+$/.test(prefix) || prefix === "." || prefix === "..")) {
|
|
4385
|
+
throw new TypeError("Code tool broker read-only prefix is invalid");
|
|
4386
|
+
}
|
|
4324
4387
|
}
|
|
4325
4388
|
function exactKeys(input, allowed) {
|
|
4326
4389
|
if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("tool input contains an unsupported field");
|
|
@@ -4428,7 +4491,8 @@ function createCodeRuntimeToolBroker(input, lease, role) {
|
|
|
4428
4491
|
recipes: input.recipes,
|
|
4429
4492
|
recipeExecutor: createContainerRecipeExecutor(input.engine),
|
|
4430
4493
|
recipeAuthorization: input.recipeAuthorization ?? "registered_recipe",
|
|
4431
|
-
readerId: `code-session:${lease.task.taskId}
|
|
4494
|
+
readerId: `code-session:${lease.task.taskId}`,
|
|
4495
|
+
readOnlyPrefixes: [".odla-references"]
|
|
4432
4496
|
});
|
|
4433
4497
|
return role === "coding" ? broker : { execute: (context, request) => request.tool === "sandbox.read" ? broker.execute(context, request) : Promise.resolve({ requestId: request.requestId, ok: false, content: "review sessions are read-only" }) };
|
|
4434
4498
|
}
|
|
@@ -4506,6 +4570,14 @@ var CodePiRuntimeEngine = class {
|
|
|
4506
4570
|
resume
|
|
4507
4571
|
});
|
|
4508
4572
|
({ workspace, sourceDigest, trustedBaseDigest: localTrustedBaseDigest } = prepared);
|
|
4573
|
+
if (command.payload.sourceSet) {
|
|
4574
|
+
const selected = await this.options.control.source(command.sessionId);
|
|
4575
|
+
if (selected.repository !== metadata2.repository || selected.commitSha !== metadata2.baseCommitSha || selected.treeDigest !== metadata2.sourceTreeDigest) {
|
|
4576
|
+
await workspace.cleanup();
|
|
4577
|
+
throw new TypeError("Code local source does not match the selected GitHub primary source");
|
|
4578
|
+
}
|
|
4579
|
+
await attachCodeRuntimeReferences(workspace, selected.references ?? []);
|
|
4580
|
+
}
|
|
4509
4581
|
} else {
|
|
4510
4582
|
const source = await this.options.control.source(command.sessionId);
|
|
4511
4583
|
const materialized = await materializeCodeRuntimeSource(source);
|
|
@@ -4533,8 +4605,8 @@ var CodePiRuntimeEngine = class {
|
|
|
4533
4605
|
repository: metadata2.repository,
|
|
4534
4606
|
sourceTreeDigest: metadata2.sourceTreeDigest,
|
|
4535
4607
|
trustedBaseDigest: requestedLocal ? localTrustedBaseDigest : await digestStagedWorkspace(workspace.baselineDir, {
|
|
4536
|
-
maxFiles:
|
|
4537
|
-
maxBytes:
|
|
4608
|
+
maxFiles: 2e4,
|
|
4609
|
+
maxBytes: 512 * 1024 * 1024
|
|
4538
4610
|
}),
|
|
4539
4611
|
planningInputDigest: metadata2.planningInputDigest ?? digestRuntimeValue(
|
|
4540
4612
|
JSON.stringify({ attestation: metadata2.attestationDigest, prompt: metadata2.prompt, tree: sourceDigest })
|