wawesome 0.0.7 → 0.0.9

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 (3) hide show
  1. package/README.md +19 -2
  2. package/dist/index.mjs +319 -52
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -52,8 +52,9 @@ npx wawesome init --template stripe-webhook
52
52
 
53
53
  This goes from nothing to a deployed Function in one command. The template is downloaded over
54
54
  plain HTTP and unpacked — **git is not required**, on any platform — and its files land exactly as
55
- they are published. Only two are touched: the `name` in `package.json`, and the App and Function
56
- names in `wawesome-function.json`.
55
+ they are published. Only the `name` in `package.json`, the App and Function names in
56
+ `wawesome-function.json`, and the `wawesome` dependency are touched — the last so the project you
57
+ get depends on the CLI that scaffolded it rather than the one the template was released against.
57
58
 
58
59
  Templates declare what they need rather than shipping placeholders, so the CLI then asks for each
59
60
  environment variable the template requires — showing where in the provider's own dashboard to find
@@ -61,6 +62,22 @@ the value — stores them (secrets write-only), enables any outbound providers t
61
62
  and deploys. If a value does not exist yet, leave it blank; the CLI tells you the `env set` command
62
63
  to run once it does.
63
64
 
65
+ Dependencies are installed for you (with whatever package manager launched the CLI — `npx`, `pnpm
66
+ dlx`, `bun x`), so the types are there when you open the project and the bundler can resolve the
67
+ template's imports. Pass `--no-install` to do it yourself. A failed install never stops the flow;
68
+ the command to re-run is printed.
69
+
70
+ Before any of that is sent anywhere, the CLI shows you the **public URL your Function will have**
71
+ and offers to change your workspace address — the part of that URL that is yours rather than this
72
+ project's, and the same one `wawesome workspace show` prints. It is randomly minted at signup and
73
+ stays changeable until your first deploy, at which point it locks for good, because live URLs carry
74
+ it. Press enter to keep it. If it is already locked, the offer is not made and the reason is said
75
+ plainly.
76
+
77
+ Being logged in is checked **before** the first question, not at the deploy: an expired session
78
+ offers you a login there and then, and declining still leaves you the project plus the two commands
79
+ that finish it.
80
+
64
81
  Run it in an empty directory: if any file would be overwritten, nothing is written at all and the
65
82
  collision is named.
66
83
 
package/dist/index.mjs CHANGED
@@ -5,8 +5,9 @@ import { build } from "esbuild";
5
5
  import os from "node:os";
6
6
  import http from "node:http";
7
7
  import readline from "node:readline";
8
+ import { confirm, select } from "@inquirer/prompts";
9
+ import { spawnSync } from "node:child_process";
8
10
  import zlib from "node:zlib";
9
- import { select } from "@inquirer/prompts";
10
11
  //#region src/config.ts
11
12
  /**
12
13
  * Hardcoded Supabase project config for the wawesome.io platform.
@@ -150,6 +151,18 @@ async function buildJs(entryInput, options) {
150
151
  }
151
152
  }
152
153
  //#endregion
154
+ //#region src/cli-version.ts
155
+ /**
156
+ * The version of the CLI that is running, read from the package it ships in.
157
+ *
158
+ * Compiled in at bundle time rather than resolved from disk, because `dist/` is
159
+ * what gets published and the manifest next to it is not where a `npx` cache,
160
+ * a global install, and a workspace checkout all agree it will be. Anything
161
+ * that has to name this version — `--version`, the dependency a scaffolded
162
+ * project pins — reads it here, so a release bumps one file.
163
+ */
164
+ const CLI_VERSION = "0.0.9";
165
+ //#endregion
153
166
  //#region src/prompt.ts
