@lucasschirm/devinp 0.1.4 → 0.1.5
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 +13 -0
- package/bin/command.js +180 -62
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,19 @@ The package ships as a self-contained bundle — no dependencies are installed.
|
|
|
24
24
|
not authenticated, `devinp` exits with an error (use `--source` for a saved
|
|
25
25
|
snapshot).
|
|
26
26
|
|
|
27
|
+
### When `devin` fails or truncates
|
|
28
|
+
|
|
29
|
+
- Prices are captured through a temp file in `os.tmpdir()` — `devin`'s
|
|
30
|
+
interactive CLI can truncate a pipe stream mid-write, so `devinp` points
|
|
31
|
+
`devin`'s stdout at a real file instead. The temp file is always removed
|
|
32
|
+
after the run.
|
|
33
|
+
- If `devin`'s output is cut off mid-stream, `devinp` repairs the document,
|
|
34
|
+
prints a warning, and renders the partial table.
|
|
35
|
+
- If the output cannot be recovered, `devinp` reports a diagnostic with the
|
|
36
|
+
size and tail of what `devin` wrote, and suggests re-running or saving the
|
|
37
|
+
output manually: `devin models list --format json > models.json`, then
|
|
38
|
+
`devinp --source models.json`.
|
|
39
|
+
|
|
27
40
|
## Usage
|
|
28
41
|
|
|
29
42
|
```sh
|
package/bin/command.js
CHANGED
|
@@ -2887,9 +2887,9 @@ var require_ejs = __commonJS({
|
|
|
2887
2887
|
createRegex: function() {
|
|
2888
2888
|
var str = _REGEX_STRING;
|
|
2889
2889
|
var delim = utils_js_1.default.escapeRegExpChars(this.opts.delimiter);
|
|
2890
|
-
var
|
|
2890
|
+
var open2 = utils_js_1.default.escapeRegExpChars(this.opts.openDelimiter);
|
|
2891
2891
|
var close = utils_js_1.default.escapeRegExpChars(this.opts.closeDelimiter);
|
|
2892
|
-
str = str.replace(/%/g, delim).replace(/</g,
|
|
2892
|
+
str = str.replace(/%/g, delim).replace(/</g, open2).replace(/>/g, close);
|
|
2893
2893
|
return new RegExp(str);
|
|
2894
2894
|
},
|
|
2895
2895
|
compile: function() {
|
|
@@ -6393,8 +6393,8 @@ var require_async = __commonJS({
|
|
|
6393
6393
|
"node_modules/get-package-type/async.cjs"(exports, module) {
|
|
6394
6394
|
"use strict";
|
|
6395
6395
|
var path = __require("path");
|
|
6396
|
-
var { promisify:
|
|
6397
|
-
var readFile3 =
|
|
6396
|
+
var { promisify: promisify3 } = __require("util");
|
|
6397
|
+
var readFile3 = promisify3(__require("fs").readFile);
|
|
6398
6398
|
var isNodeModules = require_is_node_modules();
|
|
6399
6399
|
var resultsCache = require_cache2();
|
|
6400
6400
|
var promiseCache = /* @__PURE__ */ new Map();
|
|
@@ -12628,10 +12628,10 @@ var require_commonjs3 = __commonJS({
|
|
|
12628
12628
|
}
|
|
12629
12629
|
return filtered.join("/");
|
|
12630
12630
|
}).join("|");
|
|
12631
|
-
const [
|
|
12632
|
-
re = "^" +
|
|
12631
|
+
const [open2, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
|
|
12632
|
+
re = "^" + open2 + re + close + "$";
|
|
12633
12633
|
if (this.partial) {
|
|
12634
|
-
re = "^(?:\\/|" +
|
|
12634
|
+
re = "^(?:\\/|" + open2 + re.slice(1, -1) + close + ")$";
|
|
12635
12635
|
}
|
|
12636
12636
|
if (this.negate)
|
|
12637
12637
|
re = "^(?!" + re + ").+$";
|
|
@@ -13719,16 +13719,16 @@ var require_parse2 = __commonJS({
|
|
|
13719
13719
|
const analysis = analyzeRepeatedExtglob(body, opts);
|
|
13720
13720
|
if ((token.type === "plus" || token.type === "star") && analysis.risky) {
|
|
13721
13721
|
const safeOutput = analysis.safeOutput ? (token.output ? "" : ONE_CHAR) + (opts.capture ? `(${analysis.safeOutput})` : analysis.safeOutput) : void 0;
|
|
13722
|
-
const
|
|
13723
|
-
|
|
13724
|
-
|
|
13725
|
-
|
|
13722
|
+
const open2 = tokens[token.tokensIndex];
|
|
13723
|
+
open2.type = "text";
|
|
13724
|
+
open2.value = literal;
|
|
13725
|
+
open2.output = safeOutput || utils.escapeRegex(literal);
|
|
13726
13726
|
for (let i = token.tokensIndex + 1; i < tokens.length; i++) {
|
|
13727
13727
|
tokens[i].value = "";
|
|
13728
13728
|
tokens[i].output = "";
|
|
13729
13729
|
delete tokens[i].suffix;
|
|
13730
13730
|
}
|
|
13731
|
-
state.output = token.output +
|
|
13731
|
+
state.output = token.output + open2.output;
|
|
13732
13732
|
state.backtrack = true;
|
|
13733
13733
|
push({ type: "paren", extglob: true, value, output: "" });
|
|
13734
13734
|
decrement("parens");
|
|
@@ -13948,15 +13948,15 @@ var require_parse2 = __commonJS({
|
|
|
13948
13948
|
}
|
|
13949
13949
|
if (value === "{" && opts.nobrace !== true) {
|
|
13950
13950
|
increment("braces");
|
|
13951
|
-
const
|
|
13951
|
+
const open2 = {
|
|
13952
13952
|
type: "brace",
|
|
13953
13953
|
value,
|
|
13954
13954
|
output: "(",
|
|
13955
13955
|
outputIndex: state.output.length,
|
|
13956
13956
|
tokensIndex: state.tokens.length
|
|
13957
13957
|
};
|
|
13958
|
-
braces.push(
|
|
13959
|
-
push(
|
|
13958
|
+
braces.push(open2);
|
|
13959
|
+
push(open2);
|
|
13960
13960
|
continue;
|
|
13961
13961
|
}
|
|
13962
13962
|
if (value === "}") {
|
|
@@ -27622,7 +27622,7 @@ var require_thread_stream = __commonJS({
|
|
|
27622
27622
|
var { version } = require_package2();
|
|
27623
27623
|
var { EventEmitter } = __require("events");
|
|
27624
27624
|
var { Worker } = __require("worker_threads");
|
|
27625
|
-
var { join:
|
|
27625
|
+
var { join: join3 } = __require("path");
|
|
27626
27626
|
var { pathToFileURL } = __require("url");
|
|
27627
27627
|
var { wait } = require_wait();
|
|
27628
27628
|
var {
|
|
@@ -27673,7 +27673,7 @@ var require_thread_stream = __commonJS({
|
|
|
27673
27673
|
function createWorker(stream, opts) {
|
|
27674
27674
|
const { filename, workerData } = opts;
|
|
27675
27675
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
27676
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
27676
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join3(__dirname, "lib", "worker.js");
|
|
27677
27677
|
const worker = new Worker(toExecute, {
|
|
27678
27678
|
...opts.workerOpts,
|
|
27679
27679
|
name: opts.workerOpts?.name || "thread-stream",
|
|
@@ -28141,7 +28141,7 @@ var require_transport = __commonJS({
|
|
|
28141
28141
|
var { createRequire } = __require("module");
|
|
28142
28142
|
var { existsSync } = __require("node:fs");
|
|
28143
28143
|
var getCallers = require_caller();
|
|
28144
|
-
var { join:
|
|
28144
|
+
var { join: join3, isAbsolute, sep } = __require("node:path");
|
|
28145
28145
|
var { fileURLToPath } = __require("node:url");
|
|
28146
28146
|
var sleep = require_atomic_sleep();
|
|
28147
28147
|
var onExit = require_on_exit_leak_free();
|
|
@@ -28294,7 +28294,7 @@ var require_transport = __commonJS({
|
|
|
28294
28294
|
throw new Error("only one of target or targets can be specified");
|
|
28295
28295
|
}
|
|
28296
28296
|
if (targets) {
|
|
28297
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
28297
|
+
target = bundlerOverrides["pino-worker"] || join3(__dirname, "worker.js");
|
|
28298
28298
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
28299
28299
|
return {
|
|
28300
28300
|
...dest,
|
|
@@ -28312,7 +28312,7 @@ var require_transport = __commonJS({
|
|
|
28312
28312
|
});
|
|
28313
28313
|
});
|
|
28314
28314
|
} else if (pipeline) {
|
|
28315
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
28315
|
+
target = bundlerOverrides["pino-worker"] || join3(__dirname, "worker.js");
|
|
28316
28316
|
options.pipelines = [pipeline.map((dest) => {
|
|
28317
28317
|
return {
|
|
28318
28318
|
...dest,
|
|
@@ -28335,7 +28335,7 @@ var require_transport = __commonJS({
|
|
|
28335
28335
|
return origin;
|
|
28336
28336
|
}
|
|
28337
28337
|
if (origin === "pino/file") {
|
|
28338
|
-
return
|
|
28338
|
+
return join3(__dirname, "..", "file.js");
|
|
28339
28339
|
}
|
|
28340
28340
|
let fixTarget2;
|
|
28341
28341
|
for (const filePath of callers) {
|
|
@@ -29315,7 +29315,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
29315
29315
|
return circularValue;
|
|
29316
29316
|
}
|
|
29317
29317
|
let res = "";
|
|
29318
|
-
let
|
|
29318
|
+
let join3 = ",";
|
|
29319
29319
|
const originalIndentation = indentation;
|
|
29320
29320
|
if (Array.isArray(value)) {
|
|
29321
29321
|
if (value.length === 0) {
|
|
@@ -29329,7 +29329,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
29329
29329
|
indentation += spacer;
|
|
29330
29330
|
res += `
|
|
29331
29331
|
${indentation}`;
|
|
29332
|
-
|
|
29332
|
+
join3 = `,
|
|
29333
29333
|
${indentation}`;
|
|
29334
29334
|
}
|
|
29335
29335
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -29337,13 +29337,13 @@ ${indentation}`;
|
|
|
29337
29337
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
29338
29338
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
29339
29339
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
29340
|
-
res +=
|
|
29340
|
+
res += join3;
|
|
29341
29341
|
}
|
|
29342
29342
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
29343
29343
|
res += tmp !== void 0 ? tmp : "null";
|
|
29344
29344
|
if (value.length - 1 > maximumBreadth) {
|
|
29345
29345
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
29346
|
-
res += `${
|
|
29346
|
+
res += `${join3}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
29347
29347
|
}
|
|
29348
29348
|
if (spacer !== "") {
|
|
29349
29349
|
res += `
|
|
@@ -29364,7 +29364,7 @@ ${originalIndentation}`;
|
|
|
29364
29364
|
let separator = "";
|
|
29365
29365
|
if (spacer !== "") {
|
|
29366
29366
|
indentation += spacer;
|
|
29367
|
-
|
|
29367
|
+
join3 = `,
|
|
29368
29368
|
${indentation}`;
|
|
29369
29369
|
whitespace = " ";
|
|
29370
29370
|
}
|
|
@@ -29378,13 +29378,13 @@ ${indentation}`;
|
|
|
29378
29378
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
29379
29379
|
if (tmp !== void 0) {
|
|
29380
29380
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
29381
|
-
separator =
|
|
29381
|
+
separator = join3;
|
|
29382
29382
|
}
|
|
29383
29383
|
}
|
|
29384
29384
|
if (keyLength > maximumBreadth) {
|
|
29385
29385
|
const removedKeys = keyLength - maximumBreadth;
|
|
29386
29386
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
29387
|
-
separator =
|
|
29387
|
+
separator = join3;
|
|
29388
29388
|
}
|
|
29389
29389
|
if (spacer !== "" && separator.length > 1) {
|
|
29390
29390
|
res = `
|
|
@@ -29425,7 +29425,7 @@ ${originalIndentation}`;
|
|
|
29425
29425
|
}
|
|
29426
29426
|
const originalIndentation = indentation;
|
|
29427
29427
|
let res = "";
|
|
29428
|
-
let
|
|
29428
|
+
let join3 = ",";
|
|
29429
29429
|
if (Array.isArray(value)) {
|
|
29430
29430
|
if (value.length === 0) {
|
|
29431
29431
|
return "[]";
|
|
@@ -29438,7 +29438,7 @@ ${originalIndentation}`;
|
|
|
29438
29438
|
indentation += spacer;
|
|
29439
29439
|
res += `
|
|
29440
29440
|
${indentation}`;
|
|
29441
|
-
|
|
29441
|
+
join3 = `,
|
|
29442
29442
|
${indentation}`;
|
|
29443
29443
|
}
|
|
29444
29444
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -29446,13 +29446,13 @@ ${indentation}`;
|
|
|
29446
29446
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
29447
29447
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
29448
29448
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
29449
|
-
res +=
|
|
29449
|
+
res += join3;
|
|
29450
29450
|
}
|
|
29451
29451
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
29452
29452
|
res += tmp !== void 0 ? tmp : "null";
|
|
29453
29453
|
if (value.length - 1 > maximumBreadth) {
|
|
29454
29454
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
29455
|
-
res += `${
|
|
29455
|
+
res += `${join3}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
29456
29456
|
}
|
|
29457
29457
|
if (spacer !== "") {
|
|
29458
29458
|
res += `
|
|
@@ -29465,7 +29465,7 @@ ${originalIndentation}`;
|
|
|
29465
29465
|
let whitespace = "";
|
|
29466
29466
|
if (spacer !== "") {
|
|
29467
29467
|
indentation += spacer;
|
|
29468
|
-
|
|
29468
|
+
join3 = `,
|
|
29469
29469
|
${indentation}`;
|
|
29470
29470
|
whitespace = " ";
|
|
29471
29471
|
}
|
|
@@ -29474,7 +29474,7 @@ ${indentation}`;
|
|
|
29474
29474
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
29475
29475
|
if (tmp !== void 0) {
|
|
29476
29476
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
29477
|
-
separator =
|
|
29477
|
+
separator = join3;
|
|
29478
29478
|
}
|
|
29479
29479
|
}
|
|
29480
29480
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -29532,20 +29532,20 @@ ${originalIndentation}`;
|
|
|
29532
29532
|
indentation += spacer;
|
|
29533
29533
|
let res2 = `
|
|
29534
29534
|
${indentation}`;
|
|
29535
|
-
const
|
|
29535
|
+
const join4 = `,
|
|
29536
29536
|
${indentation}`;
|
|
29537
29537
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
29538
29538
|
let i = 0;
|
|
29539
29539
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
29540
29540
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
29541
29541
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
29542
|
-
res2 +=
|
|
29542
|
+
res2 += join4;
|
|
29543
29543
|
}
|
|
29544
29544
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
29545
29545
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
29546
29546
|
if (value.length - 1 > maximumBreadth) {
|
|
29547
29547
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
29548
|
-
res2 += `${
|
|
29548
|
+
res2 += `${join4}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
29549
29549
|
}
|
|
29550
29550
|
res2 += `
|
|
29551
29551
|
${originalIndentation}`;
|
|
@@ -29561,16 +29561,16 @@ ${originalIndentation}`;
|
|
|
29561
29561
|
return '"[Object]"';
|
|
29562
29562
|
}
|
|
29563
29563
|
indentation += spacer;
|
|
29564
|
-
const
|
|
29564
|
+
const join3 = `,
|
|
29565
29565
|
${indentation}`;
|
|
29566
29566
|
let res = "";
|
|
29567
29567
|
let separator = "";
|
|
29568
29568
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
29569
29569
|
if (isTypedArrayWithEntries(value)) {
|
|
29570
|
-
res += stringifyTypedArray(value,
|
|
29570
|
+
res += stringifyTypedArray(value, join3, maximumBreadth);
|
|
29571
29571
|
keys = keys.slice(value.length);
|
|
29572
29572
|
maximumPropertiesToStringify -= value.length;
|
|
29573
|
-
separator =
|
|
29573
|
+
separator = join3;
|
|
29574
29574
|
}
|
|
29575
29575
|
if (deterministic) {
|
|
29576
29576
|
keys = sort(keys, comparator);
|
|
@@ -29581,13 +29581,13 @@ ${indentation}`;
|
|
|
29581
29581
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
29582
29582
|
if (tmp !== void 0) {
|
|
29583
29583
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
29584
|
-
separator =
|
|
29584
|
+
separator = join3;
|
|
29585
29585
|
}
|
|
29586
29586
|
}
|
|
29587
29587
|
if (keyLength > maximumBreadth) {
|
|
29588
29588
|
const removedKeys = keyLength - maximumBreadth;
|
|
29589
29589
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
29590
|
-
separator =
|
|
29590
|
+
separator = join3;
|
|
29591
29591
|
}
|
|
29592
29592
|
if (separator !== "") {
|
|
29593
29593
|
res = `
|
|
@@ -55023,41 +55023,155 @@ async function startServer({ rows, benchMeta, port, log }) {
|
|
|
55023
55023
|
}
|
|
55024
55024
|
|
|
55025
55025
|
// src/lib/source.js
|
|
55026
|
-
import {
|
|
55027
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
55028
|
-
import {
|
|
55029
|
-
|
|
55030
|
-
|
|
55026
|
+
import { spawn } from "node:child_process";
|
|
55027
|
+
import { open, readFile as readFile2, unlink } from "node:fs/promises";
|
|
55028
|
+
import { tmpdir } from "node:os";
|
|
55029
|
+
import { join as join2 } from "node:path";
|
|
55030
|
+
|
|
55031
|
+
// src/lib/repair-json.js
|
|
55032
|
+
var CLOSER = { "{": "}", "[": "]" };
|
|
55033
|
+
function recoverTruncatedJson(raw) {
|
|
55034
|
+
if (typeof raw !== "string") return null;
|
|
55035
|
+
try {
|
|
55036
|
+
return JSON.parse(raw);
|
|
55037
|
+
} catch {
|
|
55038
|
+
}
|
|
55039
|
+
const scan = scanJson(raw, raw.length);
|
|
55040
|
+
const cuts = [raw.length];
|
|
55041
|
+
if (scan.inString) cuts.push(scan.stringStart);
|
|
55042
|
+
if (scan.lastComma != null) cuts.push(scan.lastComma + 1);
|
|
55043
|
+
if (scan.lastCloser != null) cuts.push(scan.lastCloser + 1);
|
|
55044
|
+
const seen = /* @__PURE__ */ new Set();
|
|
55045
|
+
for (const cut of cuts) {
|
|
55046
|
+
if (seen.has(cut)) continue;
|
|
55047
|
+
seen.add(cut);
|
|
55048
|
+
let candidate = raw.slice(0, cut).replace(/[\s,]+$/g, "");
|
|
55049
|
+
if (candidate === "") continue;
|
|
55050
|
+
const s = scanJson(candidate, candidate.length);
|
|
55051
|
+
const repaired = candidate + s.stack.map((token) => CLOSER[token]).reverse().join("");
|
|
55052
|
+
try {
|
|
55053
|
+
return JSON.parse(repaired);
|
|
55054
|
+
} catch {
|
|
55055
|
+
}
|
|
55056
|
+
}
|
|
55057
|
+
return null;
|
|
55058
|
+
}
|
|
55059
|
+
function scanJson(s, end) {
|
|
55060
|
+
let inString = false;
|
|
55061
|
+
let escape2 = false;
|
|
55062
|
+
let stringStart = 0;
|
|
55063
|
+
const stack = [];
|
|
55064
|
+
let lastComma = null;
|
|
55065
|
+
let lastCloser = null;
|
|
55066
|
+
for (let i = 0; i < end; i++) {
|
|
55067
|
+
const ch = s[i];
|
|
55068
|
+
if (inString) {
|
|
55069
|
+
if (escape2) escape2 = false;
|
|
55070
|
+
else if (ch === "\\") escape2 = true;
|
|
55071
|
+
else if (ch === '"') inString = false;
|
|
55072
|
+
continue;
|
|
55073
|
+
}
|
|
55074
|
+
if (ch === '"') {
|
|
55075
|
+
inString = true;
|
|
55076
|
+
stringStart = i;
|
|
55077
|
+
} else if (ch === ",") {
|
|
55078
|
+
lastComma = i;
|
|
55079
|
+
} else if (ch === "{" || ch === "[") {
|
|
55080
|
+
stack.push(ch);
|
|
55081
|
+
} else if (ch === "}" || ch === "]") {
|
|
55082
|
+
stack.pop();
|
|
55083
|
+
lastCloser = i;
|
|
55084
|
+
}
|
|
55085
|
+
}
|
|
55086
|
+
return { inString, stringStart, stack, lastComma, lastCloser };
|
|
55087
|
+
}
|
|
55088
|
+
|
|
55089
|
+
// src/lib/source.js
|
|
55090
|
+
var DEVIN_TIMEOUT_MS = 3e4;
|
|
55091
|
+
async function runDevinJson({ command = "devin", timeoutMs = DEVIN_TIMEOUT_MS } = {}) {
|
|
55092
|
+
const file = join2(tmpdir(), `devinp-models-${process.pid}-${Date.now()}.json`);
|
|
55093
|
+
const handle = await open(file, "w");
|
|
55094
|
+
try {
|
|
55095
|
+
const { code, stderr, timedOut } = await new Promise((resolve, reject) => {
|
|
55096
|
+
const child = spawn(command, ["models", "list", "--format", "json"], {
|
|
55097
|
+
stdio: ["ignore", handle.fd, "pipe"]
|
|
55098
|
+
});
|
|
55099
|
+
let stderr2 = "";
|
|
55100
|
+
let timedOut2 = false;
|
|
55101
|
+
const timer = setTimeout(() => {
|
|
55102
|
+
timedOut2 = true;
|
|
55103
|
+
child.kill("SIGKILL");
|
|
55104
|
+
}, timeoutMs);
|
|
55105
|
+
child.stderr.on("data", (chunk) => {
|
|
55106
|
+
stderr2 += chunk;
|
|
55107
|
+
});
|
|
55108
|
+
child.on("error", (error) => {
|
|
55109
|
+
clearTimeout(timer);
|
|
55110
|
+
reject(error);
|
|
55111
|
+
});
|
|
55112
|
+
child.on("close", (childCode) => {
|
|
55113
|
+
clearTimeout(timer);
|
|
55114
|
+
resolve({ code: childCode, stderr: stderr2, timedOut: timedOut2 });
|
|
55115
|
+
});
|
|
55116
|
+
});
|
|
55117
|
+
if (timedOut) {
|
|
55118
|
+
throw new Error(`\`devin models list\` timed out after ${timeoutMs / 1e3}s`);
|
|
55119
|
+
}
|
|
55120
|
+
if (code !== 0) {
|
|
55121
|
+
throw new Error(`\`devin models list\` failed \u2014 ${stderr.trim() || `exited with code ${code}`}`);
|
|
55122
|
+
}
|
|
55123
|
+
return await readFile2(file, "utf8");
|
|
55124
|
+
} finally {
|
|
55125
|
+
await handle.close();
|
|
55126
|
+
await unlink(file).catch(() => {
|
|
55127
|
+
});
|
|
55128
|
+
}
|
|
55129
|
+
}
|
|
55130
|
+
function assertFamilies(data, sourceName) {
|
|
55131
|
+
if (data == null || typeof data !== "object" || !Array.isArray(data.families)) {
|
|
55132
|
+
throw new Error(`Unable to get model prices: ${sourceName} has no "families" array (expected \`devin models list --format json\` output)`);
|
|
55133
|
+
}
|
|
55134
|
+
if (data.families.length === 0) {
|
|
55135
|
+
throw new Error(`Unable to get model prices: ${sourceName} contains no model families`);
|
|
55136
|
+
}
|
|
55137
|
+
return data;
|
|
55138
|
+
}
|
|
55139
|
+
async function loadModelData({ source, command = "devin", timeoutMs = DEVIN_TIMEOUT_MS } = {}) {
|
|
55031
55140
|
if (source) {
|
|
55032
|
-
let
|
|
55141
|
+
let raw2;
|
|
55033
55142
|
try {
|
|
55034
|
-
|
|
55143
|
+
raw2 = await readFile2(source, "utf8");
|
|
55035
55144
|
} catch (error) {
|
|
55036
55145
|
throw new Error(`Unable to get model prices: cannot read source file "${source}" (${error.code ?? error.message})`);
|
|
55037
55146
|
}
|
|
55038
55147
|
try {
|
|
55039
|
-
return JSON.parse(
|
|
55040
|
-
} catch {
|
|
55148
|
+
return { data: assertFamilies(JSON.parse(raw2), source) };
|
|
55149
|
+
} catch (error) {
|
|
55150
|
+
if (error.message.startsWith("Unable to get model prices")) throw error;
|
|
55041
55151
|
throw new Error(`Unable to get model prices: "${source}" is not valid JSON (expected \`devin models list --format json\` output)`);
|
|
55042
55152
|
}
|
|
55043
55153
|
}
|
|
55044
|
-
let
|
|
55154
|
+
let raw;
|
|
55045
55155
|
try {
|
|
55046
|
-
;
|
|
55047
|
-
({ stdout } = await execFileAsync("devin", ["models", "list", "--format", "json"], {
|
|
55048
|
-
maxBuffer: 64 * 1024 * 1024
|
|
55049
|
-
}));
|
|
55156
|
+
raw = await runDevinJson({ command, timeoutMs });
|
|
55050
55157
|
} catch (error) {
|
|
55051
55158
|
if (error.code === "ENOENT") {
|
|
55052
55159
|
throw new Error("Unable to get model prices: `devin` CLI not found in PATH. Install it or pass --source <file>.");
|
|
55053
55160
|
}
|
|
55054
|
-
|
|
55055
|
-
throw new Error(`Unable to get model prices: \`devin models list\` failed \u2014 ${detail}`);
|
|
55161
|
+
throw new Error(`Unable to get model prices: ${error.message}`);
|
|
55056
55162
|
}
|
|
55057
55163
|
try {
|
|
55058
|
-
return JSON.parse(
|
|
55059
|
-
} catch {
|
|
55060
|
-
|
|
55164
|
+
return { data: assertFamilies(JSON.parse(raw), "devin models list") };
|
|
55165
|
+
} catch (error) {
|
|
55166
|
+
if (error.message.startsWith("Unable to get model prices")) throw error;
|
|
55167
|
+
const recovered = recoverTruncatedJson(raw);
|
|
55168
|
+
if (recovered != null) {
|
|
55169
|
+
return { data: assertFamilies(recovered, "devin models list (recovered)"), truncated: true };
|
|
55170
|
+
}
|
|
55171
|
+
const tail = raw.trimEnd().slice(-200);
|
|
55172
|
+
throw new Error(
|
|
55173
|
+
`Unable to get model prices: \`devin models list --format json\` returned invalid JSON (${error.message} \u2014 ${raw.length} bytes, output ends with ${JSON.stringify(tail)}). The output may have been truncated mid-stream: re-run, or save the output with \`devin models list --format json > models.json\` and pass \`--source models.json\`.`
|
|
55174
|
+
);
|
|
55061
55175
|
}
|
|
55062
55176
|
}
|
|
55063
55177
|
|
|
@@ -55111,7 +55225,11 @@ var Devinp = class _Devinp extends import_core.Command {
|
|
|
55111
55225
|
const { flags } = await this.parse(_Devinp);
|
|
55112
55226
|
let data;
|
|
55113
55227
|
try {
|
|
55114
|
-
|
|
55228
|
+
const loaded = await loadModelData({ source: flags.source });
|
|
55229
|
+
if (loaded.truncated) {
|
|
55230
|
+
this.warn("devin output was truncated mid-stream \u2014 showing partial data, re-run for the full list");
|
|
55231
|
+
}
|
|
55232
|
+
data = loaded.data;
|
|
55115
55233
|
} catch (error) {
|
|
55116
55234
|
this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
|
|
55117
55235
|
}
|