@levr-one/cli 0.10.20 → 0.10.22

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.
Files changed (23) hide show
  1. package/dist/{addHandler-r0Yw1vu-.js → addHandler-DUulKXOX.js} +68 -68
  2. package/dist/appendHandler-u-NvHPDW.js +80 -0
  3. package/dist/artifact-upload-CF2iXUiR.js +296 -0
  4. package/dist/attachHandler-DgVCvhz7.js +151 -0
  5. package/dist/cli.js +237 -17
  6. package/dist/connect-DX41Im8c.js +36 -0
  7. package/dist/{currentHandler-BBHqC26D.js → currentHandler-B_sWXNeW.js} +1 -1
  8. package/dist/{importHandler-DAXXM42X.js → importHandler-CUUroX_n.js} +8 -8
  9. package/dist/{listHandler-qFOGgW2z.js → listHandler-CB_9cISi.js} +5 -5
  10. package/dist/{loginHandler-DMjKtKj5.js → loginHandler-DSznhtmk.js} +5 -10
  11. package/dist/{logoutHandler-BFutKash.js → logoutHandler-TSQlZGpU.js} +2 -2
  12. package/dist/{pushHandler-C7nxljuh.js → pushHandler-Q9Cbo2HC.js} +142 -9
  13. package/dist/{resolve-token-DbQsmn03.js → resolve-token-7xb7kG7h.js} +2 -2
  14. package/dist/{resolve-workspace--4K7N4VU.js → resolve-workspace-B3Gz3-Vh.js} +2 -2
  15. package/dist/{runHandler-gaYkPS79.js → runHandler-DmRAoyJc.js} +8 -8
  16. package/dist/{sdk-client-vQsoMCez.js → sdk-client-CTvYVr7D.js} +310 -70
  17. package/dist/{selectHandler-CPeH8ILF.js → selectHandler-CdOVuuTa.js} +5 -5
  18. package/dist/sleep-BpJ39Ypm.js +7 -0
  19. package/dist/{statusHandler-WkN652yu.js → statusHandler-p6CmwgjS.js} +3 -3
  20. package/dist/{token-refresh-Cu5RpkLJ.js → token-refresh-DeusLK8H.js} +1 -1
  21. package/package.json +1 -1
  22. /package/dist/{env-CHeKHu5S.js → env-CdwyPHGV.js} +0 -0
  23. /package/dist/{workspace-store-DDOxnut1.js → workspace-store-CnrxyYRB.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { getApiUrl } from "./env-CHeKHu5S.js";
1
+ import { getApiUrl } from "./env-CdwyPHGV.js";
2
2
  import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { basename, delimiter, dirname, join, resolve } from "node:path";
4
4
  import { homedir } from "node:os";
@@ -90,9 +90,9 @@ function renderTomlBodyLines(value, pathPrefix) {
90
90
  lines.push(`${renderTomlKey(key)} = ${renderTomlValue(v)}`);
91
91
  }
92
92
  for (const [key, v] of nested) {
93
- const path = [...pathPrefix, key];
94
- lines.push("", `[${path.map(renderTomlKey).join(".")}]`);
95
- lines.push(...renderTomlBodyLines(v, path));
93
+ const path$1 = [...pathPrefix, key];
94
+ lines.push("", `[${path$1.map(renderTomlKey).join(".")}]`);
95
+ lines.push(...renderTomlBodyLines(v, path$1));
96
96
  }
97
97
  return lines;
98
98
  }
@@ -970,17 +970,17 @@ function readServerEntry(harness, scope, configPath, cwdKey, parseJsonc) {
970
970
  return getAtPath(parseJsonc(text), entryPathFor(harness, scope, cwdKey));
971
971
  }
972
972
  /** Read a text file, or `null` if it doesn't exist / can't be read. */
