@wervt/cli 0.2.17 → 0.2.19
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 +4 -0
- package/dist/index.mjs +250 -20
- package/package.json +5 -1
- package/skill/SKILL.md +3 -1
package/README.md
CHANGED
|
@@ -15,6 +15,10 @@ vp exec wervt access my-app --public # or --add friend@example.com
|
|
|
15
15
|
vp exec wervt logs my-app
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
Nitro apps without Nuxt use `@wervt/nitro`. A static site sets `"build": "… && wervt build --static dist"`
|
|
19
|
+
in package.json (any `export default { fetch }` module works too, without `--static`); see
|
|
20
|
+
[Platform contract](https://wervt.app/api/platform-contract#wervt-build).
|
|
21
|
+
|
|
18
22
|
Without [Vite+](https://viteplus.dev), use `npx @wervt/cli` and `pnpm exec wervt`. `wervt --help`
|
|
19
23
|
lists every command: `apps`, `releases`, `audit`, `rollback`, `activate`, `env`, `tokens`, `token`, `members`,
|
|
20
24
|
`tasks`, `stats`, `delete`, `whoami`, … Docs: [wervt.app](https://wervt.app/getting-started/quickstart).
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,160 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { appendFile, cp, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
4
|
import { homedir, hostname } from "node:os";
|
|
4
|
-
import { dirname, join, relative, resolve } from "node:path";
|
|
5
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
6
|
import { styleText } from "node:util";
|
|
6
7
|
import * as p from "@clack/prompts";
|
|
7
8
|
import { defineCommand, runMain } from "citty";
|
|
8
9
|
import { detectPackageManager } from "nypm";
|
|
9
10
|
import { x } from "tinyexec";
|
|
10
11
|
import { packTar } from "modern-tar";
|
|
12
|
+
// ../core/src/manifest.ts
|
|
13
|
+
/** The bundle's `wervt-bundle.json`, next to `server/` and `public/`. */
|
|
14
|
+
const BUNDLE_MANIFEST = "wervt-bundle.json";
|
|
15
|
+
// src/static-server.ts
|
|
16
|
+
/** The source of `server/index.mjs`. */
|
|
17
|
+
function staticServer({ pages, fallback }) {
|
|
18
|
+
return `const pages = ${JSON.stringify(pages)}
|
|
19
|
+
const fallback = ${JSON.stringify(fallback ?? null)}
|
|
20
|
+
const html = (body, status = 200) =>
|
|
21
|
+
new Response(body, { status, headers: { 'content-type': 'text/html; charset=utf-8' } })
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
fetch(request) {
|
|
25
|
+
let path
|
|
26
|
+
try {
|
|
27
|
+
path = decodeURIComponent(new URL(request.url).pathname).replace(/^\\/+|\\/+$/g, '')
|
|
28
|
+
} catch {
|
|
29
|
+
return html(pages['404.html'] ?? 'Not found', 404)
|
|
30
|
+
}
|
|
31
|
+
for (const page of path ? [path + '/index.html', path + '.html'] : ['index.html'])
|
|
32
|
+
if (Object.hasOwn(pages, page)) return html(pages[page])
|
|
33
|
+
const wantsHtml = (request.headers.get('accept') ?? '').includes('text/html')
|
|
34
|
+
if (wantsHtml && fallback && Object.hasOwn(pages, fallback)) return html(pages[fallback])
|
|
35
|
+
return html(pages['404.html'] ?? 'Not found', 404)
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
// src/build.ts
|
|
41
|
+
const ENTRIES = [
|
|
42
|
+
"server.ts",
|
|
43
|
+
"src/server.ts",
|
|
44
|
+
"src/index.ts",
|
|
45
|
+
"server.js",
|
|
46
|
+
"src/index.js"
|
|
47
|
+
];
|
|
48
|
+
const exists$1 = (path) => stat(path).then(() => true, () => false);
|
|
49
|
+
async function walk$1(dir) {
|
|
50
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
51
|
+
return (await Promise.all(entries.map(async (e) => e.isDirectory() ? walk$1(join(dir, e.name)) : [join(dir, e.name)]))).flat();
|
|
52
|
+
}
|
|
53
|
+
/** The app's `wervt` settings from its package.json. */
|
|
54
|
+
async function readBuildConfig(appDir) {
|
|
55
|
+
return JSON.parse(await readFile(join(appDir, "package.json"), "utf8").catch(() => "{}")).wervt ?? {};
|
|
56
|
+
}
|
|
57
|
+
async function build$1(appDir, options = {}) {
|
|
58
|
+
const config = {
|
|
59
|
+
...await readBuildConfig(appDir),
|
|
60
|
+
...definedOnly(options)
|
|
61
|
+
};
|
|
62
|
+
const out = join(appDir, options.out ?? ".output");
|
|
63
|
+
await rm(out, {
|
|
64
|
+
recursive: true,
|
|
65
|
+
force: true
|
|
66
|
+
});
|
|
67
|
+
await mkdir(join(out, "server"), { recursive: true });
|
|
68
|
+
let kind;
|
|
69
|
+
if (config.static) {
|
|
70
|
+
kind = "static";
|
|
71
|
+
await buildStatic(join(appDir, config.static), out, config.fallback);
|
|
72
|
+
} else {
|
|
73
|
+
kind = "server";
|
|
74
|
+
const entry = config.entry ?? await findEntry(appDir);
|
|
75
|
+
if (!entry) throw new Error(`No server entry. Add one of ${ENTRIES.join(", ")}, or set "wervt": { "entry" } in package.json (or "static" for a static site).`);
|
|
76
|
+
await bundleServer(join(appDir, entry), join(out, "server/index.mjs"));
|
|
77
|
+
const publicDir = join(appDir, config.public ?? "public");
|
|
78
|
+
if (await exists$1(publicDir)) await cp(publicDir, join(out, "public"), { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
const manifest = bundleManifest(config, kind);
|
|
81
|
+
await writeFile(join(out, BUNDLE_MANIFEST), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
82
|
+
return {
|
|
83
|
+
kind,
|
|
84
|
+
out,
|
|
85
|
+
manifest
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function findEntry(appDir) {
|
|
89
|
+
for (const entry of ENTRIES) if (await exists$1(join(appDir, entry))) return entry;
|
|
90
|
+
}
|
|
91
|
+
function bundleManifest(config, kind) {
|
|
92
|
+
const manifest = {
|
|
93
|
+
version: 2,
|
|
94
|
+
framework: config.framework ?? (kind === "static" ? "static" : "fetch")
|
|
95
|
+
};
|
|
96
|
+
if (config.database !== void 0) manifest.database = config.database;
|
|
97
|
+
if (config.immutable || config.noCache) manifest.public = {
|
|
98
|
+
immutable: config.immutable,
|
|
99
|
+
noCache: config.noCache
|
|
100
|
+
};
|
|
101
|
+
if (config.tasks || config.schedules) manifest.tasks = {
|
|
102
|
+
tasks: config.tasks ?? [],
|
|
103
|
+
schedules: config.schedules ?? [],
|
|
104
|
+
...config.timezone ? { timezone: config.timezone } : {}
|
|
105
|
+
};
|
|
106
|
+
if (config.shapes) manifest.shapes = config.shapes;
|
|
107
|
+
if (config.preview) manifest.preview = config.preview;
|
|
108
|
+
if (config.brand) manifest.brand = config.brand;
|
|
109
|
+
return manifest;
|
|
110
|
+
}
|
|
111
|
+
async function buildStatic(siteDir, out, fallback = "index.html") {
|
|
112
|
+
if (!await exists$1(siteDir)) throw new Error(`No ${siteDir} to deploy. Build the site first.`);
|
|
113
|
+
await cp(siteDir, join(out, "public"), { recursive: true });
|
|
114
|
+
const pages = {};
|
|
115
|
+
for (const file of await walk$1(siteDir)) if (file.endsWith(".html")) pages[relative(siteDir, file).split(sep).join("/")] = await readFile(file, "utf8");
|
|
116
|
+
await writeFile(join(out, "server/index.mjs"), staticServer({
|
|
117
|
+
pages,
|
|
118
|
+
fallback: fallback || void 0
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
const builtins = new Set(builtinModules);
|
|
122
|
+
/**
|
|
123
|
+
* One ES module for Supabase Edge Runtime (Deno): every dependency inlined, Node built-ins
|
|
124
|
+
* imported with `node:` (Deno rejects bare `crypto`), and the Node globals Deno lacks injected.
|
|
125
|
+
*/
|
|
126
|
+
async function bundleServer(entry, file) {
|
|
127
|
+
const { build: rolldown } = await import("rolldown");
|
|
128
|
+
await rolldown({
|
|
129
|
+
input: entry,
|
|
130
|
+
platform: "node",
|
|
131
|
+
logLevel: "warn",
|
|
132
|
+
plugins: [{
|
|
133
|
+
name: "wervt:node-prefix",
|
|
134
|
+
resolveId(id) {
|
|
135
|
+
const bare = id.startsWith("node:") ? id.slice(5) : id;
|
|
136
|
+
if (builtins.has(bare) || builtins.has(bare.split("/")[0])) return {
|
|
137
|
+
id: `node:${bare}`,
|
|
138
|
+
external: true
|
|
139
|
+
};
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}],
|
|
143
|
+
transform: { inject: {
|
|
144
|
+
Buffer: ["node:buffer", "Buffer"],
|
|
145
|
+
setImmediate: ["node:timers", "setImmediate"],
|
|
146
|
+
clearImmediate: ["node:timers", "clearImmediate"]
|
|
147
|
+
} },
|
|
148
|
+
output: {
|
|
149
|
+
file,
|
|
150
|
+
format: "esm",
|
|
151
|
+
codeSplitting: false
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function definedOnly(value) {
|
|
156
|
+
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0));
|
|
157
|
+
}
|
|
11
158
|
// src/bundle.ts
|
|
12
159
|
/** Files under `dir`. Follows symlinks: Nitro can leave traced dependencies in
|
|
13
160
|
* server/node_modules as links into the local pnpm store, which won't exist on the server. */
|
|
@@ -23,14 +170,18 @@ async function gzip(data) {
|
|
|
23
170
|
return new Uint8Array(await new Response(stream).arrayBuffer());
|
|
24
171
|
}
|
|
25
172
|
const exists = (path) => stat(path).then(() => true, () => false);
|
|
26
|
-
/**
|
|
27
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Packs the build output's `server/`, `public/` and `wervt-bundle.json` (in `out`, `.output` by
|
|
175
|
+
* default) and the app's `drizzle/` into the bundle the control plane expects.
|
|
176
|
+
*/
|
|
177
|
+
async function createBundle(appDir, out = ".output") {
|
|
178
|
+
const outDir = join(appDir, out);
|
|
28
179
|
const roots = [
|
|
29
|
-
[join(
|
|
30
|
-
[join(
|
|
180
|
+
[join(outDir, "server"), "server"],
|
|
181
|
+
[join(outDir, "public"), "public"],
|
|
31
182
|
[join(appDir, "drizzle"), "drizzle"]
|
|
32
183
|
];
|
|
33
|
-
if (!await exists(roots[0][0])) throw new Error(
|
|
184
|
+
if (!await exists(roots[0][0])) throw new Error(`No ${join(out, "server")}. Build the app first.`);
|
|
34
185
|
const files = [];
|
|
35
186
|
for (const [dir, prefix] of roots) {
|
|
36
187
|
if (!await exists(dir)) continue;
|
|
@@ -46,6 +197,17 @@ async function createBundle(appDir) {
|
|
|
46
197
|
});
|
|
47
198
|
}
|
|
48
199
|
}
|
|
200
|
+
const manifest = join(outDir, BUNDLE_MANIFEST);
|
|
201
|
+
if (await exists(manifest)) {
|
|
202
|
+
const data = await readFile(manifest);
|
|
203
|
+
files.push({
|
|
204
|
+
header: {
|
|
205
|
+
name: BUNDLE_MANIFEST,
|
|
206
|
+
size: data.byteLength
|
|
207
|
+
},
|
|
208
|
+
body: data
|
|
209
|
+
});
|
|
210
|
+
}
|
|
49
211
|
return {
|
|
50
212
|
data: await gzip(await packTar(files)),
|
|
51
213
|
files: files.length
|
|
@@ -258,6 +420,13 @@ function resolveAppDir(arg = ".") {
|
|
|
258
420
|
if (existsSync(join("apps", arg, "package.json"))) return resolve("apps", arg);
|
|
259
421
|
throw new Error(`No app found at ${arg}`);
|
|
260
422
|
}
|
|
423
|
+
const PREVIEW_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
424
|
+
/** `todos--pr-12` for `--preview pr-12`: the preview's own app name (control's previews.ts). */
|
|
425
|
+
function previewName(app, preview) {
|
|
426
|
+
if (preview === void 0) return app;
|
|
427
|
+
if (!PREVIEW_RE.test(preview)) throw new Error(`--preview must be lowercase letters, digits and dashes, like pr-12`);
|
|
428
|
+
return `${app}--${preview}`;
|
|
429
|
+
}
|
|
261
430
|
async function appName(dir, name) {
|
|
262
431
|
if (!name) {
|
|
263
432
|
const pkg = JSON.parse(await readFile(join(dir, "package.json"), "utf8"));
|
|
@@ -282,11 +451,19 @@ const deploy = defineCommand({
|
|
|
282
451
|
type: "string",
|
|
283
452
|
description: "App name (default: package.json name)"
|
|
284
453
|
},
|
|
454
|
+
preview: {
|
|
455
|
+
type: "string",
|
|
456
|
+
description: "Deploy a preview of the app instead, at <app>--<preview> (like pr-12): own empty database, the app's access and env plus its preview env"
|
|
457
|
+
},
|
|
285
458
|
build: {
|
|
286
459
|
type: "boolean",
|
|
287
460
|
default: true,
|
|
288
461
|
description: "Build before deploying"
|
|
289
462
|
},
|
|
463
|
+
out: {
|
|
464
|
+
type: "string",
|
|
465
|
+
description: "Build output to pack (default: .output)"
|
|
466
|
+
},
|
|
290
467
|
check: {
|
|
291
468
|
type: "string",
|
|
292
469
|
description: "Pages to request once the release started, a 5xx fails (default: /). Shape routes are checked too; --check= turns all checks off"
|
|
@@ -300,7 +477,7 @@ const deploy = defineCommand({
|
|
|
300
477
|
},
|
|
301
478
|
async run({ args }) {
|
|
302
479
|
const dir = resolveAppDir(args.dir);
|
|
303
|
-
const name = await appName(dir, args.name);
|
|
480
|
+
const name = previewName(await appName(dir, args.name), args.preview);
|
|
304
481
|
p.intro(`wervt deploy ${name}`);
|
|
305
482
|
if (args.build) {
|
|
306
483
|
const pm = (await detectPackageManager(dir))?.name ?? "npm";
|
|
@@ -319,7 +496,7 @@ const deploy = defineCommand({
|
|
|
319
496
|
}
|
|
320
497
|
const spinner = progress();
|
|
321
498
|
spinner.start("Packing");
|
|
322
|
-
const bundle = await createBundle(dir);
|
|
499
|
+
const bundle = await createBundle(dir, args.out);
|
|
323
500
|
spinner.stop(`Packed ${bundle.files} files (${(bundle.data.byteLength / 1024).toFixed(0)} kB)`);
|
|
324
501
|
spinner.start("Uploading");
|
|
325
502
|
const source = await deploySource(dir);
|
|
@@ -746,6 +923,10 @@ const env = defineCommand({
|
|
|
746
923
|
type: "string",
|
|
747
924
|
description: "KEY (comma separated)"
|
|
748
925
|
},
|
|
926
|
+
preview: {
|
|
927
|
+
type: "boolean",
|
|
928
|
+
description: "The app's preview env instead: set on top of its env in every preview, from their next deploy"
|
|
929
|
+
},
|
|
749
930
|
...connection
|
|
750
931
|
},
|
|
751
932
|
async run({ args, rawArgs }) {
|
|
@@ -756,17 +937,18 @@ const env = defineCommand({
|
|
|
756
937
|
return [pair.slice(0, i), pair.slice(i + 1)];
|
|
757
938
|
}));
|
|
758
939
|
const unset = toList(args.unset);
|
|
759
|
-
const
|
|
940
|
+
const path = `/v1/apps/${args.app}/${args.preview ? "preview-env" : "env"}`;
|
|
941
|
+
const { keys, warnings = [] } = await (Object.keys(set).length || unset.length ? await api(path, args, {
|
|
760
942
|
method: "PATCH",
|
|
761
943
|
headers: { "content-type": "application/json" },
|
|
762
944
|
body: JSON.stringify({
|
|
763
945
|
set,
|
|
764
946
|
unset
|
|
765
947
|
})
|
|
766
|
-
}) : await api(
|
|
948
|
+
}) : await api(path, args)).json();
|
|
767
949
|
console.log(keys.length ? keys.join("\n") : "No variables set.");
|
|
768
950
|
for (const warning of warnings) console.error(styleText("yellow", `Warning: ${warning}`));
|
|
769
|
-
if (Object.keys(set).length || unset.length) console.log("Applies from the next request.");
|
|
951
|
+
if (Object.keys(set).length || unset.length) console.log(args.preview ? "Previews get it with their next deploy." : "Applies from the next request.");
|
|
770
952
|
}
|
|
771
953
|
});
|
|
772
954
|
const SIZE_FLAGS = [
|
|
@@ -908,7 +1090,7 @@ const tokens = defineCommand({
|
|
|
908
1090
|
})).json();
|
|
909
1091
|
console.error(`Deploy token "${token.name}" for ${args.app} (shown once):`);
|
|
910
1092
|
console.log(token.token);
|
|
911
|
-
console.error(styleText("dim", "It can deploy this app and nothing else. Store it as the WERVT_TOKEN secret."));
|
|
1093
|
+
console.error(styleText("dim", "It can deploy this app and its previews, nothing else. Store it as the WERVT_TOKEN secret."));
|
|
912
1094
|
return;
|
|
913
1095
|
}
|
|
914
1096
|
if (args.revoke) {
|
|
@@ -1211,13 +1393,17 @@ const tasks = defineCommand({
|
|
|
1211
1393
|
const remove = defineCommand({
|
|
1212
1394
|
meta: {
|
|
1213
1395
|
name: "delete",
|
|
1214
|
-
description: "Delete an app (releases, access, env)"
|
|
1396
|
+
description: "Delete an app (releases, access, env) or one of its previews"
|
|
1215
1397
|
},
|
|
1216
1398
|
args: {
|
|
1217
1399
|
app: {
|
|
1218
1400
|
type: "positional",
|
|
1219
|
-
required:
|
|
1220
|
-
description: "App name"
|
|
1401
|
+
required: false,
|
|
1402
|
+
description: "App name (default: package.json name in the current directory)"
|
|
1403
|
+
},
|
|
1404
|
+
preview: {
|
|
1405
|
+
type: "string",
|
|
1406
|
+
description: "Delete the preview <app>--<preview> (like pr-12) and its data instead"
|
|
1221
1407
|
},
|
|
1222
1408
|
"drop-database": {
|
|
1223
1409
|
type: "boolean",
|
|
@@ -1230,16 +1416,18 @@ const remove = defineCommand({
|
|
|
1230
1416
|
...connection
|
|
1231
1417
|
},
|
|
1232
1418
|
async run({ args }) {
|
|
1233
|
-
const
|
|
1419
|
+
const app = previewName(args.app ?? await appName(resolveAppDir()), args.preview);
|
|
1420
|
+
const dropDb = Boolean(args["drop-database"]) || app.includes("--");
|
|
1234
1421
|
if (!args.yes) {
|
|
1235
1422
|
const ok = await p.confirm({
|
|
1236
|
-
message: `Delete ${
|
|
1423
|
+
message: `Delete ${app}${dropDb ? " and ALL its data" : " (data is kept, the name stays yours)"}${app.includes("--") ? "" : ", and all its previews"}?`,
|
|
1237
1424
|
initialValue: false
|
|
1238
1425
|
});
|
|
1239
1426
|
if (p.isCancel(ok) || !ok) return p.cancel("Cancelled");
|
|
1240
1427
|
}
|
|
1241
|
-
await api(`/v1/apps/${
|
|
1242
|
-
console.log(`Deleted ${
|
|
1428
|
+
const { previews = [] } = await (await api(`/v1/apps/${app}?dropDatabase=${dropDb}`, args, { method: "DELETE" })).json();
|
|
1429
|
+
console.log(`Deleted ${app}${dropDb ? " and its database" : ""}`);
|
|
1430
|
+
if (previews.length) console.log(`Also deleted its previews: ${previews.join(", ")}`);
|
|
1243
1431
|
}
|
|
1244
1432
|
});
|
|
1245
1433
|
const LEVEL_COLORS = {
|
|
@@ -1669,6 +1857,47 @@ const login = defineCommand({
|
|
|
1669
1857
|
outro(`Signed in to ${url} (saved to ${file})`);
|
|
1670
1858
|
}
|
|
1671
1859
|
});
|
|
1860
|
+
const build = defineCommand({
|
|
1861
|
+
meta: {
|
|
1862
|
+
name: "build",
|
|
1863
|
+
description: "Build a fetch handler (Hono, plain `export default { fetch }`) or a static site into a bundle"
|
|
1864
|
+
},
|
|
1865
|
+
args: {
|
|
1866
|
+
dir: {
|
|
1867
|
+
type: "positional",
|
|
1868
|
+
required: false,
|
|
1869
|
+
description: "App directory (default: .)"
|
|
1870
|
+
},
|
|
1871
|
+
entry: {
|
|
1872
|
+
type: "string",
|
|
1873
|
+
description: "Server module (default: server.ts, src/server.ts, src/index.ts)"
|
|
1874
|
+
},
|
|
1875
|
+
static: {
|
|
1876
|
+
type: "string",
|
|
1877
|
+
description: "Folder of a static site to deploy instead of a server, e.g. dist"
|
|
1878
|
+
},
|
|
1879
|
+
fallback: {
|
|
1880
|
+
type: "string",
|
|
1881
|
+
description: "Static site: page for unknown paths (default: index.html, for SPAs); --fallback= for none"
|
|
1882
|
+
},
|
|
1883
|
+
out: {
|
|
1884
|
+
type: "string",
|
|
1885
|
+
description: "Output directory (default: .output)"
|
|
1886
|
+
}
|
|
1887
|
+
},
|
|
1888
|
+
async run({ args }) {
|
|
1889
|
+
const dir = resolveAppDir(args.dir);
|
|
1890
|
+
const started = performance.now();
|
|
1891
|
+
const result = await build$1(dir, {
|
|
1892
|
+
entry: args.entry,
|
|
1893
|
+
static: args.static,
|
|
1894
|
+
fallback: args.fallback === "" ? false : args.fallback,
|
|
1895
|
+
out: args.out
|
|
1896
|
+
});
|
|
1897
|
+
const ms = Math.round(performance.now() - started);
|
|
1898
|
+
console.log(`Built ${result.kind === "static" ? "a static site" : "the server"} into ${result.out} in ${ms} ms (${result.manifest.framework})`);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1672
1901
|
runMain(defineCommand({
|
|
1673
1902
|
meta: {
|
|
1674
1903
|
name: "wervt",
|
|
@@ -1676,6 +1905,7 @@ runMain(defineCommand({
|
|
|
1676
1905
|
},
|
|
1677
1906
|
subCommands: {
|
|
1678
1907
|
deploy,
|
|
1908
|
+
build,
|
|
1679
1909
|
apps,
|
|
1680
1910
|
releases,
|
|
1681
1911
|
audit,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wervt/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.19",
|
|
4
4
|
"description": "Deploy and manage apps on wervt.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -32,8 +32,12 @@
|
|
|
32
32
|
"citty": "^0.2.2",
|
|
33
33
|
"modern-tar": "^0.8.6",
|
|
34
34
|
"nypm": "^0.6.12",
|
|
35
|
+
"rolldown": "^1.2.16",
|
|
35
36
|
"tinyexec": "^1.3.1"
|
|
36
37
|
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@wervt/core": "0.0.2"
|
|
40
|
+
},
|
|
37
41
|
"scripts": {
|
|
38
42
|
"build": "vp pack"
|
|
39
43
|
}
|
package/skill/SKILL.md
CHANGED
|
@@ -123,7 +123,9 @@ git push -u origin main
|
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
The pipe hands the deploy token straight to GitHub; it never appears in the output. A deploy token
|
|
126
|
-
can only deploy that one app
|
|
126
|
+
can only deploy that one app and its previews: `.github/workflows/preview.yml` deploys every pull
|
|
127
|
+
request to `<name>--pr-<number>` (own empty database, the app's access and env plus its preview
|
|
128
|
+
env, `wervt env <name> --preview --set …`) and deletes it when the pull request closes. Set the secret and variable **before** the first push, or the first
|
|
127
129
|
workflow run fails. Then `gh run watch` and check `pnpm exec wervt releases <name>`: the new
|
|
128
130
|
release lists the commit.
|
|
129
131
|
|