@levr-one/cli 0.11.5 → 0.11.7

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.
@@ -642,6 +642,8 @@ const HARNESSES = [
642
642
  docsUrl: "https://github.com/openai/codex",
643
643
  verifiedVersion: "0.154.0",
644
644
  verifiedOn: "2026-09-17",
645
+ configDirEnv: "CODEX_HOME",
646
+ configDirReplaces: "~/.codex",
645
647
  detectSignals: [
646
648
  {
647
649
  platform: "darwin",
@@ -771,6 +773,41 @@ function buildServerEntry(harness, mcpUrl, scope = defaultScope(harness), server
771
773
  args
772
774
  } };
773
775
  }
776
+ /** The workspace a scoped MCP URL names (`…/w/<url_key>`), if any. */
777
+ function workspaceKeyOf(url) {
778
+ const m = /\/w\/([^/]+)$/.exec(url.replace(/\/+$/, ""));
779
+ if (!m?.[1]) return void 0;
780
+ try {
781
+ return decodeURIComponent(m[1]);
782
+ } catch {
783
+ return m[1];
784
+ }
785
+ }
786
+ /**
787
+ * Allowed client entry names: 1-64 of `A-Za-z0-9_-`, and never starting with
788
+ * `-`. The name is passed as an argv element to the client's own CLI
789
+ * (`claude mcp add … <name> <url>`), so a leading `-` would be read there as a
790
+ * flag — the ENG-4159 argument-injection family.
791
+ */
792
+ const ENTRY_NAME = /^[A-Za-z0-9_-]{1,64}$/;
793
+ function isValidEntryName(name) {
794
+ return ENTRY_NAME.test(name) && !name.startsWith("-");
795
+ }
796
+ /** Claude Code tool names are `mcp__<entry>__<tool>`; keep that well short. */
797
+ const BESIDE_NAME_MAX = 30;
798
+ /**
799
+ * The name for a second entry beside `serverName`: `<serverName>-<url_key>`,
800
+ * so it says which workspace it is. Undefined — and "Add beside it" is not
801
+ * offered — when the target URL names no workspace, or when that name would
802
+ * be too long or not a valid entry name. It is never shortened: a cut name
803
+ * could drop the workspace, or give two workspaces the same entry.
804
+ */
805
+ function besideName(serverName, url) {
806
+ const key = workspaceKeyOf(url);
807
+ if (!key) return void 0;
808
+ const name = `${serverName}-${key}`;
809
+ return name.length <= BESIDE_NAME_MAX && isValidEntryName(name) ? name : void 0;
810
+ }
774
811
 
775
812
  //#endregion
776
813
  //#region ../mcp-harnesses/dist/node/paths.js
@@ -784,14 +821,20 @@ function defaultEnv() {
784
821
  };
785
822
  }
786
823
  /**
787
- * The directory `~` means for this harness's config paths and signals: the
788
- * harness's `configDirEnv` when that variable is set, else the home directory
789
- * (ENG-6696). A relative value resolves against `env.cwd`, the directory a
790
- * spawned client CLI runs in, so the probe and the client read one file.
824
+ * A config path or signal with `~` expanded. When the harness's `configDirEnv`
825
+ * is set to a non-empty value, the prefix it stands for (`configDirReplaces`,
826
+ * `~` by default) resolves to that directory instead (ENG-6696, ENG-6761). A
827
+ * relative value resolves against `env.cwd`, the directory a spawned client
828
+ * CLI runs in, so the probe and the client read one file.
791
829
  */
