@odla-ai/cli 0.15.0 → 0.16.1
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 +92 -12
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-E3CCYDIT.js → chunk-FRDIQURT.js} +93 -13
- package/dist/chunk-FRDIQURT.js.map +1 -0
- package/dist/index.cjs +92 -12
- 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/bin.js
CHANGED
|
@@ -2398,7 +2398,7 @@ async function stageWorkspacePair(baselineSource, workspaceSource, options = {})
|
|
|
2398
2398
|
}
|
|
2399
2399
|
}
|
|
2400
2400
|
|
|
2401
|
-
// ../harness/dist/chunk-
|
|
2401
|
+
// ../harness/dist/chunk-5E2R3LHE.js
|
|
2402
2402
|
import { createHash } from "crypto";
|
|
2403
2403
|
import { readFile, readdir as readdir2 } from "fs/promises";
|
|
2404
2404
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
@@ -2735,7 +2735,7 @@ function validateSnapshot(snapshot, limits) {
|
|
|
2735
2735
|
}
|
|
2736
2736
|
}
|
|
2737
2737
|
|
|
2738
|
-
// ../harness/dist/chunk-
|
|
2738
|
+
// ../harness/dist/chunk-5E2R3LHE.js
|
|
2739
2739
|
import { spawn as spawn4 } from "child_process";
|
|
2740
2740
|
import { lstat as lstat2 } from "fs/promises";
|
|
2741
2741
|
import { resolve as resolve23, sep as sep3 } from "path";
|
|
@@ -3028,7 +3028,7 @@ function looksLikeDestination(value) {
|
|
|
3028
3028
|
return /^(?:[a-z][a-z0-9+.-]*:\/\/|\/|\\\\)/i.test(text) || /^[\w.-]+\.[a-z]{2,}(?:[/:]|$)/i.test(text);
|
|
3029
3029
|
}
|
|
3030
3030
|
|
|
3031
|
-
// ../harness/dist/chunk-
|
|
3031
|
+
// ../harness/dist/chunk-5E2R3LHE.js
|
|
3032
3032
|
import { createHash as createHash3 } from "crypto";
|
|
3033
3033
|
async function digestStagedWorkspace(root, limits) {
|
|
3034
3034
|
const files = [];
|
|
@@ -3143,9 +3143,13 @@ function createCodeRuntimeControlClient(options) {
|
|
|
3143
3143
|
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1e3 || requestTimeoutMs > 12e4) {
|
|
3144
3144
|
throw new TypeError("requestTimeoutMs must be an integer from 1000 to 120000");
|
|
3145
3145
|
}
|
|
3146
|
+
const modelRequestTimeoutMs = options.modelRequestTimeoutMs ?? 15 * 6e4;
|
|
3147
|
+
if (!Number.isSafeInteger(modelRequestTimeoutMs) || modelRequestTimeoutMs < 3e4 || modelRequestTimeoutMs > 30 * 6e4) {
|
|
3148
|
+
throw new TypeError("modelRequestTimeoutMs must be an integer from 30000 to 1800000");
|
|
3149
|
+
}
|
|
3146
3150
|
const request = options.fetch ?? fetch;
|
|
3147
|
-
const call = async (path, body) => {
|
|
3148
|
-
const timeout = AbortSignal.timeout(
|
|
3151
|
+
const call = async (path, body, timeoutMs = requestTimeoutMs) => {
|
|
3152
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
3149
3153
|
const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
|
|
3150
3154
|
let response2;
|
|
3151
3155
|
try {
|
|
@@ -3184,14 +3188,18 @@ function createCodeRuntimeControlClient(options) {
|
|
|
3184
3188
|
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/source`, {})
|
|
3185
3189
|
),
|
|
3186
3190
|
infer: async (sessionId, inference) => {
|
|
3187
|
-
const value = record3(await call(
|
|
3191
|
+
const value = record3(await call(
|
|
3192
|
+
`/registry/code/runtime/sessions/${validSessionId(sessionId)}/inference`,
|
|
3193
|
+
inference,
|
|
3194
|
+
modelRequestTimeoutMs
|
|
3195
|
+
));
|
|
3188
3196
|
if (!value || value.requestId !== inference.requestId || !record3(value.response) || !record3(value.receipt)) {
|
|
3189
3197
|
throw new CodeRuntimeControlError("invalid Code inference response", 502, "invalid_response");
|
|
3190
3198
|
}
|
|
3191
3199
|
return value;
|
|
3192
3200
|
},
|
|
3193
3201
|
review: async (sessionId, review) => parseReview(
|
|
3194
|
-
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review)
|
|
3202
|
+
await call(`/registry/code/runtime/sessions/${validSessionId(sessionId)}/review`, review, modelRequestTimeoutMs)
|
|
3195
3203
|
),
|
|
3196
3204
|
submitCandidate: async (sessionId, checkpointId, verification) => {
|
|
3197
3205
|
if (!/^cpoint_[0-9a-f]{32}$/.test(checkpointId)) throw new TypeError("invalid Code checkpoint id");
|
|
@@ -3276,10 +3284,28 @@ async function parseSource(value) {
|
|
|
3276
3284
|
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("source file");
|
|
3277
3285
|
return { path: file.path, content: file.content };
|
|
3278
3286
|
});
|
|
3287
|
+
const referencesValue = snapshot.references === void 0 ? [] : snapshot.references;
|
|
3288
|
+
if (!Array.isArray(referencesValue) || referencesValue.length > 5) throw invalid("reference sources");
|
|
3289
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
3290
|
+
const references = [];
|
|
3291
|
+
for (const item of referencesValue) {
|
|
3292
|
+
const reference = record3(item);
|
|
3293
|
+
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");
|
|
3294
|
+
aliases.add(reference.alias);
|
|
3295
|
+
const referenceFiles = reference.files.map((entry) => {
|
|
3296
|
+
const file = record3(entry);
|
|
3297
|
+
if (!file || typeof file.path !== "string" || typeof file.content !== "string") throw invalid("reference source file");
|
|
3298
|
+
return { path: file.path, content: file.content };
|
|
3299
|
+
});
|
|
3300
|
+
const source2 = { repository: reference.repository, commitSha: reference.commitSha, files: referenceFiles };
|
|
3301
|
+
const referenceDigest = await digestCodeRepositorySnapshot(source2, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
|
|
3302
|
+
if (referenceDigest !== reference.treeDigest) throw invalid("reference source digest");
|
|
3303
|
+
references.push({ alias: reference.alias, ...source2, treeDigest: referenceDigest });
|
|
3304
|
+
}
|
|
3279
3305
|
const source = { repository: snapshot.repository, commitSha: snapshot.commitSha, files };
|
|
3280
3306
|
const digest = await digestCodeRepositorySnapshot(source, { maximumFiles: 1e4, maximumBytes: 16 * 1024 * 1024 });
|
|
3281
3307
|
if (digest !== snapshot.treeDigest) throw invalid("source digest");
|
|
3282
|
-
return { ...source, treeDigest: digest };
|
|
3308
|
+
return { ...source, treeDigest: digest, ...references.length ? { references } : {} };
|
|
3283
3309
|
}
|
|
3284
3310
|
function parseReview(value) {
|
|
3285
3311
|
const review = record3(record3(value)?.review);
|
|
@@ -3810,7 +3836,7 @@ var CodeRuntimeCheckpointManager = class {
|
|
|
3810
3836
|
await this.options.event(command, {
|
|
3811
3837
|
type: "message",
|
|
3812
3838
|
actor: "system",
|
|
3813
|
-
body: `Candidate ${candidate.candidateId} is ready for
|
|
3839
|
+
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`
|
|
3814
3840
|
}, pending.refs);
|
|
3815
3841
|
this.#pending.delete(command.commandId);
|
|
3816
3842
|
return true;
|
|
@@ -3837,12 +3863,51 @@ async function materializeCodeRuntimeSource(snapshot, tempRoot = tmpdir3()) {
|
|
|
3837
3863
|
await mkdir3(dirname4(target), { recursive: true });
|
|
3838
3864
|
await writeFile2(target, file.content, { flag: "wx", mode: 420 });
|
|
3839
3865
|
}
|
|
3866
|
+
for (const reference of snapshot.references ?? []) {
|
|
3867
|
+
validateAlias(reference.alias);
|
|
3868
|
+
if (!reference.files.length || reference.files.length > 1e4) throw new TypeError("Code reference file count is invalid");
|
|
3869
|
+
for (const file of reference.files) {
|
|
3870
|
+
validatePath(file.path);
|
|
3871
|
+
const path = `.odla-references/${reference.alias}/${file.path}`;
|
|
3872
|
+
if (seen.has(path)) throw new TypeError("Code reference repeats a path");
|
|
3873
|
+
seen.add(path);
|
|
3874
|
+
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
3875
|
+
if (bytes > 80 * 1024 * 1024) throw new TypeError("Code source set exceeds its byte bound");
|
|
3876
|
+
const target = resolve32(sourceDir, path);
|
|
3877
|
+
if (!target.startsWith(`${resolve32(sourceDir)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
|
|
3878
|
+
await mkdir3(dirname4(target), { recursive: true });
|
|
3879
|
+
await writeFile2(target, file.content, { flag: "wx", mode: 292 });
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3840
3882
|
return { sourceDir, cleanup: () => rm3(root, { recursive: true, force: true }) };
|
|
3841
3883
|
} catch (cause) {
|
|
3842
3884
|
await rm3(root, { recursive: true, force: true });
|
|
3843
3885
|
throw cause;
|
|
3844
3886
|
}
|
|
3845
3887
|
}
|
|
3888
|
+
function validateAlias(alias) {
|
|
3889
|
+
if (!/^[a-z][a-z0-9-]{0,39}$/.test(alias) || alias === "primary") {
|
|
3890
|
+
throw new TypeError("Code reference alias is invalid");
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
async function attachCodeRuntimeReferences(workspace, references) {
|
|
3894
|
+
let bytes = 0;
|
|
3895
|
+
for (const reference of references) {
|
|
3896
|
+
validateAlias(reference.alias);
|
|
3897
|
+
for (const file of reference.files) {
|
|
3898
|
+
validatePath(file.path);
|
|
3899
|
+
const path = `.odla-references/${reference.alias}/${file.path}`;
|
|
3900
|
+
bytes += Buffer.byteLength(path) + Buffer.byteLength(file.content);
|
|
3901
|
+
if (bytes > 64 * 1024 * 1024) throw new TypeError("Code reference set exceeds its byte bound");
|
|
3902
|
+
for (const root of [workspace.baselineDir, workspace.workspaceDir]) {
|
|
3903
|
+
const target = resolve32(root, path);
|
|
3904
|
+
if (!target.startsWith(`${resolve32(root)}${sep22}`)) throw new TypeError("Code reference path escapes its root");
|
|
3905
|
+
await mkdir3(dirname4(target), { recursive: true });
|
|
3906
|
+
await writeFile2(target, file.content, { flag: "wx", mode: 292 });
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
}
|
|
3846
3911
|
function validatePath(path) {
|
|
3847
3912
|
const parts = path.split("/");
|
|
3848
3913
|
if (!path || path.startsWith("/") || path.includes("\\") || path.includes("\0") || parts.some((part) => !part || part === "." || part === ".." || RESERVED2.has(part) || SECRET2.test(part))) {
|
|
@@ -4041,6 +4106,9 @@ async function patch(context, request, options, policy) {
|
|
|
4041
4106
|
exactKeys(request.input, ["patch"]);
|
|
4042
4107
|
const value = stringField(request.input, "patch");
|
|
4043
4108
|
const paths = validateCodePatch(value, options.maxPatchBytes ?? 256 * 1024);
|
|
4109
|
+
if (paths.some((path) => options.readOnlyPrefixes?.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)))) {
|
|
4110
|
+
throw new TypeError("patch targets a read-only reference source");
|
|
4111
|
+
}
|
|
4044
4112
|
const allowed = await policy.patch(policyContext(context, request, options, { patch: value }));
|
|
4045
4113
|
if (!allowed) return response(request, false, "tool denied by CaMeL policy");
|
|
4046
4114
|
await applyCodePatch(context.workspaceDir, value, paths);
|
|
@@ -4126,6 +4194,9 @@ function validateOptions(options) {
|
|
|
4126
4194
|
throw new TypeError("Code tool broker requires a reader and unique registered recipes");
|
|
4127
4195
|
}
|
|
4128
4196
|
for (const recipe2 of options.recipes) assertCodeBuildRecipe(recipe2);
|
|
4197
|
+
if (options.readOnlyPrefixes?.some((prefix) => !/^[A-Za-z0-9_.-]+$/.test(prefix) || prefix === "." || prefix === "..")) {
|
|
4198
|
+
throw new TypeError("Code tool broker read-only prefix is invalid");
|
|
4199
|
+
}
|
|
4129
4200
|
}
|
|
4130
4201
|
function exactKeys(input, allowed) {
|
|
4131
4202
|
if (Object.keys(input).some((key) => !allowed.includes(key))) throw new TypeError("tool input contains an unsupported field");
|
|
@@ -4233,7 +4304,8 @@ function createCodeRuntimeToolBroker(input, lease, role) {
|
|
|
4233
4304
|
recipes: input.recipes,
|
|
4234
4305
|
recipeExecutor: createContainerRecipeExecutor(input.engine),
|
|
4235
4306
|
recipeAuthorization: input.recipeAuthorization ?? "registered_recipe",
|
|
4236
|
-
readerId: `code-session:${lease.task.taskId}
|
|
4307
|
+
readerId: `code-session:${lease.task.taskId}`,
|
|
4308
|
+
readOnlyPrefixes: [".odla-references"]
|
|
4237
4309
|
});
|
|
4238
4310
|
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" }) };
|
|
4239
4311
|
}
|
|
@@ -4311,6 +4383,14 @@ var CodePiRuntimeEngine = class {
|
|
|
4311
4383
|
resume
|
|
4312
4384
|
});
|
|
4313
4385
|
({ workspace, sourceDigest, trustedBaseDigest: localTrustedBaseDigest } = prepared);
|
|
4386
|
+
if (command.payload.sourceSet) {
|
|
4387
|
+
const selected = await this.options.control.source(command.sessionId);
|
|
4388
|
+
if (selected.repository !== metadata2.repository || selected.commitSha !== metadata2.baseCommitSha || selected.treeDigest !== metadata2.sourceTreeDigest) {
|
|
4389
|
+
await workspace.cleanup();
|
|
4390
|
+
throw new TypeError("Code local source does not match the selected GitHub primary source");
|
|
4391
|
+
}
|
|
4392
|
+
await attachCodeRuntimeReferences(workspace, selected.references ?? []);
|
|
4393
|
+
}
|
|
4314
4394
|
} else {
|
|
4315
4395
|
const source = await this.options.control.source(command.sessionId);
|
|
4316
4396
|
const materialized = await materializeCodeRuntimeSource(source);
|
|
@@ -4338,8 +4418,8 @@ var CodePiRuntimeEngine = class {
|
|
|
4338
4418
|
repository: metadata2.repository,
|
|
4339
4419
|
sourceTreeDigest: metadata2.sourceTreeDigest,
|
|
4340
4420
|
trustedBaseDigest: requestedLocal ? localTrustedBaseDigest : await digestStagedWorkspace(workspace.baselineDir, {
|
|
4341
|
-
maxFiles:
|
|
4342
|
-
maxBytes:
|
|
4421
|
+
maxFiles: 2e4,
|
|
4422
|
+
maxBytes: 512 * 1024 * 1024
|
|
4343
4423
|
}),
|
|
4344
4424
|
planningInputDigest: metadata2.planningInputDigest ?? digestRuntimeValue(
|
|
4345
4425
|
JSON.stringify({ attestation: metadata2.attestationDigest, prompt: metadata2.prompt, tree: sourceDigest })
|
|
@@ -7674,4 +7754,4 @@ export {
|
|
|
7674
7754
|
exitCodeFor,
|
|
7675
7755
|
runCli
|
|
7676
7756
|
};
|
|
7677
|
-
//# sourceMappingURL=chunk-
|
|
7757
|
+
//# sourceMappingURL=chunk-FRDIQURT.js.map
|