sproutboat 0.4.2 → 0.4.3

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/SURFACE.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Generated by `src/surface.test.ts` from `src/surface.ts` + the pinned
4
4
  > toolchain constants. Do not edit by hand — run `UPDATE_SURFACE=1 bun test`.
5
5
 
6
- **Package:** `sproutboat` 0.4.2 · runs on Bun (use `bunx`, not `npx`)
6
+ **Package:** `sproutboat` 0.4.3 · runs on Bun (use `bunx`, not `npx`)
7
7
 
8
8
  ## Commands
9
9
 
@@ -13,16 +13,16 @@
13
13
  | `check` | `[project-dir]` | Validate the config and entry point without building. |
14
14
  | `build` | `[project-dir]` | Cross-compile the native-fetch sprout (Porffor + Zig). |
15
15
  | `deploy` | `[project-dir] [--dry-run] [--artifact <dir>] [--no-wait]` | Build (unless --artifact), print the report, upload, wait until the URL serves. --dry-run stops before upload; --no-wait skips the health check. |
16
- | `login` | `[--api-url <url>] [--token <token>]` | Device-code browser flow, or store <token> for <url> directly. |
17
- | `tail` | `[project-dir] [--sprout]` | Print recent request logs; --sprout prints the running sprout + broker stdout/stderr instead. |
18
16
  | `versions` | `list [project-dir]` | List the project's deployed versions. |
19
17
  | `rollback` | `<version-id> [project-dir]` | Re-activate a previous version. |
18
+ | `tail` | `[project-dir] [--sprout]` | Print recent request logs; --sprout prints the running sprout + broker stdout/stderr instead. |
20
19
  | `domains` | `[list | add <host> | verify <host> | rm <host>] [project-dir]` | Attach a custom domain to the project (TXT-verified). No sub-command lists. |
21
20
  | `secrets` | `[list | set <NAME> [value] | rm <NAME>] [project-dir]` | Manage encrypted project secrets (read as env.NAME). `set` takes the value from the arg or stdin; applies on next deploy. |
22
21
  | `delete` | `[project-dir] [--name <project>] --yes` | Delete the project, every version, and its route. |
22
+ | `login` | `[--api-url <url>] [--token <token>]` | Device-code browser flow, or store <token> for <url> directly. |
23
23
 
24
24
  ```
25
- usage: sproutboat <init [name] | check [project-dir] | build [project-dir] | deploy [project-dir] [--dry-run] [--artifact <dir>] [--no-wait] | login [--api-url <url>] [--token <token>] | tail [project-dir] [--sprout] | versions list [project-dir] | rollback <version-id> [project-dir] | domains [list | add <host> | verify <host> | rm <host>] [project-dir] | secrets [list | set <NAME> [value] | rm <NAME>] [project-dir] | delete [project-dir] [--name <project>] --yes>
25
+ usage: sproutboat <init [name] | check [project-dir] | build [project-dir] | deploy [project-dir] [--dry-run] [--artifact <dir>] [--no-wait] | versions list [project-dir] | rollback <version-id> [project-dir] | tail [project-dir] [--sprout] | domains [list | add <host> | verify <host> | rm <host>] [project-dir] | secrets [list | set <NAME> [value] | rm <NAME>] [project-dir] | delete [project-dir] [--name <project>] --yes | login [--api-url <url>] [--token <token>]>
26
26
  ```
27
27
 
28
28
  ## Environment variables
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sproutboat",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Wrangler-shaped CLI for Sproutboat. Deploys workers to any control plane via --api-url / SPROUTBOAT_API_URL.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/main.ts CHANGED
@@ -7,7 +7,7 @@ import { buildArtifact } from "./build";
7
7
  import { validateManifest, type ArtifactManifest } from "./manifest";
8
8
  import { printDeployReport } from "./report";
9
9
  import { activeApiUrl, savedToken, saveToken } from "./credentials";
10
- import { usageLine } from "./surface";
10
+ import { helpText, usageLine } from "./surface";
11
11
  import { amber, bold, dim, leaf, ok, rose } from "./style";
12
12
 
13
13
  const defaultApiUrl = "https://dashboard.sproutboat.com";
@@ -473,12 +473,20 @@ async function deleteProject(args: string[]) {
473
473
  if (failed.length) console.log(` ! ${failed.length} artifact file(s) left on disk — remove them manually`);
474
474
  }
475
475
 