154
167
  /**
155
168
  * Open a prompt session on stdin, queueing lines as they arrive.
@@ -271,6 +284,34 @@ async function resolveWorkspace(creds) {
271
284
  };
272
285
  }
273
286
  /**
287
+ * What a refused rename means for whoever asked for it.
288
+ *
289
+ * Shared by the two places that offer a rename, so they cannot drift into
290
+ * telling a user different things about the same reason — and `retryable` is
291
+ * what separates a name worth retyping from one that no name would fix.
292
+ */
293
+ function renameAdvice(reason) {
294
+ switch (reason) {
295
+ case "taken":
296
+ case "reserved": return {
297
+ text: "Pick a different name.",
298
+ retryable: true
299
+ };
300
+ case "malformed": return {
301
+ text: "Use lowercase letters, numbers and single hyphens.",
302
+ retryable: true
303
+ };
304
+ case "locked": return {
305
+ text: "A deploy landed while this was running, which fixed the address for good.",
306
+ retryable: false
307
+ };
308
+ default: return {
309
+ text: "",
310
+ retryable: false
311
+ };
312
+ }
313
+ }
314
+ /**
274
315
  * Ask for the workspace's public address to become `slug`.
275
316
  *
276
317
  * The slug is sent exactly as typed. Sanitizing it the way an App slug is
@@ -288,9 +329,8 @@ async function renameTenantSlug(creds, slug) {
288
329
  });
289
330
  if (!res.ok) throw await asGatewayError(res, `Rename failed (HTTP ${res.status}).`);
290
331
  const result = await res.json();
291
- const stored = readCredentials();
292
- if (stored) writeCredentials({
293
- ...stored,
332
+ writeCredentials({
333
+ ...readCredentials() ?? creds,
294
334
  tenant_slug: result.tenant_slug
295
335
  });
296
336
  return result;
@@ -806,6 +846,58 @@ async function envCommand(action, key, value, options) {
806
846
  process.exit(1);
807
847
  }
808
848
  //#endregion
849
+ //#region src/install.ts
850
+ /** Package managers whose `install` this understands. */
851
+ const KNOWN_MANAGERS = [
852
+ "npm",
853
+ "pnpm",
854
+ "yarn",
855
+ "bun"
856
+ ];
857
+ /**
858
+ * Which package manager to install with.
859
+ *
860
+ * Taken from the one that launched this process — `pnpm dlx wawesome init`
861
+ * should not leave a `package-lock.json` behind — and npm otherwise, since that
862
+ * is what `npx` is.
863
+ */
864
+ function packageManager(userAgent = process.env.npm_config_user_agent) {
865
+ const name = (userAgent ?? "").split("/")[0];
866
+ return KNOWN_MANAGERS.includes(name) ? name : "npm";
867
+ }
868
+ function installDependencies(dir, options = {}) {
869
+ const manager = packageManager();
870
+ const command = `${manager} install`;
871
+ if (!fs.existsSync(path.join(dir, "package.json"))) return {
872
+ command,
873
+ ok: false
874
+ };
875
+ console.log(`\n[wawesome] Installing dependencies (${command})...`);
876
+ const result = spawnSync(manager, ["install"], {
877
+ cwd: dir,
878
+ stdio: options.verbose ? "inherit" : [
879
+ "ignore",
880
+ "ignore",
881
+ "pipe"
882
+ ],
883
+ shell: process.platform === "win32"
884
+ });
885
+ if (result.status === 0) {
886
+ console.log("[wawesome] ✅ Dependencies installed.");
887
+ return {
888
+ command,
889
+ ok: true
890
+ };
891
+ }
892
+ const stderr = result.stderr?.toString().trim();
893
+ console.log(`[wawesome] ${command} did not finish. Run it yourself when you can.`);
894
+ if (stderr) console.log(`[wawesome] ${stderr.split("\n").slice(-3).join("\n[wawesome] ")}`);
895
+ return {
896
+ command,
897
+ ok: false
898
+ };
899
+ }
900
+ //#endregion
809
901
  //#region src/provision.ts
810
902
  /** The gateway's own words for a failure, or a plain HTTP status if it gave none. */
811
903
  async function rejected(res, fallback) {
@@ -902,15 +994,61 @@ function applyTemplateIdentity(dir, identity) {
902
994
  function: identity.functionName
903
995
  }));