792
- function configHome(harness, env) {
830
+ function expandConfigPath(p, harness, env) {
793
831
  const dir = harness.configDirEnv ? env.vars?.[harness.configDirEnv] : void 0;
794
- return dir ? resolve(env.cwd, dir) : env.homedir;
832
+ if (dir) {
833
+ const prefix = harness.configDirReplaces ?? "~";
834
+ if (p === prefix) return resolve(env.cwd, dir);
835
+ if (p.startsWith(`${prefix}/`) || p.startsWith(`${prefix}\\`)) return join(resolve(env.cwd, dir), p.slice(prefix.length + 1));
836
+ }
837
+ return expandTilde(p, env.homedir);
795
838
  }
796
839
  /** Expand a leading `~` / `~/…` to the given home directory. */
797
840
  function expandTilde(p, home) {
@@ -822,7 +865,7 @@ function resolveConfigPath(harness, env, scope = defaultScope(harness)) {
822
865
  if (def.projectPath) return join(resolveProjectRoot(env).root, ...def.projectPath.split("/"));
823
866
  const loc = locationFor(harness, env.platform, scope);
824
867
  if (!loc) return void 0;
825
- return expandTilde(loc.configPath, configHome(harness, env));
868
+ return expandConfigPath(loc.configPath, harness, env);
826
869
  }
827
870
  /**
828
871
  * Nearest ancestor of `cwd` containing `.git`, or `undefined`.
@@ -1024,11 +1067,12 @@ function whichSync(bin, env) {
1024
1067
  return whichPathSync(bin, env) !== void 0;
1025
1068
  }
1026
1069
  /** Does one `installSignals` entry match on this machine? Supports
1027
- * `which:<bin>`, `~`-prefixed paths, and absolute (app-bundle) paths. `home`
1028
- * is what `~` means for the harness — see {@link configHome}. */
1029
- function signalMatches(signal, env, home = env.homedir) {
1070
+ * `which:<bin>`, `~`-prefixed paths, and absolute (app-bundle) paths.
1071
+ * `expand` turns a `~` path into the one this harness reads — see
1072
+ * {@link expandConfigPath}. */
1073
+ function signalMatches(signal, env, expand = (p) => expandTilde(p, env.homedir)) {
1030
1074
  if (signal.startsWith("which:")) return whichSync(signal.slice(6), env);
1031
- return existsSync(signal.startsWith("~") ? expandTilde(signal, home) : signal);
1075
+ return existsSync(signal.startsWith("~") ? expand(signal) : signal);
1032
1076
  }
1033
1077
  /**
1034
1078
  * The value our server key currently holds for this scope, or `undefined`.
@@ -1541,7 +1585,7 @@ function detectScope(harness, env, def, opts) {
1541
1585
  function detectOne(harness, env, opts) {
1542
1586
  const scopes = harness.scopes.map((def) => detectScope(harness, env, def, opts));
1543
1587
  const available = scopes.some((s) => s.available);
1544
- const installed = detectSignalsFor(harness, env.platform).some((s) => signalMatches(s, env, configHome(harness, env))) || scopes.some((s) => s.configPath !== "" && existsSync(s.configPath));
1588
+ const installed = detectSignalsFor(harness, env.platform).some((s) => signalMatches(s, env, (p) => expandConfigPath(p, harness, env))) || scopes.some((s) => s.configPath !== "" && existsSync(s.configPath));
1545
1589
  const fallback = scopes.find((s) => s.scope === defaultScope(harness));
1546
1590
  return {
1547
1591
  id: harness.id,
@@ -1602,47 +1646,44 @@ function needsShell(binPath) {
1602
1646
  */
1603
1647
  const SHELL_METACHARACTERS = /[&|<>^"%!`]/;
1604
1648
  /**
1605
- * Run a harness CLI invocation, or explain why it was not run.
1649
+ * Everything decided before a command is spawned, shared by both runners: an
1650
+ * outcome when it must not run, else how to run it.
1606
1651
  *
1607
1652
  * Resolution uses {@link whichPathSync} — a pure `PATH` scan that spawns
1608
1653
  * nothing, so the "is this installed" check can never itself be the thing
1609
1654
  * that hangs. It returns the resolved path rather than a boolean because the
1610
1655
  * extension decides how the command must be launched.
1611
1656
  */
1612
- function runHarnessCommandSync(argv, opts = {}) {
1657
+ function prepare(argv, opts) {
1613
1658
  const env = opts.env ?? defaultEnv();
1614
1659
  const dryRun = opts.dryRun ?? false;
1615
1660
  const [bin, ...args] = argv;
1616
- if (!bin) return {
1661
+ if (!bin) return { skip: {
1617
1662
  executed: false,
1618
1663
  ok: false,
1619
1664
  reason: "empty-command"
1620
- };
1621
- if (dryRun) return {
1665
+ } };
1666
+ if (dryRun) return { skip: {
1622
1667
  executed: false,
1623
1668
  ok: true,
1624
1669
  reason: "dry-run"
1625
- };
1670
+ } };
1626
1671
  const binPath = whichPathSync(bin, env);
1627
- if (!binPath) return {
1672
+ if (!binPath) return { skip: {
1628
1673
  executed: false,
1629
1674
  ok: true,
1630
1675
  reason: "binary-not-found"
1631
- };
1676
+ } };
1632
1677
  const useShell = needsShell(binPath);
1633
- if (useShell && args.some((a) => SHELL_METACHARACTERS.test(a))) return {
1678
+ if (useShell && (binPath.includes("\"") || args.some((a) => SHELL_METACHARACTERS.test(a)))) return { skip: {
1634
1679
  executed: false,
1635
1680
  ok: false,
1636
1681
  reason: "unsafe-argument"
1637
- };
1638
- try {
1639
- execFileSync(binPath, args, {
1640
- encoding: "utf8",
1641
- stdio: [
1642
- "ignore",
1643
- "pipe",
1644
- "pipe"
1645
- ],
1682
+ } };
1683
+ return { spawn: {
1684
+ binPath: useShell ? `"${binPath}"` : binPath,
1685
+ args,
1686
+ options: {
1646
1687
  timeout: COMMAND_TIMEOUT_MS,
1647
1688
  env: {
1648
1689
  ...childVars(env),
@@ -1651,20 +1692,42 @@ function runHarnessCommandSync(argv, opts = {}) {
1651
1692
  },
1652
1693
  cwd: env.cwd,
1653
1694
  ...useShell ? { shell: true } : {}
1695
+ }
1696
+ } };
1697
+ }
1698
+ /** A command that ran and failed (or could not start), as an outcome. */
1699
+ function failureOutcome(err) {
1700
+ const e = err;
1701
+ const exitCode = typeof e.status === "number" ? e.status : typeof e.code === "number" ? e.code : void 0;
1702
+ const raw = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString("utf8") ?? "";
1703
+ return {
1704
+ executed: true,
1705
+ ok: false,
1706
+ ...exitCode === void 0 ? {} : { exitCode },
1707
+ stderr: (raw.trim() || e.message || "command failed").slice(0, MAX_STDERR)
1708
+ };
1709
+ }
1710
+ /** Run a harness CLI invocation, or explain why it was not run. */
1711
+ function runHarnessCommandSync(argv, opts = {}) {
1712
+ const prepared = prepare(argv, opts);
1713
+ if ("skip" in prepared) return prepared.skip;
1714
+ const { binPath, args, options } = prepared.spawn;
1715
+ try {
1716
+ execFileSync(binPath, args, {
1717
+ ...options,
1718
+ encoding: "utf8",
1719
+ stdio: [
1720
+ "ignore",
1721
+ "pipe",
1722
+ "pipe"
1723
+ ]
1654
1724
  });
1655
1725
  return {
1656
1726
  executed: true,
1657
1727
  ok: true
1658
1728
  };
1659
1729
  } catch (err) {
1660
- const e = err;
1661
- const raw = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString("utf8") ?? "";
1662
- return {
1663
- executed: true,
1664
- ok: false,
1665
- ...typeof e.status === "number" ? { exitCode: e.status } : {},
1666
- stderr: (raw.trim() || e.message || "command failed").slice(0, MAX_STDERR)
1667
- };
1730
+ return failureOutcome(err);
1668
1731
  }
1669
1732
  }
1670
1733
 
@@ -1736,8 +1799,16 @@ function canonical(value) {
1736
1799
  if (value !== null && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).sort(([x], [y]) => x < y ? -1 : x > y ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${canonical(v)}`).join(",")}}`;
1737
1800
  return JSON.stringify(value) ?? "undefined";
1738
1801
  }
1802
+ function drive(steps, run) {
1803
+ let step = steps.next();
1804
+ while (!step.done) step = steps.next(run(step.value.argv, step.value.opts));
1805
+ return step.value;
1806
+ }
1739
1807
  /** Merge (or preview) our MCP into a harness config. */
1740
1808
  function installHarnessSync(harness, mcpUrl, opts = {}) {
1809
+ return drive(installSteps(harness, mcpUrl, opts), opts.runCommand ?? runHarnessCommandSync);
1810
+ }
1811
+ function* installSteps(harness, mcpUrl, opts) {
1741
1812
  const env = opts.env ?? defaultEnv();
1742
1813
  const dryRun = opts.dryRun ?? false;
1743
1814
  const scope = opts.scope ?? defaultScope(harness);
@@ -1776,13 +1847,12 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1776
1847
  alreadyConfigured: false,
1777
1848
  reason: "unrecognized-entry"
1778
1849
  };
1779
- if (opts.replaceExisting) return replaceViaCommand(harness, argv, existing$1.url, {
1850
+ if (opts.replaceExisting) return yield* replaceSteps(harness, argv, existing$1.url, {
1780
1851
  env,
1781
1852
  dryRun,
1782
1853
  scope,
1783
1854
  serverName,
1784
- execute: opts.execute ?? true,
1785
- run: opts.runCommand ?? runHarnessCommandSync
1855
+ execute: opts.execute ?? true
1786
1856
  });
1787
1857
  return {
1788
1858
  ...base,
@@ -1793,11 +1863,13 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1793
1863
  ...existing$1.url ? { currentUrl: existing$1.url } : {}
1794
1864
  };
1795
1865
  }
1796
- const run = opts.runCommand ?? runHarnessCommandSync;
1797
- const outcome = opts.execute ?? true ? run(argv, {
1798
- env,
1799
- dryRun
1800
- }) : {
1866
+ const outcome = opts.execute ?? true ? yield {
1867
+ argv,
1868
+ opts: {
1869
+ env,
1870
+ dryRun
1871
+ }
1872
+ } : {
1801
1873
  executed: false,
1802
1874
  ok: true,
1803
1875
  reason: "not-requested"
@@ -1806,7 +1878,7 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1806
1878
  ...base,
1807
1879
  ok: outcome.ok,
1808
1880
  executed: outcome.executed,
1809
- ...outcome.ok ? {} : { commandError: outcome.stderr },
1881
+ ...outcome.ok ? {} : { commandError: notRunError(outcome) },
1810
1882
  alreadyConfigured: false
1811
1883
  };
1812
1884
  }
@@ -1898,7 +1970,14 @@ function installHarnessSync(harness, mcpUrl, opts = {}) {
1898
1970
  * second command must not leave the user with no entry at all without saying
1899
1971
  * so.
1900
1972
  */
1901
- function replaceViaCommand(harness, addArgv, previousUrl, ctx) {
1973
+ /**
1974
+ * What went wrong with a failed command: its stderr, else why it was never
1975
+ * run — so a refusal never reads as "unknown reason" (ENG-6737).
1976
+ */
1977
+ function notRunError(outcome) {
1978
+ return outcome.stderr ?? (outcome.reason ? `not run: ${outcome.reason}` : void 0);
1979
+ }
1980
+ function* replaceSteps(harness, addArgv, previousUrl, ctx) {
1902
1981
  const removeArgv = removeCommandArgv(ctx.scope, ctx.serverName);
1903
1982
  const base = {
1904
1983
  wrote: false,
@@ -1914,11 +1993,15 @@ function replaceViaCommand(harness, addArgv, previousUrl, ctx) {
1914
1993
  ok: true,
1915
1994
  executed: false
1916
1995
  };
1917
- const removed = ctx.run(removeArgv, { env: ctx.env });
1996
+ const removed = yield {
1997
+ argv: removeArgv,
1998
+ opts: { env: ctx.env }
1999
+ };
1918
2000
  if (!removed.executed) return {
1919
2001
  ...base,
1920
2002
  ok: removed.ok,
1921
- executed: false
2003
+ executed: false,
2004
+ ...removed.ok ? {} : { commandError: notRunError(removed) }
1922
2005
  };
1923
2006
  if (!removed.ok) return {
1924
2007
  ...base,
@@ -1926,21 +2009,26 @@ function replaceViaCommand(harness, addArgv, previousUrl, ctx) {
1926
2009
  executed: true,
1927
2010
  commandError: `could not remove the existing entry: ${removed.stderr ?? "unknown error"}`
1928
2011
  };
1929
- const added = ctx.run(addArgv, { env: ctx.env });
2012
+ const added = yield {
2013
+ argv: addArgv,
2014
+ opts: { env: ctx.env }
2015
+ };
1930
2016
  if (added.ok) return {
1931
2017
  ...base,
1932
2018
  ok: true,
1933
2019
  executed: true
1934
2020
  };
1935
- const restoreArgv = renderHarnessCommand(harness, previousUrl, ctx.scope, ctx.serverName);
1936
- const restore = ctx.run(restoreArgv, { env: ctx.env });
2021
+ const restore = yield {
2022
+ argv: renderHarnessCommand(harness, previousUrl, ctx.scope, ctx.serverName),
2023
+ opts: { env: ctx.env }
2024
+ };
1937
2025
  return {
1938
2026
  ...base,
1939
2027
  ok: false,
1940
2028
  executed: true,
1941
- commandError: added.stderr ?? "unknown error",
2029
+ commandError: notRunError(added) ?? "unknown error",
1942
2030
  restored: restore.ok && restore.executed,
1943
- ...restore.ok && restore.executed ? {} : { restoreError: restore.stderr ?? "the restore command did not run" }
2031
+ ...restore.ok && restore.executed ? {} : { restoreError: notRunError(restore) ?? "the restore command did not run" }
1944
2032
  };
1945
2033
  }
1946
2034
  /**
@@ -2102,16 +2190,6 @@ function servedBySessionApi(mcpUrl) {
2102
2190
  }
2103
2191
  /** `…/v1/mcp/w/<url_key>`: the URL already names one workspace (ENG-6300). */
2104
2192
  const SCOPED_SUFFIX = /\/w\/[^/]+$/;
2105
- /** The workspace a scoped MCP URL names (`…/w/<url_key>`), if any. */
2106
- function workspaceKeyOf(url) {
2107
- const m = /\/w\/([^/]+)$/.exec(stripSlash(url));
2108
- if (!m?.[1]) return void 0;
2109
- try {
2110
- return decodeURIComponent(m[1]);
2111
- } catch {
2112
- return m[1];
2113
- }
2114
- }
2115
2193
  /** Does this MCP URL already name a workspace? */
2116
2194
  function isScopedMcpUrl(url) {
2117
2195
  return SCOPED_SUFFIX.test(stripSlash(url));
@@ -2179,16 +2257,6 @@ function entryState(s, url) {
2179
2257
  return s.currentUrl === void 0 ? "unrecognized" : "other-url";
2180
2258
  }
2181
2259
  /**
2182
- * Allowed client entry names: 1-64 of `A-Za-z0-9_-`, and never starting with
2183
- * `-`. The name is passed as an argv element to the client's own CLI
2184
- * (`claude mcp add … <name> <url>`), so a leading `-` would be read there as a
2185
- * flag — the ENG-4159 argument-injection family.
2186
- */
2187
- const ENTRY_NAME = /^[A-Za-z0-9_-]{1,64}$/;
2188
- function isValidEntryName(name) {
2189
- return ENTRY_NAME.test(name) && !name.startsWith("-");
2190
- }
2191
- /**
2192
2260
  * The scope an install would land in: the requested one, or, for a harness
2193
2261
  * that cannot do it, its own default, which is where the fallback goes.
2194
2262
  */
@@ -2252,21 +2320,6 @@ function clientChoices(detected, scope, url, serverName = DEFAULT_SERVER_NAME) {
2252
2320
  };
2253
2321
  });
2254
2322
  }
2255
- /** Claude Code tool names are `mcp__<entry>__<tool>`; keep that well short. */
2256
- const BESIDE_NAME_MAX = 30;
2257
- /**
2258
- * The name for a second entry beside `serverName`: `<serverName>-<url_key>`,
2259
- * so it says which workspace it is. Undefined — and "Add beside it" is not
2260
- * offered — when the target URL names no workspace, or when that name would
2261
- * be too long or not a valid entry name. It is never shortened: a cut name
2262
- * could drop the workspace, or give two workspaces the same entry.
2263
- */
2264
- function besideName(serverName, url) {
2265
- const key = workspaceKeyOf(url);
2266
- if (!key) return void 0;
2267
- const name = `${serverName}-${key}`;
2268
- return name.length <= BESIDE_NAME_MAX && isValidEntryName(name) ? name : void 0;
2269
- }
2270
2323
  /**
2271
2324
  * The selected clients whose entry is not this URL's: each needs a decision —
2272
2325
  * switch it, add beside it, or leave it — before anything is installed
package/dist/cli.js CHANGED
@@ -209,7 +209,7 @@ Examples:
209
209
  aliases: { y: "yes" }
210
210
  },
211
211
  loader: async () => {
212
- const { mcpAddHandler } = await import("./addHandler-BL1e6anD.js");
212
+ const { mcpAddHandler } = await import("./addHandler-BqLLDeKp.js");
213
213
  return mcpAddHandler;
214
214
  }
215
215
  });
@@ -876,7 +876,7 @@ Examples:
876
876
 
877
877
  //#endregion
878
878
  //#region package.json
879
- var version = "0.11.5";
879
+ var version = "0.11.7";
880
880
 
881
881
  //#endregion
882
882
  //#region src/app.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levr-one/cli",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "description": "The command-line interface for Levr",
5
5
  "type": "module",
6
6
  "license": "MIT",