wasper-cli 0.3.1 → 0.3.2

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 (4) hide show
  1. package/README.md +77 -83
  2. package/dist/cli.js +1829 -833
  3. package/dist/index.js +72 -28
  4. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3607,7 +3607,7 @@ var package_default;
3607
3607
  var init_package = __esm(() => {
3608
3608
  package_default = {
3609
3609
  name: "wasper-cli",
3610
- version: "0.3.1",
3610
+ version: "0.3.2",
3611
3611
  description: "Host an MCP server + API proxy from any OpenAPI spec. Like Drizzle Studio, but for APIs.",
3612
3612
  type: "module",
3613
3613
  homepage: "https://wasper.site",
@@ -6581,25 +6581,69 @@ var init_routes = __esm(() => {
6581
6581
  // src/daemon.ts
6582
6582
  import { join as join2 } from "path";
6583
6583
  import { homedir as homedir2 } from "os";
6584
- import { mkdir, readFile, writeFile, unlink } from "fs/promises";
6584
+ import { mkdir, readFile, readdir, writeFile, unlink } from "fs/promises";
6585
6585
  async function ensureDir() {
6586
- await mkdir(DIR, { recursive: true });
6586
+ await mkdir(WASPER_DIR, { recursive: true });
6587
+ }
6588
+ function stateFile(port) {
6589
+ return join2(WASPER_DIR, `server-${port}.json`);
6590
+ }
6591
+ function logFile(port) {
6592
+ return join2(WASPER_DIR, `server-${port}.log`);
6587
6593
  }
6588
6594
  async function writeDaemonState(s) {
6589
6595
  await ensureDir();
6590
- await writeFile(STATE_FILE, JSON.stringify(s, null, 2), "utf-8");
6596
+ await writeFile(stateFile(s.port), JSON.stringify(s, null, 2), "utf-8");
6591
6597
  }
6592
- async function readDaemonState() {
6598
+ async function readAllDaemonStates() {
6593
6599
  try {
6594
- const raw = await readFile(STATE_FILE, "utf-8");
6595
- return JSON.parse(raw);
6600
+ const files = await readdir(WASPER_DIR);
6601
+ const states = [];
6602
+ for (const f of files) {
6603
+ if (!f.match(/^server(-\d+)?\.json$/))
6604
+ continue;
6605
+ const filePath = join2(WASPER_DIR, f);
6606
+ try {
6607
+ const raw = await readFile(filePath, "utf-8");
6608
+ const state = JSON.parse(raw);
6609
+ if (isProcessAlive(state.pid)) {
6610
+ states.push(state);
6611
+ } else {
6612
+ await unlink(filePath).catch(() => {});
6613
+ }
6614
+ } catch {}
6615
+ }
6616
+ return states.sort((a, b) => a.port - b.port);
6596
6617
  } catch {
6597
- return null;
6618
+ return [];
6598
6619
  }
6599
6620
  }
6600
- async function clearDaemonState() {
6621
+ async function readDaemonState(port) {
6622
+ if (port !== undefined) {
6623
+ try {
6624
+ const raw = await readFile(stateFile(port), "utf-8");
6625
+ const state = JSON.parse(raw);
6626
+ return isProcessAlive(state.pid) ? state : null;
6627
+ } catch {
6628
+ return null;
6629
+ }
6630
+ }
6631
+ const all = await readAllDaemonStates();
6632
+ if (all.length === 0)
6633
+ return null;
6634
+ if (all.length === 1)
6635
+ return all[0];
6636
+ return all.find((s) => s.port === DEFAULT_PORT) ?? all[0];
6637
+ }
6638
+ async function clearDaemonState(port) {
6601
6639
  try {
6602
- await unlink(STATE_FILE);
6640
+ await unlink(stateFile(port));
6641
+ } catch {}
6642
+ try {
6643
+ const raw = await readFile(join2(WASPER_DIR, "server.json"), "utf-8");
6644
+ const state = JSON.parse(raw);
6645
+ if (state.port === port)
6646
+ await unlink(join2(WASPER_DIR, "server.json")).catch(() => {});
6603
6647
  } catch {}
6604
6648
  }
6605
6649
  function isProcessAlive(pid) {
@@ -6633,22 +6677,19 @@ async function spawnDaemon(specUrl, port, opts = {}) {
6633
6677
  args.push("--readonly");
6634
6678
  }
6635
6679
  args.push("--_daemon");
6636
- const logDir = DIR;
6637
6680
  await ensureDir();
6638
- const logPath = join2(logDir, "server.log");
6639
6681
  const child = Bun.spawn([process.execPath, Bun.main, ...args], {
6640
6682
  detached: true,
6641
6683
  cwd: process.cwd(),
6642
6684
  env: { ...process.env },
6643
- stdio: ["ignore", Bun.file(logPath), Bun.file(logPath)]
6685
+ stdio: ["ignore", Bun.file(logFile(port)), Bun.file(logFile(port))]
6644
6686
  });
6645
6687
  child.unref();
6646
6688
  return child.pid;
6647
6689
  }
6648
- var DIR, STATE_FILE;
6690
+ var WASPER_DIR, DEFAULT_PORT = 3388;
6649
6691
  var init_daemon = __esm(() => {
6650
- DIR = join2(homedir2(), ".wasper");
6651
- STATE_FILE = join2(DIR, "server.json");
6692
+ WASPER_DIR = join2(homedir2(), ".wasper");
6652
6693
  });
6653
6694
 
6654
6695
  // src/ui.ts
@@ -7508,7 +7549,7 @@ async function run2(overrideOpts) {
7508
7549
  ${paint.dim("shutting down")}
7509
7550
 
7510
7551
  `);
7511
- clearDaemonState().finally(() => {
7552
+ clearDaemonState(PORT).finally(() => {
7512
7553
  db.close();
7513
7554
  server.stop();
7514
7555
  process.exit(0);
@@ -7836,16 +7877,19 @@ function printInteractiveHelp() {
7836
7877
  }
7837
7878
  function printHelp() {
7838
7879
  console.log(`
7839
- Usage: wasper [start] [options]
7880
+ Usage: wasper start [options]
7881
+
7882
+ Starts wasper in the foreground with an interactive REPL.
7883
+ For background (daemon) mode \u2014 the default \u2014 use: wasper up
7840
7884
 
7841
- wasper [--url <spec-url>] [--port <port>] Start in foreground (auto-resumes last spec)
7842
- wasper start --background Start in background
7843
- wasper stop Stop background server
7844
- wasper status Show server status
7885
+ wasper up [--url <spec>] Start daemon in background (default)
7886
+ wasper start [--url <spec>] Start in foreground with REPL
7887
+ wasper down Stop the daemon
7888
+ wasper status Show daemon status
7889
+ wasper logs [-f] Tail server logs
7890
+ wasper service install Install as system service (auto-start)
7845
7891
  wasper reload Hot-reload the spec
7846
7892
  wasper ls List saved specs (history)
7847
- wasper use <number|url> Start with a saved spec
7848
- wasper rm <number|url> Remove a spec from history
7849
7893
 
7850
7894
  Options:
7851
7895
  --url, -u OpenAPI spec URL or local path
@@ -7860,17 +7904,17 @@ Options:
7860
7904
  --no-proxy Start with the HTTP proxy disabled
7861
7905
  --no-ai Start with the AI chat endpoint disabled
7862
7906
  --readonly Block all non-GET upstream requests (agent guardrail)
7863
- --background, -b Start detached in background
7907
+ --background, -b Start detached in background (same as wasper up)
7864
7908
  --daemon, -d Same as --background
7865
7909
  -h, --help Show this help
7866
7910
 
7867
- Interactive mode supports slash commands \u2014 press / and type:
7911
+ Interactive REPL slash commands (foreground mode):
7868
7912
  /mcp on|off \xB7 /proxy on|off \xB7 /ai on|off \xB7 /readonly on|off
7869
7913
  /auth use <role> \xB7 /token new \xB7 /spec <url> \xB7 /tail \xB7 /help
7870
7914
 
7871
7915
  Self-hosting:
7872
- wasper start --url <spec> --origin https://api.example.com --token <secret> -b
7873
- Then open the studio with ?server=https://api.example.com&token=<secret>
7916
+ wasper up --url <spec> --origin https://api.example.com --token <secret>
7917
+ wasper service install --url <spec> --port 3388
7874
7918
  `);
7875
7919
  }
7876
7920
  function buildScalarHtml(title, req) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wasper-cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Host an MCP server + API proxy from any OpenAPI spec. Like Drizzle Studio, but for APIs.",
5
5
  "type": "module",
6
6
  "homepage": "https://wasper.site",