904
996
  }
997
+ /** The package the CLI publishes itself as, and which a template depends on. */
998
+ const CLI_PACKAGE = "wawesome";
999
+ /**
1000
+ * Point the template's dependency on the CLI at the CLI that is scaffolding it.
1001
+ *
1002
+ * A template pins the version it was released against, and below 1.0 a caret
1003
+ * range is an exact pin — `^0.0.6` never resolves to `0.0.7`. So a template
1004
+ * whose repository has not been touched since installs an older CLI than the
1005
+ * one the user just ran, and ships types that predate the platform it is about
1006
+ * to deploy to. Rewriting it here keeps the two in step without a template
1007
+ * release for every CLI release.
1008
+ *
1009
+ * Only a declaration that already exists is rewritten: what a template depends
1010
+ * on is the template's business, and a template that does not use the CLI does
1011
+ * not acquire it here.
1012
+ */
1013
+ function alignCliDependency(dir, version) {
1014
+ const file = path.join(dir, PACKAGE_FILE);
1015
+ const pkg = readJson(file);
1016
+ const wanted = `^${version}`;
1017
+ for (const field of ["dependencies", "devDependencies"]) {
1018
+ const deps = pkg[field];
1019
+ if (!isRecord(deps)) continue;
1020
+ const current = deps[CLI_PACKAGE];
1021
+ if (typeof current !== "string" || current === wanted) continue;
1022
+ writeJson(file, {
1023
+ ...pkg,
1024
+ [field]: {
1025
+ ...deps,
1026
+ [CLI_PACKAGE]: wanted
1027
+ }
1028
+ });
1029
+ return {
1030
+ from: current,
1031
+ to: wanted
1032
+ };
1033
+ }
1034
+ return null;
1035
+ }
1036
+ function isRecord(value) {
1037
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1038
+ }
905
1039
  function patchJson(file, patch) {
1040
+ writeJson(file, patch(readJson(file)));
1041
+ }
1042
+ function readJson(file) {
906
1043
  if (!fs.existsSync(file)) throw new Error(`The template is missing ${path.basename(file)}, which scaffolding must patch.`);
907
- let current;
908
1044
  try {
909
- current = JSON.parse(fs.readFileSync(file, "utf-8"));
1045
+ return JSON.parse(fs.readFileSync(file, "utf-8"));
910
1046
  } catch (err) {
911
1047
  throw new Error(`The template's ${path.basename(file)} is not valid JSON: ${errorText(err)}`);
912
1048
  }
913
- fs.writeFileSync(file, JSON.stringify(patch(current), null, 2) + "\n", "utf-8");
1049
+ }
1050
+ function writeJson(file, content) {
1051
+ fs.writeFileSync(file, JSON.stringify(content, null, 2) + "\n", "utf-8");
914
1052
  }
