@openape/apes 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -2702,23 +2702,9 @@ chown root:wheel ${shQuote(bridge.plistPath)}
2702
2702
  chmod 644 ${shQuote(bridge.plistPath)}
2703
2703
  `;
2704
2704
  }
2705
- function buildBridgeBootstrapBlock(bridge, name) {
2705
+ function buildBridgeBootstrapBlock(bridge, _name) {
2706
2706
  if (!bridge) return "";
2707
2707
  return `
2708
- echo "==> Installing bridge stack as ${name} via bun (one-time)\u2026"
2709
- su - ${shQuote(name)} -c '
2710
- set -euo pipefail
2711
- if ! command -v bun >/dev/null 2>&1 && [ ! -x "$HOME/.bun/bin/bun" ]; then
2712
- echo "==> bun not found \u2014 installing via official installer"
2713
- curl -fsSL https://bun.sh/install | bash
2714
- fi
2715
- export PATH="$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:$HOME/.bun/install/global/bin"
2716
- bun add -g @openape/chat-bridge @openape/apes
2717
- '
2718
-
2719
- # Bootstrap into the system domain. Spawn already runs as root via
2720
- # \`apes run --as root\`, so we have permission. Stale label is bootouted
2721
- # first to make re-spawn idempotent.
2722
2708
  launchctl bootout "system/${bridge.plistLabel}" 2>/dev/null || true
2723
2709
  launchctl bootstrap system ${shQuote(bridge.plistPath)} || \\
2724
2710
  echo "warn: bridge bootstrap into system domain failed; check ${bridge.plistPath}"
@@ -4135,7 +4121,7 @@ async function handleInbound(msg, sessions) {
4135
4121
  }
4136
4122
 
4137
4123
  // src/commands/agents/spawn.ts
4138
- import { execFileSync as execFileSync7 } from "child_process";
4124
+ import { execFileSync as execFileSync8 } from "child_process";
4139
4125
  import { mkdtempSync as mkdtempSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
4140
4126
  import { tmpdir as tmpdir2 } from "os";
4141
4127
  import { join as join6 } from "path";
@@ -4152,7 +4138,8 @@ function escape(s) {
4152
4138
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4153
4139
  }
4154
4140
  function buildSyncPlist(input) {
4155
- const pathLine = ` <key>PATH</key><string>${escape(input.homeDir)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
4141
+ const pathDirs = (input.hostBinDirs && input.hostBinDirs.length > 0 ? input.hostBinDirs : ["/opt/homebrew/bin", "/usr/local/bin"]).join(":");
4142
+ const pathLine = ` <key>PATH</key><string>${escape(pathDirs)}:/usr/bin:/bin</string>
4156
4143
  `;
4157
4144
  const agentUserLine = ` <key>AGENT_USER</key><string>${escape(input.userName)}</string>
4158
4145
  `;
@@ -4250,9 +4237,10 @@ function generateKeyPairInMemory() {
4250
4237
  }
4251
4238
 
4252
4239
  // src/lib/llm-bridge.ts
4240
+ import { execFileSync as execFileSync7 } from "child_process";
4253
4241
  import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
4254
4242
  import { homedir as homedir8 } from "os";
4255
- import { join as join5 } from "path";
4243
+ import { dirname as dirname3, join as join5 } from "path";
4256
4244
  var PLIST_LABEL_PREFIX = "eco.hofmann.apes.bridge";
4257
4245
  function readLitellmEnv(envPath = join5(homedir8(), "litellm", ".env")) {
4258
4246
  if (!existsSync8(envPath)) return null;
@@ -4287,6 +4275,25 @@ function resolveBridgeConfig(opts) {
4287
4275
  }
4288
4276
  return { baseUrl, apiKey, model };
4289
4277
  }
4278
+ function captureHostBinDirs() {
4279
+ const dirs = [];
4280
+ const seen = /* @__PURE__ */ new Set();
4281
+ for (const bin of ["node", "openape-chat-bridge", "apes"]) {
4282
+ let resolved;
4283
+ try {
4284
+ resolved = execFileSync7("/usr/bin/which", [bin], { encoding: "utf8" }).trim();
4285
+ } catch {
4286
+ const installCmd = bin === "openape-chat-bridge" ? "npm i -g @openape/chat-bridge" : bin === "apes" ? "npm i -g @openape/apes" : "install Node.js (e.g. brew install node)";
4287
+ throw new Error(`'${bin}' not found on host PATH. ${installCmd} before spawning agents \u2014 the bridge runtime resolves these at spawn time and bakes the dir into the agent's launchd plist.`);
4288
+ }
4289
+ const dir = dirname3(resolved);
4290
+ if (!seen.has(dir)) {
4291
+ seen.add(dir);
4292
+ dirs.push(dir);
4293
+ }
4294
+ }
4295
+ return dirs;
4296
+ }
4290
4297
  function bridgePlistLabel(agentName) {
4291
4298
  return `${PLIST_LABEL_PREFIX}.${agentName}`;
4292
4299
  }
