@wervt/cli 0.2.11 → 0.2.13

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/README.md CHANGED
@@ -1,3 +1,20 @@
1
1
  # @wervt/cli
2
2
 
3
- Build and host small Nuxt apps written by agents. Work in progress, nothing usable yet.
3
+ The `wervt` command: deploy and manage apps on a [wervt](https://wervt.app) server.
4
+
5
+ ```bash
6
+ vpx @wervt/cli login --url https://api.wervt.app # approve the code in the dashboard
7
+ vpx @wervt/cli skill install # teach your agent wervt
8
+ ```
9
+
10
+ In an app (`vp create wervt my-app` adds the CLI as a dev dependency):
11
+
12
+ ```bash
13
+ vp exec wervt deploy # build, upload, migrate, activate
14
+ vp exec wervt access my-app --public # or --add friend@example.com
15
+ vp exec wervt logs my-app
16
+ ```
17
+
18
+ Without [Vite+](https://viteplus.dev), use `npx @wervt/cli` and `pnpm exec wervt`. `wervt --help`
19
+ lists every command: `apps`, `releases`, `rollback`, `activate`, `env`, `tokens`, `members`,
20
+ `tasks`, `stats`, `delete`, `whoami`, … Docs: [wervt.app](https://wervt.app/getting-started/quickstart).
package/dist/index.mjs CHANGED
@@ -463,6 +463,45 @@ const access = defineCommand({
463
463
  if (result.visibility === "private") console.log(result.allow.length ? `allowed: ${result.allow.join(", ")}` : "allowed: admins only");
464
464
  }
465
465
  });
466
+ const members = defineCommand({
467
+ meta: {
468
+ name: "members",
469
+ description: "Show or change who manages an app (its owner and members)"
470
+ },
471
+ args: {
472
+ app: {
473
+ type: "positional",
474
+ required: true,
475
+ description: "App name"
476
+ },
477
+ add: {
478
+ type: "string",
479
+ description: "Add members by email (comma separated)"
480
+ },
481
+ remove: {
482
+ type: "string",
483
+ description: "Remove members by email (comma separated)"
484
+ },
485
+ owner: {
486
+ type: "string",
487
+ description: "Hand the app to another owner"
488
+ },
489
+ ...connection
490
+ },
491
+ async run({ args }) {
492
+ const change = {};
493
+ if (args.add) change.add = toList(args.add);
494
+ if (args.remove) change.remove = toList(args.remove);
495
+ if (args.owner) change.owner = args.owner;
496
+ const result = await (Object.keys(change).length ? await api(`/v1/apps/${args.app}/owners`, args, {
497
+ method: "PATCH",
498
+ headers: { "content-type": "application/json" },
499
+ body: JSON.stringify(change)
500
+ }) : await api(`/v1/apps/${args.app}/owners`, args)).json();
501
+ console.log(`owner: ${result.owner}`);
502
+ console.log(`members: ${result.members.length ? result.members.join(", ") : "none"}`);
503
+ }
504
+ });
466
505
  const env = defineCommand({
467
506
  meta: {
468
507
  name: "env",
@@ -504,6 +543,48 @@ const env = defineCommand({
504
543
  if (Object.keys(set).length || unset.length) console.log("Applies from the next request.");
505
544
  }
506
545
  });
546
+ const accounts = defineCommand({
547
+ meta: {
548
+ name: "accounts",
549
+ description: "Admins: list accounts and their app limits, or set one"
550
+ },
551
+ args: {
552
+ email: {
553
+ type: "positional",
554
+ required: false,
555
+ description: "Account to change"
556
+ },
557
+ apps: {
558
+ type: "string",
559
+ description: "Apps the account may own: a number, \"none\" for no limit, \"default\""
560
+ },
561
+ ...connection
562
+ },
563
+ async run({ args }) {
564
+ if (args.email) {
565
+ if (!args.apps) throw new Error("Pass --apps <n|none|default> to change the limit");
566
+ const apps = args.apps === "none" ? null : args.apps === "default" ? "default" : Number(args.apps);
567
+ const result = await (await api(`/v1/accounts/${encodeURIComponent(args.email)}`, args, {
568
+ method: "PATCH",
569
+ headers: { "content-type": "application/json" },
570
+ body: JSON.stringify({ apps })
571
+ })).json();
572
+ return console.log(`${result.email}: ${result.limits.apps ?? "no limit"} apps`);
573
+ }
574
+ const result = await (await api("/v1/accounts", args)).json();
575
+ console.log(`default: ${result.defaults.apps ?? "no limit"} apps per account`);
576
+ for (const account of result.accounts) {
577
+ const list = account.apps.length ? ` ${account.apps.join(", ")}` : "";
578
+ if (account.admin) {
579
+ console.log(`${account.email} ${account.apps.length} apps (admin)${list}`);
580
+ continue;
581
+ }
582
+ const limit = account.limits.apps ?? "no limit";
583
+ const own = "apps" in account.custom ? "" : " (default)";
584
+ console.log(`${account.email} ${account.apps.length}/${limit}${own}${list}`);
585
+ }
586
+ }
587
+ });
507
588
  const tokens = defineCommand({
508
589
  meta: {
509
590
  name: "tokens",
@@ -791,12 +872,16 @@ const whoami = defineCommand({
791
872
  args: connection,
792
873
  async run({ args }) {
793
874
  const { url, token } = await resolveConfig(args);
794
- const deployApp = token.match(/^wervtd_([a-z0-9-]+)_/)?.[1];
795
- let role = deployApp ? `deploy token for ${deployApp}` : "unknown";
796
- if (!deployApp) {
797
- const res = await fetch(`${url}/v1/databases`, { headers: { authorization: `Bearer ${token}` } });
798
- if (res.status === 401) throw new Error(`The token is not valid for ${url}`);
799
- role = res.ok ? "admin" : res.status === 403 ? "read" : `unknown (${res.status})`;
875
+ const res = await fetch(`${url}/v1/whoami`, { headers: { authorization: `Bearer ${token}` } });
876
+ if (res.status === 401) throw new Error(`The token is not valid for ${url}`);
877
+ let role;
878
+ if (res.ok) {
879
+ const me = await res.json();
880
+ role = me.role === "deploy" ? `deploy token for ${me.app}` : me.email ? `${me.role} (${me.email})` : me.role;
881
+ } else {
882
+ const deployApp = token.match(/^wervtd_([a-z0-9-]+)_/)?.[1];
883
+ const probe = deployApp ? void 0 : await fetch(`${url}/v1/databases`, { headers: { authorization: `Bearer ${token}` } });
884
+ role = deployApp ? `deploy token for ${deployApp}` : probe.ok ? "admin" : probe.status === 403 ? "read" : `unknown (${probe.status})`;
800
885
  }
801
886
  console.log(`${url}\n${role}`);
802
887
  }
@@ -939,6 +1024,8 @@ runMain(defineCommand({
939
1024
  rollback,
940
1025
  reprovision,
941
1026
  access,
1027
+ members,
1028
+ accounts,
942
1029
  env,
943
1030
  tokens,
944
1031
  tasks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wervt/cli",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Deploy and manage apps on wervt.",
5
5
  "keywords": [
6
6
  "agents",
package/skill/SKILL.md CHANGED
@@ -63,8 +63,9 @@ existing app, work in its repository instead.
63
63
 
64
64
  ```bash
65
65
  pnpm exec wervt deploy # build, upload, migrate, activate
66
- pnpm exec wervt access <name> --public # new apps are private (signed-in admins only)
66
+ pnpm exec wervt access <name> --public # new apps are private (owner, members, admins)
67
67
  pnpm exec wervt access <name> --add a@x.com,*@company.com # or let specific people in
68
+ pnpm exec wervt members <name> --add a@x.com # who may deploy and manage it (the first deploy owns it)
68
69
  pnpm exec wervt env <name> --set KEY=value # server env vars, applied from the next request
69
70
  ```
70
71