973
- function readTextOrNull(path) {
973
+ function readTextOrNull(path$1) {
974
974
  try {
975
- return readFileSync(path, "utf8");
975
+ return readFileSync(path$1, "utf8");
976
976
  } catch {
977
977
  return null;
978
978
  }
979
979
  }
980
980
  /** Safe nested lookup over an unknown-typed parsed JSON value. */
981
- function getAtPath(obj, path) {
981
+ function getAtPath(obj, path$1) {
982
982
  let cur = obj;
983
- for (const key of path) {
983
+ for (const key of path$1) {
984
984
  if (cur === null || typeof cur !== "object") return void 0;
985
985
  cur = cur[key];
986
986
  }
@@ -1057,35 +1057,35 @@ function parseDocument$1(text) {
1057
1057
  }
1058
1058
  const jsoncAdapter = {
1059
1059
  empty: "{}",
1060
- readAt(text, path) {
1060
+ readAt(text, path$1) {
1061
1061
  if (text.trim() === "") return { kind: "absent" };
1062
1062
  const doc = parseDocument$1(text);
1063
1063
  if (!doc.ok) return {
1064
1064
  kind: "unsupported",
1065
1065
  detail: doc.detail
1066
1066
  };
1067
- const value = getAtPath(doc.root, path);
1067
+ const value = getAtPath(doc.root, path$1);
1068
1068
  return value === void 0 ? { kind: "absent" } : {
1069
1069
  kind: "value",
1070
1070
  value
1071
1071
  };
1072
1072
  },
1073
- setAt(text, path, value) {
1074
- return edit(text, path, value);
1073
+ setAt(text, path$1, value) {
1074
+ return edit(text, path$1, value);
1075
1075
  },
1076
- removeAt(text, path) {
1077
- return edit(text, path, void 0);
1076
+ removeAt(text, path$1) {
1077
+ return edit(text, path$1, void 0);
1078
1078
  }
1079
1079
  };
1080
1080
  /** Shared by set and remove: jsonc-parser removes a key when `value` is `undefined`. */
1081
- function edit(text, path, value) {
1081
+ function edit(text, path$1, value) {
1082
1082
  const doc = parseDocument$1(text);
1083
1083
  if (!doc.ok) throw new UnsupportedConfigShapeError(doc.detail);
1084
1084
  try {
1085
- return applyEdits(text, modify(text, path, value, { formattingOptions: FORMAT }));
1085
+ return applyEdits(text, modify(text, path$1, value, { formattingOptions: FORMAT }));
1086
1086
  } catch (err) {
1087
1087
  const message = err instanceof Error ? err.message : String(err);
1088
- throw new UnsupportedConfigShapeError(`cannot edit path ${path.join(".")}: ${message}`);
1088
+ throw new UnsupportedConfigShapeError(`cannot edit path ${path$1.join(".")}: ${message}`);
1089
1089
  }
1090
1090
  }
1091
1091
 
@@ -1267,8 +1267,8 @@ function probeHeader(doc, idx) {
1267
1267
  function samePath(a, b) {
1268
1268
  return a.length === b.length && a.every((s, i) => s === b[i]);
1269
1269
  }
1270
- function isSubPath(path, prefix) {
1271
- return path.length > prefix.length && prefix.every((s, i) => s === path[i]);
1270
+ function isSubPath(path$1, prefix) {
1271
+ return path$1.length > prefix.length && prefix.every((s, i) => s === path$1[i]);
1272
1272
  }
1273
1273
  /**
1274
1274
  * Locate our table: the header line index and the index just past its body.
@@ -1280,12 +1280,12 @@ function isSubPath(path, prefix) {
1280
1280
  * scanner cannot read, or one whose probe breaks the document, still ends
1281
1281
  * the body. Misjudging a line can only make our region smaller.
1282
1282
  */
1283
- function locateTable(doc, path) {
1283
+ function locateTable(doc, path$1) {
1284
1284
  const { lines } = doc;
1285
1285
  let start = -1;
1286
1286
  for (let i = 0; i < lines.length; i++) {
1287
1287
  const hp = headerPathOf(lines[i]);
1288
- if (hp && samePath(hp, path) && probeHeader(doc, i) === "real") {
1288
+ if (hp && samePath(hp, path$1) && probeHeader(doc, i) === "real") {
1289
1289
  start = i;
1290
1290
  break;
1291
1291
  }
@@ -1299,7 +1299,7 @@ function locateTable(doc, path) {
1299
1299
  const verdict = probeHeader(doc, i);
1300
1300
  if (verdict === "text") continue;
1301
1301
  const hp = headerPathOf(lines[i]);
1302
- if (hp && isSubPath(hp, path) && verdict === "real") continue;
1302
+ if (hp && isSubPath(hp, path$1) && verdict === "real") continue;
1303
1303
  end = i;
1304
1304
  break;
1305
1305
  }
@@ -1309,49 +1309,49 @@ function locateTable(doc, path) {
1309
1309
  end
1310
1310
  };
1311
1311
  }
1312
- function requireEditable(text, path) {
1312
+ function requireEditable(text, path$1) {
1313
1313
  const parsed = parseDocument(text);
1314
1314
  if (!parsed.ok) throw new UnsupportedConfigShapeError(parsed.detail);
1315
1315
  return {
1316
1316
  doc: splitLines(text),
1317
- exists: getAtPath(parsed.root, path) !== void 0
1317
+ exists: getAtPath(parsed.root, path$1) !== void 0
1318
1318
  };
1319
1319
  }
1320
- function notStandalone(path) {
1321
- return new UnsupportedConfigShapeError(`${path.join(".")} exists but not as a standalone [${path.join(".")}] table (inline table or dotted key) — refusing to restructure it`);
1320
+ function notStandalone(path$1) {
1321
+ return new UnsupportedConfigShapeError(`${path$1.join(".")} exists but not as a standalone [${path$1.join(".")}] table (inline table or dotted key) — refusing to restructure it`);
1322
1322
  }
1323
1323
  const tomlAdapter = {
1324
1324
  empty: "",
1325
1325
  backupSuffix: TOML_BACKUP_SUFFIX,
1326
- readAt(text, path) {
1326
+ readAt(text, path$1) {
1327
1327
  if (stripBom(text).trim() === "") return { kind: "absent" };
1328
1328
  const doc = parseDocument(text);
1329
1329
  if (!doc.ok) return {
1330
1330
  kind: "unsupported",
1331
1331
  detail: doc.detail
1332
1332
  };
1333
- const value = getAtPath(doc.root, path);
1333
+ const value = getAtPath(doc.root, path$1);
1334
1334
  return value === void 0 ? { kind: "absent" } : {
1335
1335
  kind: "value",
1336
1336
  value
1337
1337
  };
1338
1338
  },
1339
- setAt(text, path, value) {
1339
+ setAt(text, path$1, value) {
1340
1340
  if (value === null || typeof value !== "object" || Array.isArray(value)) throw new UnsupportedConfigShapeError(`a TOML table body must be an object, got ${value === null ? "null" : Array.isArray(value) ? "array" : typeof value}`);
1341
1341
  const body = value;
1342
1342
  const bom = text.startsWith(BOM) ? BOM : "";
1343
1343
  const inner = stripBom(text);
1344
- if (inner.trim() === "") return bom + renderTable(path, body, inner.includes("\r\n") ? "\r\n" : "\n");
1345
- const { doc, exists } = requireEditable(inner, path);
1346
- const table = locateTable(doc, path);
1344
+ if (inner.trim() === "") return bom + renderTable(path$1, body, inner.includes("\r\n") ? "\r\n" : "\n");
1345
+ const { doc, exists } = requireEditable(inner, path$1);
1346
+ const table = locateTable(doc, path$1);
1347
1347
  if (!table) {
1348
- if (exists) throw notStandalone(path);
1348
+ if (exists) throw notStandalone(path$1);
1349
1349
  const { eol } = doc;
1350
1350
  let base = inner.endsWith("\n") ? inner : inner + eol;
1351
1351
  if (!/\r?\n\r?\n$/.test(base)) base += eol;
1352
- return bom + base + renderTable(path, body, eol);
1352
+ return bom + base + renderTable(path$1, body, eol);
1353
1353
  }
1354
- const rendered = renderTomlBodyLines(body, path);
1354
+ const rendered = renderTomlBodyLines(body, path$1);
1355
1355
  const atEof = table.end === doc.lines.length;
1356
1356
  return bom + joinLines([
1357
1357
  ...doc.lines.slice(0, table.start + 1),
@@ -1363,14 +1363,14 @@ const tomlAdapter = {
1363
1363
  ...doc.eols.slice(table.end)
1364
1364
  ]);
1365
1365
  },
1366
- removeAt(text, path) {
1366
+ removeAt(text, path$1) {
1367
1367
  const bom = text.startsWith(BOM) ? BOM : "";
1368
1368
  const inner = stripBom(text);
1369
1369
  if (inner.trim() === "") return text;
1370
- const { doc, exists } = requireEditable(inner, path);
1370
+ const { doc, exists } = requireEditable(inner, path$1);
1371
1371
  if (!exists) return text;
1372
- const table = locateTable(doc, path);
1373
- if (!table) throw notStandalone(path);
1372
+ const table = locateTable(doc, path$1);
1373
+ if (!table) throw notStandalone(path$1);
1374
1374
  const lines = [...doc.lines.slice(0, table.start), ...doc.lines.slice(table.end)];
1375
1375
  const eols = [...doc.eols.slice(0, table.start), ...doc.eols.slice(table.end)];
1376
1376
  const at = table.start;
@@ -1386,8 +1386,8 @@ const tomlAdapter = {
1386
1386
  return bom + joinLines(lines, eols);
1387
1387
  }
1388
1388
  };
1389
- function renderTable(path, body, eol) {
1390
- return [`[${path.map(renderTomlKey).join(".")}]`, ...renderTomlBodyLines(body, path)].join(eol) + eol;
1389
+ function renderTable(path$1, body, eol) {
1390
+ return [`[${path$1.map(renderTomlKey).join(".")}]`, ...renderTomlBodyLines(body, path$1)].join(eol) + eol;
1391
1391
  }
1392
1392
 
1393
1393
  //#endregion
@@ -1565,9 +1565,9 @@ function resolveTarget(harness, env, scope) {
1565
1565
  reason: "scope-collision",
1566
1566
  collidesWith: collides
1567
1567
  };
1568
- const path = resolveConfigPath(harness, env, scope);
1569
- if (!path) return { reason: "no-location" };
1570
- return { path };
1568
+ const path$1 = resolveConfigPath(harness, env, scope);
1569
+ if (!path$1) return { reason: "no-location" };
1570
+ return { path: path$1 };
1571
1571
  }
1572
1572
  /**
1573
1573
  * What the client currently holds for this scope, read from its own config.
@@ -1683,36 +1683,36 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1683
1683
  reason: target.reason,
1684
1684
  ...target.collidesWith ? { collidesWith: target.collidesWith } : {}
1685
1685
  };
1686
- const { path } = target;
1686
+ const { path: path$1 } = target;
1687
1687
  const entryValue = buildServerEntry(harness, mcpUrl, scope)[SERVER_NAME];
1688
1688
  const modPath = entryPathFor(harness, scope, env.cwd);
1689
1689
  const adapter = adapterFor(harness);
1690
- const existing = readTextOrNull(path);
1690
+ const existing = readTextOrNull(path$1);
1691
1691
  const baseText = existing && existing.trim() ? existing : adapter.empty;
1692
1692
  const current = adapter.readAt(baseText, modPath);
1693
- if (current.kind === "unsupported") return unsupportedShape(path, dryRun, scope, current.detail);
1693
+ if (current.kind === "unsupported") return unsupportedShape(path$1, dryRun, scope, current.detail);
1694
1694
  const alreadyConfigured = current.kind === "value" && sameValue(current.value, entryValue);
1695
1695
  let nextText;
1696
1696
  try {
1697
1697
  nextText = adapter.setAt(baseText, modPath, entryValue);
1698
1698
  } catch (err) {
1699
- if (err instanceof UnsupportedConfigShapeError) return unsupportedShape(path, dryRun, scope, err.detail);
1699
+ if (err instanceof UnsupportedConfigShapeError) return unsupportedShape(path$1, dryRun, scope, err.detail);
1700
1700
  throw err;
1701
1701
  }
1702
1702
  if (alreadyConfigured) return {
1703
1703
  ok: true,
1704
1704
  wrote: false,
1705
- path,
1705
+ path: path$1,
1706
1706
  alreadyConfigured: true,
1707
1707
  dryRun,
1708
1708
  scope,
1709
1709
  preview: nextText
1710
1710
  };
1711
- const backupPath = backupPathFor(path, adapter.backupSuffix);
1711
+ const backupPath = backupPathFor(path$1, adapter.backupSuffix);
1712
1712
  if (dryRun) return {
1713
1713
  ok: true,
1714
1714
  wrote: false,
1715
- path,
1715
+ path: path$1,
1716
1716
  alreadyConfigured: false,
1717
1717
  dryRun: true,
1718
1718
  scope,
@@ -1721,16 +1721,16 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1721
1721
  };
1722
1722
  const finalText = nextText.endsWith("\n") ? nextText : `${nextText}\n`;
1723
1723
  try {
1724
- mkdirSync(dirname(path), { recursive: true });
1725
- if (backupPath) backupOnce(path, backupPath);
1726
- writeConfigAtomic(path, finalText);
1724
+ mkdirSync(dirname(path$1), { recursive: true });
1725
+ if (backupPath) backupOnce(path$1, backupPath);
1726
+ writeConfigAtomic(path$1, finalText);
1727
1727
  } catch (err) {
1728
- return writeFailed(path, scope, err);
1728
+ return writeFailed(path$1, scope, err);
1729
1729
  }
1730
1730
  return {
1731
1731
  ok: true,
1732
1732
  wrote: true,
1733
- path,
1733
+ path: path$1,
1734
1734
  alreadyConfigured: false,
1735
1735
  dryRun: false,
1736
1736
  scope,
@@ -1743,21 +1743,21 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1743
1743
  * asks for one — `undefined` when the format does not, or when there is no
1744
1744
  * original to protect yet.
1745
1745
  */
1746
- function backupPathFor(path, suffix) {
1747
- if (!suffix || !existsSync(path)) return void 0;
1748
- return `${path}${suffix}`;
1746
+ function backupPathFor(path$1, suffix) {
1747
+ if (!suffix || !existsSync(path$1)) return void 0;
1748
+ return `${path$1}${suffix}`;
1749
1749
  }
1750
1750
  /** Copy the original once. An existing backup is never overwritten. */
1751
- function backupOnce(path, backupPath) {
1751
+ function backupOnce(path$1, backupPath) {
1752
1752
  if (existsSync(backupPath)) return;
1753
- copyFileSync(path, backupPath);
1753
+ copyFileSync(path$1, backupPath);
1754
1754
  }
1755
1755
  /** The refusal every format shares: the file is there, and we will not touch it. */
1756
- function unsupportedShape(path, dryRun, scope, detail) {
1756
+ function unsupportedShape(path$1, dryRun, scope, detail) {
1757
1757
  return {
1758
1758
  ok: false,
1759
1759
  wrote: false,
1760
- path,
1760
+ path: path$1,
1761
1761
  alreadyConfigured: false,
1762
1762
  dryRun,
1763
1763
  scope,
@@ -1766,11 +1766,11 @@ function unsupportedShape(path, dryRun, scope, detail) {
1766
1766
  };
1767
1767
  }
1768
1768
  /** The filesystem said no: report it per client instead of aborting the run. */
1769
- function writeFailed(path, scope, err) {
1769
+ function writeFailed(path$1, scope, err) {
1770
1770
  return {
1771
1771
  ok: false,
1772
1772
  wrote: false,
1773
- path,
1773
+ path: path$1,
1774
1774
  alreadyConfigured: false,
1775
1775
  dryRun: false,
1776
1776
  scope,
@@ -1788,11 +1788,11 @@ function writeFailed(path, scope, err) {
1788
1788
  * This matters more than it did: a harness config can be the file that also
1789
1789
  * holds a user's model providers, profiles and sandbox policy (review F-005).
1790
1790
  */
1791
- function writeConfigAtomic(path, text) {
1792
- let target = path;
1791
+ function writeConfigAtomic(path$1, text) {
1792
+ let target = path$1;
1793
1793
  let mode;
1794
1794
  try {
1795
- target = realpathSync(path);
1795
+ target = realpathSync(path$1);
1796
1796
  mode = statSync(target).mode & 511;
1797
1797
  } catch {}
1798
1798
  const tmp = join(dirname(target), `.${basename(target)}.${process.pid}.tmp`);
@@ -0,0 +1,80 @@
1
+ import { appendActualResult } from "./sdk-client-CTvYVr7D.js";
2
+ import "./env-CdwyPHGV.js";
3
+ import "./token-refresh-DeusLK8H.js";
4
+ import "./resolve-token-7xb7kG7h.js";
5
+ import "./workspace-store-CnrxyYRB.js";
6
+ import "./resolve-workspace-B3Gz3-Vh.js";
7
+ import { connect } from "./connect-DX41Im8c.js";
8
+ import "./sleep-BpJ39Ypm.js";
9
+ import { embedMarkdown, parseTarget, uploadArtifactFile } from "./artifact-upload-CF2iXUiR.js";
10
+ import { readFileSync } from "node:fs";
11
+ import { basename } from "node:path";
12
+
13
+ //#region src/commands/result/appendHandler.ts
14
+ /**
15
+ * `levr result append` (ENG-6164 D4, DD4). Uploads the --attach files first
16
+ * (private: Levr serves their url to signed-in members, so the embedded
17
+ * links render there; ENG-6165 code review F-006), then sends ONE server-side
18
+ * `append_actual_result` carrying the text plus a markdown link per file,
19
+ * through PATCH /v1/run/run-result-variant/:id — the only public append that
20
+ * needs no run or run-result id (audit A2). fail_closed: any failed upload
21
+ * or a failed append exits 1; nothing is appended when an upload failed.
22
+ */
23
+ async function resultAppendHandler(flags, targetArg) {
24
+ if (flags.verbose) this.logger.setVerbose(true);
25
+ let target;
26
+ try {
27
+ target = parseTarget(targetArg);
28
+ } catch (err) {
29
+ this.logger.error(err instanceof Error ? err.message : String(err));
30
+ this.process.exitCode = 1;
31
+ return;
32
+ }
33
+ if (target.relatedType !== "run_result_variant") {
34
+ this.logger.error("levr result append takes run_result_variant:<uuid> (an execution result id).");
35
+ this.process.exitCode = 1;
36
+ return;
37
+ }
38
+ if (flags.text === void 0 === (flags.file === void 0)) {
39
+ this.logger.error("Provide exactly one of --text or --file.");
40
+ this.process.exitCode = 1;
41
+ return;
42
+ }
43
+ let text;
44
+ try {
45
+ text = flags.text ?? readFileSync(flags.file, "utf8");
46
+ } catch (err) {
47
+ this.logger.error(`Cannot read ${flags.file}: ${err instanceof Error ? err.message : String(err)}`);
48
+ this.process.exitCode = 1;
49
+ return;
50
+ }
51
+ if (!await connect(this, flags["workspace-id"])) return;
52
+ const links = [];
53
+ for (const file of flags.attach ?? []) {
54
+ const result = await uploadArtifactFile({
55
+ target,
56
+ filePath: file
57
+ });
58
+ if ((result.status === "uploaded" || result.status === "deduplicated") && result.url) {
59
+ links.push(embedMarkdown(basename(file), result.url));
60
+ this.process.stdout.write(` attached ${file} (${result.id})\n`);
61
+ continue;
62
+ }
63
+ const why = result.status === "skipped" ? "larger than 50 MiB" : result.status === "missing" ? "not found" : "message" in result ? result.message : result.status;
64
+ this.logger.error(`failed ${file}: ${why}`);
65
+ this.process.exitCode = 1;
66
+ return;
67
+ }
68
+ const appendText = [text.trimEnd(), ...links].filter((t) => t).join("\n");
69
+ try {
70
+ await appendActualResult(target.relatedId, appendText);
71
+ } catch (err) {
72
+ this.logger.error(err instanceof Error ? err.message : String(err));
73
+ this.process.exitCode = 1;
74
+ return;
75
+ }
76
+ this.process.stdout.write(`Appended to ${target.relatedId}${links.length ? ` with ${links.length} attachment(s)` : ""}.\n`);
77
+ }
78
+
79
+ //#endregion
80
+ export { resultAppendHandler };