915
1053
  /**
916
1054
  * Which of the files a scaffold is about to write are already there.
@@ -1299,6 +1437,134 @@ async function promptForDeclaredEnv(session, declared) {
1299
1437
  return answers;
1300
1438
  }
1301
1439
  /**
1440
+ * The stored credentials, if the platform still accepts them, with whatever it
1441
+ * said about the Tenant while answering.
1442
+ *
1443
+ * Only a refusal counts as an answer — that is the null. An unreachable gateway
1444
+ * says nothing about whether the credentials are good, and treating it as a dead
1445
+ * session would send a user who is merely offline through a login they do not
1446
+ * need, so it comes back as a session with nothing known about the Tenant.
1447
+ */
1448
+ async function probeSession(creds) {
1449
+ try {
1450
+ return {
1451
+ creds,
1452
+ tenant: await fetchTenantDetails(creds)
1453
+ };
1454
+ } catch (err) {
1455
+ return err instanceof GatewayError && (err.status === 401 || err.status === 403) ? null : {
1456
+ creds,
1457
+ tenant: null
1458
+ };
1459
+ }
1460
+ }
1461
+ /**
1462
+ * Get a working session before the first question is asked.
1463
+ *
1464
+ * The credentials file existing is not the same as being logged in — a JWT
1465
+ * expires — and everything this command does after scaffolding needs a live
1466
+ * session. Checked here, the two answers a user can get are "log in now" and a
1467
+ * project that scaffolds without one. Checked where it used to be, at the first
1468
+ * write, the answer was an error *after* they had typed a webhook secret in.
1469
+ *
1470
+ * Returns null when there is no session to work with, which is a supported way
1471
+ * to finish: the files are still worth having.
1472
+ */
1473
+ async function ensureSession(options) {
1474
+ const stored = readCredentials();
1475
+ if (stored) {
1476
+ const probed = await probeSession(stored);
1477
+ if (probed) return probed;
1478
+ }
1479
+ console.log(stored ? "\n[wawesome] Your session has expired." : "\n[wawesome] You're not logged in yet — that's what a deploy needs.");
1480
+ if (!isInteractive()) {
1481
+ console.log("[wawesome] Run 'wawesome login', then 'wawesome deploy'.");
1482
+ return null;
1483
+ }
1484
+ let wantsLogin;
1485
+ try {
1486
+ wantsLogin = await confirm({
1487
+ message: "Log in now?",
1488
+ default: true
1489
+ });
1490
+ } catch {
1491
+ console.log("[wawesome] Cancelled. Nothing was written.");
1492
+ process.exit(130);
1493
+ }
1494
+ if (!wantsLogin) return null;
1495
+ try {
1496
+ await login({
1497
+ api: options.api,
1498
+ verbose: options.verbose
1499
+ });
1500
+ } catch (err) {
1501
+ console.error(`[wawesome] Login failed: ${errorText(err)}`);
1502
+ return null;
1503
+ }
1504
+ const fresh = readCredentials();
1505
+ return fresh ? probeSession(fresh) : null;
1506
+ }
1507
+ /** Bound on the rename loop, so a name the gateway keeps refusing ends the offer. */
1508
+ const MAX_RENAME_ATTEMPTS = 3;
1509
+ /** Show the address the Function will answer on, once it is deployed. */
1510
+ function announceUrl(creds, slug, appSlug, functionName) {
1511
+ console.log("\n[wawesome] Your Function will answer on:\n");
1512
+ console.log(` \x1b[36m${publicInvokeUrl(creds.gateway_url, slug, appSlug, functionName)}\x1b[0m\n`);
1513
+ }
1514
+ /**
1515
+ * Show the URL, and offer to fix the workspace address while it can still be fixed.
1516
+ *
1517
+ * This is the last moment that address is free: it locks the first time a
1518
+ * version is promoted, which is what the deploy at the end of this flow does.
1519
+ * Asking here rather than at signup is the whole reason naming was deferred —
1520
+ * it is client-facing branding, and nobody chooses it well before seeing the
1521
+ * product work. The URL comes first so the offer is about something concrete.
1522
+ *
1523
+ * The lock is read from the gateway rather than guessed, so a user who cannot
1524
+ * rename is told why instead of being walked into a rejection. Nothing here can
1525
+ * fail the flow: the project is already on disk and the deploy still works under
1526
+ * the address the workspace has.
1527
+ */
1528
+ async function offerWorkspaceAddress(session, creds, tenant, appSlug, functionName) {
1529
+ const current = tenant?.tenant_slug ?? creds.tenant_slug;
1530
+ if (!current) return;
1531
+ announceUrl(creds, current, appSlug, functionName);
1532
+ if (!tenant) {
1533
+ console.log(` '${current}' is your workspace address. Whether it can still be changed`);
1534
+ console.log(" is a question for the gateway, which did not answer.\n");
1535
+ return;
1536
+ }
1537
+ if (tenant.slug_locked) {
1538
+ console.log(` 🔒 '${current}' is fixed — a Function version has been promoted, and live URLs already carry it.\n`);
1539
+ return;
1540
+ }
1541
+ console.log(` '${current}' is your workspace address — the part of that URL that is yours`);
1542
+ console.log(" rather than this project's, and this is the moment to change it: it locks");
1543
+ console.log(" for good the first time you deploy, because live URLs carry it.");
1544
+ console.log(" Press enter to keep it.\n");
1545
+ let refusal;
1546
+ for (let attempt = 0; attempt < MAX_RENAME_ATTEMPTS; attempt++) {
1547
+ const answer = await session.ask(" Workspace address?", current);
1548
+ if (answer === current) {
1549
+ console.log(`[wawesome] Keeping '${current}'.`);
1550
+ return;
1551
+ }
1552
+ try {
1553
+ const renamed = await renameTenantSlug(creds, answer);
1554
+ console.log(`\n[wawesome] ✅ Workspace renamed to '${renamed.tenant_slug}'.`);
1555
+ announceUrl(creds, renamed.tenant_slug, appSlug, functionName);
1556
+ return;
1557
+ } catch (err) {
1558
+ console.log(`[wawesome] ${errorText(err)}`);
1559
+ refusal = err instanceof GatewayError ? err.reason : void 0;
1560
+ const { text, retryable } = renameAdvice(refusal);
1561
+ if (text) console.log(`[wawesome] ${text}`);
1562
+ if (!retryable) break;
1563
+ }
1564
+ }
1565
+ console.log(refusal === "locked" ? `[wawesome] Keeping '${current}'.` : `[wawesome] Keeping '${current}'. Change it later with: wawesome workspace rename <name>`);
1566
+ }
1567
+ /**
1302
1568
  * Satisfy the template's declarations against the platform.
1303
1569
  *
1304
1570
  * The manifest declares; nothing here is template-specific. Each declaration
@@ -1367,10 +1633,11 @@ async function initFromTemplate(templateName, options) {
1367
1633
  force: true
1368
1634
  });
1369
1635
  process.once("exit", sweepStaging);
1370
- const session = openPromptSession();
1371
1636
  let functionName;
1372
1637
  let appSlug;
1373
1638
  let manifest;
1639
+ let tenantSession;
1640
+ let answers = [];
1374
1641
  try {
1375
1642
  let files;
1376
1643
  try {
@@ -1384,39 +1651,47 @@ async function initFromTemplate(templateName, options) {
1384
1651
  }
1385
1652
  const collisions = collidingPaths(cwd, files);
1386
1653
  if (collisions.length > 0) fail(`${collisions.join(", ")} already exist${collisions.length === 1 ? "s" : ""} here.`, "Run this in an empty directory, or move those files aside first. Nothing was written.");
1387
- functionName = await promptForSlug(session, "Function name", readFunctionConfig(staging)?.function || dirName);
1388
- console.log(" (an App groups the Functions of one project — its slug is part of the public URL)");
1389
- appSlug = await promptForSlug(session, "App slug", dirName);
1654
+ tenantSession = await ensureSession(options);
1655
+ const session = openPromptSession();
1390
1656
  try {
1391
- fs.cpSync(staging, cwd, { recursive: true });
1392
- applyTemplateIdentity(cwd, {
1393
- app: appSlug,
1394
- functionName
1395
- });
1396
- } catch (err) {
1397
- fail(errorText(err), `Some of '${template.name}' may have been written to ${cwd}.`);
1657
+ functionName = await promptForSlug(session, "Function name", readFunctionConfig(staging)?.function || dirName);
1658
+ console.log(" (an App groups the Functions of one project — its slug is part of the public URL)");
1659
+ appSlug = await promptForSlug(session, "App slug", dirName);
1660
+ let repinned = null;
1661
+ try {
1662
+ fs.cpSync(staging, cwd, { recursive: true });
1663
+ applyTemplateIdentity(cwd, {
1664
+ app: appSlug,
1665
+ functionName
1666
+ });
1667
+ repinned = alignCliDependency(cwd, CLI_VERSION);
1668
+ } catch (err) {
1669
+ fail(errorText(err), `Some of '${template.name}' may have been written to ${cwd}.`);
1670
+ }
1671
+ console.log(`\n[wawesome] ✅ Scaffolded ${files.length} files from '${template.name}'.`);
1672
+ if (repinned) console.log(`[wawesome] Using wawesome ${repinned.to} — the template pinned ${repinned.from}.`);
1673
+ if (tenantSession) await offerWorkspaceAddress(session, tenantSession.creds, tenantSession.tenant, appSlug, functionName);
1674
+ answers = tenantSession ? await promptForDeclaredEnv(session, manifest.env) : [];
1675
+ } finally {
1676
+ session.close();
1398
1677
  }
1399
- console.log(`\n[wawesome] ✅ Scaffolded ${files.length} files from '${template.name}'.`);
1400
1678
  } finally {
1401
1679
  sweepStaging();
1402
1680
  process.off("exit", sweepStaging);
1403
1681
  }
1404
- let answers = [];
1405
- let creds;
1406
- try {
1407
- creds = readCredentials();
1408
- if (creds) answers = await promptForDeclaredEnv(session, manifest.env);
1409
- } finally {
1410
- session.close();
1411
- }
1412
- if (!creds) {
1682
+ if (!tenantSession) {
1683
+ const installed = options.install === false ? null : installDependencies(cwd, { verbose: options.verbose });
1413
1684
  console.log("\n[wawesome] 🎉 Project ready. Next steps:");
1414
- console.log(" 1. npm install");
1415
- console.log(" 2. wawesome login");
1416
- console.log(" 3. wawesome deploy\n");
1685
+ [
1686
+ ...installed?.ok ? [] : [installed?.command ?? "npm install"],
1687
+ "wawesome login",
1688
+ "wawesome deploy"
1689
+ ].forEach((step, index) => console.log(` ${index + 1}. ${step}`));
1690
+ console.log("");
1417
1691
  return;
1418
1692
  }
1419
- await wireUp(creds, appSlug, manifest, answers);
1693
+ await wireUp(tenantSession.creds, appSlug, manifest, answers);
1694
+ if (options.install !== false) installDependencies(cwd, { verbose: options.verbose });
1420
1695
  const result = await deploy(void 0, {
1421
1696
  out: "dist/index.js",
1422
1697
  verbose: options.verbose
@@ -1491,7 +1766,7 @@ export default {
1491
1766
  typecheck: "tsc --noEmit"
1492
1767
  },
1493
1768
  devDependencies: {
1494
- "wawesome": "^1.0.0",
1769
+ "wawesome": `^${CLI_VERSION}`,
1495
1770
  typescript: "^7.0.0"
1496
1771
  }
1497
1772
  };
@@ -1524,10 +1799,14 @@ export default {
1524
1799
  fs.writeFileSync(gitignorePath, "node_modules\ndist\n", "utf-8");
1525
1800
  console.log("[wawesome] ✅ Created .gitignore");
1526
1801
  }
1802
+ const installed = options.install === false ? null : installDependencies(cwd, { verbose: options.verbose });
1527
1803
  console.log("\n[wawesome] 🎉 Project initialized! Next steps:");
1528
- console.log(" 1. npm install");
1529
- console.log(" 2. wawesome login");
1530
- console.log(" 3. wawesome deploy\n");
1804
+ [
1805
+ ...installed?.ok ? [] : [installed?.command ?? "npm install"],
1806
+ "wawesome login",
1807
+ "wawesome deploy"
1808
+ ].forEach((step, index) => console.log(` ${index + 1}. ${step}`));
1809
+ console.log("");
1531
1810
  }
1532
1811
  //#endregion
1533
1812
  //#region src/version.ts
@@ -2228,20 +2507,8 @@ async function renameWorkspace(slug, options) {
2228
2507
  } catch (err) {
2229
2508
  if (err instanceof GatewayError) {
2230
2509
  console.error(`\n[wawesome] \x1b[31m${err.message}\x1b[0m`);
2231
- switch (err.reason) {
2232
- case "taken":
2233
- console.error("[wawesome] Pick a different name.");
2234
- break;
2235
- case "reserved":
2236
- console.error("[wawesome] Pick a different name.");
2237
- break;
2238
- case "malformed":
2239
- console.error("[wawesome] Use lowercase letters, numbers and single hyphens.");
2240
- break;
2241
- case "locked":
2242
- console.error("[wawesome] A deploy landed while this was running, which fixed the address for good.");
2243
- break;
2244
- }
2510
+ const { text } = renameAdvice(err.reason);
2511
+ if (text) console.error(`[wawesome] ${text}`);
2245
2512
  console.error("");
2246
2513
  process.exit(1);
2247
2514
  return;
@@ -2279,7 +2546,7 @@ cli.command("logout", "Clear stored authentication credentials").action(() => lo
2279
2546
  cli.command("whoami", "Show current login session info").action(() => whoami());
2280
2547
  cli.command("workspace [action] [name]", "Show the workspace, or rename its public address").usage("workspace <action> [name]\n\nActions:\n show Show the workspace name, address, and whether it can still change\n rename <name> Change the public address, while nothing live depends on it").example("wawesome workspace").example("wawesome workspace rename northwind").option("-v, --verbose", "Enable verbose debug output").action((action, name, options) => workspaceCommand(action, name, options));
2281
2548
  cli.command("templates [action]", "Browse the template catalog").usage("templates [action]\n\nActions:\n list (ls) Show every available template (default)").example("wawesome templates").example("wawesome templates list").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("-v, --verbose", "Enable verbose debug output").action((action, options) => templatesCommand(action, options));
2282
- cli.command("init", "Scaffold a new function project in the current directory").usage("init [options]\n\nWith --template, the project is fetched from the template catalog, wired up\nfrom what the template declares it needs, and deployed. Run 'wawesome templates'\nto see what is available.").example("wawesome init").example("wawesome init --template stripe-webhook").option("-t, --template <name>", "Scaffold from a catalog template and deploy it").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
2549
+ cli.command("init", "Scaffold a new function project in the current directory").usage("init [options]\n\nWith --template, the project is fetched from the template catalog, wired up\nfrom what the template declares it needs, and deployed. Run 'wawesome templates'\nto see what is available.").example("wawesome init").example("wawesome init --template stripe-webhook").option("-t, --template <name>", "Scaffold from a catalog template and deploy it").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("--no-install", "Skip installing dependencies after scaffolding").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
2283
2550
  cli.command("logs [function-name-or-invocation-id]", "View invocation history, fetch log output, or follow live").usage(`logs [target] [options]
2284
2551
 
2285
2552
  The target argument determines what the command does:
@@ -2302,7 +2569,7 @@ cli.command("logs [function-name-or-invocation-id]", "View invocation history, f
2302
2569
  invocations — new output appears each time the function runs, no need to catch a
2303
2570
  specific invocation. Press Ctrl-C to stop at any time.`).option("-f, --follow", "Stream live output (tail -f style). Follows a running invocation or waits for the next one. Ctrl-C to stop").option("-i, --invocation <id>", "Fetch stdout/stderr log body for a specific invocation ID").option("-a, --app <app>", "App slug override (defaults to wawesome-function.json)").option("-s, --status <status>", "Filter invocations by status (success, error, timeout, running)").option("--success", "Shorthand for --status success").option("--error", "Shorthand for --status error").option("--timeout", "Shorthand for --status timeout").option("--running", "Shorthand for --status running").option("-v, --verbose", "Enable verbose debug output").action((target, options) => logsCommand(target, options));
2304
2571
  cli.help();
2305
- cli.version("1.0.0");
2572
+ cli.version(CLI_VERSION);
2306
2573
  cli.parse();
2307
2574
  //#endregion
2308
2575
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wawesome",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "CLI tool for building and deploying serverless functions on wawesome.io platform",
5
5
  "type": "module",
6
6
  "bin": {