rightmodeler 0.1.0 → 0.2.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 -1
- package/dist-bundle/cli.js +681 -126
- package/docs/commands.md +4 -2
- package/docs/exit-codes.md +1 -1
- package/docs/getting-started.md +32 -0
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Rightmodeler CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Published on npm as `rightmodeler`; inside this repository the workspace package is named
|
|
4
|
+
`@rightmodeler/cli` so it cannot collide with the repository root package.
|
|
4
5
|
|
|
5
6
|
Start with [the packaged getting-started guide](docs/getting-started.md).
|
package/dist-bundle/cli.js
CHANGED
|
@@ -6418,7 +6418,7 @@ var require_compile = __commonJS({
|
|
|
6418
6418
|
const schOrFunc = root.refs[ref];
|
|
6419
6419
|
if (schOrFunc)
|
|
6420
6420
|
return schOrFunc;
|
|
6421
|
-
let _sch =
|
|
6421
|
+
let _sch = resolve12.call(this, root, ref);
|
|
6422
6422
|
if (_sch === void 0) {
|
|
6423
6423
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
6424
6424
|
const { schemaId } = this.opts;
|
|
@@ -6445,7 +6445,7 @@ var require_compile = __commonJS({
|
|
|
6445
6445
|
function sameSchemaEnv(s1, s2) {
|
|
6446
6446
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
6447
6447
|
}
|
|
6448
|
-
function
|
|
6448
|
+
function resolve12(root, ref) {
|
|
6449
6449
|
let sch;
|
|
6450
6450
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
6451
6451
|
ref = sch;
|
|
@@ -7076,55 +7076,55 @@ var require_fast_uri = __commonJS({
|
|
|
7076
7076
|
}
|
|
7077
7077
|
return uri;
|
|
7078
7078
|
}
|
|
7079
|
-
function
|
|
7079
|
+
function resolve12(baseURI, relativeURI, options) {
|
|
7080
7080
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
7081
7081
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
7082
7082
|
schemelessOptions.skipEscape = true;
|
|
7083
7083
|
return serialize(resolved, schemelessOptions);
|
|
7084
7084
|
}
|
|
7085
|
-
function resolveComponent(base,
|
|
7085
|
+
function resolveComponent(base, relative9, options, skipNormalization) {
|
|
7086
7086
|
const target = {};
|
|
7087
7087
|
if (!skipNormalization) {
|
|
7088
7088
|
base = parse3(serialize(base, options), options);
|
|
7089
|
-
|
|
7089
|
+
relative9 = parse3(serialize(relative9, options), options);
|
|
7090
7090
|
}
|
|
7091
7091
|
options = options || {};
|
|
7092
|
-
if (!options.tolerant &&
|
|
7093
|
-
target.scheme =
|
|
7094
|
-
target.userinfo =
|
|
7095
|
-
target.host =
|
|
7096
|
-
target.port =
|
|
7097
|
-
target.path = removeDotSegments(
|
|
7098
|
-
target.query =
|
|
7092
|
+
if (!options.tolerant && relative9.scheme) {
|
|
7093
|
+
target.scheme = relative9.scheme;
|
|
7094
|
+
target.userinfo = relative9.userinfo;
|
|
7095
|
+
target.host = relative9.host;
|
|
7096
|
+
target.port = relative9.port;
|
|
7097
|
+
target.path = removeDotSegments(relative9.path || "");
|
|
7098
|
+
target.query = relative9.query;
|
|
7099
7099
|
} else {
|
|
7100
|
-
if (
|
|
7101
|
-
target.userinfo =
|
|
7102
|
-
target.host =
|
|
7103
|
-
target.port =
|
|
7104
|
-
target.path = removeDotSegments(
|
|
7105
|
-
target.query =
|
|
7100
|
+
if (relative9.userinfo !== void 0 || relative9.host !== void 0 || relative9.port !== void 0) {
|
|
7101
|
+
target.userinfo = relative9.userinfo;
|
|
7102
|
+
target.host = relative9.host;
|
|
7103
|
+
target.port = relative9.port;
|
|
7104
|
+
target.path = removeDotSegments(relative9.path || "");
|
|
7105
|
+
target.query = relative9.query;
|
|
7106
7106
|
} else {
|
|
7107
|
-
if (!
|
|
7107
|
+
if (!relative9.path) {
|
|
7108
7108
|
target.path = base.path;
|
|
7109
|
-
if (
|
|
7110
|
-
target.query =
|
|
7109
|
+
if (relative9.query !== void 0) {
|
|
7110
|
+
target.query = relative9.query;
|
|
7111
7111
|
} else {
|
|
7112
7112
|
target.query = base.query;
|
|
7113
7113
|
}
|
|
7114
7114
|
} else {
|
|
7115
|
-
if (
|
|
7116
|
-
target.path = removeDotSegments(
|
|
7115
|
+
if (relative9.path[0] === "/") {
|
|
7116
|
+
target.path = removeDotSegments(relative9.path);
|
|
7117
7117
|
} else {
|
|
7118
7118
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
7119
|
-
target.path = "/" +
|
|
7119
|
+
target.path = "/" + relative9.path;
|
|
7120
7120
|
} else if (!base.path) {
|
|
7121
|
-
target.path =
|
|
7121
|
+
target.path = relative9.path;
|
|
7122
7122
|
} else {
|
|
7123
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
7123
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative9.path;
|
|
7124
7124
|
}
|
|
7125
7125
|
target.path = removeDotSegments(target.path);
|
|
7126
7126
|
}
|
|
7127
|
-
target.query =
|
|
7127
|
+
target.query = relative9.query;
|
|
7128
7128
|
}
|
|
7129
7129
|
target.userinfo = base.userinfo;
|
|
7130
7130
|
target.host = base.host;
|
|
@@ -7132,7 +7132,7 @@ var require_fast_uri = __commonJS({
|
|
|
7132
7132
|
}
|
|
7133
7133
|
target.scheme = base.scheme;
|
|
7134
7134
|
}
|
|
7135
|
-
target.fragment =
|
|
7135
|
+
target.fragment = relative9.fragment;
|
|
7136
7136
|
return target;
|
|
7137
7137
|
}
|
|
7138
7138
|
function equal(uriA, uriB, options) {
|
|
@@ -7340,7 +7340,7 @@ var require_fast_uri = __commonJS({
|
|
|
7340
7340
|
var fastUri = {
|
|
7341
7341
|
SCHEMES,
|
|
7342
7342
|
normalize,
|
|
7343
|
-
resolve:
|
|
7343
|
+
resolve: resolve12,
|
|
7344
7344
|
resolveComponent,
|
|
7345
7345
|
equal,
|
|
7346
7346
|
serialize,
|
|
@@ -10590,7 +10590,9 @@ var require__ = __commonJS({
|
|
|
10590
10590
|
// src/cli.ts
|
|
10591
10591
|
import { spawn } from "node:child_process";
|
|
10592
10592
|
import { realpathSync } from "node:fs";
|
|
10593
|
-
import {
|
|
10593
|
+
import { homedir as homedir2 } from "node:os";
|
|
10594
|
+
import { resolve as resolve11 } from "node:path";
|
|
10595
|
+
import { Writable } from "node:stream";
|
|
10594
10596
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10595
10597
|
|
|
10596
10598
|
// ../../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
|
|
@@ -15363,8 +15365,8 @@ var error3 = () => {
|
|
|
15363
15365
|
const sizing = getSizing(issue3.origin);
|
|
15364
15366
|
if (sizing) {
|
|
15365
15367
|
const maxValue = Number(issue3.maximum);
|
|
15366
|
-
const
|
|
15367
|
-
return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue3.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue3.maximum.toString()} ${
|
|
15368
|
+
const unit2 = getBelarusianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
|
|
15369
|
+
return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue3.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue3.maximum.toString()} ${unit2}`;
|
|
15368
15370
|
}
|
|
15369
15371
|
return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u0432\u044F\u043B\u0456\u043A\u0456: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue3.origin ?? "\u0437\u043D\u0430\u0447\u044D\u043D\u043D\u0435"} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${adj}${issue3.maximum.toString()}`;
|
|
15370
15372
|
}
|
|
@@ -15373,8 +15375,8 @@ var error3 = () => {
|
|
|
15373
15375
|
const sizing = getSizing(issue3.origin);
|
|
15374
15376
|
if (sizing) {
|
|
15375
15377
|
const minValue = Number(issue3.minimum);
|
|
15376
|
-
const
|
|
15377
|
-
return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue3.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue3.minimum.toString()} ${
|
|
15378
|
+
const unit2 = getBelarusianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
|
|
15379
|
+
return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue3.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 ${sizing.verb} ${adj}${issue3.minimum.toString()} ${unit2}`;
|
|
15378
15380
|
}
|
|
15379
15381
|
return `\u0417\u0430\u043D\u0430\u0434\u0442\u0430 \u043C\u0430\u043B\u044B: \u0447\u0430\u043A\u0430\u043B\u0430\u0441\u044F, \u0448\u0442\u043E ${issue3.origin} \u043F\u0430\u0432\u0456\u043D\u043D\u0430 \u0431\u044B\u0446\u044C ${adj}${issue3.minimum.toString()}`;
|
|
15380
15382
|
}
|
|
@@ -17433,8 +17435,8 @@ var error20 = () => {
|
|
|
17433
17435
|
const sizing = getSizing(issue3.origin);
|
|
17434
17436
|
if (sizing) {
|
|
17435
17437
|
const maxValue = Number(issue3.maximum);
|
|
17436
|
-
const
|
|
17437
|
-
return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue3.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue3.maximum.toString()} ${
|
|
17438
|
+
const unit2 = getArmenianPlural(maxValue, sizing.unit.one, sizing.unit.many);
|
|
17439
|
+
return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue3.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue3.maximum.toString()} ${unit2}`;
|
|
17438
17440
|
}
|
|
17439
17441
|
return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0574\u0565\u056E \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue3.origin ?? "\u0561\u0580\u056A\u0565\u0584")} \u056C\u056B\u0576\u056B ${adj}${issue3.maximum.toString()}`;
|
|
17440
17442
|
}
|
|
@@ -17443,8 +17445,8 @@ var error20 = () => {
|
|
|
17443
17445
|
const sizing = getSizing(issue3.origin);
|
|
17444
17446
|
if (sizing) {
|
|
17445
17447
|
const minValue = Number(issue3.minimum);
|
|
17446
|
-
const
|
|
17447
|
-
return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue3.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue3.minimum.toString()} ${
|
|
17448
|
+
const unit2 = getArmenianPlural(minValue, sizing.unit.one, sizing.unit.many);
|
|
17449
|
+
return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue3.origin)} \u056F\u0578\u0582\u0576\u0565\u0576\u0561 ${adj}${issue3.minimum.toString()} ${unit2}`;
|
|
17448
17450
|
}
|
|
17449
17451
|
return `\u0549\u0561\u0583\u0561\u0566\u0561\u0576\u0581 \u0583\u0578\u0584\u0580 \u0561\u0580\u056A\u0565\u0584\u2024 \u057D\u057A\u0561\u057D\u057E\u0578\u0582\u0574 \u0567, \u0578\u0580 ${withDefiniteArticle(issue3.origin)} \u056C\u056B\u0576\u056B ${adj}${issue3.minimum.toString()}`;
|
|
17450
17452
|
}
|
|
@@ -18207,18 +18209,18 @@ var error27 = () => {
|
|
|
18207
18209
|
const adj = issue3.inclusive ? "\uC774\uD558" : "\uBBF8\uB9CC";
|
|
18208
18210
|
const suffix = adj === "\uBBF8\uB9CC" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
|
|
18209
18211
|
const sizing = getSizing(issue3.origin);
|
|
18210
|
-
const
|
|
18212
|
+
const unit2 = sizing?.unit ?? "\uC694\uC18C";
|
|
18211
18213
|
if (sizing)
|
|
18212
|
-
return `${issue3.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue3.maximum.toString()}${
|
|
18214
|
+
return `${issue3.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue3.maximum.toString()}${unit2} ${adj}${suffix}`;
|
|
18213
18215
|
return `${issue3.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${issue3.maximum.toString()} ${adj}${suffix}`;
|
|
18214
18216
|
}
|
|
18215
18217
|
case "too_small": {
|
|
18216
18218
|
const adj = issue3.inclusive ? "\uC774\uC0C1" : "\uCD08\uACFC";
|
|
18217
18219
|
const suffix = adj === "\uC774\uC0C1" ? "\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4" : "\uC5EC\uC57C \uD569\uB2C8\uB2E4";
|
|
18218
18220
|
const sizing = getSizing(issue3.origin);
|
|
18219
|
-
const
|
|
18221
|
+
const unit2 = sizing?.unit ?? "\uC694\uC18C";
|
|
18220
18222
|
if (sizing) {
|
|
18221
|
-
return `${issue3.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue3.minimum.toString()}${
|
|
18223
|
+
return `${issue3.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue3.minimum.toString()}${unit2} ${adj}${suffix}`;
|
|
18222
18224
|
}
|
|
18223
18225
|
return `${issue3.origin ?? "\uAC12"}\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4: ${issue3.minimum.toString()} ${adj}${suffix}`;
|
|
18224
18226
|
}
|
|
@@ -19571,8 +19573,8 @@ var error38 = () => {
|
|
|
19571
19573
|
const sizing = getSizing(issue3.origin);
|
|
19572
19574
|
if (sizing) {
|
|
19573
19575
|
const maxValue = Number(issue3.maximum);
|
|
19574
|
-
const
|
|
19575
|
-
return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue3.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue3.maximum.toString()} ${
|
|
19576
|
+
const unit2 = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
|
|
19577
|
+
return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue3.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue3.maximum.toString()} ${unit2}`;
|
|
19576
19578
|
}
|
|
19577
19579
|
return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue3.origin ?? "\u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435"} \u0431\u0443\u0434\u0435\u0442 ${adj}${issue3.maximum.toString()}`;
|
|
19578
19580
|
}
|
|
@@ -19581,8 +19583,8 @@ var error38 = () => {
|
|
|
19581
19583
|
const sizing = getSizing(issue3.origin);
|
|
19582
19584
|
if (sizing) {
|
|
19583
19585
|
const minValue = Number(issue3.minimum);
|
|
19584
|
-
const
|
|
19585
|
-
return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue3.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue3.minimum.toString()} ${
|
|
19586
|
+
const unit2 = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
|
|
19587
|
+
return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue3.origin} \u0431\u0443\u0434\u0435\u0442 \u0438\u043C\u0435\u0442\u044C ${adj}${issue3.minimum.toString()} ${unit2}`;
|
|
19586
19588
|
}
|
|
19587
19589
|
return `\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435: \u043E\u0436\u0438\u0434\u0430\u043B\u043E\u0441\u044C, \u0447\u0442\u043E ${issue3.origin} \u0431\u0443\u0434\u0435\u0442 ${adj}${issue3.minimum.toString()}`;
|
|
19588
19590
|
}
|
|
@@ -27220,8 +27222,8 @@ function compareSignals(left, right) {
|
|
|
27220
27222
|
// ../kernel/dist/selection.js
|
|
27221
27223
|
import { createHash as createHash2 } from "node:crypto";
|
|
27222
27224
|
function assignSplits(caseIds, seed) {
|
|
27223
|
-
const
|
|
27224
|
-
if (
|
|
27225
|
+
const unique2 = new Set(caseIds);
|
|
27226
|
+
if (unique2.size !== caseIds.length) {
|
|
27225
27227
|
throw new Error("caseIds contains a duplicate identifier");
|
|
27226
27228
|
}
|
|
27227
27229
|
const ranked = [...caseIds].sort((left, right) => {
|
|
@@ -28010,10 +28012,10 @@ var AdaptiveLimiter = class {
|
|
|
28010
28012
|
this.active += 1;
|
|
28011
28013
|
return Promise.resolve();
|
|
28012
28014
|
}
|
|
28013
|
-
return new Promise((
|
|
28015
|
+
return new Promise((resolve12) => {
|
|
28014
28016
|
this.waiters.push(() => {
|
|
28015
28017
|
this.active += 1;
|
|
28016
|
-
|
|
28018
|
+
resolve12();
|
|
28017
28019
|
});
|
|
28018
28020
|
});
|
|
28019
28021
|
}
|
|
@@ -28081,7 +28083,7 @@ function retryDelay(response, attempt) {
|
|
|
28081
28083
|
return backoff + Math.random() * backoff * 0.25;
|
|
28082
28084
|
}
|
|
28083
28085
|
function sleep(milliseconds) {
|
|
28084
|
-
return new Promise((
|
|
28086
|
+
return new Promise((resolve12) => setTimeout(resolve12, milliseconds));
|
|
28085
28087
|
}
|
|
28086
28088
|
function isRetryable(status) {
|
|
28087
28089
|
return status === 429 || status >= 500;
|
|
@@ -28629,7 +28631,7 @@ async function replayModeA(input) {
|
|
|
28629
28631
|
if (refunds.length > 0) {
|
|
28630
28632
|
await Promise.race(refunds);
|
|
28631
28633
|
} else {
|
|
28632
|
-
await new Promise((
|
|
28634
|
+
await new Promise((resolve12) => setTimeout(resolve12, 0));
|
|
28633
28635
|
}
|
|
28634
28636
|
continue;
|
|
28635
28637
|
}
|
|
@@ -28647,8 +28649,8 @@ async function replayModeA(input) {
|
|
|
28647
28649
|
}
|
|
28648
28650
|
}
|
|
28649
28651
|
let resolveRefund = () => void 0;
|
|
28650
|
-
const refundComplete = new Promise((
|
|
28651
|
-
resolveRefund =
|
|
28652
|
+
const refundComplete = new Promise((resolve12) => {
|
|
28653
|
+
resolveRefund = resolve12;
|
|
28652
28654
|
});
|
|
28653
28655
|
activeRefunds.add(refundComplete);
|
|
28654
28656
|
const executionId = mintExecutionId();
|
|
@@ -28835,8 +28837,8 @@ function redactCredential(body, credential) {
|
|
|
28835
28837
|
return Buffer.from(body.toString("utf8").split(credential).join("[REDACTED]"), "utf8");
|
|
28836
28838
|
}
|
|
28837
28839
|
function close(server) {
|
|
28838
|
-
return new Promise((
|
|
28839
|
-
server.close((error51) => error51 === void 0 ?
|
|
28840
|
+
return new Promise((resolve12, reject) => {
|
|
28841
|
+
server.close((error51) => error51 === void 0 ? resolve12() : reject(error51));
|
|
28840
28842
|
});
|
|
28841
28843
|
}
|
|
28842
28844
|
async function startEgressListener(options) {
|
|
@@ -28904,9 +28906,9 @@ async function startEgressListener(options) {
|
|
|
28904
28906
|
incoming.pipe(upstream);
|
|
28905
28907
|
});
|
|
28906
28908
|
const hostname5 = options.hostname ?? "0.0.0.0";
|
|
28907
|
-
await new Promise((
|
|
28909
|
+
await new Promise((resolve12, reject) => {
|
|
28908
28910
|
server.once("error", reject);
|
|
28909
|
-
server.listen(options.port ?? 0, hostname5,
|
|
28911
|
+
server.listen(options.port ?? 0, hostname5, resolve12);
|
|
28910
28912
|
});
|
|
28911
28913
|
const address = server.address();
|
|
28912
28914
|
return {
|
|
@@ -29066,11 +29068,11 @@ function validatePricing(steps, policy, table) {
|
|
|
29066
29068
|
}
|
|
29067
29069
|
}
|
|
29068
29070
|
function createWaiter() {
|
|
29069
|
-
let
|
|
29071
|
+
let resolve12 = () => void 0;
|
|
29070
29072
|
const promise2 = new Promise((done) => {
|
|
29071
|
-
|
|
29073
|
+
resolve12 = done;
|
|
29072
29074
|
});
|
|
29073
|
-
return { promise: promise2, resolve:
|
|
29075
|
+
return { promise: promise2, resolve: resolve12 };
|
|
29074
29076
|
}
|
|
29075
29077
|
async function reserveCase(input, activeRefunds) {
|
|
29076
29078
|
for (; ; ) {
|
|
@@ -29173,7 +29175,7 @@ async function waitForExit(executor, handle) {
|
|
|
29173
29175
|
const status = await executor.status(handle);
|
|
29174
29176
|
if (status.state === "exited")
|
|
29175
29177
|
return status;
|
|
29176
|
-
await new Promise((
|
|
29178
|
+
await new Promise((resolve12) => setTimeout(resolve12, 50));
|
|
29177
29179
|
}
|
|
29178
29180
|
}
|
|
29179
29181
|
function textFiles(collected) {
|
|
@@ -30511,12 +30513,12 @@ function enclosingSymbol(content, position) {
|
|
|
30511
30513
|
return nearest;
|
|
30512
30514
|
}
|
|
30513
30515
|
function extractCallText(content, position) {
|
|
30514
|
-
const
|
|
30515
|
-
if (
|
|
30516
|
+
const open2 = content.indexOf("(", position);
|
|
30517
|
+
if (open2 === -1)
|
|
30516
30518
|
return content.slice(position, position + 240);
|
|
30517
30519
|
let depth = 0;
|
|
30518
30520
|
let quote = null;
|
|
30519
|
-
for (let index =
|
|
30521
|
+
for (let index = open2; index < content.length; index += 1) {
|
|
30520
30522
|
const character = content[index];
|
|
30521
30523
|
if (quote !== null) {
|
|
30522
30524
|
if (character === "\\")
|
|
@@ -30576,11 +30578,11 @@ function splitTopLevel(value) {
|
|
|
30576
30578
|
return parts;
|
|
30577
30579
|
}
|
|
30578
30580
|
function argumentKeys(callText) {
|
|
30579
|
-
const
|
|
30581
|
+
const open2 = callText.indexOf("(");
|
|
30580
30582
|
const close2 = callText.lastIndexOf(")");
|
|
30581
|
-
if (
|
|
30583
|
+
if (open2 === -1 || close2 <= open2)
|
|
30582
30584
|
return [];
|
|
30583
|
-
let argumentsText = callText.slice(
|
|
30585
|
+
let argumentsText = callText.slice(open2 + 1, close2).trim();
|
|
30584
30586
|
let separator = "=";
|
|
30585
30587
|
if (argumentsText.startsWith("{")) {
|
|
30586
30588
|
const objectClose = argumentsText.lastIndexOf("}");
|
|
@@ -33978,7 +33980,7 @@ function isRateLimited(response) {
|
|
|
33978
33980
|
return response.status === 429 || response.status === 403 && (response.headers.has("retry-after") || response.headers.get("x-ratelimit-remaining") === "0");
|
|
33979
33981
|
}
|
|
33980
33982
|
function sleep2(milliseconds) {
|
|
33981
|
-
return new Promise((
|
|
33983
|
+
return new Promise((resolve12) => setTimeout(resolve12, milliseconds));
|
|
33982
33984
|
}
|
|
33983
33985
|
function parseJson(text, label) {
|
|
33984
33986
|
try {
|
|
@@ -34487,10 +34489,10 @@ function lineText(content, line) {
|
|
|
34487
34489
|
}
|
|
34488
34490
|
function callEnd(content, start) {
|
|
34489
34491
|
const searchable = scanSource(content).mask;
|
|
34490
|
-
const
|
|
34491
|
-
if (
|
|
34492
|
+
const open2 = searchable.indexOf("(", start);
|
|
34493
|
+
if (open2 === -1) return -1;
|
|
34492
34494
|
let depth = 0;
|
|
34493
|
-
for (let index =
|
|
34495
|
+
for (let index = open2; index < searchable.length; index += 1) {
|
|
34494
34496
|
const character = searchable[index];
|
|
34495
34497
|
if (character === "(") {
|
|
34496
34498
|
depth += 1;
|
|
@@ -34536,13 +34538,13 @@ function splitTopLevel2(content, start, end) {
|
|
|
34536
34538
|
return ranges;
|
|
34537
34539
|
}
|
|
34538
34540
|
function topLevelModelSpans(content, callStart, callEndOffset) {
|
|
34539
|
-
const
|
|
34540
|
-
if (
|
|
34541
|
-
const outer = splitTopLevel2(content,
|
|
34541
|
+
const open2 = content.indexOf("(", callStart);
|
|
34542
|
+
if (open2 === -1 || callEndOffset <= open2 + 1) return null;
|
|
34543
|
+
const outer = splitTopLevel2(content, open2 + 1, callEndOffset - 1);
|
|
34542
34544
|
if (outer === null) return null;
|
|
34543
34545
|
const first = trimRange(
|
|
34544
34546
|
content,
|
|
34545
|
-
outer[0] ?? { start:
|
|
34547
|
+
outer[0] ?? { start: open2 + 1, end: open2 + 1 }
|
|
34546
34548
|
);
|
|
34547
34549
|
let parts = outer;
|
|
34548
34550
|
let separator = "=";
|
|
@@ -35195,14 +35197,14 @@ function formatterCommand(repoDir, formatter) {
|
|
|
35195
35197
|
return null;
|
|
35196
35198
|
}
|
|
35197
35199
|
function runFormatter(command, filePath, cwd, input) {
|
|
35198
|
-
return new Promise((
|
|
35200
|
+
return new Promise((resolve12, reject) => {
|
|
35199
35201
|
const child = execFile2(
|
|
35200
35202
|
command.executable,
|
|
35201
35203
|
command.args(filePath),
|
|
35202
35204
|
{ cwd, encoding: "utf8" },
|
|
35203
35205
|
(error51, stdout) => {
|
|
35204
35206
|
if (error51 !== null) reject(error51);
|
|
35205
|
-
else
|
|
35207
|
+
else resolve12(command.stdin ? stdout : null);
|
|
35206
35208
|
}
|
|
35207
35209
|
);
|
|
35208
35210
|
if (command.stdin) child.stdin?.end(input);
|
|
@@ -36236,7 +36238,7 @@ function reviewersFor(verdicts) {
|
|
|
36236
36238
|
slash === -1 ? { kind: "user", value: identity } : { kind: "team", value: identity.slice(slash + 1) }
|
|
36237
36239
|
);
|
|
36238
36240
|
}
|
|
36239
|
-
const
|
|
36241
|
+
const unique2 = [
|
|
36240
36242
|
...new Map(
|
|
36241
36243
|
selected.map((reviewer) => [
|
|
36242
36244
|
`${reviewer.kind}:${reviewer.value.toLowerCase()}`,
|
|
@@ -36245,8 +36247,8 @@ function reviewersFor(verdicts) {
|
|
|
36245
36247
|
).values()
|
|
36246
36248
|
];
|
|
36247
36249
|
return {
|
|
36248
|
-
reviewers:
|
|
36249
|
-
teamReviewers:
|
|
36250
|
+
reviewers: unique2.flatMap(({ kind, value }) => kind === "user" ? [value] : []).slice(0, reviewerLimit),
|
|
36251
|
+
teamReviewers: unique2.flatMap(({ kind, value }) => kind === "team" ? [value] : []).slice(0, reviewerLimit)
|
|
36250
36252
|
};
|
|
36251
36253
|
}
|
|
36252
36254
|
async function gitOutput(repoDir, args) {
|
|
@@ -38523,7 +38525,7 @@ async function pollEvaluator(evaluator, providerRunId) {
|
|
|
38523
38525
|
const delays = [0, 25, 50, 100, 200, 400];
|
|
38524
38526
|
for (const delay of delays) {
|
|
38525
38527
|
if (delay > 0) {
|
|
38526
|
-
await new Promise((
|
|
38528
|
+
await new Promise((resolve12) => setTimeout(resolve12, delay));
|
|
38527
38529
|
}
|
|
38528
38530
|
const status = await evaluator.status(providerRunId);
|
|
38529
38531
|
if (status === "complete" || status === "failed") return status;
|
|
@@ -38685,13 +38687,13 @@ async function importCorpus(config2, options) {
|
|
|
38685
38687
|
`Cannot import an empty ${config2.provider} dataset: ${config2.dataset}`
|
|
38686
38688
|
);
|
|
38687
38689
|
}
|
|
38688
|
-
const
|
|
38690
|
+
const unique2 = /* @__PURE__ */ new Map();
|
|
38689
38691
|
for (const item of items) {
|
|
38690
38692
|
const content = importedContent(config2.provider, item);
|
|
38691
38693
|
const caseId = computeRunSpecDigest(contentJson(content));
|
|
38692
|
-
|
|
38694
|
+
unique2.set(caseId, { caseId, content, split: "shortlist" });
|
|
38693
38695
|
}
|
|
38694
|
-
const unsplitCases = [...
|
|
38696
|
+
const unsplitCases = [...unique2.values()];
|
|
38695
38697
|
const splits = assignSplits(
|
|
38696
38698
|
unsplitCases.map(({ caseId }) => caseId),
|
|
38697
38699
|
options.seed
|
|
@@ -40688,24 +40690,24 @@ ${marker}`
|
|
|
40688
40690
|
await renew();
|
|
40689
40691
|
observedIssueComments.push(...issueComments);
|
|
40690
40692
|
const handled = new Set(state.handledEventKeys);
|
|
40691
|
-
for (const
|
|
40693
|
+
for (const question2 of questions(
|
|
40692
40694
|
reviews,
|
|
40693
40695
|
reviewComments,
|
|
40694
40696
|
issueComments,
|
|
40695
40697
|
input.prNumber
|
|
40696
40698
|
)) {
|
|
40697
|
-
const key =
|
|
40699
|
+
const key = question2.eventKey;
|
|
40698
40700
|
if (handled.has(key)) continue;
|
|
40699
40701
|
const selected = affectedVerdicts(
|
|
40700
40702
|
prVerdicts,
|
|
40701
|
-
[
|
|
40702
|
-
|
|
40703
|
+
[question2.body],
|
|
40704
|
+
question2.path === void 0 ? [] : [question2.path]
|
|
40703
40705
|
);
|
|
40704
40706
|
const postedCommentId = await comment(key, evidenceReply(selected));
|
|
40705
40707
|
const event = await record2("comment_posted", {
|
|
40706
40708
|
handledEventKey: key,
|
|
40707
|
-
source:
|
|
40708
|
-
sourceCommentId:
|
|
40709
|
+
source: question2.source,
|
|
40710
|
+
sourceCommentId: question2.id,
|
|
40709
40711
|
postedCommentId,
|
|
40710
40712
|
familyIds: selected.map(({ verdict }) => verdict.familyId)
|
|
40711
40713
|
});
|
|
@@ -40717,8 +40719,8 @@ ${marker}`
|
|
|
40717
40719
|
};
|
|
40718
40720
|
actions.push(
|
|
40719
40721
|
action("evidence_replied", {
|
|
40720
|
-
source:
|
|
40721
|
-
commentId:
|
|
40722
|
+
source: question2.source,
|
|
40723
|
+
commentId: question2.id,
|
|
40722
40724
|
familyIds: selected.map(({ verdict }) => verdict.familyId)
|
|
40723
40725
|
})
|
|
40724
40726
|
);
|
|
@@ -41287,6 +41289,17 @@ async function runPipeline(options) {
|
|
|
41287
41289
|
recommendationExists: false
|
|
41288
41290
|
};
|
|
41289
41291
|
}
|
|
41292
|
+
await repositoryRevision(context2.repo);
|
|
41293
|
+
const initialState = await readSetupState(context2.store, context2.projectId);
|
|
41294
|
+
const ingestCheckpoint = initialState.stages.ingest;
|
|
41295
|
+
if (stagesThrough(options.through).includes("ingest") && context2.traces === void 0 && (ingestCheckpoint === void 0 || !await checkpointOutputExists(context2, "ingest", ingestCheckpoint))) {
|
|
41296
|
+
throw new ProtocolError({
|
|
41297
|
+
exitCode: 2,
|
|
41298
|
+
code: "missing_traces_path",
|
|
41299
|
+
message: "A trace input path is required when ingest is reached.",
|
|
41300
|
+
remedy: "Pass --traces <path> with an OTel GenAI JSON or OpenAI JSONL trace file."
|
|
41301
|
+
});
|
|
41302
|
+
}
|
|
41290
41303
|
if (options.existingRunId !== void 0) {
|
|
41291
41304
|
const existing = await requireRunningReplayRun(
|
|
41292
41305
|
context2,
|
|
@@ -43938,12 +43951,40 @@ async function repositoryDigest(repo, storeRoot) {
|
|
|
43938
43951
|
});
|
|
43939
43952
|
}
|
|
43940
43953
|
async function repositoryRevision(repo) {
|
|
43941
|
-
|
|
43942
|
-
|
|
43943
|
-
|
|
43944
|
-
|
|
43945
|
-
|
|
43946
|
-
|
|
43954
|
+
let insideWorkTree;
|
|
43955
|
+
try {
|
|
43956
|
+
({ stdout: insideWorkTree } = await execFileAsync6(
|
|
43957
|
+
"git",
|
|
43958
|
+
["-C", repo, "rev-parse", "--is-inside-work-tree"],
|
|
43959
|
+
{ encoding: "utf8" }
|
|
43960
|
+
));
|
|
43961
|
+
} catch {
|
|
43962
|
+
throw notGitRepository();
|
|
43963
|
+
}
|
|
43964
|
+
if (insideWorkTree.trim() !== "true") throw notGitRepository();
|
|
43965
|
+
try {
|
|
43966
|
+
const { stdout } = await execFileAsync6(
|
|
43967
|
+
"git",
|
|
43968
|
+
["-C", repo, "rev-parse", "--verify", "HEAD"],
|
|
43969
|
+
{ encoding: "utf8" }
|
|
43970
|
+
);
|
|
43971
|
+
return stdout.trim();
|
|
43972
|
+
} catch {
|
|
43973
|
+
throw new ProtocolError({
|
|
43974
|
+
exitCode: 2,
|
|
43975
|
+
code: "git_repository_has_no_commits",
|
|
43976
|
+
message: "This git repository has no commits. Rightmodeler ties findings to your code, so make an initial commit and rerun.",
|
|
43977
|
+
remedy: "Create the first commit, then rerun the command."
|
|
43978
|
+
});
|
|
43979
|
+
}
|
|
43980
|
+
}
|
|
43981
|
+
function notGitRepository() {
|
|
43982
|
+
return new ProtocolError({
|
|
43983
|
+
exitCode: 2,
|
|
43984
|
+
code: "not_git_repository",
|
|
43985
|
+
message: "This folder is not a git repository. Rightmodeler ties findings to your code, so run it inside the project you want audited (or run git init and commit first).",
|
|
43986
|
+
remedy: "Run the command again from a Git repository with at least one commit."
|
|
43987
|
+
});
|
|
43947
43988
|
}
|
|
43948
43989
|
function corpusOutput(corpus) {
|
|
43949
43990
|
return {
|
|
@@ -44564,9 +44605,399 @@ function applySwaps2(options) {
|
|
|
44564
44605
|
});
|
|
44565
44606
|
}
|
|
44566
44607
|
|
|
44608
|
+
// src/data/discover.ts
|
|
44609
|
+
import { open, readdir as readdir4, realpath as realpath2, stat } from "node:fs/promises";
|
|
44610
|
+
import { homedir } from "node:os";
|
|
44611
|
+
import { isAbsolute as isAbsolute2, join as join14, relative as relative7, resolve as resolve8 } from "node:path";
|
|
44612
|
+
var MAX_FILES = 50;
|
|
44613
|
+
var MAX_READ_BYTES = 64 * 1024;
|
|
44614
|
+
var MAX_SAMPLE_RECORDS = 20;
|
|
44615
|
+
var SOURCE_BUDGETS = { local: 20, claude: 15, codex: 15 };
|
|
44616
|
+
async function discoverTraces(options) {
|
|
44617
|
+
const repo = resolve8(options.repo);
|
|
44618
|
+
const homeDir = resolve8(options.homeDir ?? homedir());
|
|
44619
|
+
const candidates = await candidateFiles(repo, homeDir);
|
|
44620
|
+
const discovered = [];
|
|
44621
|
+
for (const candidate of candidates.slice(0, MAX_FILES)) {
|
|
44622
|
+
try {
|
|
44623
|
+
const head = await detectionHead(candidate.path, candidate.size);
|
|
44624
|
+
if (candidate.source === "codex" && codexSessionCwd(head.text) !== repo) {
|
|
44625
|
+
continue;
|
|
44626
|
+
}
|
|
44627
|
+
const adapter = detectFormat(head.text, traceAdapters);
|
|
44628
|
+
discovered.push({
|
|
44629
|
+
path: candidate.path,
|
|
44630
|
+
format: adapter.name,
|
|
44631
|
+
approximateRecords: head.approximateRecords,
|
|
44632
|
+
modifiedAt: new Date(candidate.modifiedAtMs),
|
|
44633
|
+
sourceOrder: candidate.sourceOrder
|
|
44634
|
+
});
|
|
44635
|
+
} catch {
|
|
44636
|
+
}
|
|
44637
|
+
}
|
|
44638
|
+
return discovered.sort(
|
|
44639
|
+
(left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime() || left.sourceOrder - right.sourceOrder || compareText14(left.path, right.path)
|
|
44640
|
+
).map(({ sourceOrder: _sourceOrder, ...candidate }) => candidate);
|
|
44641
|
+
}
|
|
44642
|
+
function sanitizeClaudeProjectPath(repo) {
|
|
44643
|
+
const absolute = resolve8(repo);
|
|
44644
|
+
const sanitized = absolute.replace(/[^a-zA-Z0-9]/g, "-");
|
|
44645
|
+
if (sanitized.length <= 200) return sanitized;
|
|
44646
|
+
let hash2 = 0;
|
|
44647
|
+
for (let index = 0; index < absolute.length; index += 1) {
|
|
44648
|
+
hash2 = hash2 * 31 + absolute.charCodeAt(index) | 0;
|
|
44649
|
+
}
|
|
44650
|
+
return `${sanitized.slice(0, 200)}-${Math.abs(hash2).toString(36)}`;
|
|
44651
|
+
}
|
|
44652
|
+
async function candidateFiles(repo, homeDir) {
|
|
44653
|
+
const localPaths = await localCandidatePaths(repo);
|
|
44654
|
+
const claudePaths = (await jsonFiles(
|
|
44655
|
+
join14(homeDir, ".claude", "projects", sanitizeClaudeProjectPath(repo)),
|
|
44656
|
+
false
|
|
44657
|
+
)).sort(compareText14);
|
|
44658
|
+
const codexPaths = (await jsonFiles(join14(homeDir, ".codex", "sessions"), true)).filter((path) => path.endsWith(".jsonl")).sort((left, right) => compareText14(right, left));
|
|
44659
|
+
const sources = [
|
|
44660
|
+
{
|
|
44661
|
+
source: "local",
|
|
44662
|
+
files: await statCandidates(localPaths, "local", false)
|
|
44663
|
+
},
|
|
44664
|
+
{
|
|
44665
|
+
source: "claude",
|
|
44666
|
+
files: await statCandidates(claudePaths, "claude", true)
|
|
44667
|
+
},
|
|
44668
|
+
{
|
|
44669
|
+
source: "codex",
|
|
44670
|
+
files: await statCandidates(codexPaths, "codex", false)
|
|
44671
|
+
}
|
|
44672
|
+
];
|
|
44673
|
+
const selected = [];
|
|
44674
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
44675
|
+
for (const source of sources) {
|
|
44676
|
+
const count = Math.min(SOURCE_BUDGETS[source.source], source.files.length);
|
|
44677
|
+
selected.push(...source.files.slice(0, count));
|
|
44678
|
+
offsets.set(source.source, count);
|
|
44679
|
+
}
|
|
44680
|
+
for (const source of sources) {
|
|
44681
|
+
const remaining = MAX_FILES - selected.length;
|
|
44682
|
+
if (remaining === 0) break;
|
|
44683
|
+
const offset = offsets.get(source.source) ?? 0;
|
|
44684
|
+
const extra = source.files.slice(offset, offset + remaining);
|
|
44685
|
+
selected.push(...extra);
|
|
44686
|
+
offsets.set(source.source, offset + extra.length);
|
|
44687
|
+
}
|
|
44688
|
+
return selected.map((file2, sourceOrder) => ({ ...file2, sourceOrder }));
|
|
44689
|
+
}
|
|
44690
|
+
async function statCandidates(paths, source, sortNewest) {
|
|
44691
|
+
const selectedPaths = sortNewest ? paths : paths.slice(0, MAX_FILES);
|
|
44692
|
+
const files = (await Promise.all(
|
|
44693
|
+
selectedPaths.map(async (path) => {
|
|
44694
|
+
try {
|
|
44695
|
+
const metadata = await stat(path);
|
|
44696
|
+
return {
|
|
44697
|
+
path,
|
|
44698
|
+
source,
|
|
44699
|
+
sourceOrder: 0,
|
|
44700
|
+
size: metadata.size,
|
|
44701
|
+
modifiedAtMs: metadata.mtimeMs
|
|
44702
|
+
};
|
|
44703
|
+
} catch {
|
|
44704
|
+
return void 0;
|
|
44705
|
+
}
|
|
44706
|
+
})
|
|
44707
|
+
)).filter((file2) => file2 !== void 0);
|
|
44708
|
+
if (sortNewest) {
|
|
44709
|
+
files.sort(
|
|
44710
|
+
(left, right) => right.modifiedAtMs - left.modifiedAtMs || compareText14(left.path, right.path)
|
|
44711
|
+
);
|
|
44712
|
+
}
|
|
44713
|
+
return files.slice(0, MAX_FILES);
|
|
44714
|
+
}
|
|
44715
|
+
async function localCandidatePaths(repo) {
|
|
44716
|
+
const tracesDirectory = join14(repo, "traces");
|
|
44717
|
+
const [realRepo, realTracesDirectory] = await Promise.all([
|
|
44718
|
+
realpath2(repo).catch(() => void 0),
|
|
44719
|
+
realpath2(tracesDirectory).catch(() => void 0)
|
|
44720
|
+
]);
|
|
44721
|
+
const nested = realRepo !== void 0 && realTracesDirectory !== void 0 && isWithin(realRepo, realTracesDirectory) ? (await jsonFiles(tracesDirectory, false)).sort(compareText14) : [];
|
|
44722
|
+
const topLevel = (await jsonFiles(repo, false)).sort((left, right) => {
|
|
44723
|
+
const leftJsonl = left.endsWith(".jsonl");
|
|
44724
|
+
const rightJsonl = right.endsWith(".jsonl");
|
|
44725
|
+
return leftJsonl === rightJsonl ? compareText14(left, right) : leftJsonl ? -1 : 1;
|
|
44726
|
+
});
|
|
44727
|
+
const topLevelSet = new Set(topLevel);
|
|
44728
|
+
return unique([
|
|
44729
|
+
...[join14(repo, "traces.json"), join14(repo, "traces.jsonl")].filter(
|
|
44730
|
+
(path) => topLevelSet.has(path)
|
|
44731
|
+
),
|
|
44732
|
+
...nested,
|
|
44733
|
+
...topLevel
|
|
44734
|
+
]);
|
|
44735
|
+
}
|
|
44736
|
+
async function jsonFiles(root, recursive) {
|
|
44737
|
+
let entries;
|
|
44738
|
+
try {
|
|
44739
|
+
entries = await readdir4(root, { recursive, withFileTypes: true });
|
|
44740
|
+
} catch {
|
|
44741
|
+
return [];
|
|
44742
|
+
}
|
|
44743
|
+
return entries.filter(
|
|
44744
|
+
(entry) => entry.isFile() && (entry.name.endsWith(".json") || entry.name.endsWith(".jsonl"))
|
|
44745
|
+
).map((entry) => join14(entry.parentPath, entry.name));
|
|
44746
|
+
}
|
|
44747
|
+
function isWithin(root, path) {
|
|
44748
|
+
const fromRoot = relative7(root, path);
|
|
44749
|
+
return fromRoot === "" || !fromRoot.startsWith("..") && !isAbsolute2(fromRoot);
|
|
44750
|
+
}
|
|
44751
|
+
async function detectionHead(path, fileSize) {
|
|
44752
|
+
const handle = await open(path, "r");
|
|
44753
|
+
try {
|
|
44754
|
+
const buffer = Buffer.alloc(Math.min(MAX_READ_BYTES, fileSize));
|
|
44755
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
44756
|
+
const raw = buffer.toString("utf8", 0, bytesRead);
|
|
44757
|
+
const complete = bytesRead === fileSize;
|
|
44758
|
+
const parsed2 = complete ? parseWholeJson(raw) : void 0;
|
|
44759
|
+
if (parsed2 !== void 0) {
|
|
44760
|
+
const records = Array.isArray(parsed2) ? parsed2 : [parsed2];
|
|
44761
|
+
return {
|
|
44762
|
+
text: JSON.stringify(records.slice(0, MAX_SAMPLE_RECORDS)),
|
|
44763
|
+
approximateRecords: Math.max(1, records.length)
|
|
44764
|
+
};
|
|
44765
|
+
}
|
|
44766
|
+
const trimmed = raw.trimStart();
|
|
44767
|
+
if (trimmed.startsWith("[")) {
|
|
44768
|
+
const records = leadingArrayRecords(trimmed, MAX_SAMPLE_RECORDS);
|
|
44769
|
+
return {
|
|
44770
|
+
text: JSON.stringify(records),
|
|
44771
|
+
approximateRecords: approximateRecordCount(
|
|
44772
|
+
records.length,
|
|
44773
|
+
bytesRead,
|
|
44774
|
+
fileSize
|
|
44775
|
+
)
|
|
44776
|
+
};
|
|
44777
|
+
}
|
|
44778
|
+
const completeText = complete ? raw : raw.slice(0, Math.max(0, raw.lastIndexOf("\n") + 1));
|
|
44779
|
+
const lines = completeText.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
44780
|
+
return {
|
|
44781
|
+
text: lines.slice(0, MAX_SAMPLE_RECORDS).join("\n"),
|
|
44782
|
+
approximateRecords: approximateRecordCount(
|
|
44783
|
+
lines.length,
|
|
44784
|
+
bytesRead,
|
|
44785
|
+
fileSize
|
|
44786
|
+
)
|
|
44787
|
+
};
|
|
44788
|
+
} finally {
|
|
44789
|
+
await handle.close();
|
|
44790
|
+
}
|
|
44791
|
+
}
|
|
44792
|
+
function leadingArrayRecords(text, limit) {
|
|
44793
|
+
const records = [];
|
|
44794
|
+
let start = -1;
|
|
44795
|
+
let depth = 0;
|
|
44796
|
+
let inString = false;
|
|
44797
|
+
let escaped = false;
|
|
44798
|
+
for (let index = 1; index < text.length && records.length < limit; index += 1) {
|
|
44799
|
+
const character = text[index];
|
|
44800
|
+
if (inString) {
|
|
44801
|
+
if (escaped) escaped = false;
|
|
44802
|
+
else if (character === "\\") escaped = true;
|
|
44803
|
+
else if (character === '"') inString = false;
|
|
44804
|
+
continue;
|
|
44805
|
+
}
|
|
44806
|
+
if (character === '"') {
|
|
44807
|
+
inString = true;
|
|
44808
|
+
if (start === -1) start = index;
|
|
44809
|
+
continue;
|
|
44810
|
+
}
|
|
44811
|
+
if (start === -1) {
|
|
44812
|
+
if (/\s|,/u.test(character)) continue;
|
|
44813
|
+
if (character === "]") break;
|
|
44814
|
+
start = index;
|
|
44815
|
+
}
|
|
44816
|
+
if (character === "{" || character === "[") depth += 1;
|
|
44817
|
+
if (character === "}" || character === "]") depth -= 1;
|
|
44818
|
+
if (depth === 0 && (character === "}" || character === "]")) {
|
|
44819
|
+
const parsed2 = parseWholeJson(text.slice(start, index + 1));
|
|
44820
|
+
if (parsed2 !== void 0) records.push(parsed2);
|
|
44821
|
+
start = -1;
|
|
44822
|
+
}
|
|
44823
|
+
}
|
|
44824
|
+
return records;
|
|
44825
|
+
}
|
|
44826
|
+
function codexSessionCwd(text) {
|
|
44827
|
+
for (const line of text.split(/\r?\n/)) {
|
|
44828
|
+
if (line.trim() === "") continue;
|
|
44829
|
+
const record2 = parseWholeJson(line);
|
|
44830
|
+
if (isRecord4(record2) && record2.type === "session_meta" && isRecord4(record2.payload) && typeof record2.payload.cwd === "string") {
|
|
44831
|
+
return resolve8(record2.payload.cwd);
|
|
44832
|
+
}
|
|
44833
|
+
}
|
|
44834
|
+
return void 0;
|
|
44835
|
+
}
|
|
44836
|
+
function parseWholeJson(text) {
|
|
44837
|
+
try {
|
|
44838
|
+
return JSON.parse(text);
|
|
44839
|
+
} catch {
|
|
44840
|
+
return void 0;
|
|
44841
|
+
}
|
|
44842
|
+
}
|
|
44843
|
+
function approximateRecordCount(observed, bytesRead, fileSize) {
|
|
44844
|
+
if (observed === 0) return 0;
|
|
44845
|
+
if (bytesRead === 0 || bytesRead === fileSize) return observed;
|
|
44846
|
+
return Math.max(observed, Math.round(observed * fileSize / bytesRead));
|
|
44847
|
+
}
|
|
44848
|
+
function unique(values) {
|
|
44849
|
+
return [...new Set(values)];
|
|
44850
|
+
}
|
|
44851
|
+
function compareText14(left, right) {
|
|
44852
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
44853
|
+
}
|
|
44854
|
+
|
|
44855
|
+
// src/guidance.ts
|
|
44856
|
+
import { createInterface } from "node:readline";
|
|
44857
|
+
import { isAbsolute as isAbsolute3, relative as relative8, resolve as resolve9 } from "node:path";
|
|
44858
|
+
async function promptForTracePath(options) {
|
|
44859
|
+
if (options.candidates.length === 0) {
|
|
44860
|
+
options.output.write(
|
|
44861
|
+
[
|
|
44862
|
+
"Traces are logs that your AI tools already write.",
|
|
44863
|
+
"If you use Claude Code or Codex in this project, run a few tasks there and run this command again. Rightmodeler finds those logs automatically.",
|
|
44864
|
+
"If your app logs to Langfuse, Braintrust, LangSmith, Helicone, or W&B Weave, export a file and enter its path.",
|
|
44865
|
+
"See the supported sources at https://www.rightmodeler.com/integrations",
|
|
44866
|
+
""
|
|
44867
|
+
].join("\n")
|
|
44868
|
+
);
|
|
44869
|
+
const asked2 = await question(
|
|
44870
|
+
options,
|
|
44871
|
+
"Trace file path (leave empty to stop): "
|
|
44872
|
+
);
|
|
44873
|
+
const typed = asked2.answer.trim();
|
|
44874
|
+
return asked2.cancelled || typed === "" ? void 0 : resolve9(options.repo, typed);
|
|
44875
|
+
}
|
|
44876
|
+
options.output.write("Found trace files:\n");
|
|
44877
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
44878
|
+
for (const [index, candidate] of options.candidates.entries()) {
|
|
44879
|
+
options.output.write(
|
|
44880
|
+
`${index + 1}. ${formatName(candidate.format)}, about ${candidate.approximateRecords} ${unit("model call", candidate.approximateRecords)}, ${formatAge(candidate.modifiedAt, now)}, ${shortPath(candidate.path, options.repo, options.homeDir)}
|
|
44881
|
+
`
|
|
44882
|
+
);
|
|
44883
|
+
}
|
|
44884
|
+
const asked = await question(
|
|
44885
|
+
options,
|
|
44886
|
+
"Choose a trace file [1]: ",
|
|
44887
|
+
(value) => {
|
|
44888
|
+
const choice = value.trim();
|
|
44889
|
+
if (/^[0-9]+$/u.test(choice)) {
|
|
44890
|
+
const selected = options.candidates[Number(choice) - 1];
|
|
44891
|
+
if (selected === void 0) {
|
|
44892
|
+
options.output.write(
|
|
44893
|
+
`Choose a number from 1 to ${options.candidates.length}.
|
|
44894
|
+
`
|
|
44895
|
+
);
|
|
44896
|
+
return false;
|
|
44897
|
+
}
|
|
44898
|
+
}
|
|
44899
|
+
return true;
|
|
44900
|
+
}
|
|
44901
|
+
);
|
|
44902
|
+
if (asked.cancelled) return void 0;
|
|
44903
|
+
const answer = asked.answer.trim();
|
|
44904
|
+
if (answer === "") return options.candidates[0].path;
|
|
44905
|
+
if (/^[1-9][0-9]*$/u.test(answer)) {
|
|
44906
|
+
const selected = options.candidates[Number(answer) - 1];
|
|
44907
|
+
if (selected !== void 0) return selected.path;
|
|
44908
|
+
}
|
|
44909
|
+
return resolve9(options.repo, answer);
|
|
44910
|
+
}
|
|
44911
|
+
async function promptForProviderBaseUrl(options) {
|
|
44912
|
+
options.output.write(
|
|
44913
|
+
"Replay calls cheaper models through any OpenAI-compatible endpoint, such as OpenRouter or the Vercel AI Gateway.\n"
|
|
44914
|
+
);
|
|
44915
|
+
const suffix = options.current === void 0 ? "" : ` [${options.current}]`;
|
|
44916
|
+
const asked = await question(options, `Provider base URL${suffix}: `);
|
|
44917
|
+
if (asked.cancelled) return options.current;
|
|
44918
|
+
const answer = asked.answer.trim();
|
|
44919
|
+
return answer === "" ? options.current : answer;
|
|
44920
|
+
}
|
|
44921
|
+
function question(streams, prompt, accept = () => true) {
|
|
44922
|
+
const readline = createInterface({
|
|
44923
|
+
input: streams.input,
|
|
44924
|
+
output: streams.output
|
|
44925
|
+
});
|
|
44926
|
+
return new Promise((resolveAnswer) => {
|
|
44927
|
+
let settled = false;
|
|
44928
|
+
const finish = (asked) => {
|
|
44929
|
+
if (settled) return;
|
|
44930
|
+
settled = true;
|
|
44931
|
+
resolveAnswer(asked);
|
|
44932
|
+
};
|
|
44933
|
+
const ask = () => {
|
|
44934
|
+
readline.question(prompt, (answer) => {
|
|
44935
|
+
if (!accept(answer)) {
|
|
44936
|
+
ask();
|
|
44937
|
+
return;
|
|
44938
|
+
}
|
|
44939
|
+
finish({ answer, cancelled: false });
|
|
44940
|
+
readline.close();
|
|
44941
|
+
});
|
|
44942
|
+
};
|
|
44943
|
+
readline.once("close", () => finish({ answer: "", cancelled: true }));
|
|
44944
|
+
streams.input.once("close", () => {
|
|
44945
|
+
readline.close();
|
|
44946
|
+
finish({ answer: "", cancelled: true });
|
|
44947
|
+
});
|
|
44948
|
+
streams.output.once("close", () => {
|
|
44949
|
+
readline.close();
|
|
44950
|
+
finish({ answer: "", cancelled: true });
|
|
44951
|
+
});
|
|
44952
|
+
ask();
|
|
44953
|
+
});
|
|
44954
|
+
}
|
|
44955
|
+
function formatName(format9) {
|
|
44956
|
+
const names = {
|
|
44957
|
+
"otel-genai": "OpenTelemetry GenAI export",
|
|
44958
|
+
"openai-jsonl": "OpenAI log",
|
|
44959
|
+
langfuse: "Langfuse export",
|
|
44960
|
+
braintrust: "Braintrust export",
|
|
44961
|
+
langsmith: "LangSmith export",
|
|
44962
|
+
openinference: "OpenInference export",
|
|
44963
|
+
helicone: "Helicone export",
|
|
44964
|
+
weave: "Weave export",
|
|
44965
|
+
"claude-code": "Claude Code session",
|
|
44966
|
+
codex: "Codex session"
|
|
44967
|
+
};
|
|
44968
|
+
return names[format9];
|
|
44969
|
+
}
|
|
44970
|
+
function shortPath(path, repo, homeDir) {
|
|
44971
|
+
const fromRepo = relative8(resolve9(repo), path);
|
|
44972
|
+
if (fromRepo !== "" && !fromRepo.startsWith("..") && !isAbsolute3(fromRepo)) {
|
|
44973
|
+
return `./${fromRepo}`;
|
|
44974
|
+
}
|
|
44975
|
+
const fromHome = relative8(resolve9(homeDir), path);
|
|
44976
|
+
if (fromHome !== "" && !fromHome.startsWith("..") && !isAbsolute3(fromHome)) {
|
|
44977
|
+
return `~/${fromHome}`;
|
|
44978
|
+
}
|
|
44979
|
+
return path;
|
|
44980
|
+
}
|
|
44981
|
+
function formatAge(modifiedAt, now) {
|
|
44982
|
+
const seconds = Math.max(
|
|
44983
|
+
0,
|
|
44984
|
+
Math.floor((now.getTime() - modifiedAt.getTime()) / 1e3)
|
|
44985
|
+
);
|
|
44986
|
+
if (seconds < 60) return "just now";
|
|
44987
|
+
const minutes = Math.floor(seconds / 60);
|
|
44988
|
+
if (minutes < 60) return `${minutes} ${unit("minute", minutes)} ago`;
|
|
44989
|
+
const hours = Math.floor(minutes / 60);
|
|
44990
|
+
if (hours < 24) return `${hours} ${unit("hour", hours)} ago`;
|
|
44991
|
+
const days = Math.floor(hours / 24);
|
|
44992
|
+
return `${days} ${unit("day", days)} ago`;
|
|
44993
|
+
}
|
|
44994
|
+
function unit(label, value) {
|
|
44995
|
+
return value === 1 ? label : `${label}s`;
|
|
44996
|
+
}
|
|
44997
|
+
|
|
44567
44998
|
// src/rollback.ts
|
|
44568
44999
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
44569
|
-
import { basename as basename3, join as
|
|
45000
|
+
import { basename as basename3, join as join15, resolve as resolve10 } from "node:path";
|
|
44570
45001
|
var projectId2 = "project";
|
|
44571
45002
|
var RollbackServiceError = class extends Error {
|
|
44572
45003
|
code;
|
|
@@ -45166,10 +45597,10 @@ async function rollbackPreparedSwaps({
|
|
|
45166
45597
|
}
|
|
45167
45598
|
}
|
|
45168
45599
|
function rollbackSwaps(options) {
|
|
45169
|
-
const repoDir =
|
|
45600
|
+
const repoDir = resolve10(options.repo);
|
|
45170
45601
|
return rollbackPreparedSwaps({
|
|
45171
45602
|
store: new FsStore(
|
|
45172
|
-
|
|
45603
|
+
resolve10(options.store ?? join15(repoDir, ".rightmodeler"))
|
|
45173
45604
|
),
|
|
45174
45605
|
githubClient: createGithubClient({
|
|
45175
45606
|
baseUrl: options.githubBaseUrl,
|
|
@@ -45182,10 +45613,17 @@ function rollbackSwaps(options) {
|
|
|
45182
45613
|
}
|
|
45183
45614
|
|
|
45184
45615
|
// src/version.ts
|
|
45185
|
-
var version2 = "0.
|
|
45616
|
+
var version2 = "0.2.0";
|
|
45186
45617
|
|
|
45187
45618
|
// src/cli.ts
|
|
45188
|
-
|
|
45619
|
+
var processRuntime = {
|
|
45620
|
+
stdin: process.stdin,
|
|
45621
|
+
stdout: process.stdout,
|
|
45622
|
+
env: process.env,
|
|
45623
|
+
homeDir: homedir2(),
|
|
45624
|
+
now: () => /* @__PURE__ */ new Date()
|
|
45625
|
+
};
|
|
45626
|
+
function createProgram(io = processIo, runtime = processRuntime) {
|
|
45189
45627
|
let code = 0;
|
|
45190
45628
|
const program2 = new Command().name("rightmodeler").description("Find and prove safe model substitutions.").addHelpText(
|
|
45191
45629
|
"after",
|
|
@@ -45213,13 +45651,23 @@ function createProgram(io = processIo) {
|
|
|
45213
45651
|
);
|
|
45214
45652
|
run(init, async (reporter, global) => {
|
|
45215
45653
|
const local = init.opts();
|
|
45216
|
-
const
|
|
45217
|
-
|
|
45218
|
-
|
|
45219
|
-
|
|
45220
|
-
|
|
45221
|
-
|
|
45222
|
-
|
|
45654
|
+
const prepared = await guidedPipelineOptions(
|
|
45655
|
+
global,
|
|
45656
|
+
local,
|
|
45657
|
+
reporter,
|
|
45658
|
+
runtime
|
|
45659
|
+
);
|
|
45660
|
+
const result2 = await withInputGuidance(
|
|
45661
|
+
prepared,
|
|
45662
|
+
local,
|
|
45663
|
+
runtime,
|
|
45664
|
+
async (options) => local.plan ? {
|
|
45665
|
+
stages: await planPipeline(options),
|
|
45666
|
+
executedStages: [],
|
|
45667
|
+
verdicts: [],
|
|
45668
|
+
recommendationExists: false
|
|
45669
|
+
} : runPipeline(options)
|
|
45670
|
+
);
|
|
45223
45671
|
reporter.result(result2);
|
|
45224
45672
|
return local.plan || local.through !== void 0 && local.through !== "report" ? 0 : result2.recommendationExists ? 1 : 0;
|
|
45225
45673
|
});
|
|
@@ -45231,13 +45679,23 @@ function createProgram(io = processIo) {
|
|
|
45231
45679
|
"scope projection to one merged approved swap"
|
|
45232
45680
|
);
|
|
45233
45681
|
run(estimate, async (reporter, global) => {
|
|
45234
|
-
const
|
|
45682
|
+
const local = estimate.opts();
|
|
45683
|
+
const prepared = await guidedPipelineOptions(
|
|
45235
45684
|
global,
|
|
45236
|
-
|
|
45237
|
-
reporter
|
|
45685
|
+
local,
|
|
45686
|
+
reporter,
|
|
45687
|
+
runtime
|
|
45688
|
+
);
|
|
45689
|
+
const result2 = await withInputGuidance(
|
|
45690
|
+
prepared,
|
|
45691
|
+
local,
|
|
45692
|
+
runtime,
|
|
45693
|
+
async (options) => {
|
|
45694
|
+
await runPipeline({ ...options, through: "shortlist" });
|
|
45695
|
+
return estimateReplay(options);
|
|
45696
|
+
}
|
|
45238
45697
|
);
|
|
45239
|
-
|
|
45240
|
-
reporter.result(await estimateReplay(options));
|
|
45698
|
+
reporter.result(result2);
|
|
45241
45699
|
return 0;
|
|
45242
45700
|
});
|
|
45243
45701
|
for (const stage of PIPELINE_STAGES.slice(0, -1)) {
|
|
@@ -45539,7 +45997,13 @@ function addPipelineOptions(command, provider) {
|
|
|
45539
45997
|
new Option("--through <stage>", "stop after this stage").choices([
|
|
45540
45998
|
...PIPELINE_STAGES
|
|
45541
45999
|
])
|
|
45542
|
-
)
|
|
46000
|
+
);
|
|
46001
|
+
}
|
|
46002
|
+
if (command.name() === "init" || command.name() === "estimate") {
|
|
46003
|
+
command.option(
|
|
46004
|
+
"--yes",
|
|
46005
|
+
"accept the newest discovered trace without prompting"
|
|
46006
|
+
);
|
|
45543
46007
|
}
|
|
45544
46008
|
return command;
|
|
45545
46009
|
}
|
|
@@ -45582,6 +46046,97 @@ function pipelineOptions(global, local, reporter) {
|
|
|
45582
46046
|
reporter
|
|
45583
46047
|
};
|
|
45584
46048
|
}
|
|
46049
|
+
async function guidedPipelineOptions(global, local, reporter, runtime) {
|
|
46050
|
+
const options = pipelineOptions(global, local, reporter);
|
|
46051
|
+
const interactive = global.output === "human" && runtime.stdin.isTTY === true && runtime.stdout.isTTY === true;
|
|
46052
|
+
if (local.traces !== void 0 || local.plan || local.through === "scan") {
|
|
46053
|
+
return { options, candidates: [], interactive };
|
|
46054
|
+
}
|
|
46055
|
+
const candidates = await discoverTraces({
|
|
46056
|
+
repo: global.repo,
|
|
46057
|
+
homeDir: runtime.homeDir
|
|
46058
|
+
});
|
|
46059
|
+
const traces = local.yes ? candidates[0]?.path : interactive ? await promptForTracePath({
|
|
46060
|
+
candidates,
|
|
46061
|
+
repo: resolve11(global.repo),
|
|
46062
|
+
homeDir: runtime.homeDir,
|
|
46063
|
+
now: runtime.now(),
|
|
46064
|
+
input: runtime.stdin,
|
|
46065
|
+
output: promptOutput(reporter.io, runtime.stdout.isTTY)
|
|
46066
|
+
}) : void 0;
|
|
46067
|
+
if (local.yes && traces !== void 0 && global.output === "human") {
|
|
46068
|
+
reporter.io.stdout(`Using trace file: ${traces}
|
|
46069
|
+
`);
|
|
46070
|
+
}
|
|
46071
|
+
return {
|
|
46072
|
+
options: traces === void 0 ? options : { ...options, traces },
|
|
46073
|
+
candidates,
|
|
46074
|
+
interactive,
|
|
46075
|
+
...traces === void 0 ? {} : { selectedDiscoveredTrace: traces }
|
|
46076
|
+
};
|
|
46077
|
+
}
|
|
46078
|
+
async function withInputGuidance(prepared, local, runtime, operation) {
|
|
46079
|
+
try {
|
|
46080
|
+
return await operation(prepared.options);
|
|
46081
|
+
} catch (error51) {
|
|
46082
|
+
if (error51 instanceof ProtocolError && error51.code === "missing_traces_path" && prepared.candidates.length > 0) {
|
|
46083
|
+
throw new ProtocolError({
|
|
46084
|
+
exitCode: error51.exitCode,
|
|
46085
|
+
code: error51.code,
|
|
46086
|
+
message: error51.message,
|
|
46087
|
+
remedy: `${error51.remedy} ${candidateRemedy(prepared.candidates)}`
|
|
46088
|
+
});
|
|
46089
|
+
}
|
|
46090
|
+
if (prepared.selectedDiscoveredTrace !== void 0 && error51 instanceof TraceAdaptError) {
|
|
46091
|
+
throw new ProtocolError({
|
|
46092
|
+
exitCode: 2,
|
|
46093
|
+
code: "unusable_trace_input",
|
|
46094
|
+
message: error51.message,
|
|
46095
|
+
remedy: "Rerun the command and choose a different trace file."
|
|
46096
|
+
});
|
|
46097
|
+
}
|
|
46098
|
+
if (!(error51 instanceof ProtocolError) || error51.code !== "missing_provider_configuration" || !prepared.interactive) {
|
|
46099
|
+
throw error51;
|
|
46100
|
+
}
|
|
46101
|
+
const baseUrl = await promptForProviderBaseUrl({
|
|
46102
|
+
current: local.baseUrl,
|
|
46103
|
+
input: runtime.stdin,
|
|
46104
|
+
output: promptOutput(prepared.options.reporter.io, runtime.stdout.isTTY)
|
|
46105
|
+
});
|
|
46106
|
+
if (baseUrl === void 0) throw error51;
|
|
46107
|
+
const apiKeyEnv = local.apiKeyEnv ?? "RIGHTMODELER_API_KEY";
|
|
46108
|
+
if (!runtime.env[apiKeyEnv]) {
|
|
46109
|
+
throw new ProtocolError({
|
|
46110
|
+
exitCode: 2,
|
|
46111
|
+
code: "missing_provider_configuration",
|
|
46112
|
+
message: `Provider API key environment variable is not set: ${apiKeyEnv}.`,
|
|
46113
|
+
remedy: `Set the environment variable ${apiKeyEnv} to your provider API key, then rerun.`
|
|
46114
|
+
});
|
|
46115
|
+
}
|
|
46116
|
+
return operation({
|
|
46117
|
+
...prepared.options,
|
|
46118
|
+
baseUrl,
|
|
46119
|
+
apiKeyEnv
|
|
46120
|
+
});
|
|
46121
|
+
}
|
|
46122
|
+
}
|
|
46123
|
+
function promptOutput(io, isTTY) {
|
|
46124
|
+
return Object.assign(
|
|
46125
|
+
new Writable({
|
|
46126
|
+
write(chunk, _encoding, callback) {
|
|
46127
|
+
io.stdout(String(chunk));
|
|
46128
|
+
callback();
|
|
46129
|
+
}
|
|
46130
|
+
}),
|
|
46131
|
+
{ isTTY }
|
|
46132
|
+
);
|
|
46133
|
+
}
|
|
46134
|
+
function candidateRemedy(candidates) {
|
|
46135
|
+
const shown = candidates.slice(0, 3).map(({ path }) => path);
|
|
46136
|
+
const more = candidates.length - shown.length;
|
|
46137
|
+
const paths = more === 0 ? shown.join(", ") : `${shown.join(", ")}, and ${more} more`;
|
|
46138
|
+
return `Found ${candidates.length} candidate ${candidates.length === 1 ? "trace file" : "trace files"}: ${paths}. Pass --traces <path>.`;
|
|
46139
|
+
}
|
|
45585
46140
|
function evaluatorConfig(local, gateThreshold) {
|
|
45586
46141
|
const provider = local.evaluator;
|
|
45587
46142
|
const scoring = {
|
|
@@ -45735,23 +46290,23 @@ async function startDetachedReplay(global, local, runId) {
|
|
|
45735
46290
|
const args = [
|
|
45736
46291
|
fileURLToPath2(import.meta.url),
|
|
45737
46292
|
"--repo",
|
|
45738
|
-
|
|
46293
|
+
resolve11(global.repo),
|
|
45739
46294
|
"--output",
|
|
45740
46295
|
"json"
|
|
45741
46296
|
];
|
|
45742
46297
|
if (global.store !== void 0) {
|
|
45743
|
-
args.push("--store",
|
|
46298
|
+
args.push("--store", resolve11(global.store));
|
|
45744
46299
|
}
|
|
45745
46300
|
args.push("replay");
|
|
45746
46301
|
appendCliOption(
|
|
45747
46302
|
args,
|
|
45748
46303
|
"--traces",
|
|
45749
|
-
local.traces === void 0 ? void 0 :
|
|
46304
|
+
local.traces === void 0 ? void 0 : resolve11(local.traces)
|
|
45750
46305
|
);
|
|
45751
46306
|
appendCliOption(
|
|
45752
46307
|
args,
|
|
45753
46308
|
"--modeb-config",
|
|
45754
|
-
local.modebConfig === void 0 ? void 0 :
|
|
46309
|
+
local.modebConfig === void 0 ? void 0 : resolve11(local.modebConfig)
|
|
45755
46310
|
);
|
|
45756
46311
|
appendCliOption(args, "--base-url", local.baseUrl);
|
|
45757
46312
|
appendCliOption(args, "--api-key-env", local.apiKeyEnv);
|
|
@@ -45775,7 +46330,7 @@ async function startDetachedReplay(global, local, runId) {
|
|
|
45775
46330
|
appendCliOption(
|
|
45776
46331
|
args,
|
|
45777
46332
|
"--evaluator-config",
|
|
45778
|
-
local.evaluatorConfig === void 0 ? void 0 :
|
|
46333
|
+
local.evaluatorConfig === void 0 ? void 0 : resolve11(local.evaluatorConfig)
|
|
45779
46334
|
);
|
|
45780
46335
|
for (const scorer of local.evaluatorScorer ?? []) {
|
|
45781
46336
|
appendCliOption(args, "--evaluator-scorer", scorer);
|
|
@@ -45789,7 +46344,7 @@ async function startDetachedReplay(global, local, runId) {
|
|
|
45789
46344
|
appendCliOption(args, "--internal-run-id", runId);
|
|
45790
46345
|
await new Promise((resolveSpawn, rejectSpawn) => {
|
|
45791
46346
|
const child = spawn(process.execPath, args, {
|
|
45792
|
-
cwd:
|
|
46347
|
+
cwd: resolve11(global.repo),
|
|
45793
46348
|
detached: true,
|
|
45794
46349
|
env: process.env,
|
|
45795
46350
|
stdio: "ignore"
|
|
@@ -45803,13 +46358,13 @@ async function startDetachedReplay(global, local, runId) {
|
|
|
45803
46358
|
}
|
|
45804
46359
|
function detachedCommand(value) {
|
|
45805
46360
|
if (value === void 0) return void 0;
|
|
45806
|
-
return value.startsWith("./") || value.startsWith("../") || value.includes("/") || value.includes("\\") ?
|
|
46361
|
+
return value.startsWith("./") || value.startsWith("../") || value.includes("/") || value.includes("\\") ? resolve11(value) : value;
|
|
45807
46362
|
}
|
|
45808
46363
|
function appendCliOption(args, flag, value) {
|
|
45809
46364
|
if (value !== void 0) args.push(flag, value);
|
|
45810
46365
|
}
|
|
45811
|
-
async function executeCli(argv, io = processIo) {
|
|
45812
|
-
const handle = createProgram(io);
|
|
46366
|
+
async function executeCli(argv, io = processIo, runtime = processRuntime) {
|
|
46367
|
+
const handle = createProgram(io, runtime);
|
|
45813
46368
|
try {
|
|
45814
46369
|
await handle.program.parseAsync([...argv], { from: "user" });
|
|
45815
46370
|
return handle.exitCode();
|
package/docs/commands.md
CHANGED
|
@@ -84,8 +84,8 @@ Options:
|
|
|
84
84
|
"ingest", "reconcile", "scrub", "corpus",
|
|
85
85
|
"audit-sample", "shortlist", "replay",
|
|
86
86
|
"aggregate", "confirm", "report")
|
|
87
|
-
--yes accept
|
|
88
|
-
|
|
87
|
+
--yes accept the newest discovered trace without
|
|
88
|
+
prompting
|
|
89
89
|
-h, --help display help for command
|
|
90
90
|
```
|
|
91
91
|
|
|
@@ -125,6 +125,8 @@ Options:
|
|
|
125
125
|
--evaluator-gate-metric <name> scorer metric used for release gates
|
|
126
126
|
--evaluator-gate-threshold <value> fallback pass threshold when the evaluator
|
|
127
127
|
omits a pass decision
|
|
128
|
+
--yes accept the newest discovered trace without
|
|
129
|
+
prompting
|
|
128
130
|
--approved-run <digest> scope projection to one merged approved
|
|
129
131
|
swap
|
|
130
132
|
-h, --help display help for command
|
package/docs/exit-codes.md
CHANGED
|
@@ -6,7 +6,7 @@ Rightmodeler reserves exit codes `0` through `3` for machine-readable outcomes.
|
|
|
6
6
|
|
|
7
7
|
- `0`: the command completed and no recommendation is being reported. Planning and partial `--through` runs also return `0` when successful.
|
|
8
8
|
- `1`: a complete `init` or `report` found an actionable recommendation.
|
|
9
|
-
- `2`: the run needs input at a resumable boundary, such as missing traces, provider configuration, or required confirmation configuration.
|
|
9
|
+
- `2`: the run needs input at a resumable boundary, such as missing traces, a cancelled trace prompt, provider configuration, or required confirmation configuration.
|
|
10
10
|
- `3`: the cost budget was reached at a resumable boundary.
|
|
11
11
|
- `10` or greater: command-line or runtime failure.
|
|
12
12
|
|
package/docs/getting-started.md
CHANGED
|
@@ -9,6 +9,25 @@ Rightmodeler analyzes recorded model calls, replays them against cheaper candida
|
|
|
9
9
|
- Trace input in a supported format.
|
|
10
10
|
- An OpenAI-compatible provider base URL and the name of an environment variable containing its API key before replay begins.
|
|
11
11
|
|
|
12
|
+
Supported trace sources are OTel GenAI, OpenAI JSONL, Langfuse, Braintrust,
|
|
13
|
+
LangSmith, OpenInference, Helicone, W&B Weave, Claude Code, and Codex.
|
|
14
|
+
|
|
15
|
+
## Start with automatic discovery
|
|
16
|
+
|
|
17
|
+
Run this from the repository you want to analyze:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
npx rightmodeler init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Rightmodeler checks conventional local trace files, Claude Code transcripts for
|
|
24
|
+
the repository, and Codex sessions whose recorded working directory matches the
|
|
25
|
+
repository. In an interactive terminal it lists matches newest-first with an
|
|
26
|
+
approximate model-call count. If nothing is found, it explains how to produce or
|
|
27
|
+
export a trace and asks for a path. Leaving the answer empty, pressing Ctrl-C or
|
|
28
|
+
Ctrl-D, or closing standard input stops cleanly with exit code `2` and a remedy
|
|
29
|
+
for rerunning.
|
|
30
|
+
|
|
12
31
|
## Preview without changing the repository
|
|
13
32
|
|
|
14
33
|
```sh
|
|
@@ -28,6 +47,19 @@ export RIGHTMODELER_API_KEY="provider-key"
|
|
|
28
47
|
npx rightmodeler init --traces /path/to/traces.json --base-url https://provider.example/v1 --output json --repo /path/to/repository
|
|
29
48
|
```
|
|
30
49
|
|
|
50
|
+
`RIGHTMODELER_API_KEY` is the default key variable. Pass
|
|
51
|
+
`--api-key-env <name>` to use a different exported variable. The CLI does not ask
|
|
52
|
+
for a secret value.
|
|
53
|
+
|
|
54
|
+
## Estimate replay spend
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npx rightmodeler estimate --traces /path/to/traces.json --base-url https://provider.example/v1 --output json --repo /path/to/repository
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Estimate projects candidate replay spend from recorded token usage and the current
|
|
61
|
+
model catalog before paid model calls begin.
|
|
62
|
+
|
|
31
63
|
The default store is `.rightmodeler/` inside the analyzed repository. Completed stages resume when their inputs and outputs are still current. A complete run writes `.rightmodeler/project/reports/report.md` and `.rightmodeler/project/reports/report.json`.
|
|
32
64
|
|
|
33
65
|
Read the generated [command reference](commands.md), the [evaluator guide](evaluators.md), [Mode B configuration](modeb.md), and the [exit-code convention](exit-codes.md) before automating a full run.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rightmodeler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"rightmodeler": "./dist-bundle/cli.js"
|
|
@@ -12,5 +12,11 @@
|
|
|
12
12
|
],
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=24"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/elm-os/rightmodeler.git",
|
|
20
|
+
"directory": "harness/packages/rightmodeler"
|
|
15
21
|
}
|
|
16
22
|
}
|