@openape/apes 0.29.0 → 0.31.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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG_DIR
4
- } from "./chunk-IDPV5SNB.js";
4
+ } from "./chunk-OBF7IMQ2.js";
5
5
 
6
6
  // src/auth-lock.ts
7
7
  import { open, rm, stat } from "fs/promises";
@@ -38,4 +38,4 @@ export {
38
38
  acquireAuthLock,
39
39
  releaseAuthLock
40
40
  };
41
- //# sourceMappingURL=auth-lock-GPLHRXOM.js.map
41
+ //# sourceMappingURL=auth-lock-4IRWI3ED.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  loadConfig
4
- } from "./chunk-IDPV5SNB.js";
4
+ } from "./chunk-OBF7IMQ2.js";
5
5
 
6
6
  // src/notifications.ts
7
7
  import { spawn } from "child_process";
@@ -77,4 +77,4 @@ export {
77
77
  isApesSelfDispatch,
78
78
  checkSudoRejection
79
79
  };
80
- //# sourceMappingURL=chunk-EDYICCBC.js.map
80
+ //# sourceMappingURL=chunk-3COOEDPF.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getGenericAuditLogPath
4
- } from "./chunk-IDPV5SNB.js";
4
+ } from "./chunk-OBF7IMQ2.js";
5
5
 
6
6
  // src/shapes/adapters.ts
7
7
  import { createHash } from "crypto";
@@ -1212,4 +1212,4 @@ export {
1212
1212
  buildExactCommandGrantRequest,
1213
1213
  buildStructuredCliGrantRequest
1214
1214
  };
1215
- //# sourceMappingURL=chunk-IT6T6AKX.js.map
1215
+ //# sourceMappingURL=chunk-DYSFQ26B.js.map
@@ -5,7 +5,7 @@ import {
5
5
  loadAuth,
6
6
  loadConfig,
7
7
  saveAuth
8
- } from "./chunk-IDPV5SNB.js";
8
+ } from "./chunk-OBF7IMQ2.js";
9
9
 
10
10
  // src/http.ts
11
11
  import consola from "consola";
