wawesome 0.11.0 → 0.13.0
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 +19 -1
- package/dist/index.mjs +110 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,6 +117,8 @@ npx wawesome deploy
|
|
|
117
117
|
| `npx wawesome env list` | View environment variables for the current app |
|
|
118
118
|
| `npx wawesome env set <key> <val>` | Set an environment variable (add `--secret` for write-only) |
|
|
119
119
|
| `npx wawesome env rm <key>` | Delete an environment variable |
|
|
120
|
+
| `npx wawesome domains` | Show the app's domain, its state, and the DNS records to add |
|
|
121
|
+
| `npx wawesome domains detach <host>` | Stop serving the app at a domain you attached |
|
|
120
122
|
| `npx wawesome credentials` | List deploy credentials: name, prefix, capabilities, Apps, last use |
|
|
121
123
|
| `npx wawesome credentials mint <name>` | Mint a deploy credential and print its secret, once |
|
|
122
124
|
| `npx wawesome credentials revoke <name>`| Revoke a deploy credential by name or prefix |
|
|
@@ -314,11 +316,14 @@ you say otherwise:
|
|
|
314
316
|
```bash
|
|
315
317
|
# reads logs and cannot deploy, restricted to one App, never expires
|
|
316
318
|
npx wawesome credentials mint agent -c logs:read -a prod --expires never
|
|
319
|
+
|
|
320
|
+
# ships code and attaches the domain the project declares
|
|
321
|
+
npx wawesome credentials mint ci -c deploy,domains:attach -a prod
|
|
317
322
|
```
|
|
318
323
|
|
|
319
324
|
| Flag | Meaning |
|
|
320
325
|
|:---------------------|:------------------------------------------------------------------------------|
|
|
321
|
-
| `-c, --capability` | `deploy`, `logs:read`, or
|
|
326
|
+
| `-c, --capability` | `deploy`, `logs:read`, `domains:attach`, or any mix. Repeatable or comma-separated. Default `deploy` |
|
|
322
327
|
| `-a, --app` | Restrict to these Apps, by slug. Repeatable. Default: every App in the workspace |
|
|
323
328
|
| `--expires` | Days, or `never`. Default: 90 |
|
|
324
329
|
|
|
@@ -524,6 +529,19 @@ Your deploy waits for neither. It attaches the domain, prints the records and fi
|
|
|
524
529
|
later deploy states where the domain got to: not verified, ownership verified, certificate issued, or
|
|
525
530
|
serving. A domain that is already serving is a line saying so and nothing else.
|
|
526
531
|
|
|
532
|
+
The same block is there when you are not deploying:
|
|
533
|
+
|
|
534
|
+
```bash
|
|
535
|
+
npx wawesome domains
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
It prints the domain, where it got to, when the platform last checked, and the records to add. It
|
|
539
|
+
changes nothing, and a deploy credential carrying `deploy` can run it, so a pipeline reads the same
|
|
540
|
+
thing you do. The TXT record is minted a moment after the name is claimed, and an attach that outran
|
|
541
|
+
it prints a row saying so rather than dropping the record from the list. That row is what this
|
|
542
|
+
command fills in, and it is where the deploy sends you: deploying again would change nothing, and a
|
|
543
|
+
deploy that changes nothing is refused.
|
|
544
|
+
|
|
527
545
|
A custom domain is granted from the Solo plan upwards, one per App. On a plan that grants none, the
|
|
528
546
|
deploy prints the refusal and lands everything else it was doing.
|
|
529
547
|
|
package/dist/index.mjs
CHANGED
|
@@ -883,7 +883,7 @@ async function buildJs(entryInput, options) {
|
|
|
883
883
|
* that has to name this version — `--version`, the dependency a scaffolded
|
|
884
884
|
* project pins — reads it here, so a release bumps one file.
|
|
885
885
|
*/
|
|
886
|
-
const CLI_VERSION = "0.
|
|
886
|
+
const CLI_VERSION = "0.13.0";
|
|
887
887
|
//#endregion
|
|
888
888
|
//#region src/prompt.ts
|
|
889
889
|
/**
|
|
@@ -1630,13 +1630,11 @@ async function detachDomain(creds, app, hostname) {
|
|
|
1630
1630
|
});
|
|
1631
1631
|
if (!res.ok) throw await asGatewayError(res, `'${hostname}' could not be detached (HTTP ${res.status}).`);
|
|
1632
1632
|
}
|
|
1633
|
-
/**
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
process.exit(1);
|
|
1639
|
-
}
|
|
1633
|
+
/**
|
|
1634
|
+
* Who is asking and which App they are asking about. A deploy credential in the
|
|
1635
|
+
* environment is a caller here, so this never insists on a session.
|
|
1636
|
+
*/
|
|
1637
|
+
function credentialsAndApp(options) {
|
|
1640
1638
|
const creds = readCredentials();
|
|
1641
1639
|
if (!creds) {
|
|
1642
1640
|
console.error("[wawesome] Error: Not logged in. Run 'wawesome login' first.");
|
|
@@ -1648,6 +1646,19 @@ async function detachDomainCommand(hostname, options = {}) {
|
|
|
1648
1646
|
console.error("[wawesome] Run this from your project, or name the app with --app <slug>.");
|
|
1649
1647
|
process.exit(1);
|
|
1650
1648
|
}
|
|
1649
|
+
return {
|
|
1650
|
+
creds,
|
|
1651
|
+
app
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
/** A missing name is refused here rather than sent, so the reason reads at the keyboard. */
|
|
1655
|
+
async function detachDomainCommand(hostname, options = {}) {
|
|
1656
|
+
if (!hostname || !hostname.trim()) {
|
|
1657
|
+
console.error("[wawesome] Error: Missing domain. Usage: wawesome domains detach <hostname>");
|
|
1658
|
+
console.error("[wawesome] The domain is typed out because detaching one takes it dark.");
|
|
1659
|
+
process.exit(1);
|
|
1660
|
+
}
|
|
1661
|
+
const { creds, app } = credentialsAndApp(options);
|
|
1651
1662
|
const name = hostname.trim();
|
|
1652
1663
|
console.log(`[wawesome] Detaching '${name}' from app '${app}':`);
|
|
1653
1664
|
console.log("[wawesome] • the domain stops resolving here, so anything pointed at it stops");
|
|
@@ -1668,10 +1679,40 @@ async function detachDomainCommand(hostname, options = {}) {
|
|
|
1668
1679
|
console.log("[wawesome] or the next deploy attaches the domain again.");
|
|
1669
1680
|
}
|
|
1670
1681
|
}
|
|
1682
|
+
/**
|
|
1683
|
+
* What this App answers at, read and printed.
|
|
1684
|
+
*
|
|
1685
|
+
* The one place a Tenant can ask. The records used to exist only in the output
|
|
1686
|
+
* of a deploy that changed something, so a deploy that outran the vendor left
|
|
1687
|
+
* nothing to run but a second deploy, which is refused for changing nothing
|
|
1688
|
+
* (#617). Open to a deploy credential carrying `deploy`, since a pipeline asks
|
|
1689
|
+
* this too.
|
|
1690
|
+
*/
|
|
1691
|
+
async function showDomainCommand(options = {}) {
|
|
1692
|
+
const { creds, app } = credentialsAndApp(options);
|
|
1693
|
+
let held;
|
|
1694
|
+
try {
|
|
1695
|
+
held = await listDomains(creds, app);
|
|
1696
|
+
} catch (err) {
|
|
1697
|
+
console.error(`[wawesome] Error: ${whatWentWrong(err)}`);
|
|
1698
|
+
process.exit(1);
|
|
1699
|
+
}
|
|
1700
|
+
if (held.length === 0) {
|
|
1701
|
+
console.log(`[wawesome] No domain is attached to app '${app}'.`);
|
|
1702
|
+
console.log("[wawesome] Declare one in wawesome-function.json and deploy:");
|
|
1703
|
+
console.log(`[wawesome] \x1b[36m${DOMAIN_CONFIG_LINE}\x1b[0m`);
|
|
1704
|
+
console.log("[wawesome] The deploy attaches it and prints the records to add.");
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
console.log(`[wawesome] The domain on app '${app}':`);
|
|
1708
|
+
for (const domain of held) for (const line of attachedDomainLines(domain, "domains")) console.log(line);
|
|
1709
|
+
}
|
|
1671
1710
|
async function domainsCommand(action, target, options = {}) {
|
|
1672
|
-
|
|
1711
|
+
const verb = (action || "").toLowerCase().trim();
|
|
1712
|
+
if (verb === "detach") return detachDomainCommand(target, options);
|
|
1713
|
+
if (!verb) return showDomainCommand(options);
|
|
1673
1714
|
console.error(`[wawesome] Error: Unknown command '${["domains", action].filter(Boolean).join(" ")}'.`);
|
|
1674
|
-
console.error("[wawesome] Usage: wawesome domains detach <hostname>");
|
|
1715
|
+
console.error("[wawesome] Usage: wawesome domains [detach <hostname>]");
|
|
1675
1716
|
console.error("[wawesome] A domain is attached by declaring it in wawesome-function.json.");
|
|
1676
1717
|
process.exit(1);
|
|
1677
1718
|
}
|
|
@@ -1685,17 +1726,34 @@ function domainsUrl(creds, app) {
|
|
|
1685
1726
|
* added a day apart: the TXT issues the certificate, and the CNAME is pointed
|
|
1686
1727
|
* here afterwards so a site that is already live never answers on a
|
|
1687
1728
|
* certificate that is not there yet.
|
|
1729
|
+
*
|
|
1730
|
+
* Read off the records too, never the states alone: naming a record the payload
|
|
1731
|
+
* does not carry left a reader holding the CNAME alone (#610).
|
|
1688
1732
|
*/
|
|
1689
|
-
function whereItGotTo(
|
|
1733
|
+
function whereItGotTo(domain) {
|
|
1734
|
+
const { state } = domain;
|
|
1690
1735
|
if (state.serving) return "serving";
|
|
1691
1736
|
if (state.certificate_active) return "certificate issued, so point the CNAME here to cut over";
|
|
1692
1737
|
if (state.ownership_verified) return "ownership verified, and the certificate is being issued";
|
|
1738
|
+
if (domain.certificate_record_pending) return "not verified yet, and the TXT record is not ready";
|
|
1693
1739
|
return "not verified yet, so add the TXT record below";
|
|
1694
1740
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1741
|
+
/**
|
|
1742
|
+
* What a record is for, and when to add it.
|
|
1743
|
+
*
|
|
1744
|
+
* The order is stated rather than implied. The TXT proves control and issues
|
|
1745
|
+
* the certificate; the CNAME is pointed here afterwards, so a name already
|
|
1746
|
+
* serving a live site never answers on a certificate that is not there yet.
|
|
1747
|
+
* That is why the platform validates before the cutover, and a reader holding
|
|
1748
|
+
* the CNAME alone could read the old caption as a label rather than as an
|
|
1749
|
+
* instruction to wait (#617).
|
|
1750
|
+
*/
|
|
1751
|
+
function whatItIsFor(purpose) {
|
|
1752
|
+
return purpose === "certificate" ? "Add this first. It proves you own the name and issues the certificate" : "Add this once the certificate is active. It points traffic here";
|
|
1697
1753
|
}
|
|
1698
1754
|
const VALUE_COLUMN = " ".repeat(14);
|
|
1755
|
+
/** What the dashboard's empty state shows. Kept the same by hand, not by anything. */
|
|
1756
|
+
const DOMAIN_CONFIG_LINE = "\"domain\": \"shop.client.com\"";
|
|
1699
1757
|
/**
|
|
1700
1758
|
* The block a deploy prints for the domain its configuration declares, in the
|
|
1701
1759
|
* columns the visibility and URL lines above it already use.
|
|
@@ -1725,7 +1783,18 @@ function domainLines(outcome) {
|
|
|
1725
1783
|
...outcome.held ? [`${VALUE_COLUMN}Nothing was detached. Take '${outcome.held}' off first with`, `${VALUE_COLUMN} \x1b[36mwawesome domains detach ${outcome.held}\x1b[0m`] : [],
|
|
1726
1784
|
`${VALUE_COLUMN}The deploy landed, and this App's other address still answers.`
|
|
1727
1785
|
];
|
|
1728
|
-
|
|
1786
|
+
return attachedDomainLines(outcome.domain, "deploy");
|
|
1787
|
+
}
|
|
1788
|
+
/**
|
|
1789
|
+
* The block for a domain the App carries: where it got to, when the platform
|
|
1790
|
+
* last looked, and the records to add.
|
|
1791
|
+
*
|
|
1792
|
+
* The deploy and `wawesome domains` print this same block. One renderer rather
|
|
1793
|
+
* than two, because two of them drift, and a record going missing from one is
|
|
1794
|
+
* the whole of #610 and #617.
|
|
1795
|
+
*/
|
|
1796
|
+
function attachedDomainLines(domain, printedBy = "deploy") {
|
|
1797
|
+
const { hostname, records, state, last_checked_at, detaching, certificate_record_pending } = domain;
|
|
1729
1798
|
if (detaching) return [
|
|
1730
1799
|
"",
|
|
1731
1800
|
` Domain: \x1b[33m${hostname}: being detached\x1b[0m`,
|
|
@@ -1733,18 +1802,36 @@ function domainLines(outcome) {
|
|
|
1733
1802
|
`${VALUE_COLUMN}Remove 'domain' from wawesome-function.json, or deploy again`,
|
|
1734
1803
|
`${VALUE_COLUMN}once it is gone to attach it afresh.`
|
|
1735
1804
|
];
|
|
1736
|
-
const lines = ["", ` Domain: \x1b[36m${hostname}\x1b[0m: ${whereItGotTo(
|
|
1805
|
+
const lines = ["", ` Domain: \x1b[36m${hostname}\x1b[0m: ${whereItGotTo(domain)}`];
|
|
1806
|
+
if (last_checked_at) lines.push(`${VALUE_COLUMN}Last checked ${asUtc$1(last_checked_at)}.`);
|
|
1737
1807
|
if (state.serving) {
|
|
1738
|
-
lines.push(`${VALUE_COLUMN}
|
|
1808
|
+
lines.push(`${VALUE_COLUMN}This app's own address answers too, so nothing pointed at it breaks.`);
|
|
1739
1809
|
return lines;
|
|
1740
1810
|
}
|
|
1741
|
-
if (last_checked_at) lines.push(`${VALUE_COLUMN}Last checked ${asUtc$1(last_checked_at)}.`);
|
|
1742
1811
|
const nameWidth = Math.max(...records.map((record) => record.name.length));
|
|
1812
|
+
const row = (type, name, value = "") => ` ${type.padEnd(5)} ${name.padEnd(nameWidth)} ${value}`.trimEnd();
|
|
1743
1813
|
lines.push("", " DNS records to add at your registrar:");
|
|
1744
|
-
|
|
1745
|
-
lines.push("",
|
|
1814
|
+
if (certificate_record_pending) lines.push("", ` ${whatItIsFor("certificate")}:`, row("TXT", "not ready yet"));
|
|
1815
|
+
for (const record of records) lines.push("", ` ${whatItIsFor(record.purpose)}:`, row(record.type, record.name, record.value));
|
|
1816
|
+
lines.push("", ...closingLines(certificate_record_pending, printedBy));
|
|
1746
1817
|
return lines;
|
|
1747
1818
|
}
|
|
1819
|
+
/**
|
|
1820
|
+
* What is left to do, which is nothing.
|
|
1821
|
+
*
|
|
1822
|
+
* The pending case pointed at another deploy until #617: a deploy that changed
|
|
1823
|
+
* nothing is refused, so the one piece of advice the reader was given was the
|
|
1824
|
+
* one they could not take.
|
|
1825
|
+
*/
|
|
1826
|
+
function closingLines(certificateRecordPending, printedBy) {
|
|
1827
|
+
if (!certificateRecordPending) return indented(`${printedBy === "deploy" ? "The deploy is finished. " : ""}This domain comes up on its own once the records resolve.`);
|
|
1828
|
+
const asking = "The TXT record is not ready yet. The platform asks the certificate vendor again on its own.";
|
|
1829
|
+
if (printedBy === "domains") return indented(`${asking} Run this again in a minute.`);
|
|
1830
|
+
return [...indented(`${asking} The deploy is finished either way. To read the record when it lands, run`), `${VALUE_COLUMN} \x1b[36mwawesome domains\x1b[0m`];
|
|
1831
|
+
}
|
|
1832
|
+
function indented(said) {
|
|
1833
|
+
return wrapped(said).map((line) => `${VALUE_COLUMN}${line}`);
|
|
1834
|
+
}
|
|
1748
1835
|
/** The platform's own prose, kept inside the block it is printed in. */
|
|
1749
1836
|
function wrapped(said) {
|
|
1750
1837
|
const width = 64;
|
|
@@ -5292,7 +5379,7 @@ cli.command("env [action] [key] [value]", "Manage environment variables (set, li
|
|
|
5292
5379
|
cli.command("env set <key> <value>", "Set or overwrite an environment variable on the current app").option("-s, --secret", "Flag variable as secret (write-only)").option("-v, --verbose", "Enable verbose debug output").action((key, value, options) => setEnvVar(key, value, options));
|
|
5293
5380
|
cli.command("env list", "List environment variables for the current app").alias("env ls").option("-v, --verbose", "Enable verbose debug output").action((options) => listEnvVars(options));
|
|
5294
5381
|
cli.command("env rm <key>", "Delete an environment variable from the current app").alias("env remove").alias("env delete").alias("env unset").option("-v, --verbose", "Enable verbose debug output").action((key, options) => removeEnvVar(key, options));
|
|
5295
|
-
cli.command("domains [action] [hostname]", "
|
|
5382
|
+
cli.command("domains [action] [hostname]", "Show the custom domain on the current app, its state and its DNS records").usage("domains [action] [hostname]\n\nWith no action, prints the app's domain, where it got to, when the platform last checked, and the records to add.\n\nActions:\n detach <hostname> Stop serving the app at a domain you attached\n\nA domain is attached by declaring it in wawesome-function.json and deploying.").example("wawesome domains").example("wawesome domains detach shop.client.com").option("-a, --app <slug>", "App to act on (default: the app in wawesome-function.json)").action((action, hostname, options) => domainsCommand(action, hostname, options));
|
|
5296
5383
|
cli.command("domains detach <hostname>", "Stop serving the current app at a domain you attached").option("-a, --app <slug>", "App to act on (default: the app in wawesome-function.json)").action((hostname, options) => detachDomainCommand(hostname, options));
|
|
5297
5384
|
cli.command("login", "Authenticate with the wawesome.io platform").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("--gateway <url>", "Alias for --api <url>").option("--dashboard <url>", "Dashboard URL (default: https://dashboard.wawesome.io)").option("--provider <name>", "OAuth provider (default: github)").option("--workspace <name>", "Name for the workspace, when signing up without a terminal to prompt").option("--accept-terms", "Accept the terms of service and privacy policy, when signing up without a terminal to prompt").option("-v, --verbose", "Enable verbose debug output").action((options) => login(options));
|
|
5298
5385
|
cli.command("logout", "Clear stored authentication credentials").action(() => logout());
|
|
@@ -5315,10 +5402,11 @@ cli.command("credentials [action] [target]", "Mint, list and revoke deploy crede
|
|
|
5315
5402
|
wawesome credentials mint ci-pipeline
|
|
5316
5403
|
wawesome credentials mint agent -c logs:read --expires never
|
|
5317
5404
|
wawesome credentials mint prod-deploy -c deploy -a prod --expires 30
|
|
5405
|
+
wawesome credentials mint ci -c deploy,domains:attach -a prod
|
|
5318
5406
|
wawesome credentials mint ci-pipeline > /tmp/secret
|
|
5319
5407
|
wawesome credentials revoke ci-pipeline
|
|
5320
|
-
wawesome credentials revoke wawe_ab3k9x --yes`).option("-c, --capability <capability>", "Capability to grant: deploy, logs:read (repeatable or comma-separated, default: deploy)").option("-a, --app <app>", "Restrict the credential to this App (repeatable or comma-separated, default: every App)").option("--expires <days|never>", "Days until the credential expires, or 'never' (default: 90)").option("-y, --yes", "Skip the revoke confirmation").option("-v, --verbose", "Enable verbose debug output").action((action, target, options) => credentialsCommand(action, target, options));
|
|
5321
|
-
cli.command("credentials mint <name>", "Mint a deploy credential, printing its secret exactly once").option("-c, --capability <capability>", "Capability to grant: deploy, logs:read (repeatable or comma-separated)").option("-a, --app <app>", "Restrict the credential to this App (repeatable or comma-separated)").option("--expires <days|never>", "Days until the credential expires, or 'never' (default: 90)").option("-v, --verbose", "Enable verbose debug output").action((name, options) => mintCredentialCommand(name, options));
|
|
5408
|
+
wawesome credentials revoke wawe_ab3k9x --yes`).option("-c, --capability <capability>", "Capability to grant: deploy, logs:read, domains:attach (repeatable or comma-separated, default: deploy)").option("-a, --app <app>", "Restrict the credential to this App (repeatable or comma-separated, default: every App)").option("--expires <days|never>", "Days until the credential expires, or 'never' (default: 90)").option("-y, --yes", "Skip the revoke confirmation").option("-v, --verbose", "Enable verbose debug output").action((action, target, options) => credentialsCommand(action, target, options));
|
|
5409
|
+
cli.command("credentials mint <name>", "Mint a deploy credential, printing its secret exactly once").option("-c, --capability <capability>", "Capability to grant: deploy, logs:read, domains:attach (repeatable or comma-separated)").option("-a, --app <app>", "Restrict the credential to this App (repeatable or comma-separated)").option("--expires <days|never>", "Days until the credential expires, or 'never' (default: 90)").option("-v, --verbose", "Enable verbose debug output").action((name, options) => mintCredentialCommand(name, options));
|
|
5322
5410
|
cli.command("credentials list", "List deploy credentials, never their secrets").alias("credentials ls").option("-v, --verbose", "Enable verbose debug output").action((options) => listCredentialsCommand(options));
|
|
5323
5411
|
cli.command("credentials revoke <name-or-prefix>", "Revoke a deploy credential, immediately and for good").option("-y, --yes", "Revoke without being asked to confirm").option("-v, --verbose", "Enable verbose debug output").action((target, options) => revokeCredentialCommand(target, options));
|
|
5324
5412
|
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("--dashboard <url>", "Dashboard URL (default: https://dashboard.wawesome.io)").option("--no-install", "Skip installing dependencies after scaffolding").option("--root", "Generate a root function router template").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
|