476
+ /** `sproutboat` alone or with -h/--help/help: the friendly grouped list, exit 0. */
477
+ function help(): never {
478
+ console.log(helpText());
479
+ process.exit(0);
480
+ }
481
+
482
+ /** An unrecognised command: a short pointer on stderr, exit 1. */
476
483
  function usage(): never {
477
- console.error(usageLine());
484
+ console.error(`unknown command "${command}"\n${usageLine()}\nrun \`sproutboat\` with no arguments for the grouped command list`);
478
485
  process.exit(1);
479
486
  }
480
487
 
481
488
  const [command, ...args] = process.argv.slice(2);
489
+ if (command === undefined || command === "help" || command === "-h" || command === "--help") help();
482
490
  switch (command) {
483
491
  case "init": await init(args[0]); break;
484
492
  case "check": await check(args[0]); break;
package/src/surface.ts CHANGED
@@ -1,24 +1,57 @@
1
1
  /**
2
- * The CLI's public surface, as data. `main.ts` builds its usage string from
3
- * this, and `surface.test.ts` renders it to `SURFACE.md` and checks that the
4
- * command switch, the referenced env vars, and the doc can't drift apart.
2
+ * The CLI's public surface, as data. `main.ts` builds its help + usage strings
3
+ * from this, and `surface.test.ts` renders it to `SURFACE.md` and checks that
4
+ * the command switch, the referenced env vars, and the doc can't drift apart.
5
5
  */
6
+ import { bold, dim, leaf } from "./style";
7
+
6
8
  export const CLI_NAME = "sproutboat";
9
+ export const TAGLINE = "Deploy JavaScript handlers as tiny native binaries on your own VPS.";
10
+ export const REPO_URL = "https://github.com/baronunread/sproutboat";
11
+
12
+ export type Group = "Develop" | "Ship" | "Configure" | "Account";
7
13
 
8
- export type Command = { name: string; args: string; summary: string };
14
+ export type Command = {
15
+ name: string;
16
+ /** Full argument grammar — used by SURFACE.md and the one-line usage string. */
17
+ args: string;
18
+ /** Short argument hint shown in the grouped help list (falls back to `args`). */
19
+ brief?: string;
20
+ group: Group;
21
+ emoji: string;
22
+ summary: string;
23
+ };
9
24
 
10
25
  export const COMMANDS: readonly Command[] = [
11
- { name: "init", args: "[name]", summary: "Scaffold sproutboat.jsonc + src/index.js in ./<name>." },
12
- { name: "check", args: "[project-dir]", summary: "Validate the config and entry point without building." },
13
- { name: "build", args: "[project-dir]", summary: "Cross-compile the native-fetch sprout (Porffor + Zig)." },
14
- { name: "deploy", args: "[project-dir] [--dry-run] [--artifact <dir>] [--no-wait]", summary: "Build (unless --artifact), print the report, upload, wait until the URL serves. --dry-run stops before upload; --no-wait skips the health check." },
15
- { name: "login", args: "[--api-url <url>] [--token <token>]", summary: "Device-code browser flow, or store <token> for <url> directly." },
16
- { name: "tail", args: "[project-dir] [--sprout]", summary: "Print recent request logs; --sprout prints the running sprout + broker stdout/stderr instead." },
17
- { name: "versions", args: "list [project-dir]", summary: "List the project's deployed versions." },
18
- { name: "rollback", args: "<version-id> [project-dir]", summary: "Re-activate a previous version." },
19
- { name: "domains", args: "[list | add <host> | verify <host> | rm <host>] [project-dir]", summary: "Attach a custom domain to the project (TXT-verified). No sub-command lists." },
20
- { name: "secrets", args: "[list | set <NAME> [value] | rm <NAME>] [project-dir]", summary: "Manage encrypted project secrets (read as env.NAME). `set` takes the value from the arg or stdin; applies on next deploy." },
21
- { name: "delete", args: "[project-dir] [--name <project>] --yes", summary: "Delete the project, every version, and its route." },
26
+ { name: "init", group: "Develop", emoji: "🌱", args: "[name]",
27
+ summary: "Scaffold sproutboat.jsonc + src/index.js in ./<name>." },
28
+ { name: "check", group: "Develop", emoji: "🔍", args: "[project-dir]",
29
+ summary: "Validate the config and entry point without building." },
30
+ { name: "build", group: "Develop", emoji: "🔨", args: "[project-dir]",
31
+ summary: "Cross-compile the native-fetch sprout (Porffor + Zig)." },
32
+
33
+ { name: "deploy", group: "Ship", emoji: "🚀",
34
+ args: "[project-dir] [--dry-run] [--artifact <dir>] [--no-wait]", brief: "[project-dir] [--dry-run]",
35
+ summary: "Build (unless --artifact), print the report, upload, wait until the URL serves. --dry-run stops before upload; --no-wait skips the health check." },
36
+ { name: "versions", group: "Ship", emoji: "📜", args: "list [project-dir]",
37
+ summary: "List the project's deployed versions." },
38
+ { name: "rollback", group: "Ship", emoji: "⏮", args: "<version-id> [project-dir]", brief: "<version-id>",
39
+ summary: "Re-activate a previous version." },
40
+ { name: "tail", group: "Ship", emoji: "📡", args: "[project-dir] [--sprout]",
41
+ summary: "Print recent request logs; --sprout prints the running sprout + broker stdout/stderr instead." },
42
+
43
+ { name: "domains", group: "Configure", emoji: "🌐",
44
+ args: "[list | add <host> | verify <host> | rm <host>] [project-dir]", brief: "[list | add | verify | rm]",
45
+ summary: "Attach a custom domain to the project (TXT-verified). No sub-command lists." },
46
+ { name: "secrets", group: "Configure", emoji: "🔑",
47
+ args: "[list | set <NAME> [value] | rm <NAME>] [project-dir]", brief: "[list | set | rm]",
48
+ summary: "Manage encrypted project secrets (read as env.NAME). `set` takes the value from the arg or stdin; applies on next deploy." },
49
+ { name: "delete", group: "Configure", emoji: "🗑",
50
+ args: "[project-dir] [--name <project>] --yes", brief: "[project-dir] --yes",
51
+ summary: "Delete the project, every version, and its route." },
52
+
53
+ { name: "login", group: "Account", emoji: "🔓", args: "[--api-url <url>] [--token <token>]", brief: "[--token <token>]",
54
+ summary: "Device-code browser flow, or store <token> for <url> directly." },
22
55
  ];
23
56
 
24
57
  export type EnvVar = { name: string; purpose: string };
@@ -40,8 +73,38 @@ export const ENV_VARS: readonly EnvVar[] = [
40
73
  { name: "SB_SPROUT_URL", purpose: "http://127.0.0.1:<PORT> of the sprout; when set, `src/broker.ts` runs the cron scheduler and queue consumer and delivers triggers to it." },
41
74
  ];
42
75
 
43
- /** One-line usage string, e.g. for `usage()` and `--help`. */
76
+ const GROUP_ORDER: readonly Group[] = ["Develop", "Ship", "Configure", "Account"];
77
+
78
+ /** One-line usage string, e.g. for `usage()` and SURFACE.md. */
44
79
  export function usageLine(): string {
45
80
  const parts = COMMANDS.map((c) => (c.args ? `${c.name} ${c.args}` : c.name));
46
81
  return `usage: ${CLI_NAME} <${parts.join(" | ")}>`;
47
82
  }
83
+
84
+ /** First sentence of a summary — enough for the at-a-glance command list. */
85
+ function firstSentence(text: string): string {
86
+ const end = text.indexOf(". ");
87
+ return end === -1 ? text.replace(/\.$/, "") : text.slice(0, end);
88
+ }
89
+
90
+ /** Wrangler-style grouped help: `sproutboat` with no command, or `--help`. */
91
+ export function helpText(): string {
92
+ const invocations = COMMANDS.map((c) => `${c.name} ${c.brief ?? c.args}`.trim());
93
+ const width = Math.max(...invocations.map((s) => s.length));
94
+ const lines: string[] = [`${bold(CLI_NAME)} — ${TAGLINE}`, "", leaf("USAGE"), ` ${CLI_NAME} <command> [options]`];
95
+
96
+ for (const group of GROUP_ORDER) {
97
+ lines.push("", leaf(group.toUpperCase()));
98
+ for (const command of COMMANDS.filter((c) => c.group === group)) {
99
+ const invocation = `${command.name} ${command.brief ?? command.args}`.trim();
100
+ lines.push(` ${invocation.padEnd(width)} ${command.emoji} ${firstSentence(command.summary)}`);
101
+ }
102
+ }
103
+
104
+ lines.push(
105
+ "",
106
+ dim(`Run \`${CLI_NAME} <command>\` with no/invalid args to see that command's full usage.`),
107
+ dim(`Docs: ${REPO_URL} · runs on Bun — use \`bunx\`, not \`npx\`.`),
108
+ );
109
+ return lines.join("\n");
110
+ }