@open-cr-agent/cli 0.5.0 → 0.6.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/dist/cloud/account-salt.d.ts +6 -0
- package/dist/cloud/account-salt.js +33 -0
- package/dist/{review → cloud}/account-settings.js +5 -4
- package/dist/cloud/browser.d.ts +17 -0
- package/dist/cloud/browser.js +37 -0
- package/dist/cloud/client.d.ts +93 -0
- package/dist/cloud/client.js +221 -0
- package/dist/cloud/credentials.d.ts +18 -0
- package/dist/cloud/credentials.js +52 -0
- package/dist/cloud/deps.d.ts +14 -0
- package/dist/cloud/deps.js +34 -0
- package/dist/cloud/file-lock.d.ts +14 -0
- package/dist/cloud/file-lock.js +77 -0
- package/dist/cloud/findings.d.ts +8 -0
- package/dist/{review/cloud-findings.js → cloud/findings.js} +10 -32
- package/dist/cloud/gateway-token.d.ts +18 -0
- package/dist/cloud/gateway-token.js +63 -0
- package/dist/cloud/memory.d.ts +8 -0
- package/dist/cloud/memory.js +68 -0
- package/dist/cloud/private-file.d.ts +8 -0
- package/dist/cloud/private-file.js +46 -0
- package/dist/{review/cloud-providers.d.ts → cloud/providers.d.ts} +12 -6
- package/dist/cloud/providers.js +132 -0
- package/dist/cloud/review.d.ts +28 -0
- package/dist/cloud/review.js +76 -0
- package/dist/cloud/settings.d.ts +22 -0
- package/dist/cloud/settings.js +64 -0
- package/dist/cloud/upload.d.ts +20 -0
- package/dist/{review/cloud-upload.js → cloud/upload.js} +74 -71
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.js +164 -0
- package/dist/commands/memory.d.ts +3 -0
- package/dist/{memory.js → commands/memory.js} +21 -20
- package/dist/commands/metrics.d.ts +45 -0
- package/dist/{metrics.js → commands/metrics.js} +32 -45
- package/dist/{plugins-command.d.ts → commands/plugins.d.ts} +3 -9
- package/dist/{plugins-command.js → commands/plugins.js} +29 -53
- package/dist/{review → commands/review}/args.d.ts +7 -10
- package/dist/{review → commands/review}/args.js +11 -16
- package/dist/commands/review/deliver.d.ts +7 -0
- package/dist/commands/review/deliver.js +56 -0
- package/dist/commands/review/deps.d.ts +18 -0
- package/dist/commands/review/deps.js +2 -0
- package/dist/commands/review/execute-run.d.ts +13 -0
- package/dist/commands/review/execute-run.js +97 -0
- package/dist/commands/review/exit-code.d.ts +4 -0
- package/dist/commands/review/exit-code.js +29 -0
- package/dist/commands/review/plan-prices.d.ts +3 -0
- package/dist/commands/review/plan-prices.js +18 -0
- package/dist/{review → commands/review}/plan-render.js +22 -2
- package/dist/commands/review/plan-run.d.ts +5 -0
- package/dist/commands/review/plan-run.js +38 -0
- package/dist/commands/review/plan-schema.d.ts +97 -0
- package/dist/commands/review/plan-schema.js +34 -0
- package/dist/{review → commands/review}/progress.d.ts +3 -5
- package/dist/{review → commands/review}/progress.js +3 -3
- package/dist/{review → commands/review}/provenance.d.ts +4 -2
- package/dist/{review → commands/review}/provenance.js +2 -3
- package/dist/{review → commands/review}/render.js +1 -1
- package/dist/commands/review/resolve-run.d.ts +29 -0
- package/dist/commands/review/resolve-run.js +125 -0
- package/dist/commands/review/review-options.d.ts +6 -0
- package/dist/commands/review/review-options.js +35 -0
- package/dist/{review → commands/review}/runtimes.js +1 -1
- package/dist/{review → commands/review}/sarif-input.d.ts +0 -1
- package/dist/{review → commands/review}/sarif-input.js +5 -5
- package/dist/commands/review/sarif.d.ts +96 -0
- package/dist/{review → commands/review}/sarif.js +29 -7
- package/dist/commands/review/settings-sources.d.ts +17 -0
- package/dist/commands/review/settings-sources.js +30 -0
- package/dist/commands/review/target.d.ts +38 -0
- package/dist/commands/review/target.js +126 -0
- package/dist/commands/review.d.ts +7 -0
- package/dist/commands/review.js +34 -0
- package/dist/config/cli-config.d.ts +41 -0
- package/dist/config/cli-config.js +199 -0
- package/dist/{review/remote-config.d.ts → config/remote.d.ts} +3 -3
- package/dist/{review/remote-config.js → config/remote.js} +5 -23
- package/dist/{review/config.d.ts → config/schema.d.ts} +8 -31
- package/dist/config/schema.js +138 -0
- package/dist/config/settings.d.ts +37 -0
- package/dist/config/settings.js +133 -0
- package/dist/config/user-dir.d.ts +3 -0
- package/dist/config/user-dir.js +10 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -1
- package/dist/internal.d.ts +6 -0
- package/dist/internal.js +7 -0
- package/dist/io/exit.d.ts +9 -0
- package/dist/io/exit.js +12 -0
- package/dist/io/output.d.ts +4 -0
- package/dist/io/output.js +2 -0
- package/dist/io/usage-error.d.ts +5 -0
- package/dist/io/usage-error.js +8 -0
- package/dist/main.js +2 -2
- package/dist/{review/account-plugins.d.ts → plugins/account.d.ts} +2 -2
- package/dist/{review/account-plugins.js → plugins/account.js} +7 -7
- package/dist/{review/plugins.d.ts → plugins/load.d.ts} +2 -2
- package/dist/{review/plugins.js → plugins/load.js} +6 -5
- package/dist/plugins/npm.d.ts +8 -0
- package/dist/plugins/npm.js +24 -0
- package/dist/{plugin-store.d.ts → plugins/store.d.ts} +1 -1
- package/dist/{plugin-store.js → plugins/store.js} +5 -4
- package/dist/repository-id.d.ts +12 -0
- package/dist/repository-id.js +72 -0
- package/dist/run.d.ts +2 -2
- package/dist/run.js +15 -12
- package/dist/session/store.d.ts +9 -0
- package/dist/session/store.js +31 -0
- package/package.json +17 -7
- package/dist/account-salt.d.ts +0 -7
- package/dist/account-salt.js +0 -43
- package/dist/cloud.d.ts +0 -34
- package/dist/cloud.js +0 -272
- package/dist/memory.d.ts +0 -4
- package/dist/metrics.d.ts +0 -47
- package/dist/review/cloud-findings.d.ts +0 -31
- package/dist/review/cloud-memory.d.ts +0 -6
- package/dist/review/cloud-memory.js +0 -58
- package/dist/review/cloud-providers.js +0 -60
- package/dist/review/cloud-review.d.ts +0 -19
- package/dist/review/cloud-review.js +0 -51
- package/dist/review/cloud-settings.d.ts +0 -16
- package/dist/review/cloud-settings.js +0 -118
- package/dist/review/cloud-upload.d.ts +0 -60
- package/dist/review/command.d.ts +0 -33
- package/dist/review/command.js +0 -293
- package/dist/review/config.js +0 -304
- package/dist/review/sarif.d.ts +0 -4
- package/dist/review/settings-sources.d.ts +0 -11
- package/dist/review/settings-sources.js +0 -58
- package/dist/review/target.d.ts +0 -18
- package/dist/review/target.js +0 -210
- /package/dist/{review → cloud}/account-settings.d.ts +0 -0
- /package/dist/{review → commands/review}/plan-render.d.ts +0 -0
- /package/dist/{review → commands/review}/render.d.ts +0 -0
- /package/dist/{review → commands/review}/runtimes.d.ts +0 -0
- /package/dist/{review → io}/terminal.d.ts +0 -0
- /package/dist/{review → io}/terminal.js +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { coverageGaps } from "@open-cr-agent/core";
|
|
2
|
+
import { EXIT } from "../../io/exit.js";
|
|
3
|
+
import { forTerminal } from "../../io/terminal.js";
|
|
4
|
+
export function exitCode(report, err) {
|
|
5
|
+
if (report.tasks.length > 0 && report.tasks.every((t) => t.status !== "completed")) {
|
|
6
|
+
err.write("[ocra] No review task completed; see the errors above.\n");
|
|
7
|
+
return EXIT.error;
|
|
8
|
+
}
|
|
9
|
+
// Incomplete comes first, even over a blocking verdict: the action lets
|
|
10
|
+
// exit 1 pass unless fail-on-concerns is set, and a review that missed
|
|
11
|
+
// files or could not check a critical finding must never pass.
|
|
12
|
+
const { notReviewed } = coverageGaps(report);
|
|
13
|
+
if (notReviewed > 0) {
|
|
14
|
+
err.write(`[ocra] ${notReviewed} selected file(s) were not reviewed; the review is incomplete.\n`);
|
|
15
|
+
return EXIT.incomplete;
|
|
16
|
+
}
|
|
17
|
+
if (report.unverifiedCriticals > 0) {
|
|
18
|
+
err.write(`[ocra] ${report.unverifiedCriticals} critical finding(s) could not be verified; the review is incomplete.\n`);
|
|
19
|
+
return EXIT.incomplete;
|
|
20
|
+
}
|
|
21
|
+
if (report.verdict !== "significant_concerns")
|
|
22
|
+
return EXIT.ok;
|
|
23
|
+
const override = report.changeRequest.override;
|
|
24
|
+
if (!override)
|
|
25
|
+
return EXIT.blocking;
|
|
26
|
+
err.write(`[ocra] The blocking verdict was overridden by ${forTerminal(override.by)}: ${forTerminal(override.reason)}\n`);
|
|
27
|
+
return EXIT.ok;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=exit-code.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CLOUD_PREFIX } from "@open-cr-agent/cloud-contract";
|
|
2
|
+
// The input prices --plan estimates with, in US dollars per million tokens:
|
|
3
|
+
// a declared provider's; 0 for a model through ocra Cloud, which is not
|
|
4
|
+
// priced; undefined for any other, which only the runtime's catalog prices
|
|
5
|
+
// and a plan, calling nothing, does not read.
|
|
6
|
+
export function inputPriceOf(providers) {
|
|
7
|
+
return (model) => {
|
|
8
|
+
const slash = model.indexOf("/");
|
|
9
|
+
if (slash < 0)
|
|
10
|
+
return undefined;
|
|
11
|
+
const provider = model.slice(0, slash);
|
|
12
|
+
const declared = providers[provider]?.models[model.slice(slash + 1)];
|
|
13
|
+
if (declared)
|
|
14
|
+
return declared.input;
|
|
15
|
+
return provider.startsWith(CLOUD_PREFIX) && !providers[provider] ? 0 : undefined;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=plan-prices.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forTerminal } from "
|
|
1
|
+
import { forTerminal } from "../../io/terminal.js";
|
|
2
2
|
const number = new Intl.NumberFormat("en-US");
|
|
3
3
|
export function renderPlan(preview) {
|
|
4
4
|
const lines = [
|
|
@@ -20,7 +20,7 @@ export function renderPlan(preview) {
|
|
|
20
20
|
lines.push("", `Review tasks: ${preview.tasks.length}`);
|
|
21
21
|
const width = Math.max(0, ...preview.tasks.map((t) => t.taskId.length));
|
|
22
22
|
for (const t of preview.tasks) {
|
|
23
|
-
lines.push(` ${t.taskId.padEnd(width)} ~${number.format(t.promptTokens)} prompt tokens${t.planPromptTokens === undefined ? "" : ` + plan ~${number.format(t.planPromptTokens)}`}${t.effort === undefined ? "" : ` effort ${t.effort}`}${t.models === undefined ? "" : ` models ${t.models.join(", ")}`} ${t.bundle}`);
|
|
23
|
+
lines.push(` ${t.taskId.padEnd(width)} ~${number.format(t.promptTokens)} prompt tokens${t.planPromptTokens === undefined ? "" : ` + plan ~${number.format(t.planPromptTokens)}`}${t.effort === undefined ? "" : ` effort ${t.effort}`}${t.models === undefined ? "" : ` models ${t.models.join(", ")}`}${costOf(t.inputCost)} ${t.bundle}`);
|
|
24
24
|
}
|
|
25
25
|
if (preview.skipped.length > 0) {
|
|
26
26
|
lines.push("", "Skipped by the review matrix:");
|
|
@@ -30,7 +30,27 @@ export function renderPlan(preview) {
|
|
|
30
30
|
}
|
|
31
31
|
for (const warning of preview.warnings)
|
|
32
32
|
lines.push(`Warning: ${warning}`);
|
|
33
|
+
if (preview.inputCost)
|
|
34
|
+
lines.push("", totalCost(preview.inputCost, preview.tasks.length));
|
|
33
35
|
lines.push("", `First prompts: ~${number.format(preview.promptTokens)} input tokens in total${preview.planCalls > 0 ? `, ${preview.planCalls} plan call(s) included` : ""}. This is a floor: agents read files and take several turns, so a run usually uses several times as much, plus verification and judging. No model was called.`);
|
|
34
36
|
return forTerminal(`${lines.join("\n")}\n`);
|
|
35
37
|
}
|
|
38
|
+
function costOf(cost) {
|
|
39
|
+
if (cost === undefined)
|
|
40
|
+
return "";
|
|
41
|
+
if (cost.status === "priced")
|
|
42
|
+
return ` input ~${usd(cost.usd)}`;
|
|
43
|
+
return cost.status === "unpriced" ? " input unpriced" : " input price unknown";
|
|
44
|
+
}
|
|
45
|
+
function totalCost(total, tasks) {
|
|
46
|
+
const rest = [
|
|
47
|
+
total.unpriced > 0 ? `${total.unpriced} unpriced` : "",
|
|
48
|
+
total.unknown > 0 ? `${total.unknown} of unknown price` : "",
|
|
49
|
+
].filter(Boolean);
|
|
50
|
+
return `Estimated input cost: ~${usd(total.usd)} for the first prompts of ${total.priced} of ${tasks} task(s)${rest.length > 0 ? ` (${rest.join(", ")})` : ""}, at the input price of each chain's first model. Input only: output, later turns, verification and judging are not known before the run.`;
|
|
51
|
+
}
|
|
52
|
+
// Cents hide the size of a small prompt's cost; four decimals keep it.
|
|
53
|
+
function usd(value) {
|
|
54
|
+
return `$${value.toFixed(value >= 1 ? 2 : 4)}`;
|
|
55
|
+
}
|
|
36
56
|
//# sourceMappingURL=plan-render.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReviewArgs } from "./args.js";
|
|
2
|
+
import type { ReviewDeps } from "./deps.js";
|
|
3
|
+
import type { ResolvedRun, ReviewIo } from "./resolve-run.js";
|
|
4
|
+
export declare function planRun(run: ResolvedRun, args: ReviewArgs, io: ReviewIo, deps: ReviewDeps): Promise<number>;
|
|
5
|
+
//# sourceMappingURL=plan-run.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { previewReview, toPlanOutput } from "@open-cr-agent/core/internal";
|
|
3
|
+
import { EXIT } from "../../io/exit.js";
|
|
4
|
+
import { inputPriceOf } from "./plan-prices.js";
|
|
5
|
+
import { renderPlan } from "./plan-render.js";
|
|
6
|
+
import { safeJson } from "./render.js";
|
|
7
|
+
import { configuredOptions } from "./review-options.js";
|
|
8
|
+
import { accountOf, effectiveSettings, renderSettings, } from "./settings-sources.js";
|
|
9
|
+
// --plan: what the review would do and cost, without calling a model.
|
|
10
|
+
export async function planRun(run, args, io, deps) {
|
|
11
|
+
const { config } = run;
|
|
12
|
+
const preview = await previewReview({
|
|
13
|
+
...configuredOptions(run, args),
|
|
14
|
+
vcs: run.vcs,
|
|
15
|
+
inputPrice: inputPriceOf(config.providers),
|
|
16
|
+
});
|
|
17
|
+
const settings = effectiveSettings(run.listed);
|
|
18
|
+
const rendered = args.format === "json"
|
|
19
|
+
? `${safeJson(planJson(preview, settings, run.accountSettings))}\n`
|
|
20
|
+
: renderPlan(preview) + renderSettings(settings, run.accountSettings);
|
|
21
|
+
if (args.output === undefined)
|
|
22
|
+
io.out.write(rendered);
|
|
23
|
+
else
|
|
24
|
+
await deps.writeFile(resolve(deps.cwd, args.output), rendered);
|
|
25
|
+
return EXIT.ok;
|
|
26
|
+
}
|
|
27
|
+
function planJson(preview, settings, account) {
|
|
28
|
+
return {
|
|
29
|
+
...toPlanOutput(preview),
|
|
30
|
+
settings: settings.map((s) => ({
|
|
31
|
+
key: s.key,
|
|
32
|
+
...("value" in s ? { value: s.value } : {}),
|
|
33
|
+
source: s.source,
|
|
34
|
+
})),
|
|
35
|
+
...accountOf(account),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=plan-run.js.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const cliPlanOutputSchema: z.ZodObject<{
|
|
3
|
+
version: z.ZodLiteral<1>;
|
|
4
|
+
changeRequest: z.ZodObject<{
|
|
5
|
+
id: z.ZodString;
|
|
6
|
+
title: z.ZodString;
|
|
7
|
+
description: z.ZodString;
|
|
8
|
+
baseSha: z.ZodString;
|
|
9
|
+
headSha: z.ZodString;
|
|
10
|
+
override: z.ZodExactOptional<z.ZodObject<{
|
|
11
|
+
by: z.ZodString;
|
|
12
|
+
reason: z.ZodString;
|
|
13
|
+
}, z.core.$strict>>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
tier: z.ZodEnum<{
|
|
16
|
+
full: "full";
|
|
17
|
+
lite: "lite";
|
|
18
|
+
trivial: "trivial";
|
|
19
|
+
}>;
|
|
20
|
+
selected: z.ZodArray<z.ZodString>;
|
|
21
|
+
excluded: z.ZodArray<z.ZodObject<{
|
|
22
|
+
path: z.ZodString;
|
|
23
|
+
reason: z.ZodEnum<{
|
|
24
|
+
binary: "binary";
|
|
25
|
+
deleted: "deleted";
|
|
26
|
+
extension: "extension";
|
|
27
|
+
generated: "generated";
|
|
28
|
+
secret: "secret";
|
|
29
|
+
too_large: "too_large";
|
|
30
|
+
user_exclude: "user_exclude";
|
|
31
|
+
}>;
|
|
32
|
+
}, z.core.$strict>>;
|
|
33
|
+
bundles: z.ZodArray<z.ZodObject<{
|
|
34
|
+
label: z.ZodString;
|
|
35
|
+
files: z.ZodArray<z.ZodString>;
|
|
36
|
+
}, z.core.$strict>>;
|
|
37
|
+
groupingSkipped: z.ZodBoolean;
|
|
38
|
+
tasks: z.ZodArray<z.ZodObject<{
|
|
39
|
+
taskId: z.ZodString;
|
|
40
|
+
reviewer: z.ZodString;
|
|
41
|
+
bundle: z.ZodString;
|
|
42
|
+
files: z.ZodArray<z.ZodString>;
|
|
43
|
+
promptTokens: z.ZodInt;
|
|
44
|
+
effort: z.ZodExactOptional<z.ZodEnum<{
|
|
45
|
+
high: "high";
|
|
46
|
+
low: "low";
|
|
47
|
+
medium: "medium";
|
|
48
|
+
minimal: "minimal";
|
|
49
|
+
none: "none";
|
|
50
|
+
}>>;
|
|
51
|
+
models: z.ZodExactOptional<z.ZodArray<z.ZodString>>;
|
|
52
|
+
planPromptTokens: z.ZodExactOptional<z.ZodInt>;
|
|
53
|
+
inputCost: z.ZodExactOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
54
|
+
model: z.ZodString;
|
|
55
|
+
status: z.ZodLiteral<"priced">;
|
|
56
|
+
usd: z.ZodNumber;
|
|
57
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
58
|
+
model: z.ZodString;
|
|
59
|
+
status: z.ZodLiteral<"unpriced">;
|
|
60
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
61
|
+
model: z.ZodString;
|
|
62
|
+
status: z.ZodLiteral<"unknown">;
|
|
63
|
+
}, z.core.$strict>], "status">>;
|
|
64
|
+
}, z.core.$strict>>;
|
|
65
|
+
skipped: z.ZodArray<z.ZodObject<{
|
|
66
|
+
reviewer: z.ZodString;
|
|
67
|
+
bundle: z.ZodString;
|
|
68
|
+
reason: z.ZodEnum<{
|
|
69
|
+
below_tier: "below_tier";
|
|
70
|
+
disabled: "disabled";
|
|
71
|
+
no_guidelines: "no_guidelines";
|
|
72
|
+
no_matching_files: "no_matching_files";
|
|
73
|
+
task_limit: "task_limit";
|
|
74
|
+
}>;
|
|
75
|
+
}, z.core.$strict>>;
|
|
76
|
+
promptTokens: z.ZodInt;
|
|
77
|
+
planCalls: z.ZodInt;
|
|
78
|
+
inputCost: z.ZodExactOptional<z.ZodObject<{
|
|
79
|
+
usd: z.ZodNumber;
|
|
80
|
+
priced: z.ZodInt;
|
|
81
|
+
unpriced: z.ZodInt;
|
|
82
|
+
unknown: z.ZodInt;
|
|
83
|
+
}, z.core.$strict>>;
|
|
84
|
+
warnings: z.ZodArray<z.ZodString>;
|
|
85
|
+
settings: z.ZodArray<z.ZodObject<{
|
|
86
|
+
key: z.ZodString;
|
|
87
|
+
value: z.ZodExactOptional<z.ZodUnknown>;
|
|
88
|
+
source: z.ZodString;
|
|
89
|
+
}, z.core.$strict>>;
|
|
90
|
+
accountSettings: z.ZodExactOptional<z.ZodObject<{
|
|
91
|
+
version: z.ZodNullable<z.ZodString>;
|
|
92
|
+
}, z.core.$strict>>;
|
|
93
|
+
}, z.core.$strict>;
|
|
94
|
+
export type CliPlanOutput = z.output<typeof cliPlanOutputSchema>;
|
|
95
|
+
export declare const PLAN_SCHEMA_ID = "https://ocra.majincheng.com/schema/plan.v1.json";
|
|
96
|
+
export declare function planJsonSchema(): Record<string, unknown>;
|
|
97
|
+
//# sourceMappingURL=plan-schema.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { PLAN_VERSION, planOutputSchema } from "@open-cr-agent/core/internal";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// `ocra review --plan --format json`: core's plan, and where each setting of
|
|
4
|
+
// the review came from (ADR-0027). A contract like the report: a version
|
|
5
|
+
// only gains optional fields, and docs/schema/plan.v1.json is generated from
|
|
6
|
+
// here (npm run schema).
|
|
7
|
+
export const cliPlanOutputSchema = planOutputSchema.extend({
|
|
8
|
+
settings: z.array(z.strictObject({
|
|
9
|
+
key: z.string(),
|
|
10
|
+
// Absent for a default: ocra's own, or the runtime's.
|
|
11
|
+
value: z.unknown().exactOptional(),
|
|
12
|
+
source: z.string(),
|
|
13
|
+
})),
|
|
14
|
+
// Present when ocra Cloud account settings were layered under the
|
|
15
|
+
// configuration; null when the account has never saved any.
|
|
16
|
+
accountSettings: z.strictObject({ version: z.string().nullable() }).exactOptional(),
|
|
17
|
+
});
|
|
18
|
+
export const PLAN_SCHEMA_ID = `https://ocra.majincheng.com/schema/plan.v${PLAN_VERSION}.json`;
|
|
19
|
+
export function planJsonSchema() {
|
|
20
|
+
const generated = z.toJSONSchema(cliPlanOutputSchema, {
|
|
21
|
+
target: "draft-2020-12",
|
|
22
|
+
io: "output",
|
|
23
|
+
unrepresentable: "any",
|
|
24
|
+
});
|
|
25
|
+
const { $schema, ...rest } = generated;
|
|
26
|
+
return {
|
|
27
|
+
$schema,
|
|
28
|
+
$id: PLAN_SCHEMA_ID,
|
|
29
|
+
title: "ocra review plan",
|
|
30
|
+
description: `What ocra review --plan --format json writes (version ${PLAN_VERSION}): the files, tasks and estimated prompt sizes of a review, before any model call. A version only gains optional fields; anything else is a new version.`,
|
|
31
|
+
...rest,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=plan-schema.js.map
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { ReviewEvent } from "@open-cr-agent/core";
|
|
2
|
-
|
|
3
|
-
write(chunk: string): unknown;
|
|
4
|
-
}
|
|
2
|
+
import type { Output } from "../../io/output.js";
|
|
5
3
|
export interface ProgressOptions {
|
|
6
4
|
heartbeatMs: number;
|
|
7
5
|
now: () => number;
|
|
8
6
|
}
|
|
9
7
|
export declare class ProgressPrinter {
|
|
10
|
-
private readonly err;
|
|
11
|
-
private readonly options;
|
|
12
8
|
private lastOutput;
|
|
13
9
|
private readonly timer;
|
|
10
|
+
private readonly err;
|
|
11
|
+
private readonly options;
|
|
14
12
|
constructor(err: Output, options: ProgressOptions);
|
|
15
13
|
onEvent(event: ReviewEvent): void;
|
|
16
14
|
stop(): void;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { forTerminal } from "
|
|
1
|
+
import { forTerminal } from "../../io/terminal.js";
|
|
2
2
|
export class ProgressPrinter {
|
|
3
|
-
err;
|
|
4
|
-
options;
|
|
5
3
|
lastOutput;
|
|
6
4
|
timer;
|
|
5
|
+
err;
|
|
6
|
+
options;
|
|
7
7
|
constructor(err, options) {
|
|
8
8
|
this.err = err;
|
|
9
9
|
this.options = options;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Sampling } from "@open-cr-agent/core";
|
|
2
|
+
import type { CliConfig } from "../../config/cli-config.js";
|
|
2
3
|
import type { ReviewArgs } from "./args.js";
|
|
3
|
-
import type { CliConfig } from "./config.js";
|
|
4
4
|
export declare function requestedSampling(config: CliConfig, args: ReviewArgs): Sampling;
|
|
5
|
-
export declare function configHash(config: CliConfig, args: ReviewArgs
|
|
5
|
+
export declare function configHash(config: CliConfig, args: ReviewArgs, accountSettings?: {
|
|
6
|
+
version: string | null;
|
|
7
|
+
}): string;
|
|
6
8
|
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -16,9 +16,8 @@ export function requestedSampling(config, args) {
|
|
|
16
16
|
// unset it adds nothing, so configurations that set none keep their hash.
|
|
17
17
|
// The ocra Cloud account settings (ADR-0027) add their version and rules
|
|
18
18
|
// only when they were layered in, so a signed-out run keeps its hash.
|
|
19
|
-
export function configHash(config, args) {
|
|
20
|
-
|
|
21
|
-
const { sampling: _, runtimeSet: _set, providers, extends: shared, effort, roles, rules, accountSettings, ...rest } = config;
|
|
19
|
+
export function configHash(config, args, accountSettings) {
|
|
20
|
+
const { sampling: _, providers, extends: shared, effort, roles, rules, ...rest } = config;
|
|
22
21
|
const bare = (account) => rules
|
|
23
22
|
.filter((rule) => (rule.source === "account") === account)
|
|
24
23
|
.map(({ path, rule }) => ({ path, rule }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { coverageGaps, toReportOutput, } from "@open-cr-agent/core";
|
|
2
2
|
import { serializeOutput } from "@open-cr-agent/core/internal";
|
|
3
|
-
import { forTerminal } from "
|
|
3
|
+
import { forTerminal } from "../../io/terminal.js";
|
|
4
4
|
const SEVERITIES = ["critical", "warning", "suggestion"];
|
|
5
5
|
const VERIFICATION = {
|
|
6
6
|
confirmed: "verified",
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type PluginRegistry, type ReviewerOverrides, type SourcedRule, type VcsAdapter } from "@open-cr-agent/core";
|
|
2
|
+
import type { CloudDeps } from "../../cloud/deps.js";
|
|
3
|
+
import { type ResolvedConfig } from "../../config/cli-config.js";
|
|
4
|
+
import type { Output } from "../../io/output.js";
|
|
5
|
+
import type { ReviewArgs } from "./args.js";
|
|
6
|
+
import type { ReviewDeps } from "./deps.js";
|
|
7
|
+
import { type AccountVersion } from "./settings-sources.js";
|
|
8
|
+
import { type ReviewTarget } from "./target.js";
|
|
9
|
+
export interface ReviewIo {
|
|
10
|
+
out: Output;
|
|
11
|
+
err: Output;
|
|
12
|
+
}
|
|
13
|
+
export interface ResolvedRun extends ResolvedConfig {
|
|
14
|
+
root: string;
|
|
15
|
+
target: ReviewTarget;
|
|
16
|
+
warn: (message: string) => void;
|
|
17
|
+
signedInCloud?: CloudDeps;
|
|
18
|
+
accountSettings?: AccountVersion;
|
|
19
|
+
session: {
|
|
20
|
+
dir: string;
|
|
21
|
+
id: string;
|
|
22
|
+
};
|
|
23
|
+
registry: PluginRegistry;
|
|
24
|
+
vcs: VcsAdapter;
|
|
25
|
+
rules: SourcedRule[];
|
|
26
|
+
overrides: ReviewerOverrides;
|
|
27
|
+
}
|
|
28
|
+
export declare function resolveRun(args: ReviewArgs, io: ReviewIo, deps: ReviewDeps): Promise<ResolvedRun>;
|
|
29
|
+
//# sourceMappingURL=resolve-run.d.ts.map
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { sessionJsonlPlugin, startPlugins, } from "@open-cr-agent/core";
|
|
2
|
+
import { newRunId } from "@open-cr-agent/core/internal";
|
|
3
|
+
import { findRepositoryRoot } from "@open-cr-agent/vcs-local/internal";
|
|
4
|
+
import { sessionLostWarning } from "../../cloud/review.js";
|
|
5
|
+
import { accountLayer, fetchAccountSettings } from "../../cloud/settings.js";
|
|
6
|
+
import { cloudEnabled } from "../../cloud/upload.js";
|
|
7
|
+
import { ConfigError, resolveConfig, } from "../../config/cli-config.js";
|
|
8
|
+
import { forTerminal } from "../../io/terminal.js";
|
|
9
|
+
import { loadAccountPlugins } from "../../plugins/account.js";
|
|
10
|
+
import { pluginsDir } from "../../plugins/store.js";
|
|
11
|
+
import { sessionsDir } from "../../session/store.js";
|
|
12
|
+
import { filledByAccount } from "./settings-sources.js";
|
|
13
|
+
import { resolveReviewTarget } from "./target.js";
|
|
14
|
+
export async function resolveRun(args, io, deps) {
|
|
15
|
+
const root = await findRepositoryRoot(deps.cwd);
|
|
16
|
+
const warn = (message) => io.err.write(`[ocra] Warning: ${forTerminal(message)}\n`);
|
|
17
|
+
const target = await resolveReviewTarget(args, {
|
|
18
|
+
cwd: deps.cwd,
|
|
19
|
+
root,
|
|
20
|
+
env: deps.env,
|
|
21
|
+
warn,
|
|
22
|
+
...(deps.fetch ? { fetch: deps.fetch } : {}),
|
|
23
|
+
});
|
|
24
|
+
const account = await withAccount(target, deps.cloud, warn);
|
|
25
|
+
if (args.ultra)
|
|
26
|
+
account.layers.push({ source: "flag", settings: { ultra: true } });
|
|
27
|
+
const resolved = resolveConfig(account.layers);
|
|
28
|
+
const filled = filledByAccount(resolved.listed);
|
|
29
|
+
if (filled.length > 0) {
|
|
30
|
+
io.err.write(forTerminal(`[ocra] From your ocra Cloud settings: ${filled.join(", ")}\n`));
|
|
31
|
+
}
|
|
32
|
+
const session = { dir: sessionsDir(root), id: newRunId() };
|
|
33
|
+
const registry = await startRegistry(args, deps, {
|
|
34
|
+
target,
|
|
35
|
+
config: resolved.config,
|
|
36
|
+
account: account.plugins,
|
|
37
|
+
session,
|
|
38
|
+
warn,
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
...resolved,
|
|
42
|
+
root,
|
|
43
|
+
target,
|
|
44
|
+
warn,
|
|
45
|
+
...(account.cloud ? { signedInCloud: account.cloud } : {}),
|
|
46
|
+
...(account.settings ? { accountSettings: account.settings } : {}),
|
|
47
|
+
session,
|
|
48
|
+
registry,
|
|
49
|
+
vcs: target.createVcs(registry),
|
|
50
|
+
rules: [
|
|
51
|
+
...registry.rules.map((rule) => ({ ...rule, source: "plugin" })),
|
|
52
|
+
...resolved.config.rules,
|
|
53
|
+
],
|
|
54
|
+
overrides: reviewerOverrides(resolved.config, args, registry.reviewers.map((r) => r.id)),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// The account's settings fill what the repository leaves out (ADR-0027);
|
|
58
|
+
// unreachable, they cost a warning, so a plan still works offline.
|
|
59
|
+
async function withAccount(target, cloudDeps, warn) {
|
|
60
|
+
const layers = [...target.layers];
|
|
61
|
+
const none = { plugins: [], pluginSettings: {} };
|
|
62
|
+
if (!cloudDeps || !(await cloudEnabled(cloudDeps, warn)))
|
|
63
|
+
return { layers, plugins: none };
|
|
64
|
+
const account = await fetchAccountSettings(cloudDeps, warn);
|
|
65
|
+
if (account.kind !== "read") {
|
|
66
|
+
// Said once here; the rest of the run leaves ocra Cloud alone.
|
|
67
|
+
if (account.kind !== "signed-out")
|
|
68
|
+
warn(sessionLostWarning(account));
|
|
69
|
+
return { layers, plugins: none };
|
|
70
|
+
}
|
|
71
|
+
if (!account.settings)
|
|
72
|
+
return { layers, cloud: cloudDeps, plugins: account.plugins };
|
|
73
|
+
layers.push(accountLayer(account.settings));
|
|
74
|
+
return {
|
|
75
|
+
layers,
|
|
76
|
+
cloud: cloudDeps,
|
|
77
|
+
settings: { version: account.settings.version },
|
|
78
|
+
plugins: account.plugins,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// A plan calls no model, writes no session log and imports no runtime: it
|
|
82
|
+
// works without the optional runtime-opencode.
|
|
83
|
+
async function startRegistry(args, deps, run) {
|
|
84
|
+
const { target, config, warn } = run;
|
|
85
|
+
const builtins = args.plan
|
|
86
|
+
? deps.builtinPlugins.filter((p) => p.name !== sessionJsonlPlugin.name)
|
|
87
|
+
: [...deps.builtinPlugins, ...(await builtinRuntime(deps.runtimes, config.runtime))];
|
|
88
|
+
const account = await loadAccountPlugins(run.account, {
|
|
89
|
+
allowed: target.accountPlugins,
|
|
90
|
+
configured: config.plugins,
|
|
91
|
+
taken: [...builtins, ...target.plugins],
|
|
92
|
+
dir: pluginsDir(deps.env),
|
|
93
|
+
warn,
|
|
94
|
+
});
|
|
95
|
+
// The repository's own settings win; a repository plugin's come from it alone.
|
|
96
|
+
const pluginSettings = { ...account.settings, ...config.pluginSettings };
|
|
97
|
+
return startPlugins([...builtins, ...target.plugins, ...account.plugins], {
|
|
98
|
+
settings: args.plan
|
|
99
|
+
? pluginSettings
|
|
100
|
+
: { ...pluginSettings, [sessionJsonlPlugin.name]: run.session },
|
|
101
|
+
env: deps.env,
|
|
102
|
+
warn,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// A runtime not built in comes from a plugin of the configuration.
|
|
106
|
+
async function builtinRuntime(runtimes, name) {
|
|
107
|
+
const load = Object.hasOwn(runtimes, name) ? runtimes[name] : undefined;
|
|
108
|
+
return load ? [await load()] : [];
|
|
109
|
+
}
|
|
110
|
+
// --reviewers narrows the run to the named reviewers on top of the config.
|
|
111
|
+
function reviewerOverrides(config, args, registered) {
|
|
112
|
+
if (args.reviewers === undefined)
|
|
113
|
+
return config.reviewers;
|
|
114
|
+
const unknown = args.reviewers.filter((id) => !registered.includes(id));
|
|
115
|
+
if (unknown.length > 0) {
|
|
116
|
+
throw new ConfigError(`Unknown reviewer(s): ${unknown.join(", ")} (available: ${registered.join(", ")})`);
|
|
117
|
+
}
|
|
118
|
+
const overrides = { ...config.reviewers };
|
|
119
|
+
for (const id of registered) {
|
|
120
|
+
if (!args.reviewers.includes(id))
|
|
121
|
+
overrides[id] = { ...overrides[id], enabled: false };
|
|
122
|
+
}
|
|
123
|
+
return overrides;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=resolve-run.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReviewOptions } from "@open-cr-agent/core";
|
|
2
|
+
import type { ReviewArgs } from "./args.js";
|
|
3
|
+
import type { ResolvedRun } from "./resolve-run.js";
|
|
4
|
+
export type ConfiguredOptions = Omit<ReviewOptions, "vcs" | "runtime" | "identity" | "sarif" | "accountMemory" | "signal" | "onEvent">;
|
|
5
|
+
export declare function configuredOptions(run: ResolvedRun, args: ReviewArgs): ConfiguredOptions;
|
|
6
|
+
//# sourceMappingURL=review-options.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// What the configuration and the flags decide about a review, mapped once
|
|
2
|
+
// for the review and its --plan preview: minutes become milliseconds, and
|
|
3
|
+
// --max-cost-usd overrides the configured limit.
|
|
4
|
+
export function configuredOptions(run, args) {
|
|
5
|
+
const { config } = run;
|
|
6
|
+
const maxCostUsd = args.maxCostUsd ?? config.maxCostUsd;
|
|
7
|
+
const limits = {
|
|
8
|
+
...(config.concurrency !== undefined ? { concurrency: config.concurrency } : {}),
|
|
9
|
+
...(config.taskTimeoutMinutes !== undefined
|
|
10
|
+
? { taskTimeoutMs: config.taskTimeoutMinutes * 60_000 }
|
|
11
|
+
: {}),
|
|
12
|
+
...(config.runTimeoutMinutes !== undefined
|
|
13
|
+
? { runTimeoutMs: config.runTimeoutMinutes * 60_000 }
|
|
14
|
+
: {}),
|
|
15
|
+
...(maxCostUsd !== undefined ? { maxCostUsd } : {}),
|
|
16
|
+
...(config.maxTasks !== undefined ? { maxTasks: config.maxTasks } : {}),
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
reviewers: run.registry.reviewers,
|
|
20
|
+
reviewerOverrides: run.overrides,
|
|
21
|
+
effort: config.effort,
|
|
22
|
+
roles: config.roles,
|
|
23
|
+
models: config.models,
|
|
24
|
+
rules: run.rules,
|
|
25
|
+
selection: { include: config.include, exclude: config.exclude },
|
|
26
|
+
...(run.target.readTrusted ? { readTrusted: run.target.readTrusted } : {}),
|
|
27
|
+
limits,
|
|
28
|
+
stages: {
|
|
29
|
+
...(config.verify !== undefined ? { verify: config.verify } : {}),
|
|
30
|
+
...(config.judge !== undefined ? { judge: config.judge } : {}),
|
|
31
|
+
},
|
|
32
|
+
mode: { ...(run.ultra ? { ultra: true } : {}), ...(args.full ? { full: true } : {}) },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=review-options.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OcraError } from "@open-cr-agent/core";
|
|
2
|
-
import { VERSION } from "
|
|
2
|
+
import { VERSION } from "../../version.js";
|
|
3
3
|
export const BUILTIN_RUNTIMES = {
|
|
4
4
|
opencode: () => loadRuntime("opencode", "@open-cr-agent/runtime-opencode", async () => (await import("@open-cr-agent/runtime-opencode")).opencodeRuntimePlugin),
|
|
5
5
|
direct: () => loadRuntime("direct", "@open-cr-agent/runtime-direct", async () => (await import("@open-cr-agent/runtime-direct")).directRuntimePlugin),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { parseSarifLog } from "@open-cr-agent/core";
|
|
4
|
-
import { ConfigError } from "
|
|
3
|
+
import { errorMessage, parseSarifLog } from "@open-cr-agent/core";
|
|
4
|
+
import { ConfigError } from "../../config/cli-config.js";
|
|
5
5
|
// A log is untrusted input like everything else that reaches the review; it
|
|
6
6
|
// is bounded before it is parsed.
|
|
7
|
-
|
|
7
|
+
const MAX_SARIF_BYTES = 20 * 1024 * 1024;
|
|
8
8
|
export async function loadSarifLogs(paths, cwd) {
|
|
9
9
|
const logs = [];
|
|
10
10
|
for (const path of paths) {
|
|
@@ -20,13 +20,13 @@ export async function loadSarifLogs(paths, cwd) {
|
|
|
20
20
|
catch (error) {
|
|
21
21
|
if (error instanceof ConfigError)
|
|
22
22
|
throw error;
|
|
23
|
-
throw new ConfigError(`--import-sarif ${path}: ${error
|
|
23
|
+
throw new ConfigError(`--import-sarif ${path}: ${errorMessage(error)}`);
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
26
|
logs.push(parseSarifLog(text));
|
|
27
27
|
}
|
|
28
28
|
catch (error) {
|
|
29
|
-
throw new ConfigError(`--import-sarif ${path}: ${error
|
|
29
|
+
throw new ConfigError(`--import-sarif ${path}: ${errorMessage(error)}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
return logs;
|