CLI reference
npx wawesome fetches the CLI each time you name it, so there is nothing to install and nothing to
keep up to date. Node 22.13 or newer is the whole requirement.
Most commands read wawesome-function.json in the current directory to find the App and the
Function they act on. logs, invoke and cron take a Function name as an argument. Those three
and domains take --app <slug> to reach another App. Most commands take -v, which prints the
gateway they are talking to and the App and Function they resolved.
When a command fails, what it prints is the gateway’s own error. Errors lists every reason one carries and what to do about each.
Signing in
Section titled “Signing in”| Command | What it does |
|---|---|
npx wawesome login |
Opens your browser at the wawesome sign-in page and writes ~/.wawesome/credentials.json |
npx wawesome logout |
Ends the session on the platform and clears those credentials |
npx wawesome whoami |
Prints who you are, the workspace, and the gateway your token belongs to |
Everything you choose happens on that page. You sign in there. If you belong to one workspace, that
is the whole of it: the tab finishes by itself and your terminal is signed in, with nothing to press.
If you belong to more than one, you pick which one this terminal acts in. If you belong to none yet,
you make your first one on the same page: it asks what to call it and what address it should answer
at, offering the name in address form, and it takes your acceptance of the terms. Leave the address
empty and one is made up for you. Whichever of the three you are, one run of login is enough.
login opens a tab and waits for it on localhost:9999. The listener is on loopback only, so no
other machine on your network can reach it, and it answers one browser: the tab login opened.
Anything else that arrives is turned away, your terminal says so, and the login keeps waiting. If
something else is already holding port 9999, login says which port and stops. A sign-in is good
for ten minutes and is finished once, so a tab you walked away from means running login again. If many
sign-ins start from one address at once, which an office behind one connection can do, the platform
asks you to wait a minute and try again. Nothing is wrong with your account when that happens.
Your session outlives the token it is built on. The token a command sends is worth half an hour;
when one runs out mid-command, the CLI renews it against the platform and the command carries on.
Nothing is printed about it, and a logs --follow running for hours keeps its stream. Two commands
running at the same time both keep working when they meet the same expiry.
A session nobody renews for ninety days is over, and the next command asks you to run login
again. Using the CLI renews it, so this only happens to a terminal you stopped using. The same
happens at once if somebody signs you out of the workspace.
logout ends the session on the platform first, then deletes the file. A copy of
~/.wawesome/credentials.json taken before you signed out is worth nothing afterwards. If the
platform cannot be reached, the file stays where it is and logout says so. That file holds the
only copy of the token that can end the session, so run logout again when you are back online.
A token is only valid at the gateway that issued it, so login records which gateway it used and
every later command goes there. To reach a local or self-hosted one, pass npx wawesome login --gateway http://localhost:3000, set WAWESOME_GATEWAY_URL, or put {"gateway_url": "..."} in
~/.wawesome/settings.json. login reads only those three. A bare npx wawesome login always goes
to https://api.wawesome.io.
Your workspace
Section titled “Your workspace”| Command | What it does |
|---|---|
npx wawesome workspace |
The workspace name, its address, and when the address can next change |
npx wawesome workspace show |
The same thing, spelled out |
npx wawesome workspace list |
Every workspace you belong to, with the one you are in marked |
npx wawesome workspace switch <name> |
Moves this terminal to another workspace you belong to |
npx wawesome workspace rename <name> |
Changes the public address. After your first deploy, once every 30 days |
The address is the first half of the hostname every Function you deploy answers on. You choose it
when you sign up, and rename changes it. Only the workspace owner can rename.
Before your first deploy, a rename is free. The address you leave stops working at once and goes back on offer for anyone to take.
After your first deploy, live URLs carry the address. You can still rename, once every 30 days. The
old address answers 308 for 90 days and sends every request to the same path under the new one.
Preview links under the old address stop working. After the 90 days the old address stops
answering, and we hold it for 90 more days so nobody else can take it. Until those 180 days are
over you can rename back to it, which counts as a rename like any other. Every owner and admin of the
workspace gets an email with the old and the new address.
A rename after your first deploy says what it will do and asks you to type the new address again.
--yes skips that. Without a terminal, for example in CI, the rename is refused unless you pass
--yes. Inside the 30 days, rename is refused before anything is sent, and it names the date the
next rename is allowed.
If you belong to more than one workspace, this terminal is in one of them at a time, and list
shows which. switch moves it, and no browser opens: your session belongs to you rather than to one
workspace. Name the one you want by its address or by its name. The move sticks: every later
command goes there, whoami says so, and a session that renews hours later stays put. A workspace
you are not a member of is refused, and the refusal lists the ones you are in.
Starting a project
Section titled “Starting a project”| Command | What it does |
|---|---|
npx wawesome templates |
Lists the template catalogue. No login needed |
npx wawesome templates list |
The same list |
npx wawesome init |
Scaffolds a project in the current directory |
npx wawesome init --template <name> |
Scaffolds from a template, asks for what it needs, and deploys |
npx wawesome init --root |
Scaffolds a root Function, which answers at the App’s own address |
npx wawesome init --no-install |
Scaffolds without running your package manager |
init asks for the Function name and the App slug, and both default to the directory name. Both go
into the public URL, so both have to be legal hostname labels: lowercase letters, numbers, and
single hyphens between them, no hyphen at either end, 63 characters at most. Answer My Client and
the CLI shows you the my-client it would have to use, then asks again. It does not rewrite what
you typed.
Run it in an empty directory. If any file would be overwritten, nothing is written at all and the CLI names the collision.
--template goes from nothing to a deployed Function in one command. The template is downloaded over
plain HTTP, so git is not required. The CLI asks for each environment variable the template declares,
enables the outbound providers it calls, installs your dependencies, and deploys. Leave a value blank
if it does not exist yet and the CLI prints the env set command to run once it does.
Building and deploying
Section titled “Building and deploying”| Command | What it does |
|---|---|
npx wawesome build [entry] |
Bundles the entry point to dist/index.js |
npx wawesome build -o <path> |
Writes the bundle somewhere else |
npx wawesome deploy [entry] |
Builds, uploads, and promotes a new version to production |
npx wawesome deploy --skip-build |
Uploads the bundle already on disk |
npx wawesome deploy -m "<text>" |
Records one line about what this deploy carries |
npx wawesome deploy --publish |
Confirms putting a private Function back on its public address |
npx wawesome deploy --drop-assets |
Confirms a deploy carrying no static files where the live version serves some |
deploy prints the address the Function answers on, the version it made, and its visibility. Every
path beneath that address reaches the same Function.
If the code and files are byte for byte the version that’s live, deploy makes no new version. It
says so and exits 0, so a CI job that deploys on every push stays green when only a README changed.
The -m text doesn’t count as a change. If they match an older version that isn’t live, deploy
exits 1 and names the version switch that puts it back.
-m writes one line about what you are shipping, and the version keeps it. version list shows it
under the version number, so a list of numbers becomes a list you can read. Keep it to one line and
200 characters. A longer one turns the deploy down instead of cutting it, so nothing you wrote goes
missing quietly. -m "" deploys with no message, which is what every deploy did before the flag
existed.
--publish and --drop-assets exist because a deleted line should never make either change on its
own. Deleting "visibility": "private" puts a job back on the open internet. Dropping "assets"
takes every file on a site down at once. Each is turned down until you type the flag.
What "assets" points at, and where each file it carries answers, is in
documents. So is "files", the directory of private files no address serves.
Versions and rolling back
Section titled “Versions and rolling back”| Command | What it does |
|---|---|
npx wawesome version list |
Every version of this Function, and which one is live |
npx wawesome versions |
The same list |
npx wawesome version switch <version> |
Puts a version the Function already has back on its address |
npx wawesome switch <version> |
The same switch |
Switching builds nothing and uploads nothing. It points the Function’s public address at a version we already hold, and the next request arrives on it. Requests already running finish on the version they started on. The version you switched away from is still there, so switching back is the same command with the other number.
A version carries the code and the static files that one deploy declared. Nothing is inherited from the version before it, so a rollback takes the pages back along with the handler. What a Version is, and why a deploy makes one, is in concepts.
version list prints the message each deploy stated under its version, cut to the width of your
terminal. Switching takes no message of its own. The message belongs to the version, so switching
back to v13 shows you what v13 was deployed with.
All four take -e <environment>, which defaults to production.
Environment variables
Section titled “Environment variables”| Command | What it does |
|---|---|
npx wawesome env list |
Every variable on this App |
npx wawesome env set <key> <value> |
Sets one, or overwrites it |
npx wawesome env set <key> <value> --secret |
Sets one write-only |
npx wawesome env rm <key> |
Deletes one |
Variables belong to the App rather than to any one Function inside it, so every Function in the App
reads the same set. A variable set --secret is never read back, by you or by anything else. env list
shows that it is set and not what it says.
Logs and running a Function
Section titled “Logs and running a Function”| Command | What it does |
|---|---|
npx wawesome logs |
Recent invocations of the Function in this directory |
npx wawesome logs <function> |
Recent invocations of a Function you name |
npx wawesome logs <invocation-id> |
The stdout and stderr one run captured |
npx wawesome logs --invocation <id> |
The same body, with the id behind a flag |
npx wawesome logs <function> --follow |
Streams output as it runs, and waits for the next run if nothing is going |
npx wawesome logs <function> --error |
Failed runs only. --success, --timeout, --running and --status <status> filter the same way |
npx wawesome invoke |
Fires a run of this Function now and follows its output |
npx wawesome invoke <function> |
Fires a run of a Function you name |
npx wawesome invoke -m POST -d '{"event":"audit"}' |
Sends a method and a body with the run |
npx wawesome invoke --no-follow |
Fires the run and exits |
An invocation id is what a response carries back in x-wawesome-invocation-id, so a caller who saw a
failure can hand you the exact run to read.
--follow reconnects with back-off on a dropped connection or a 5xx, three times. It does not retry
a 401 or a 404, since neither improves by being asked again. You hold one live tail per credential.
Schedules
Section titled “Schedules”| Command | What it does |
|---|---|
npx wawesome cron |
Schedules for this Function, with their expression, state and next run in UTC |
npx wawesome cron list <function> |
Schedules for a Function you name. --app <slug> lists a whole App |
npx wawesome cron pause <schedule> |
Stops a schedule. -r "<reason>" records why |
npx wawesome cron resume <schedule> |
Starts it again, counting from now |
npx wawesome cron history |
Runs that already happened: when each was due, when it started, how it ended |
npx wawesome cron history <function> --state failed |
The same history, filtered by state |
npx wawesome schedules |
Another name for cron |
You declare schedules in wawesome-function.json, and a deploy applies them. Pausing is not in the
file, and it survives every later deploy. When a job runs is code. Whether it is running is not.
Resuming counts the next run from now and backfills nothing.
The listing tells three off-states apart. paused was stopped by a person and cron resume starts it.
not in this config file was removed from the config, and only declaring it again turns it back on.
suspended was stopped by non-payment and resumes once the balance is settled.
Declaring one, the plan you need for one, and what each run state in the history means are in schedules.
Domains
Section titled “Domains”| Command | What it does |
|---|---|
npx wawesome domains |
The App’s domain, where it got to, when that was last checked, and the DNS records to add |
npx wawesome domains adopt |
Writes the hostname the App answers at into wawesome-function.json |
npx wawesome domains detach <hostname> |
Stops serving the App at a domain you attached |
You attach a domain by declaring "domain" in wawesome-function.json and deploying. The dashboard
attaches one too, and so does an agent. Both leave the file saying nothing about it, which is what
adopt fixes. domains changes nothing, and a deploy credential carrying read:domains can run
it, so a pipeline reads the same states you do.
Detaching is typed out in full because it takes a live site dark. Deleting the line from the file detaches nothing.
The records to add, what each state means for traffic, and the record that is in but proxied are in custom domains.
Deploy credentials
Section titled “Deploy credentials”A deploy credential is what a CI pipeline or an agent authenticates with, where nobody is at a keyboard to log in. What each capability lets you do, and how to hand one to a pipeline, is in deploy credentials.
| Command | What it does |
|---|---|
npx wawesome credentials |
Name, prefix, capabilities, Apps, last use and expiry. Never the secret |
npx wawesome credentials list |
The same listing |
npx wawesome credentials mint <name> |
Mints one and prints its secret, once |
npx wawesome credentials regenerate <name-or-prefix> |
Replaces the secret, keeping the name and the grant |
npx wawesome credentials revoke <name-or-prefix> |
Stops it working, immediately and for good |
npx wawesome credentials delete <name-or-prefix> |
Removes a revoked or expired one, freeing its name |
Minting, listing, regenerating, revoking and deleting all run on your own npx wawesome login session.
They are closed to deploy credentials whatever those carry, so a leaked credential cannot mint another
or renew its own expiry.
mint and regenerate print the secret on stdout alone and everything else on stderr, so
npx wawesome credentials mint ci > secret.txt catches the secret and nothing besides.
Exporting the workspace
Section titled “Exporting the workspace”| Command | What it does |
|---|---|
npx wawesome export |
Asks for an archive of the whole workspace, waits, and prints the link |
npx wawesome export --no-wait |
Asks for one and returns straight away |
npx wawesome export status |
The latest export, with a new link if it is ready |
Only the workspace owner can export, on their own login. The link works for an hour, and the archive is deleted after seven days. What is in it is in exporting a workspace.
Help and version
Section titled “Help and version”| Command | What it does |
|---|---|
npx wawesome --help |
Every command. npx wawesome <command> --help for one of them |
npx wawesome --version |
The version of the CLI that just ran |
Unsupported globals
Section titled “Unsupported globals”Your Function does not run on Node. Every build scans the bundle it just produced against the globals we provide, and says nothing unless it finds something.
These are not provided: Intl, URLPattern, WebSocket, EventSource, BroadcastChannel,
caches, FileReader, WebAssembly, XMLHttpRequest, navigator, localStorage,
sessionStorage, indexedDB, setImmediate, reportError, Atomics and SharedArrayBuffer.
Limits says what to reach for in place of each.
Where the bundle reaches one as it loads, in your own module scope or a dependency’s, the build is
turned down before anything is uploaded. That bundle would not evaluate here. Where the reference
sits inside a function that may never be called, behind a typeof check, or inside a try, you get
a warning and the deploy goes ahead. A library that asks whether WebSocket is there before it uses
one is that second case, and it deploys.
toLocaleString, toLocaleDateString, toLocaleTimeString, toLocaleLowerCase and
toLocaleUpperCase ignore the locale you pass them. They run and return an unlocalised answer, so these
warn rather than stop the build. (1234.5).toLocaleString('en-US') comes back as "1234.5", not "1,234.50".
Each message names the global, the file and the line in your own source, and what to do about it.
The scan reads static references only, so a global reached through globalThis['Intl'] is invisible
to it. No deploy is ever turned down on a guess. deploy --skip-build scans the bundle it found on
disk before uploading it.
To silence the report, declare a polyfill in your package.json as you normally would, or install
one on globalThis in the bundle itself. A dependency that provides Intl or URLPattern is left
in place rather than stripped out from under you. The others have no polyfill to declare. What they
need is a socket, a disk, a second thread or a browser, and none of those is in the sandbox for a
polyfill to build on.
Catching this in your tests
Section titled “Catching this in your tests”Node has Intl, and its toLocaleString honours the locale you pass. That is how a green test
suite ships a Function that throws in production. Point your suite at the same globals we have
instead:
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: { setupFiles: ["wawesome/vitest-setup"] },
});
Templates scaffolded with wawesome init --template ship this already. With it in place the globals
above are gone, MessageChannel, MessagePort, MessageEvent, TextEncoderStream and
TextDecoderStream are our implementations rather than Node’s, and the locale-sensitive methods
throw with a message naming the fix. They throw rather than return the unlocalised answer. A wrong
string that fails nowhere is what this is here to stop you shipping.
node:fs/promises reads your private files from your files
folder, and every write rejects, as it does in production.
WebAssembly is the one exception. Node compiles its own HTTP parser from it the first time
anything calls fetch, so taking it away would fail your suite somewhere you did not write. Your
Function still has none, and the build still turns down a bundle that reaches for it.