@@ -104,7 +104,7 @@ async function refreshOAuthToken() {
104
104
  const auth = loadAuth();
105
105
  if (!auth?.refresh_token)
106
106
  return null;
107
- const { acquireAuthLock, releaseAuthLock } = await import("./auth-lock-GPLHRXOM.js");
107
+ const { acquireAuthLock, releaseAuthLock } = await import("./auth-lock-4IRWI3ED.js");
108
108
  const lock = await acquireAuthLock({ timeoutMs: 5e3 });
109
109
  if (!lock) {
110
110
  return getAuthToken();
@@ -217,4 +217,4 @@ export {
217
217
  ensureFreshToken,
218
218
  apiFetch
219
219
  };
220
- //# sourceMappingURL=chunk-7NUT2PFT.js.map
220
+ //# sourceMappingURL=chunk-N3THIFIS.js.map
@@ -23,7 +23,24 @@ function loadAuth() {
23
23
  }
24
24
  function saveAuth(data) {
25
25
  ensureDir();
26
- writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
26
+ let extra = {};
27
+ if (existsSync(AUTH_FILE)) {
28
+ try {
29
+ const raw = readFileSync(AUTH_FILE, "utf-8");
30
+ if (raw.trim()) {
31
+ const prev = JSON.parse(raw);
32
+ for (const key of Object.keys(prev)) {
33
+ if (!(key in data)) {
34
+ extra[key] = prev[key];
35
+ }
36
+ }
37
+ }
38
+ } catch {
39
+ extra = {};
40
+ }
41
+ }
42
+ const merged = { ...extra, ...data };
43
+ writeFileSync(AUTH_FILE, JSON.stringify(merged, null, 2), { mode: 384 });
27
44
  }
28
45
  function clearAuth() {
29
46
  if (existsSync(AUTH_FILE)) {
@@ -169,4 +186,4 @@ export {
169
186
  getAuthToken,
170
187
  getRequesterIdentity
171
188
  };
172
- //# sourceMappingURL=chunk-IDPV5SNB.js.map
189
+ //# sourceMappingURL=chunk-OBF7IMQ2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport interface AuthData {\n idp: string\n access_token: string\n refresh_token?: string\n email: string\n expires_at: number\n /**\n * Set by `apes login --key …` (and `apes agents spawn`), absolute\n * path to the Ed25519 key the agent signs challenges with. Lets\n * `@openape/cli-auth` refresh agent tokens in-process; see #259.\n */\n key_path?: string\n /**\n * Email of the human who owns this agent — written by\n * `apes agents spawn`. The chat-bridge reads it for owner-only\n * contact handshakes. Optional for human auth.json files.\n */\n owner_email?: string\n}\n\nexport interface ApesConfig {\n defaults?: {\n idp?: string\n approval?: string\n /**\n * Audience for the `apes run` async info block. `agent` (default)\n * emits verbose agent-facing instructions with a polling protocol;\n * `human` emits a short friendly block. Env var `APES_USER` wins.\n */\n user?: 'agent' | 'human'\n /**\n * Poll interval (seconds) embedded in the agent-mode instructions.\n * Default 10. Env var `APES_GRANT_POLL_INTERVAL` wins. Stored as a\n * string in TOML because the hand-rolled parser only handles quoted\n * values — casting to number happens at read time.\n */\n grant_poll_interval_seconds?: string\n /**\n * Maximum poll duration (minutes) embedded in the agent-mode\n * instructions. Default 5. Env var `APES_GRANT_POLL_MAX_MINUTES` wins.\n */\n grant_poll_max_minutes?: string\n /**\n * Exit code emitted by `apes run` / `ape-shell -c` when the async\n * default path creates a pending grant. Default `75` (`EX_TEMPFAIL`\n * from sysexits.h — \"temporary failure, retry later\"). Set to `0`\n * to restore the pre-0.10.0 exit-0 behaviour. Env var\n * `APES_ASYNC_EXIT_CODE` wins. Valid range 0–255.\n */\n async_exit_code?: string\n }\n agent?: {\n key?: string\n email?: string\n }\n notifications?: {\n pending_command?: string\n }\n /**\n * Generic-fallback mode: when `apes run -- <cli>` is called with a CLI\n * that has no registered shape, fall through to a synthetic adapter that\n * requests a single-use, forced-high-risk grant for the exact argv.\n * See `shapes/generic.ts`.\n */\n generic?: {\n /**\n * Master switch. Default `true` (permissive). Set to `false` to restore\n * the legacy \"No adapter found\" hard-fail. Stored as string in TOML\n * (hand-parser limitation) and parsed as `value !== 'false'` at read time.\n */\n enabled?: string\n /** Override the audit-log location. Default `~/.config/apes/generic-calls.log`. Tilde-expanded at read time. */\n audit_log?: string\n }\n}\n\nconst CONFIG_DIR = join(homedir(), '.config', 'apes')\nconst AUTH_FILE = join(CONFIG_DIR, 'auth.json')\nconst CONFIG_FILE = join(CONFIG_DIR, 'config.toml')\n\nfunction ensureDir() {\n if (!existsSync(CONFIG_DIR)) {\n mkdirSync(CONFIG_DIR, { recursive: true })\n }\n}\n\nexport function loadAuth(): AuthData | null {\n if (!existsSync(AUTH_FILE))\n return null\n try {\n return JSON.parse(readFileSync(AUTH_FILE, 'utf-8'))\n }\n catch {\n return null\n }\n}\n\nexport function saveAuth(data: AuthData): void {\n ensureDir()\n // Preserve unmodelled fields from any prior version of auth.json\n // (e.g. older installs of this package, or fields a sibling CLI\n // wrote). Symmetric with cli-auth's saveIdpAuth — see #257 for the\n // bridge crash-loop the bare overwrite caused before.\n let extra: Record<string, unknown> = {}\n if (existsSync(AUTH_FILE)) {\n try {\n const raw = readFileSync(AUTH_FILE, 'utf-8')\n if (raw.trim()) {\n const prev = JSON.parse(raw) as Record<string, unknown>\n for (const key of Object.keys(prev)) {\n if (!(key in (data as unknown as Record<string, unknown>))) {\n extra[key] = prev[key]\n }\n }\n }\n }\n catch {\n extra = {}\n }\n }\n const merged = { ...extra, ...data }\n writeFileSync(AUTH_FILE, JSON.stringify(merged, null, 2), { mode: 0o600 })\n}\n\nexport function clearAuth(): void {\n if (existsSync(AUTH_FILE)) {\n writeFileSync(AUTH_FILE, '', { mode: 0o600 })\n }\n // Also wipe the [agent] section from config.toml so logout disables\n // auto-refresh. Preserves [defaults] so the IdP URL stays configured.\n if (existsSync(CONFIG_FILE)) {\n const existing = loadConfig()\n if (existing.agent) {\n const { agent: _removed, ...rest } = existing\n saveConfig(rest)\n }\n }\n}\n\nexport function loadConfig(): ApesConfig {\n if (!existsSync(CONFIG_FILE))\n return {}\n try {\n return parseTOML(readFileSync(CONFIG_FILE, 'utf-8'))\n }\n catch {\n return {}\n }\n}\n\nfunction parseTOML(content: string): ApesConfig {\n const config: ApesConfig = {}\n let section = ''\n\n for (const line of content.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed || trimmed.startsWith('#'))\n continue\n\n const sectionMatch = trimmed.match(/^\\[(.+)\\]$/)\n if (sectionMatch) {\n section = sectionMatch[1]!\n continue\n }\n\n // Accept quoted strings and bare booleans/tokens — the generic section\n // uses `enabled = false` (no quotes) which the quoted-only match would\n // silently drop.\n const kvQuoted = trimmed.match(/^(\\w+)\\s*=\\s*\"(.*)\"$/)\n const kvBare = trimmed.match(/^(\\w+)\\s*=\\s*([^\"\\s]\\S*)$/)\n const kvMatch = kvQuoted ?? kvBare\n if (kvMatch) {\n const [, key, value] = kvMatch\n if (section === 'defaults') {\n config.defaults = config.defaults || {}\n ;(config.defaults as Record<string, string>)[key!] = value!\n }\n else if (section === 'agent') {\n config.agent = config.agent || {}\n ;(config.agent as Record<string, string>)[key!] = value!\n }\n else if (section === 'notifications') {\n config.notifications = config.notifications || {}\n ;(config.notifications as Record<string, string>)[key!] = value!\n }\n else if (section === 'generic') {\n config.generic = config.generic || {}\n ;(config.generic as Record<string, string>)[key!] = value!\n }\n }\n }\n\n return config\n}\n\nexport function saveConfig(config: ApesConfig): void {\n ensureDir()\n const lines: string[] = []\n\n if (config.defaults) {\n lines.push('[defaults]')\n for (const [key, value] of Object.entries(config.defaults)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n if (config.agent) {\n lines.push('[agent]')\n for (const [key, value] of Object.entries(config.agent)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n if (config.notifications) {\n lines.push('[notifications]')\n for (const [key, value] of Object.entries(config.notifications)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n if (config.generic) {\n lines.push('[generic]')\n for (const [key, value] of Object.entries(config.generic)) {\n if (value)\n lines.push(`${key} = \"${value}\"`)\n }\n lines.push('')\n }\n\n writeFileSync(CONFIG_FILE, lines.join('\\n'), { mode: 0o600 })\n}\n\n/**\n * Is generic-fallback enabled? Permissive default: `true` unless the user\n * explicitly sets `[generic] enabled = false`.\n */\nexport function isGenericFallbackEnabled(config?: ApesConfig): boolean {\n const cfg = config ?? loadConfig()\n const raw = cfg.generic?.enabled\n if (raw === undefined) return true\n return raw !== 'false'\n}\n\n/**\n * Resolve the audit-log path for generic calls, expanding `~` to `$HOME`.\n */\nexport function getGenericAuditLogPath(config?: ApesConfig): string {\n const cfg = config ?? loadConfig()\n const raw = cfg.generic?.audit_log\n const path = raw && raw.length > 0\n ? raw\n : join(homedir(), '.config', 'apes', 'generic-calls.log')\n return path.startsWith('~/')\n ? join(homedir(), path.slice(2))\n : path\n}\n\nexport function getIdpUrl(explicit?: string): string | null {\n if (explicit)\n return explicit\n if (process.env.APES_IDP)\n return process.env.APES_IDP\n\n const auth = loadAuth()\n if (auth?.idp)\n return auth.idp\n\n const config = loadConfig()\n if (config.defaults?.idp)\n return config.defaults.idp\n\n return null\n}\n\nexport function getAuthToken(): string | null {\n const auth = loadAuth()\n if (!auth)\n return null\n\n // Check expiry (with 30s buffer)\n if (auth.expires_at && Date.now() / 1000 > auth.expires_at - 30) {\n return null // expired\n }\n\n return auth.access_token\n}\n\nexport function getRequesterIdentity(): string | null {\n return loadAuth()?.email ?? null\n}\n\nexport { CONFIG_DIR, AUTH_FILE }\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  checkSudoRejection,
13
13
  isApesSelfDispatch,
14
14
  notifyGrantPending
15
- } from "./chunk-EDYICCBC.js";
15
+ } from "./chunk-3COOEDPF.js";
16
16
  import {
17
17
  GENERIC_OPERATION_ID,
18
18
  buildGenericResolved,
@@ -40,7 +40,7 @@ import {
40
40
  searchAdapters,
41
41
  verifyAndExecute,
42
42
  waitForGrantStatus
43
- } from "./chunk-IT6T6AKX.js";
43
+ } from "./chunk-DYSFQ26B.js";
44
44
  import {
45
45
  ApiError,
46
46
  apiFetch,
@@ -48,7 +48,7 @@ import {
48
48
  getAgentChallengeEndpoint,
49
49
  getDelegationsEndpoint,
50
50
  getGrantsEndpoint
51
- } from "./chunk-7NUT2PFT.js";
51
+ } from "./chunk-N3THIFIS.js";
52
52
  import {
53
53
  AUTH_FILE,
54
54
  CONFIG_DIR,
@@ -60,7 +60,7 @@ import {
60
60
  loadConfig,
61
61
  saveAuth,
62
62
  saveConfig
63
- } from "./chunk-IDPV5SNB.js";
63
+ } from "./chunk-OBF7IMQ2.js";
64
64
 
65
65
  // src/cli.ts
66
66
  import consola37 from "consola";
@@ -409,13 +409,14 @@ async function loginWithKey(idp, keyPath, agentEmail) {
409
409
  throw new CliError(`Authentication failed: ${await authResp.text()}`);
410
410
  }
411
411
  const { token, expires_in } = await authResp.json();
412
+ const absoluteKeyPath = resolvePath(keyPath.replace(/^~/, homedir2()));
412
413
  saveAuth({
413
414
  idp,
414
415
  access_token: token,
415
416
  email: agentEmail,
416
- expires_at: Math.floor(Date.now() / 1e3) + (expires_in || 3600)
417
+ expires_at: Math.floor(Date.now() / 1e3) + (expires_in || 3600),
418
+ key_path: absoluteKeyPath
417
419
  });
418
- const absoluteKeyPath = resolvePath(keyPath.replace(/^~/, homedir2()));
419
420
  const existingConfig = loadConfig();
420
421
  saveConfig({
421
422
  ...existingConfig,
@@ -2008,6 +2009,7 @@ function buildAgentAuthJson(input) {
2008
2009
  access_token: input.accessToken,
2009
2010
  email: input.email,
2010
2011
  expires_at: input.expiresAt,
2012
+ key_path: input.keyPath,
2011
2013
  owner_email: input.ownerEmail
2012
2014
  }, null, 2)}
2013
2015
  `;
@@ -2611,22 +2613,10 @@ set -euo pipefail
2611
2613
 
2612
2614
  export PATH="$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
2613
2615
 
2614
- # Refresh IdP token. Agents auth via SSH-key signing \u2014 the cached IdP
2615
- # token has no refresh_token and expires after ~1h. Re-running apes
2616
- # login re-signs against the registered key. Soft-fails to leave the
2617
- # bridge usable on cached token if the IdP is briefly unreachable.
2618
- if [ -f "$HOME/.config/apes/auth.json" ] && command -v apes >/dev/null 2>&1; then
2619
- agent_email=$(python3 -c "import json,os,sys
2620
- try: print(json.load(open(os.path.expanduser('~/.config/apes/auth.json')))['email'])
2621
- except Exception: sys.exit(1)" 2>/dev/null || true)
2622
- agent_idp=$(python3 -c "import json,os,sys
2623
- try: print(json.load(open(os.path.expanduser('~/.config/apes/auth.json')))['idp'])
2624
- except Exception: sys.exit(1)" 2>/dev/null || true)
2625
- if [ -n "$agent_email" ] && [ -n "$agent_idp" ]; then
2626
- apes login "$agent_email" --idp "$agent_idp" >/dev/null 2>&1 \\
2627
- || echo "warn: apes login failed for $agent_email; continuing with cached token"
2628
- fi
2629
- fi
2616
+ # Token refresh is in-process via @openape/cli-auth's challenge-response
2617
+ # path (auth.json.key_path -> ~/.ssh/id_ed25519). No "apes login" needed
2618
+ # at boot \u2014 keeping start.sh slim avoids the rate-limit dance the old
2619
+ # refresh hit when KeepAlive crash-restarted the daemon every 1h.
2630
2620
 
2631
2621
  EXT_DIR="$HOME/.pi/agent/extensions"
2632
2622
  mkdir -p "$EXT_DIR"
@@ -2672,7 +2662,7 @@ set +a
2672
2662
  exec openape-chat-bridge
2673
2663
  `;
2674
2664
  }
2675
- function buildBridgePlist(agentName, homeDir) {
2665
+ function buildBridgePlist(agentName, homeDir, ownerEmail) {
2676
2666
  const startScript = `${homeDir}/Library/Application Support/openape/bridge/start.sh`;
2677
2667
  const stdoutLog = `${homeDir}/Library/Logs/openape-chat-bridge.log`;
2678
2668
  const stderrLog = `${homeDir}/Library/Logs/openape-chat-bridge.err.log`;
@@ -2707,6 +2697,8 @@ function buildBridgePlist(agentName, homeDir) {
2707
2697
  <string>${homeDir}</string>
2708
2698
  <key>PATH</key>
2709
2699
  <string>${homeDir}/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
2700
+ <key>OPENAPE_OWNER_EMAIL</key>
2701
+ <string>${ownerEmail}</string>
2710
2702
  </dict>
2711
2703
  </dict>
2712
2704
  </plist>
@@ -2816,6 +2808,7 @@ and try again.`
2816
2808
  accessToken: token,
2817
2809
  email: registration.email,
2818
2810
  expiresAt: Math.floor(Date.now() / 1e3) + expiresIn,
2811
+ keyPath: `${homeDir}/.ssh/id_ed25519`,
2819
2812
  ownerEmail: auth.email
2820
2813
  });
2821
2814
  const includeClaudeHook = !args["no-claude-hook"];
@@ -2831,7 +2824,7 @@ and try again.`
2831
2824
  return {
2832
2825
  plistLabel: bridgePlistLabel(name),
2833
2826
  plistPath: bridgePlistPath(name),
2834
- plistContent: buildBridgePlist(name, homeDir),
2827
+ plistContent: buildBridgePlist(name, homeDir, auth.email),
2835
2828
  startScript: buildBridgeStartScript(),
2836
2829
  envFile: buildBridgeEnvFile(cfg)
2837
2830
  };
@@ -4164,7 +4157,7 @@ var mcpCommand = defineCommand33({
4164
4157
  if (transport !== "stdio" && transport !== "sse") {
4165
4158
  throw new Error('Transport must be "stdio" or "sse"');
4166
4159
  }
4167
- const { startMcpServer } = await import("./server-QGV3ED6I.js");
4160
+ const { startMcpServer } = await import("./server-TBXFWNUV.js");
4168
4161
  await startMcpServer(transport, port);
4169
4162
  }
4170
4163
  });
@@ -4802,7 +4795,7 @@ async function bestEffortGrantCount(idp) {
4802
4795
  }
4803
4796
  }
4804
4797
  async function runHealth(args) {
4805
- const version = true ? "0.29.0" : "0.0.0";
4798
+ const version = true ? "0.31.0" : "0.0.0";
4806
4799
  const auth = loadAuth();
4807
4800
  if (!auth) {
4808
4801
  throw new CliError("Not logged in. Run `apes login` first.", 1);
@@ -5075,10 +5068,10 @@ if (shellRewrite) {
5075
5068
  if (shellRewrite.action === "rewrite") {
5076
5069
  process.argv = shellRewrite.argv;
5077
5070
  } else if (shellRewrite.action === "version") {
5078
- console.log(`ape-shell ${"0.29.0"} (OpenApe DDISA shell wrapper)`);
5071
+ console.log(`ape-shell ${"0.31.0"} (OpenApe DDISA shell wrapper)`);
5079
5072
  process.exit(0);
5080
5073
  } else if (shellRewrite.action === "help") {
5081
- console.log(`ape-shell ${"0.29.0"} \u2014 OpenApe DDISA shell wrapper`);
5074
+ console.log(`ape-shell ${"0.31.0"} \u2014 OpenApe DDISA shell wrapper`);
5082
5075
  console.log("");
5083
5076
  console.log("Usage:");
5084
5077
  console.log(" ape-shell Start interactive grant-mediated REPL");
@@ -5093,7 +5086,7 @@ if (shellRewrite) {
5093
5086
  console.log(" --help, -h Show this help message");
5094
5087
  process.exit(0);
5095
5088
  } else if (shellRewrite.action === "interactive") {
5096
- const { runInteractiveShell } = await import("./orchestrator-RWTALOSA.js");
5089
+ const { runInteractiveShell } = await import("./orchestrator-EH6V5ATG.js");
5097
5090
  await runInteractiveShell();
5098
5091
  process.exit(0);
5099
5092
  } else {
@@ -5136,7 +5129,7 @@ var configCommand = defineCommand45({
5136
5129
  var main = defineCommand45({
5137
5130
  meta: {
5138
5131
  name: "apes",
5139
- version: "0.29.0",
5132
+ version: "0.31.0",
5140
5133
  description: "Unified CLI for OpenApe"
5141
5134
  },
5142
5135
  subCommands: {
@@ -5182,16 +5175,16 @@ var NO_REFRESH_COMMANDS = /* @__PURE__ */ new Set([
5182
5175
  async function maybeRefreshAuth() {
5183
5176
  const sub = process.argv[2];
5184
5177
  if (!sub || NO_REFRESH_COMMANDS.has(sub)) return;
5185
- const { loadAuth: loadAuth2 } = await import("./config-JH2IEPIR.js");
5178
+ const { loadAuth: loadAuth2 } = await import("./config-MOB5DJ6H.js");
5186
5179
  if (!loadAuth2()) return;
5187
5180
  try {
5188
- const { ensureFreshToken } = await import("./http-JZT4XV5I.js");
5181
+ const { ensureFreshToken } = await import("./http-5F7FX4V7.js");
5189
5182
  await ensureFreshToken();
5190
5183
  } catch {
5191
5184
  }
5192
5185
  }
5193
5186
  await maybeRefreshAuth();
5194
- await maybeWarnStaleVersion("0.29.0").catch(() => {
5187
+ await maybeWarnStaleVersion("0.31.0").catch(() => {
5195
5188
  });
5196
5189
  runMain(main).catch((err) => {
5197
5190
  if (err instanceof CliExit) {