@@ -4302,34 +4309,31 @@ LITELLM_BASE_URL=${cfg.baseUrl}
4302
4309
  LITELLM_API_KEY=${cfg.apiKey}
4303
4310
  ${modelLine}`;
4304
4311
  }
4305
- function buildBridgeStartScript() {
4312
+ function buildBridgeStartScript(hostBinDirs) {
4313
+ const pathLine = `export PATH="${hostBinDirs.join(":")}:/usr/bin:/bin"`;
4306
4314
  return `#!/usr/bin/env bash
4307
4315
  # Auto-generated by 'apes agents spawn --bridge'.
4308
- # Slim launcher \u2014 install stack lives in spawn, not here.
4316
+ # Slim launcher \u2014 bridge stack lives on the host, no per-agent install.
4309
4317
  set -euo pipefail
4310
4318
 
4311
- export PATH="$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
4319
+ ${pathLine}
4312
4320
 
4313
4321
  # Token refresh is in-process via @openape/cli-auth's challenge-response
4314
4322
  # path (auth.json.key_path -> ~/.ssh/id_ed25519). No "apes login" needed
4315
4323
  # at boot \u2014 keeping start.sh slim avoids the rate-limit dance the old
4316
4324
  # refresh hit when KeepAlive crash-restarted the daemon every 1h.
4317
4325
 
4318
- # M6 dropped the third-party LLM-runtime install + extension write
4319
- # that used to live here. The bridge now spawns
4320
- # \`apes agents serve --rpc\` directly (M8) which calls LiteLLM
4321
- # itself, so no third-party extension config is needed.
4322
-
4323
4326
  set -a
4324
4327
  . "$HOME/Library/Application Support/openape/bridge/.env"
4325
4328
  set +a
4326
4329
  exec openape-chat-bridge
4327
4330
  `;
4328
4331
  }
4329
- function buildBridgePlist(agentName, homeDir, ownerEmail) {
4332
+ function buildBridgePlist(agentName, homeDir, ownerEmail, hostBinDirs) {
4330
4333
  const startScript = `${homeDir}/Library/Application Support/openape/bridge/start.sh`;
4331
4334
  const stdoutLog = `${homeDir}/Library/Logs/openape-chat-bridge.log`;
4332
4335
  const stderrLog = `${homeDir}/Library/Logs/openape-chat-bridge.err.log`;
4336
+ const pathValue = `${hostBinDirs.join(":")}:/usr/bin:/bin`;
4333
4337
  return `<?xml version="1.0" encoding="UTF-8"?>
4334
4338
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4335
4339
  <plist version="1.0">
