@wervt/cli 0.2.1 → 0.2.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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
3
4
  import { dirname, join, relative, resolve } from "node:path";
4
5
  import { styleText } from "node:util";
5
6
  import * as p from "@clack/prompts";
@@ -7,7 +8,6 @@ import { defineCommand, runMain } from "citty";
7
8
  import { detectPackageManager } from "nypm";
8
9
  import { x } from "tinyexec";
9
10
  import { packTar } from "modern-tar";
10
- import { homedir } from "node:os";
11
11
  //#region src/bundle.ts
12
12
  /** Files under `dir`. Follows symlinks: Nitro can leave traced dependencies in
13
13
  * server/node_modules as links into the local pnpm store, which won't exist on the server. */
@@ -600,6 +600,55 @@ const logs = defineCommand({
600
600
  for (const entry of entries.reverse()) printLog(entry, Boolean(args.json));
601
601
  }
602
602
  });
603
+ const whoami = defineCommand({
604
+ meta: {
605
+ name: "whoami",
606
+ description: "Show the control plane and what the token can do"
607
+ },
608
+ args: connection,
609
+ async run({ args }) {
610
+ const { url, token } = await resolveConfig(args);
611
+ const deployApp = token.match(/^wervtd_([a-z0-9-]+)_/)?.[1];
612
+ let role = deployApp ? `deploy token for ${deployApp}` : "unknown";
613
+ if (!deployApp) {
614
+ const res = await fetch(`${url}/v1/databases`, { headers: { authorization: `Bearer ${token}` } });
615
+ if (res.status === 401) throw new Error(`The token is not valid for ${url}`);
616
+ role = res.ok ? "admin" : res.status === 403 ? "read" : `unknown (${res.status})`;
617
+ }
618
+ console.log(`${url}\n${role}`);
619
+ }
620
+ });
621
+ const skill = defineCommand({
622
+ meta: {
623
+ name: "skill",
624
+ description: "Install the wervt agent skill"
625
+ },
626
+ subCommands: { install: defineCommand({
627
+ meta: {
628
+ name: "install",
629
+ description: "Write the wervt skill to ~/.claude/skills/wervt, so agents can build and ship apps"
630
+ },
631
+ args: {
632
+ dir: {
633
+ type: "string",
634
+ description: "Skills directory (default: ~/.claude/skills; .claude/skills for --project)"
635
+ },
636
+ project: {
637
+ type: "boolean",
638
+ description: "Install into this project instead"
639
+ }
640
+ },
641
+ async run({ args }) {
642
+ const base = args.dir ?? (args.project ? ".claude/skills" : join(homedir(), ".claude", "skills"));
643
+ const target = resolve(base, "wervt", "SKILL.md");
644
+ const content = await readFile(new URL("../skill/SKILL.md", import.meta.url), "utf8");
645
+ const previous = await readFile(target, "utf8").catch(() => void 0);
646
+ await mkdir(join(target, ".."), { recursive: true });
647
+ await writeFile(target, content);
648
+ console.log(`${previous === void 0 ? "Installed" : previous === content ? "Up to date" : "Updated"}: ${target}`);
649
+ }
650
+ }) }
651
+ });
603
652
  const login = defineCommand({
604
653
  meta: {
605
654
  name: "login",
@@ -644,7 +693,9 @@ runMain(defineCommand({
644
693
  env,
645
694
  tokens,
646
695
  delete: remove,
647
- login
696
+ login,
697
+ whoami,
698
+ skill
648
699
  }
649
700
  }));
650
701
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wervt/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Deploy and manage apps on wervt.",
5
5
  "keywords": [
6
6
  "agents",
@@ -23,6 +23,7 @@
23
23
  "files": [
24
24
  "bin",
25
25
  "dist",
26
+ "skill",
26
27
  "README.md"
27
28
  ],
28
29
  "type": "module",
package/skill/SKILL.md ADDED
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: wervt
3
+ description: Build a small web app and ship it on the user's wervt server, or deploy, configure and debug an existing wervt app. Use when the user says "build X and ship it", "put it on wervt", "deploy this to wervt", "make it public", "set an env var", or asks why a wervt app fails. Covers scaffolding with create-wervt, deploying with the wervt CLI, access, env vars, GitHub Actions deploys, releases and logs.
4
+ ---
5
+
6
+ # wervt
7
+
8
+ wervt hosts small Nuxt apps on the user's own server: every app gets `https://<app>.<domain>`,
9
+ its own Postgres schema, live data (Electric), sign-in through wervt, and logs. You build the app
10
+ and ship it with the `wervt` CLI. One request ("build a habit tracker and ship it") should end with
11
+ a working URL.
12
+
13
+ ## 1. Check the connection
14
+
15
+ ```bash
16
+ npx -y @wervt/cli whoami
17
+ ```
18
+
19
+ It prints the control plane URL and what the saved token can do (`admin`, `read`, or a deploy
20
+ token for one app). Shipping new apps needs `admin`. If it says "Not logged in", ask the user to
21
+ run `wervt login --url <control plane URL> --token <admin token>` themselves (for example with
22
+ `! npx -y @wervt/cli login --url … --token …`). Never look for tokens in files, env or
23
+ keychains, and never print or ask to paste one into the chat.
24
+
25
+ ## 2. Scaffold
26
+
27
+ ```bash
28
+ pnpm create wervt <name> # a-z, 0-9 and dashes; becomes the subdomain
29
+ cd <name>
30
+ ```
31
+
32
+ The app comes with `AGENTS.md`, skills in `.claude/skills/` and a deploy workflow. **Read
33
+ `AGENTS.md` before writing code** and follow it: server routes, valibot validation, Drizzle schema
34
+ and migrations, live data with shapes, users, logging, and what the isolate runtime can't do
35
+ (no filesystem, no child processes, no long-running work, no WebSockets, no cron).
36
+
37
+ Before picking a name, run `wervt apps` so you don't overwrite an existing app. If the user named an
38
+ existing app, work in its repository instead.
39
+
40
+ ## 3. Build
41
+
42
+ - Keep the app small and complete: data in Postgres through `useDb()`, writes through API routes,
43
+ lists through `useShape` so every open tab stays live. Ask only when the request is genuinely
44
+ ambiguous; otherwise choose sensible defaults and say what you chose.
45
+ - Users: wervt signs people in. Use `useWervtUser()`; never build login, passwords or OAuth.
46
+ - After changing `server/db/schema.ts`: `pnpm db:generate`, and commit the migration.
47
+ - `pnpm dev` runs against a local wervt stack, which the user may not have. When it isn't running,
48
+ don't set one up: `pnpm build` plus `pnpm exec vp check --fix` are the checks, and the
49
+ deployed app is the test.
50
+ - Finish with `pnpm exec vp check --fix` passing.
51
+
52
+ ## 4. Ship
53
+
54
+ ```bash
55
+ pnpm exec wervt deploy # build, upload, migrate, activate
56
+ pnpm exec wervt access <name> --public # new apps are private (signed-in admins only)
57
+ pnpm exec wervt access <name> --add a@x.com,*@company.com # or let specific people in
58
+ pnpm exec wervt env <name> --set KEY=value # server env vars, applied from the next request
59
+ ```
60
+
61
+ - Ask before making an app public if it shows personal or private data. "Ship it" alone doesn't
62
+ decide that. Private, with the user's email allowed, is the safe default.
63
+ - Secrets for `wervt env` (API keys) come from the user. Ask them to run the command themselves
64
+ (`! pnpm exec wervt env <name> --set API_KEY=…`) instead of pasting the value to you.
65
+ - Then open the URL `wervt deploy` printed (curl it: expect 200, or a redirect to sign-in for
66
+ private apps) and check `pnpm exec wervt logs <name> 'level:error' --since 15m`. Fix and
67
+ redeploy until both are clean.
68
+
69
+ ## 5. Deploy on push (when the user wants a repository)
70
+
71
+ Only when asked, or when the user wants a GitHub repo for the app:
72
+
73
+ ```bash
74
+ git init -b main && git add -A && git commit -m "Initial app"
75
+ gh repo create <name> --private --source . --remote origin
76
+ pnpm exec wervt tokens <name> --create github | gh secret set WERVT_TOKEN -R <owner>/<name>
77
+ gh variable set WERVT_URL -R <owner>/<name> --body <control plane URL from whoami>
78
+ git push -u origin main
79
+ ```
80
+
81
+ The pipe hands the deploy token straight to GitHub; it never appears in the output. A deploy token
82
+ can only deploy that one app. Set the secret and variable **before** the first push, or the first
83
+ workflow run fails. Then `gh run watch` and check `pnpm exec wervt releases <name>`: the new
84
+ release lists the commit.
85
+
86
+ ## Existing apps
87
+
88
+ ```bash
89
+ pnpm exec wervt apps # every app, active release, URL
90
+ pnpm exec wervt releases <name> # newest first, with commit and author
91
+ pnpm exec wervt logs <name> 'status:>=500' --since 1d --json
92
+ pnpm exec wervt rollback <name> # the previous release (migrations stay)
93
+ ```
94
+
95
+ For debugging, use the app's `wervt-logs` skill (logs live 14 days; queries are LogsQL).
96
+
97
+ ## Never without asking
98
+
99
+ `wervt delete` (never with `--drop-database` unless the user says so explicitly), `rollback`,
100
+ `activate`, revoking tokens, making a private app public, or deploying over an app you didn't
101
+ create in this conversation. Outside an app directory, `pnpm exec wervt` isn't installed: use
102
+ `npx -y @wervt/cli <command>`.