apiblaze 0.21.39 → 0.21.41

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 (2) hide show
  1. package/dist/index.js +107 -57
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34,7 +34,7 @@ var __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__
34
34
  var version;
35
35
  var init_package = __esm({
36
36
  "package.json"() {
37
- version = "0.21.39";
37
+ version = "0.21.41";
38
38
  }
39
39
  });
40
40
 
@@ -137,21 +137,26 @@ function handleUpgradeRequired(err) {
137
137
  })();
138
138
  return handling;
139
139
  }
140
+ function cliHeaders() {
141
+ if (!guardVersion) return {};
142
+ const h = { [CLI_VERSION_HEADER]: guardVersion, "user-agent": `apiblaze-cli/${guardVersion}` };
143
+ const command = getCommandLabel();
144
+ if (command) {
145
+ h[CLI_COMMAND_HEADER] = command;
146
+ h[CLI_RUN_HEADER] = getRunId();
147
+ }
148
+ return h;
149
+ }
140
150
  function installFetchGuard(version2) {
141
151
  if (installed || typeof globalThis.fetch !== "function") return;
142
152
  installed = true;
153
+ guardVersion = version2;
143
154
  const base2 = globalThis.fetch.bind(globalThis);
144
155
  globalThis.fetch = (async (input, init) => {
145
156
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
146
157
  if (!isControlPlaneHost(url)) return base2(input, init);
147
158
  const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
148
- headers.set(CLI_VERSION_HEADER, version2);
149
- const command = getCommandLabel();
150
- if (command) {
151
- headers.set(CLI_COMMAND_HEADER, command);
152
- headers.set(CLI_RUN_HEADER, getRunId());
153
- }
154
- if (!headers.has("user-agent")) headers.set("user-agent", `apiblaze-cli/${version2}`);
159
+ for (const [k, v] of Object.entries(cliHeaders())) if (k !== "user-agent" || !headers.has(k)) headers.set(k, v);
155
160
  const res = await base2(input, { ...init, headers });
156
161
  if (res.status === 426) {
157
162
  let body = null;
@@ -237,7 +242,7 @@ function watchCliPolicy(version2) {
237
242
  t.unref?.();
238
243
  return () => clearInterval(t);
239
244
  }
240
- var import_fs, import_os, import_path, import_chalk, DASHBOARD_BASE, CACHE_MS, UPGRADE_EXIT_CODE, CONTROL_PLANE_HOSTS, CONTROL_PLANE_ENV, CliUpgradeRequired, shutdownHook, handling, installed;
245
+ var import_fs, import_os, import_path, import_chalk, DASHBOARD_BASE, CACHE_MS, UPGRADE_EXIT_CODE, CONTROL_PLANE_HOSTS, CONTROL_PLANE_ENV, CliUpgradeRequired, shutdownHook, handling, guardVersion, installed;
241
246
  var init_version_guard = __esm({
242
247
  "src/lib/version-guard.ts"() {
243
248
  "use strict";
@@ -273,6 +278,7 @@ var init_version_guard = __esm({
273
278
  };
274
279
  shutdownHook = null;
275
280
  handling = null;
281
+ guardVersion = "";
276
282
  installed = false;
277
283
  }
278
284
  });
@@ -372,6 +378,7 @@ __export(api_exports, {
372
378
  bothDoorsBody: () => bothDoorsBody,
373
379
  checkProxyName: () => checkProxyName,
374
380
  claimTeam: () => claimTeam,
381
+ countRun: () => countRun,
375
382
  createProxy: () => createProxy,
376
383
  createProxyAnonymous: () => createProxyAnonymous,
377
384
  deleteDevTunnel: () => deleteDevTunnel,
@@ -385,6 +392,22 @@ __export(api_exports, {
385
392
  putDevTunnel: () => putDevTunnel,
386
393
  sidecarInitAnonymous: () => sidecarInitAnonymous
387
394
  });
395
+ function countRun(opts = {}) {
396
+ if (/^(1|true|yes)$/i.test((process.env.DO_NOT_TRACK ?? "").trim())) return;
397
+ if (opts.event !== void 0 && !/^[a-z0-9-]{1,48}$/.test(opts.event)) return;
398
+ try {
399
+ const child = (0, import_child_process.spawn)(process.execPath, ["-e", COUNT_RUN_CHILD], {
400
+ detached: true,
401
+ stdio: "ignore",
402
+ windowsHide: true,
403
+ env: { ...process.env, ABZ_COUNT_URL: (opts.url ?? `${PUBLIC_API_BASE}/cli/hello`) + (opts.event ? `/${opts.event}` : ""), ABZ_COUNT_HEADERS: JSON.stringify(cliHeaders()) }
404
+ });
405
+ child.on("error", () => {
406
+ });
407
+ child.unref();
408
+ } catch {
409
+ }
410
+ }
388
411
  async function createProxyAnonymous(body) {
389
412
  const res = await fetch(`${PUBLIC_API_BASE}/proxy`, {
390
413
  method: "POST",
@@ -543,15 +566,18 @@ function integrationKitCall(project, stack) {
543
566
  summary: `Integration kit for ${project} (${stack})`
544
567
  };
545
568
  }
546
- var import_yaml, DASHBOARD_BASE2, PUBLIC_API_BASE, GITHUB_LOGIN, LOCAL_SPEC_PATHS, INTEGRATION_STACKS;
569
+ var import_child_process, import_yaml, DASHBOARD_BASE2, PUBLIC_API_BASE, COUNT_RUN_CHILD, GITHUB_LOGIN, LOCAL_SPEC_PATHS, INTEGRATION_STACKS;
547
570
  var init_api = __esm({
548
571
  "src/lib/api.ts"() {
549
572
  "use strict";
573
+ import_child_process = require("child_process");
550
574
  init_auth();
575
+ init_version_guard();
551
576
  import_yaml = require("yaml");
552
577
  init_types();
553
578
  DASHBOARD_BASE2 = process.env.APIBLAZE_DASHBOARD_BASE || "https://dashboard.apiblaze.com";
554
579
  PUBLIC_API_BASE = "https://api.apiblaze.com";
580
+ COUNT_RUN_CHILD = "fetch(process.env.ABZ_COUNT_URL,{method:'POST',headers:JSON.parse(process.env.ABZ_COUNT_HEADERS||'{}'),signal:AbortSignal.timeout(5000)}).then((r)=>r.body&&r.body.cancel(),()=>{})";
555
581
  GITHUB_LOGIN = { providers: [{ type: "github", managed: true }] };
556
582
  LOCAL_SPEC_PATHS = ["/openapi.json", "/openapi.yaml", "/docs/openapi.json"];
557
583
  INTEGRATION_STACKS = ["nextjs", "express", "fastapi", "other"];
@@ -2281,7 +2307,7 @@ function waitForClaudeConnection(name, timeoutMs = 12e3) {
2281
2307
  for (; ; ) {
2282
2308
  last = claudeServerStatus(name);
2283
2309
  if (last === "connected" || Date.now() >= deadline) return last;
2284
- (0, import_child_process.spawnSync)(process.execPath, ["-e", "setTimeout(()=>{},1200)"], { timeout: 3e3 });
2310
+ (0, import_child_process2.spawnSync)(process.execPath, ["-e", "setTimeout(()=>{},1200)"], { timeout: 3e3 });
2285
2311
  }
2286
2312
  }
2287
2313
  function claudeOneShot(spec2, prompt) {
@@ -2632,18 +2658,18 @@ async function installAndDemo(cli, spec2, getQuestion, log4 = console.log) {
2632
2658
  log4(import_chalk12.default.red(` ${import_chalk12.default.bold(off)}`));
2633
2659
  return true;
2634
2660
  }
2635
- var fs6, os4, path7, import_child_process, import_chalk12, useShell, winQuote, run, detected, mcpLoginSupport, OURS, mask, tomlStr, shellQuote, marker;
2661
+ var fs6, os4, path7, import_child_process2, import_chalk12, useShell, winQuote, run, detected, mcpLoginSupport, OURS, mask, tomlStr, shellQuote, marker;
2636
2662
  var init_external_mcp = __esm({
2637
2663
  "src/lib/external-mcp.ts"() {
2638
2664
  "use strict";
2639
2665
  fs6 = __toESM(require("fs"));
2640
2666
  os4 = __toESM(require("os"));
2641
2667
  path7 = __toESM(require("path"));
2642
- import_child_process = require("child_process");
2668
+ import_child_process2 = require("child_process");
2643
2669
  import_chalk12 = __toESM(require("chalk"));
2644
2670
  useShell = process.platform === "win32";
2645
2671
  winQuote = (a) => /^[A-Za-z0-9_\-.:/\\=]+$/.test(a) ? a : `"${a.replace(/"/g, '""')}"`;
2646
- run = (cmd, args, opts = {}) => (0, import_child_process.spawnSync)(cmd, useShell ? args.map(winQuote) : args, {
2672
+ run = (cmd, args, opts = {}) => (0, import_child_process2.spawnSync)(cmd, useShell ? args.map(winQuote) : args, {
2647
2673
  encoding: "utf-8",
2648
2674
  stdio: opts.interactive ? ["inherit", "inherit", "inherit"] : opts.inherit ? ["ignore", "inherit", "inherit"] : ["ignore", "pipe", "pipe"],
2649
2675
  timeout: opts.inherit || opts.interactive ? void 0 : 15e3,
@@ -2952,7 +2978,7 @@ function decodeJwt2(token) {
2952
2978
  function openBrowser2(url) {
2953
2979
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2954
2980
  try {
2955
- (0, import_child_process2.spawn)(cmd, [url], { stdio: "ignore", detached: true }).unref();
2981
+ (0, import_child_process3.spawn)(cmd, [url], { stdio: "ignore", detached: true }).unref();
2956
2982
  } catch {
2957
2983
  }
2958
2984
  }
@@ -3028,7 +3054,7 @@ async function validConsumerToken(creds) {
3028
3054
  if (Date.now() < creds.expiresAt - 3e4) return creds;
3029
3055
  return refreshConsumer(creds);
3030
3056
  }
3031
- var fs7, os5, path8, crypto, import_child_process2, AUTH_BASE, APIBLAZE_DIR2, CONSUMER_PATH, DEVICE_GRANT;
3057
+ var fs7, os5, path8, crypto, import_child_process3, AUTH_BASE, APIBLAZE_DIR2, CONSUMER_PATH, DEVICE_GRANT;
3032
3058
  var init_consumer_auth = __esm({
3033
3059
  "src/lib/consumer-auth.ts"() {
3034
3060
  "use strict";
@@ -3036,7 +3062,7 @@ var init_consumer_auth = __esm({
3036
3062
  os5 = __toESM(require("os"));
3037
3063
  path8 = __toESM(require("path"));
3038
3064
  crypto = __toESM(require("crypto"));
3039
- import_child_process2 = require("child_process");
3065
+ import_child_process3 = require("child_process");
3040
3066
  AUTH_BASE = process.env.APIBLAZE_AUTH_BASE || "https://auth.apiblaze.com";
3041
3067
  APIBLAZE_DIR2 = path8.join(os5.homedir(), ".apiblaze");
3042
3068
  CONSUMER_PATH = path8.join(APIBLAZE_DIR2, "consumer.json");
@@ -4738,7 +4764,7 @@ async function removeExternalMcp(projectId, cliKind) {
4738
4764
  const label3 = kind === "claude" ? "Claude CLI" : "Codex CLI";
4739
4765
  let removed = false;
4740
4766
  if (kind === "claude") {
4741
- const r = (0, import_child_process3.spawnSync)("claude", ["mcp", "remove", projectId], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 });
4767
+ const r = (0, import_child_process4.spawnSync)("claude", ["mcp", "remove", projectId], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: 15e3 });
4742
4768
  removed = r.status === 0;
4743
4769
  } else {
4744
4770
  removed = removeCodexServer(projectId);
@@ -4842,14 +4868,14 @@ async function runApichat(opts) {
4842
4868
  if (mcpUrl2 && await maybeInstallExternalCli(p, opts)) return;
4843
4869
  await startChat(p, [], opts);
4844
4870
  }
4845
- var fs9, path9, crypto2, import_child_process3, import_chalk15, import_ora2, import_yaml3, GENERATOR_HINT, revealAuth, apichatsPath;
4871
+ var fs9, path9, crypto2, import_child_process4, import_chalk15, import_ora2, import_yaml3, GENERATOR_HINT, revealAuth, apichatsPath;
4846
4872
  var init_apichat = __esm({
4847
4873
  "src/commands/apichat.ts"() {
4848
4874
  "use strict";
4849
4875
  fs9 = __toESM(require("fs"));
4850
4876
  path9 = __toESM(require("path"));
4851
4877
  crypto2 = __toESM(require("crypto"));
4852
- import_child_process3 = require("child_process");
4878
+ import_child_process4 = require("child_process");
4853
4879
  import_chalk15 = __toESM(require("chalk"));
4854
4880
  import_ora2 = __toESM(require("ora"));
4855
4881
  import_yaml3 = require("yaml");
@@ -7659,6 +7685,7 @@ __export(demo_exports, {
7659
7685
  DEMO_RPS: () => DEMO_RPS,
7660
7686
  demoName: () => demoName,
7661
7687
  isDemoProcess: () => isDemoProcess,
7688
+ portFree: () => portFree,
7662
7689
  routesOf: () => routesOf,
7663
7690
  runDemo: () => runDemo,
7664
7691
  runDemoLogs: () => runDemoLogs,
@@ -7670,17 +7697,31 @@ __export(demo_exports, {
7670
7697
  function demoName() {
7671
7698
  return `demo${pick2(ADJ)}${pick2(NOUN)}${import_crypto.default.randomBytes(2).toString("hex")}`;
7672
7699
  }
7673
- function portFree(port) {
7674
- return new Promise((resolve4) => {
7700
+ async function portFree(port) {
7701
+ const answers = (host) => new Promise((resolve4) => {
7702
+ const sock = import_net2.default.connect({ port, host });
7703
+ sock.setTimeout(1e3, () => {
7704
+ sock.destroy();
7705
+ resolve4(true);
7706
+ });
7707
+ sock.once("connect", () => {
7708
+ sock.destroy();
7709
+ resolve4(true);
7710
+ });
7711
+ sock.once("error", () => resolve4(false));
7712
+ });
7713
+ const bindable = (host) => new Promise((resolve4) => {
7675
7714
  const s = import_net2.default.createServer();
7676
- s.once("error", () => resolve4(false));
7715
+ s.once("error", (e) => resolve4(host === "::1" && (e.code === "EADDRNOTAVAIL" || e.code === "EAFNOSUPPORT")));
7677
7716
  s.once("listening", () => s.close(() => resolve4(true)));
7678
- s.listen(port, "127.0.0.1");
7717
+ s.listen(port, host);
7679
7718
  });
7719
+ const [v4, v6] = await Promise.all([answers("127.0.0.1"), answers("::1")]);
7720
+ return !v4 && !v6 && await bindable("127.0.0.1") && await bindable("::1");
7680
7721
  }
7681
7722
  async function freePort(from, avoid = []) {
7682
7723
  for (let p = from; p < from + 200; p++) if (!avoid.includes(p) && await portFree(p)) return p;
7683
- throw new DemoError("No free port for the demo app.", "Close another local server, then run again.");
7724
+ throw new DemoError("No free port for the demo app.", "Close another local server, then run again.", "no-free-port");
7684
7725
  }
7685
7726
  function readRegistry() {
7686
7727
  try {
@@ -7737,15 +7778,16 @@ async function stopTunnels() {
7737
7778
  }
7738
7779
  function appCtl(dir, arg) {
7739
7780
  if (!import_fs6.default.existsSync(import_path5.default.join(dir, "start.js"))) return false;
7740
- const r = (0, import_child_process4.spawnSync)(process.execPath, [import_path5.default.join(dir, "start.js"), arg], { cwd: dir, stdio: "ignore", timeout: 15e3 });
7781
+ const r = (0, import_child_process5.spawnSync)(process.execPath, [import_path5.default.join(dir, "start.js"), arg], { cwd: dir, stdio: "ignore", timeout: 15e3 });
7741
7782
  return r.status === 0;
7742
7783
  }
7743
7784
  function isDemoProcess(args) {
7744
- return /[\\/](apiblaze|mcpblaze)-demo[\\/]start\.js(\s|$)/.test(args) || /\sdemo --serve\s/.test(args);
7785
+ const node = String.raw`^(?:\S*[\\/])?node(?:\.exe)?\s+`;
7786
+ return new RegExp(node + String.raw`.*[\\/](?:apiblaze|mcpblaze)-demo[\\/]start\.js$`).test(args.trim()) || new RegExp(node + String.raw`\S.*\sdemo --serve\s`).test(args);
7745
7787
  }
7746
7788
  function demoProcesses() {
7747
7789
  if (process.platform === "win32") return [];
7748
- const r = (0, import_child_process4.spawnSync)("ps", ["-Ao", "pid=,args="], { encoding: "utf8" });
7790
+ const r = (0, import_child_process5.spawnSync)("ps", ["-Ao", "pid=,args="], { encoding: "utf8" });
7749
7791
  if (r.status !== 0 || !r.stdout) return [];
7750
7792
  const out = [];
7751
7793
  for (const line of r.stdout.split("\n")) {
@@ -7860,19 +7902,19 @@ async function createProxy2(name, backendPort2, openapi, tenantName) {
7860
7902
  return await createProxyAnonymous(body);
7861
7903
  } catch (err) {
7862
7904
  const status2 = err.status;
7863
- if (status2 === 429) throw new DemoError("Too many demos were started from this network in the last hour.", `Wait a few minutes, then run: ${LATEST} demo`);
7864
- if (status2 && status2 >= 500) throw new DemoError(`${B.product} is having trouble right now.`, `Try again in a minute: ${LATEST} demo`);
7865
- throw new DemoError(`Could not create the demo (${err.message}).`, `Run again: ${LATEST} demo`);
7905
+ if (status2 === 429) throw new DemoError("Too many demos were started from this network in the last hour.", `Wait a few minutes, then run: ${LATEST} demo`, "create-429");
7906
+ if (status2 && status2 >= 500) throw new DemoError(`${B.product} is having trouble right now.`, `Try again in a minute: ${LATEST} demo`, "create-5xx");
7907
+ throw new DemoError(`Could not create the demo (${err.message}).`, `Run again: ${LATEST} demo`, "create-failed");
7866
7908
  }
7867
7909
  }
7868
7910
  async function waitTunnel(dir) {
7869
7911
  for (let i = 0; i < 200; i++) {
7870
7912
  const st = loadState(dir);
7871
- if (st?.error) throw new DemoError(st.error, `Run again: ${LATEST} demo`);
7913
+ if (st?.error) throw new DemoError(st.error, `Run again: ${LATEST} demo`, "tunnel-error");
7872
7914
  if (st?.ready) return;
7873
7915
  await new Promise((r) => setTimeout(r, 100));
7874
7916
  }
7875
- throw new DemoError("The tunnel did not open in time.", `Try: ${LATEST} demo restart`);
7917
+ throw new DemoError("The tunnel did not open in time.", `Try: ${LATEST} demo restart`, "tunnel-timeout");
7876
7918
  }
7877
7919
  async function waitThrough(apiUrl, serverKey) {
7878
7920
  let last = 0;
@@ -7881,13 +7923,13 @@ async function waitThrough(apiUrl, serverKey) {
7881
7923
  const r = await fetch(`${apiUrl}/reservations`, { headers: { "x-api-key": serverKey, "x-end-user-id": "ana" } });
7882
7924
  last = r.status;
7883
7925
  if (r.status === 200) return;
7884
- if (r.status === 402) throw new DemoError(`${B.product} refused the demo's free calls. That is a problem on our side, not yours.`, `Try again in a few minutes: ${LATEST} demo`);
7926
+ if (r.status === 402) throw new DemoError(`${B.product} refused the demo's free calls. That is a problem on our side, not yours.`, `Try again in a few minutes: ${LATEST} demo`, "trial-402");
7885
7927
  } catch (e) {
7886
7928
  if (e instanceof DemoError) throw e;
7887
7929
  }
7888
7930
  await new Promise((r) => setTimeout(r, 1e3));
7889
7931
  }
7890
- throw new DemoError(`The demo is running but ${B.product} cannot reach it yet (last answer ${last}).`, `Run again: ${LATEST} demo`);
7932
+ throw new DemoError(`The demo is running but ${B.product} cannot reach it yet (last answer ${last}).`, `Run again: ${LATEST} demo`, `cannot-reach-${last}`);
7891
7933
  }
7892
7934
  async function confirmStep(opts, question) {
7893
7935
  if (opts.yes || !interactive()) return true;
@@ -7918,7 +7960,7 @@ function openBrowser3(url) {
7918
7960
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
7919
7961
  const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
7920
7962
  try {
7921
- (0, import_child_process4.spawn)(cmd, args, { stdio: "ignore", detached: true }).on("error", () => {
7963
+ (0, import_child_process5.spawn)(cmd, args, { stdio: "ignore", detached: true }).on("error", () => {
7922
7964
  }).unref();
7923
7965
  } catch {
7924
7966
  }
@@ -7962,9 +8004,10 @@ function routesOf(openapi) {
7962
8004
  async function runDemo(opts = {}) {
7963
8005
  if (opts.serve) return serveTunnel(import_path5.default.resolve(opts.serve));
7964
8006
  try {
7965
- await startDemo(opts);
8007
+ countRun({ event: await startDemo(opts) });
7966
8008
  } catch (err) {
7967
8009
  const e = err instanceof DemoError ? err : new DemoError(`The demo could not start (${err.message}).`, `Run again: ${LATEST} demo`);
8010
+ countRun({ event: `demo-failed-${e.why}` });
7968
8011
  await stopDemos().catch(() => 0);
7969
8012
  console.log(`${import_chalk24.default.red("\u2717")} ${e.message}`);
7970
8013
  console.log(` ${e.fix}`);
@@ -8020,9 +8063,10 @@ async function startDemo(opts) {
8020
8063
  const cdDir = rel === "." ? "" : `cd ${rel} && `;
8021
8064
  const shell = (c, note = "") => console.log(` ${import_chalk24.default.dim("$")} ${import_chalk24.default.cyan(c)}${note ? ` ${import_chalk24.default.dim(note)}` : ""}`);
8022
8065
  const ok = (text, note = "") => console.log(` ${import_chalk24.default.green("\u2713")} ${text}${note ? ` ${import_chalk24.default.dim(note)}` : ""}`);
8023
- const stopHere = (lines) => {
8066
+ const stopHere = (step, lines) => {
8024
8067
  console.log();
8025
8068
  for (const l of lines) console.log(` ${l}`);
8069
+ return `demo-declined-${step}`;
8026
8070
  };
8027
8071
  titleCard(["Restaurant Reservation Service Demo", `ResiResi, with ${B.product} in front`]);
8028
8072
  await stopDemos();
@@ -8049,16 +8093,16 @@ paths:`));
8049
8093
  for (const r of routesOf(openapi)) console.log(` ${r.method.padEnd(7)}${r.path.padEnd(21)}${import_chalk24.default.dim(r.summary)}`);
8050
8094
  console.log(import_chalk24.default.dim(" No login, no API keys, no permission checks in its code. It only checks one shared backend secret (in .env.local)."));
8051
8095
  if (!await confirmStep(opts, `Ok to serve the backend on localhost:${backendPort2}?`)) {
8052
- return stopHere([
8096
+ return stopHere("backend", [
8053
8097
  `Stopped before starting anything. ResiResi is written to ${rel}.`,
8054
8098
  `Start it any time: ${cdDir}npm run start ${import_chalk24.default.dim("\xB7")} or run the whole demo again: ${LATEST} demo`
8055
8099
  ]);
8056
8100
  }
8057
8101
  shell(`${cdDir}npm run start`, "npm run stop \xB7 npm run restart");
8058
- if (!appCtl(dir, "--background")) throw new DemoError("The ResiResi app could not start.", `Run again: ${LATEST} demo`);
8102
+ if (!appCtl(dir, "--background")) throw new DemoError("The ResiResi app could not start.", `Run again: ${LATEST} demo`, "app-start");
8059
8103
  registerDemo(dir);
8060
8104
  const appUp = await busy("Starting the reservation backend and its front-end\u2026", () => waitPorts([backendPort2, directPort]), 1800);
8061
- if (!appUp) throw new DemoError(`The ResiResi app did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Run again: ${LATEST} demo`);
8105
+ if (!appUp) throw new DemoError(`The ResiResi app did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Run again: ${LATEST} demo`, "app-not-up");
8062
8106
  ok(`${import_chalk24.default.bold("Reservation backend")}: localhost:${backendPort2}`, "unprotected");
8063
8107
  ok(`${import_chalk24.default.bold("Unprotected reservation front-end")}, going directly to the reservation backend: ${import_chalk24.default.bold(`http://localhost:${directPort}`)}`);
8064
8108
  console.log(" Book a table directly, the way the unprotected front-end does:");
@@ -8067,7 +8111,7 @@ paths:`));
8067
8111
  console.log(import_chalk24.default.cyan(` -H "x-abz-user-id: ana" -H "x-abz-tenant-id: ${ninoTenant}" \\`));
8068
8112
  console.log(import_chalk24.default.cyan(` -H "content-type: application/json" -d '{"time":"19:30","guests":2}'`));
8069
8113
  if (!await confirmStep(opts, `Ok to put an ${B.product} proxy and MCP in front of the reservation backend?`)) {
8070
- return stopHere([
8114
+ return stopHere("proxy", [
8071
8115
  `Stopped before ${B.product}. ResiResi keeps running on this laptop: http://localhost:${directPort} ${import_chalk24.default.dim(`(${cdDir}npm run stop stops it)`)}`,
8072
8116
  `Run the whole demo again any time: ${LATEST} demo`
8073
8117
  ]);
@@ -8086,7 +8130,7 @@ paths:`));
8086
8130
  const rr = r;
8087
8131
  const tenant3 = rr.tenant ?? tenantFromPortal(rr.portal ?? rr.devPortal) ?? "";
8088
8132
  const missing = [!cpKey2 && "workspace key", !teamId2 && "team", !serverKey2 && "API key", !tenant3 && "tenant"].filter(Boolean);
8089
- if (missing.length) throw new DemoError(`${B.product} did not return everything the demo needs (${missing.join(", ")}).`, `Run again: ${LATEST} demo`);
8133
+ if (missing.length) throw new DemoError(`${B.product} did not return everything the demo needs (${missing.join(", ")}).`, `Run again: ${LATEST} demo`, "setup-incomplete");
8090
8134
  const createSecs2 = ((Date.now() - tCreate) / 1e3).toFixed(1);
8091
8135
  adoptWorkspace(cpKey2, teamId2, rr.claim_code);
8092
8136
  const cp = (p, init) => cpFetch(cpKey2, p, init).then(() => true).catch(() => false);
@@ -8108,7 +8152,7 @@ paths:`));
8108
8152
  return { limited: lim, secretOk: sec };
8109
8153
  })()
8110
8154
  ]);
8111
- if (!secretOk) throw new DemoError(`Could not give ${B.product} the backend secret.`, `Run again: ${LATEST} demo`);
8155
+ if (!secretOk) throw new DemoError(`Could not give ${B.product} the backend secret.`, `Run again: ${LATEST} demo`, "secret-failed");
8112
8156
  return { cpKey: cpKey2, teamId: teamId2, serverKey: serverKey2, project: project2, version: version3, tenant: tenant3, gino: gino2, createSecs: createSecs2, adminOk: adminOk2, ginaOk: ginaOk2, limited: limited2, claimCode: rr.claim_code, applied: rr.secure?.applied ?? [] };
8113
8157
  });
8114
8158
  const { cpKey, teamId, serverKey, project, version: version2, tenant: tenant2, gino, createSecs, adminOk, ginaOk, limited } = setup;
@@ -8160,7 +8204,7 @@ paths:`));
8160
8204
  }
8161
8205
  ]);
8162
8206
  if (!await confirmStep(opts, `Ok to create a tunnel allowing ${project} to forward requests to localhost:${backendPort2}?`)) {
8163
- return stopHere([
8207
+ return stopHere("tunnel", [
8164
8208
  `Stopped before the tunnel. The proxy exists but cannot reach this laptop yet.`,
8165
8209
  `Open the tunnel any time: ${LATEST} demo start`
8166
8210
  ]);
@@ -8175,7 +8219,7 @@ paths:`));
8175
8219
  ok(`${import_chalk24.default.bold("Tunnel")}: the public address reaches localhost:${backendPort2} on this laptop`, `checked with a real call \xB7 ${Math.round((Date.now() - t0) / 1e3)} s so far`);
8176
8220
  printAddresses(dir);
8177
8221
  if (!await confirmStep(opts, "Ok to launch the protected website, which will use the proxy to reach the backend?")) {
8178
- return stopHere([
8222
+ return stopHere("website", [
8179
8223
  `Stopped before the protected website. The API and MCP addresses above work now.`,
8180
8224
  `Start the website any time: ${cdDir}npm run restart`
8181
8225
  ]);
@@ -8188,12 +8232,13 @@ paths:`));
8188
8232
  appCtl(dir, "--background");
8189
8233
  return waitPorts([appPort, directPort, backendPort2]);
8190
8234
  }, 1500);
8191
- if (!siteUp) throw new DemoError(`The protected website did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Try: ${cdDir}npm run restart`);
8235
+ if (!siteUp) throw new DemoError(`The protected website did not start (see ${import_path5.default.join(rel, STATE_DIR, "app.log")}).`, `Try: ${cdDir}npm run restart`, "site-not-up");
8192
8236
  ok(`${import_chalk24.default.bold("Protected reservation website")}, going through the ${B.product} proxy ${apiUrl}: ${import_chalk24.default.bold(`http://localhost:${appPort}`)}`);
8193
8237
  ok(`${import_chalk24.default.bold("Unprotected reservation front-end")}, still going directly to the backend: http://localhost:${directPort}`, "the switch at the top of the page goes between them");
8194
8238
  console.log();
8195
8239
  console.log(import_chalk24.default.dim(` Logs: ${logsCommand(readEnvFile(import_path5.default.join(dir, ".env.local")))} \xB7 Stop the tunnel: ${LATEST} demo stop \xB7 Your own app: ${LATEST} dev --port <port>`));
8196
8240
  await offerBrowserAndLogs(dir, opts);
8241
+ return "demo-ready";
8197
8242
  }
8198
8243
  function printAddresses(dir) {
8199
8244
  const env = readEnvFile(import_path5.default.join(dir, ".env.local"));
@@ -8204,7 +8249,7 @@ function printAddresses(dir) {
8204
8249
  }
8205
8250
  async function spawnTunnel(dir, state) {
8206
8251
  const log4 = import_fs6.default.openSync(import_path5.default.join(dir, STATE_DIR, "tunnel.log"), "a");
8207
- const child = (0, import_child_process4.spawn)(process.execPath, [process.argv[1], "demo", "--serve", dir], { cwd: dir, detached: true, stdio: ["ignore", log4, log4] });
8252
+ const child = (0, import_child_process5.spawn)(process.execPath, [process.argv[1], "demo", "--serve", dir], { cwd: dir, detached: true, stdio: ["ignore", log4, log4] });
8208
8253
  child.unref();
8209
8254
  saveState(dir, { ...state, pid: child.pid, ready: false, error: void 0 });
8210
8255
  registerDemo(dir, child.pid ?? 0);
@@ -8225,10 +8270,12 @@ async function runDemoStart(opts = {}) {
8225
8270
  await spawnTunnel(dir, state);
8226
8271
  } catch (err) {
8227
8272
  const e = err instanceof DemoError ? err : new DemoError(`The tunnel could not start (${err.message}).`, `Start over: ${LATEST} demo`);
8273
+ countRun({ event: `demo-restart-failed-${e.why}` });
8228
8274
  console.log(`${import_chalk24.default.red("\u2717")} ${e.message}`);
8229
8275
  console.log(` ${e.fix}`);
8230
8276
  process.exit(1);
8231
8277
  }
8278
+ countRun({ event: "demo-restarted" });
8232
8279
  console.log(`${import_chalk24.default.green("\u2713")} Tunnel open: the public address reaches localhost:${env.BACKEND_PORT} again ${import_chalk24.default.dim(`(${Math.round((Date.now() - t0) / 1e3)} s)`)}`);
8233
8280
  printAddresses(dir);
8234
8281
  const appUp = await fetch(`http://127.0.0.1:${env.APP_PORT}/api/config`).then(() => true).catch(() => false);
@@ -8306,14 +8353,14 @@ async function serveTunnel(dir) {
8306
8353
  await new Promise(() => {
8307
8354
  });
8308
8355
  }
8309
- var import_fs6, import_path5, import_net2, import_child_process4, import_crypto, import_chalk24, import_ora9, DIR_NAME, STATE_DIR, LATEST, DEMO_RPS, DEMO_ADMIN_EMAIL, DEMO_GINO_ADMIN_EMAIL, RULE_SENTENCE, DemoError, ADJ, NOUN, pick2, registryFile, alive, interactive, logsCommand;
8356
+ var import_fs6, import_path5, import_net2, import_child_process5, import_crypto, import_chalk24, import_ora9, DIR_NAME, STATE_DIR, LATEST, DEMO_RPS, DEMO_ADMIN_EMAIL, DEMO_GINO_ADMIN_EMAIL, RULE_SENTENCE, DemoError, ADJ, NOUN, pick2, registryFile, alive, interactive, logsCommand;
8310
8357
  var init_demo = __esm({
8311
8358
  "src/commands/demo.ts"() {
8312
8359
  "use strict";
8313
8360
  import_fs6 = __toESM(require("fs"));
8314
8361
  import_path5 = __toESM(require("path"));
8315
8362
  import_net2 = __toESM(require("net"));
8316
- import_child_process4 = require("child_process");
8363
+ import_child_process5 = require("child_process");
8317
8364
  import_crypto = __toESM(require("crypto"));
8318
8365
  import_chalk24 = __toESM(require("chalk"));
8319
8366
  import_ora9 = __toESM(require("ora"));
@@ -8332,9 +8379,10 @@ var init_demo = __esm({
8332
8379
  DEMO_GINO_ADMIN_EMAIL = "gina@ginopizza.example";
8333
8380
  RULE_SENTENCE = "Only the person who made a reservation can change or cancel it";
8334
8381
  DemoError = class extends Error {
8335
- constructor(message, fix) {
8382
+ constructor(message, fix, why = "unexpected") {
8336
8383
  super(message);
8337
8384
  this.fix = fix;
8385
+ this.why = why;
8338
8386
  }
8339
8387
  };
8340
8388
  ADJ = ["brave", "calm", "clever", "eager", "happy", "jolly", "lucky", "merry", "noble", "quick", "sunny", "swift", "witty", "zesty", "bright", "gentle"];
@@ -8587,7 +8635,7 @@ __export(op_anon_exports, {
8587
8635
  function runCli(args, cwd, home, timeoutMs) {
8588
8636
  return new Promise((resolve4) => {
8589
8637
  const t0 = Date.now();
8590
- const child = (0, import_child_process5.spawn)(process.execPath, [process.argv[1], ...args], { cwd, env: { ...process.env, HOME: home, USERPROFILE: home }, stdio: ["ignore", "pipe", "pipe"] });
8638
+ const child = (0, import_child_process6.spawn)(process.execPath, [process.argv[1], ...args], { cwd, env: { ...process.env, HOME: home, USERPROFILE: home }, stdio: ["ignore", "pipe", "pipe"] });
8591
8639
  let out = "";
8592
8640
  child.stdout.on("data", (d) => {
8593
8641
  out += d;
@@ -8721,7 +8769,7 @@ async function runOpAnon(o = {}) {
8721
8769
  const demoFolder = import_path7.default.join(work, `${B.cli}-demo`);
8722
8770
  if (import_fs10.default.existsSync(import_path7.default.join(demoFolder, "start.js"))) {
8723
8771
  await new Promise((res) => {
8724
- const c = (0, import_child_process5.spawn)(process.execPath, [import_path7.default.join(demoFolder, "start.js"), "--stop"], { cwd: demoFolder, stdio: "ignore" });
8772
+ const c = (0, import_child_process6.spawn)(process.execPath, [import_path7.default.join(demoFolder, "start.js"), "--stop"], { cwd: demoFolder, stdio: "ignore" });
8725
8773
  c.on("close", () => res());
8726
8774
  });
8727
8775
  }
@@ -8745,14 +8793,14 @@ async function runOpAnon(o = {}) {
8745
8793
  if (failed) process.exitCode = 1;
8746
8794
  return { ok: !failed, checks };
8747
8795
  }
8748
- var import_fs10, import_os3, import_path7, import_child_process5, import_chalk70, SECRET;
8796
+ var import_fs10, import_os3, import_path7, import_child_process6, import_chalk70, SECRET;
8749
8797
  var init_op_anon = __esm({
8750
8798
  "src/commands/op-anon.ts"() {
8751
8799
  "use strict";
8752
8800
  import_fs10 = __toESM(require("fs"));
8753
8801
  import_os3 = __toESM(require("os"));
8754
8802
  import_path7 = __toESM(require("path"));
8755
- import_child_process5 = require("child_process");
8803
+ import_child_process6 = require("child_process");
8756
8804
  import_chalk70 = __toESM(require("chalk"));
8757
8805
  init_brand();
8758
8806
  SECRET = /\bsk_(?:dev|test|prod)_[A-Za-z0-9]{6,}/;
@@ -9546,6 +9594,7 @@ function backendPort(f) {
9546
9594
  }
9547
9595
 
9548
9596
  // src/commands/skill.ts
9597
+ init_api();
9549
9598
  var SKILL_NAME = "ship-safely";
9550
9599
  var SKILL_RELATIVE_PATH = import_path6.default.join(".claude", "skills", SKILL_NAME, "SKILL.md");
9551
9600
  var CP_TOOLS = [
@@ -10189,6 +10238,7 @@ async function runForPerson(found) {
10189
10238
  await runDemo2({});
10190
10239
  }
10191
10240
  async function runSkill(opts = {}) {
10241
+ countRun();
10192
10242
  if (opts.install) return installSkill(opts);
10193
10243
  if (opts.static) {
10194
10244
  process.stdout.write(skillMarkdown());
@@ -13482,10 +13532,10 @@ async function runBillingTopup(amount, opts) {
13482
13532
  if (c?.auto_rebuy?.enabled) console.log(import_chalk48.default.dim(` Auto-rebuy is on (${money(c.auto_rebuy.amount_cents)} under ${money(c.auto_rebuy.threshold_cents)}).`));
13483
13533
  else console.log(import_chalk48.default.dim(` Never run dry: ${B.cli} billing rebuy on --amount 50 --threshold 10`));
13484
13534
  if (opts.open) {
13485
- const { spawn: spawn4 } = await import("child_process");
13535
+ const { spawn: spawn5 } = await import("child_process");
13486
13536
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
13487
13537
  try {
13488
- spawn4(cmd, [url], { stdio: "ignore", detached: true }).unref();
13538
+ spawn5(cmd, [url], { stdio: "ignore", detached: true }).unref();
13489
13539
  } catch {
13490
13540
  }
13491
13541
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.21.39",
3
+ "version": "0.21.41",
4
4
  "description": "APIblaze CLI — Chat with your APIs, Manage your API keys, users and groups with the APIblaze serverless proxy",
5
5
  "keywords": [
6
6
  "apiblaze",