@@ -4360,7 +4364,7 @@ function buildBridgePlist(agentName, homeDir, ownerEmail) {
4360
4364
  <key>HOME</key>
4361
4365
  <string>${homeDir}</string>
4362
4366
  <key>PATH</key>
4363
- <string>${homeDir}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
4367
+ <string>${pathValue}</string>
4364
4368
  <key>OPENAPE_OWNER_EMAIL</key>
4365
4369
  <string>${ownerEmail}</string>
4366
4370
  </dict>
@@ -4498,20 +4502,22 @@ and try again.`
4498
4502
  cliBaseUrl: typeof args["bridge-base-url"] === "string" ? args["bridge-base-url"] : void 0,
4499
4503
  cliModel: typeof args["bridge-model"] === "string" ? args["bridge-model"] : void 0
4500
4504
  });
4505
+ const hostBinDirs = captureHostBinDirs();
4501
4506
  return {
4502
4507
  plistLabel: bridgePlistLabel(name),
4503
4508
  plistPath: bridgePlistPath(name),
4504
- plistContent: buildBridgePlist(name, homeDir, auth.email),
4505
- startScript: buildBridgeStartScript(),
4509
+ plistContent: buildBridgePlist(name, homeDir, auth.email, hostBinDirs),
4510
+ startScript: buildBridgeStartScript(hostBinDirs),
4506
4511
  envFile: buildBridgeEnvFile(cfg)
4507
4512
  };
4508
4513
  })() : null;
4509
4514
  const troopPlistLabel = `openape.troop.sync.${name}`;
4510
4515
  const troopPlistPath = `/Library/LaunchDaemons/${troopPlistLabel}.plist`;
4516
+ const troopBinDirs = bridge ? captureHostBinDirs() : captureHostBinDirs();
4511
4517
  const troop = {
4512
4518
  plistLabel: troopPlistLabel,
4513
4519
  plistPath: troopPlistPath,
4514
- plistContent: buildSyncPlist({ agentName: name, apesBin: apes, homeDir, userName: name })
4520
+ plistContent: buildSyncPlist({ agentName: name, apesBin: apes, homeDir, userName: name, hostBinDirs: troopBinDirs })
4515
4521
  };
4516
4522
  const script = buildSpawnSetupScript({
4517
4523
  name,
@@ -4529,7 +4535,7 @@ and try again.`
4529
4535
  writeFileSync4(scriptPath, script, { mode: 448 });
4530
4536
  consola23.start("Running privileged setup as root via `apes run --as root --wait`\u2026");
4531
4537
  consola23.info("You will be asked to approve the as=root grant in your DDISA inbox; this command blocks until you do.");
4532
- execFileSync7(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
4538
+ execFileSync8(apes, ["run", "--as", "root", "--wait", "--", "bash", scriptPath], { stdio: "inherit" });
4533
4539
  consola23.success(`Agent ${name} spawned.`);
4534
4540
  consola23.info(`\u{1F517} Troop: https://troop.openape.ai/agents/${name}`);
4535
4541
  if (args.bridge) {
@@ -4575,11 +4581,11 @@ import { defineCommand as defineCommand27 } from "citty";
4575
4581
  import consola24 from "consola";
4576
4582
 
4577
4583
  // src/lib/macos-host.ts
4578
- import { execFileSync as execFileSync8 } from "child_process";
4584
+ import { execFileSync as execFileSync9 } from "child_process";
4579
4585
  import { hostname as hostname3 } from "os";
4580
4586
  function getHostId() {
4581
4587
  try {
4582
- const output = execFileSync8(
4588
+ const output = execFileSync9(
4583
4589
  "/usr/sbin/ioreg",
4584
4590
  ["-d2", "-c", "IOPlatformExpertDevice"],
4585
4591
  { encoding: "utf8", timeout: 2e3 }
@@ -4728,7 +4734,7 @@ var agentsCommand = defineCommand28({
4728
4734
  import { defineCommand as defineCommand37 } from "citty";
4729
4735
 
4730
4736
  // src/commands/nest/authorize.ts
4731
- import { execFileSync as execFileSync9 } from "child_process";
4737
+ import { execFileSync as execFileSync10 } from "child_process";
4732
4738
  import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
4733
4739
  import { join as join9 } from "path";
4734
4740
  import { defineCommand as defineCommand30 } from "citty";
@@ -4884,7 +4890,7 @@ var authorizeNestCommand = defineCommand30({
4884
4890
  cmdArgs.push("--expires-in", args["expires-in"]);
4885
4891
  }
4886
4892
  try {
4887
- execFileSync9("apes", cmdArgs, { stdio: "inherit" });
4893
+ execFileSync10("apes", cmdArgs, { stdio: "inherit" });
4888
4894
  } catch (err) {
4889
4895
  throw new CliError(err instanceof Error ? err.message : String(err));
4890
4896
  }
@@ -5029,10 +5035,10 @@ var destroyNestCommand = defineCommand31({
5029
5035
  });
5030
5036
 
5031
5037
  // src/commands/nest/install.ts
5032
- import { execFileSync as execFileSync10 } from "child_process";
5038
+ import { execFileSync as execFileSync11 } from "child_process";
5033
5039
  import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync7 } from "fs";
5034
5040
  import { homedir as homedir11, userInfo as userInfo2 } from "os";
5035
- import { dirname as dirname3, join as join10 } from "path";
5041
+ import { dirname as dirname4, join as join10 } from "path";
5036
5042
  import { defineCommand as defineCommand32 } from "citty";
5037
5043
  import consola29 from "consola";
5038
5044
 
@@ -5129,7 +5135,7 @@ function buildPlist(args) {
5129
5135
  <key>EnvironmentVariables</key>
5130
5136
  <dict>
5131
5137
  <key>HOME</key><string>${escape2(args.nestHome)}</string>
5132
- <key>PATH</key><string>${escape2(args.userHome)}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
5138
+ <key>PATH</key><string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
5133
5139
  <key>OPENAPE_NEST_PORT</key><string>${args.port}</string>
5134
5140
  <key>OPENAPE_APES_BIN</key><string>${escape2(args.apesBin)}</string>
5135
5141
  </dict>
@@ -5143,7 +5149,7 @@ function buildPlist(args) {
5143
5149
  }
5144
5150
  function installAdapter2() {
5145
5151
  const target = join10(homedir11(), ".openape", "shapes", "adapters", "apes-agents.toml");
5146
- mkdirSync5(dirname3(target), { recursive: true });
5152
+ mkdirSync5(dirname4(target), { recursive: true });
5147
5153
  let existing = "";
5148
5154
  try {
5149
5155
  existing = readFileSync11(target, "utf8");
@@ -5227,10 +5233,10 @@ var installNestCommand = defineCommand32({
5227
5233
  }
5228
5234
  const uid = userInfo2().uid;
5229
5235
  try {
5230
- execFileSync10("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
5236
+ execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL}`], { stdio: "ignore" });
5231
5237
  } catch {
5232
5238
  }
5233
- execFileSync10("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
5239
+ execFileSync11("/bin/launchctl", ["bootstrap", `gui/${uid}`, plistPath()], { stdio: "inherit" });
5234
5240
  consola29.success(`Nest daemon bootstrapped \u2014 http://127.0.0.1:${port}`);
5235
5241
  consola29.info("");
5236
5242
  consola29.info("Next steps for zero-prompt spawn \u2014 both one-time:");
@@ -5393,7 +5399,7 @@ var statusNestCommand = defineCommand35({
5393
5399
  });
5394
5400
 
5395
5401
  // src/commands/nest/uninstall.ts
5396
- import { execFileSync as execFileSync11 } from "child_process";
5402
+ import { execFileSync as execFileSync12 } from "child_process";
5397
5403
  import { existsSync as existsSync13, unlinkSync } from "fs";
5398
5404
  import { homedir as homedir12, userInfo as userInfo3 } from "os";
5399
5405
  import { join as join11 } from "path";
@@ -5409,7 +5415,7 @@ var uninstallNestCommand = defineCommand36({
5409
5415
  const uid = userInfo3().uid;
5410
5416
  const path2 = join11(homedir12(), "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
5411
5417
  try {
5412
- execFileSync11("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
5418
+ execFileSync12("/bin/launchctl", ["bootout", `gui/${uid}/${PLIST_LABEL2}`], { stdio: "ignore" });
5413
5419
  consola33.success("Nest daemon stopped");
5414
5420
  } catch {
5415
5421
  consola33.info("Nest daemon was not loaded");
@@ -5964,7 +5970,7 @@ var adapterCommand = defineCommand42({
5964
5970
  });
5965
5971
 
5966
5972
  // src/commands/run.ts
5967
- import { execFileSync as execFileSync12 } from "child_process";
5973
+ import { execFileSync as execFileSync13 } from "child_process";
5968
5974
  import { hostname as hostname6 } from "os";
5969
5975
  import { basename } from "path";
5970
5976
  import { defineCommand as defineCommand43 } from "citty";
@@ -6242,7 +6248,7 @@ function execShellCommand(command) {
6242
6248
  throw new CliError("No command to execute");
6243
6249
  try {
6244
6250
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
6245
- execFileSync12(command[0], command.slice(1), {
6251
+ execFileSync13(command[0], command.slice(1), {
6246
6252
  stdio: "inherit",
6247
6253
  env: inheritedEnv
6248
6254
  });
@@ -6394,7 +6400,7 @@ async function runAudienceMode(audience, action, args) {
6394
6400
  consola38.info(`Executing: ${command.join(" ")}`);
6395
6401
  try {
6396
6402
  const { APES_SHELL_WRAPPER: _wrapperMarker, ...inheritedEnv } = process.env;
6397
- execFileSync12(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
6403
+ execFileSync13(args["escapes-path"] || "escapes", ["--grant", authz_jwt, "--", ...command], {
6398
6404
  stdio: "inherit",
6399
6405
  env: inheritedEnv
6400
6406
  });
@@ -6447,7 +6453,7 @@ import { spawn } from "child_process";
6447
6453
  import { mkdtempSync as mkdtempSync3, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
6448
6454
  import { createRequire } from "module";
6449
6455
  import { tmpdir as tmpdir3 } from "os";
6450
- import { dirname as dirname4, join as join12, resolve as resolve4 } from "path";
6456
+ import { dirname as dirname5, join as join12, resolve as resolve4 } from "path";
6451
6457
  var require2 = createRequire(import.meta.url);
6452
6458
  function findProxyBin() {
6453
6459
  const pkgPath = require2.resolve("@openape/proxy/package.json");
@@ -6456,7 +6462,7 @@ function findProxyBin() {
6456
6462
  if (!binRel) {
6457
6463
  throw new Error("@openape/proxy is missing the openape-proxy bin entry");
6458
6464
  }
6459
- return resolve4(dirname4(pkgPath), binRel);
6465
+ return resolve4(dirname5(pkgPath), binRel);
6460
6466
  }
6461
6467
  async function startEphemeralProxy(configToml) {
6462
6468
  const tmpDir = mkdtempSync3(join12(tmpdir3(), "openape-proxy-"));
@@ -6896,7 +6902,7 @@ var mcpCommand = defineCommand49({
6896
6902
  if (transport !== "stdio" && transport !== "sse") {
6897
6903
  throw new Error('Transport must be "stdio" or "sse"');
6898
6904
  }
6899
- const { startMcpServer } = await import("./server-6WQYPDVO.js");
6905
+ const { startMcpServer } = await import("./server-AZOEKT55.js");
6900
6906
  await startMcpServer(transport, port);
6901
6907
  }
6902
6908
  });
@@ -6904,7 +6910,7 @@ var mcpCommand = defineCommand49({
6904
6910
  // src/commands/init/index.ts
6905
6911
  import { existsSync as existsSync14, copyFileSync, writeFileSync as writeFileSync9 } from "fs";
6906
6912
  import { randomBytes } from "crypto";
6907
- import { execFileSync as execFileSync13 } from "child_process";
6913
+ import { execFileSync as execFileSync14 } from "child_process";
6908
6914
  import { join as join13 } from "path";
6909
6915
  import { defineCommand as defineCommand50 } from "citty";
6910
6916
  import consola42 from "consola";
@@ -6916,11 +6922,11 @@ async function downloadTemplate(repo, targetDir) {
6916
6922
  function installDeps(dir) {
6917
6923
  const hasLockFile = (name) => existsSync14(join13(dir, name));
6918
6924
  if (hasLockFile("pnpm-lock.yaml")) {
6919
- execFileSync13("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6925
+ execFileSync14("pnpm", ["install"], { cwd: dir, stdio: "inherit" });
6920
6926
  } else if (hasLockFile("bun.lockb")) {
6921
- execFileSync13("bun", ["install"], { cwd: dir, stdio: "inherit" });
6927
+ execFileSync14("bun", ["install"], { cwd: dir, stdio: "inherit" });
6922
6928
  } else {
6923
- execFileSync13("npm", ["install"], { cwd: dir, stdio: "inherit" });
6929
+ execFileSync14("npm", ["install"], { cwd: dir, stdio: "inherit" });
6924
6930
  }
6925
6931
  }
6926
6932
  async function promptChoice(message, choices) {
@@ -7534,7 +7540,7 @@ async function bestEffortGrantCount(idp) {
7534
7540
  }
7535
7541
  }
7536
7542
  async function runHealth(args) {
7537
- const version = true ? "1.9.0" : "0.0.0";
7543
+ const version = true ? "1.10.0" : "0.0.0";
7538
7544
  const auth = loadAuth();
7539
7545
  if (!auth) {
7540
7546
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -7807,10 +7813,10 @@ if (shellRewrite) {
7807
7813
  if (shellRewrite.action === "rewrite") {
7808
7814
  process.argv = shellRewrite.argv;
7809
7815
  } else if (shellRewrite.action === "version") {
7810
- console.log(`ape-shell ${"1.9.0"} (OpenApe DDISA shell wrapper)`);
7816
+ console.log(`ape-shell ${"1.10.0"} (OpenApe DDISA shell wrapper)`);
7811
7817
  process.exit(0);
7812
7818
  } else if (shellRewrite.action === "help") {
7813
- console.log(`ape-shell ${"1.9.0"} \u2014 OpenApe DDISA shell wrapper`);
7819
+ console.log(`ape-shell ${"1.10.0"} \u2014 OpenApe DDISA shell wrapper`);
7814
7820
  console.log("");
7815
7821
  console.log("Usage:");
7816
7822
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -7868,7 +7874,7 @@ var configCommand = defineCommand61({
7868
7874
  var main = defineCommand61({
7869
7875
  meta: {
7870
7876
  name: "apes",
7871
- version: "1.9.0",
7877
+ version: "1.10.0",
7872
7878
  description: "Unified CLI for OpenApe"
7873
7879
  },
7874
7880
  subCommands: {
@@ -7925,7 +7931,7 @@ async function maybeRefreshAuth() {
7925
7931
  }
7926
7932
  }
7927
7933
  await maybeRefreshAuth();
7928
- await maybeWarnStaleVersion("1.9.0").catch(() => {
7934
+ await maybeWarnStaleVersion("1.10.0").catch(() => {
7929
7935
  });
7930
7936
  runMain(main).catch((err) => {
7931
7937
  if (err instanceof CliExit) {