wawesome 0.0.12 → 0.0.14
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 +1 -1
- package/dist/index.mjs +227 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -213,7 +213,7 @@ Three headers arrive or leave on it, and the stripping is what makes them worth
|
|
|
213
213
|
| --- | --- | --- |
|
|
214
214
|
| `x-wawesome-forwarded-prefix` | inbound | The mount that was stripped from the path. Join it to the path you observe to rebuild the caller's URL. |
|
|
215
215
|
| `x-wawesome-invocation-id` | outbound | The id of this run — the key to fetch its logs with `npx wawesome logs --invocation <id>`. |
|
|
216
|
-
| `x-wawesome-error` | outbound | Present only when the platform failed, never when your Function did. Its *absence* means the status on the wire is yours. |
|
|
216
|
+
| `x-wawesome-error` | outbound | Present only when the platform failed, never when your Function did. Its *absence* means the status on the wire is yours — up to the moment your response is committed, and no further. |
|
|
217
217
|
|
|
218
218
|
### Local Development / Gateway Overrides
|
|
219
219
|
|
package/dist/index.mjs
CHANGED
|
@@ -31,25 +31,40 @@ function readSettings() {
|
|
|
31
31
|
return {};
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
const GATEWAY_ENV_NAMES = ["GATEWAY_URL", "WAWESOME_GATEWAY_URL"];
|
|
35
|
+
const DASHBOARD_ENV_NAMES = ["DASHBOARD_URL", "WAWESOME_DASHBOARD_URL"];
|
|
36
|
+
const GATEWAY_URL_FALLBACK = "https://api.wawesome.io";
|
|
37
|
+
const DASHBOARD_URL_FALLBACK = "https://dashboard.wawesome.io";
|
|
38
|
+
function resolveAddress(overrideUrl, envNames, settingsKey, stored, fallback) {
|
|
39
|
+
if (overrideUrl) return overrideUrl;
|
|
40
|
+
for (const name of envNames) {
|
|
41
|
+
const fromEnv = process.env[name];
|
|
42
|
+
if (fromEnv) return fromEnv;
|
|
43
|
+
}
|
|
44
|
+
const configured = readSettings()[settingsKey];
|
|
45
|
+
if (configured && typeof configured === "string") return configured;
|
|
46
|
+
return stored || fallback;
|
|
47
|
+
}
|
|
48
|
+
function getGatewayUrl(overrideUrl) {
|
|
49
|
+
return resolveAddress(overrideUrl, GATEWAY_ENV_NAMES, "gateway_url", readCredentials()?.gateway_url, GATEWAY_URL_FALLBACK);
|
|
50
|
+
}
|
|
51
|
+
getGatewayUrl();
|
|
52
|
+
function getDashboardUrl(overrideUrl) {
|
|
53
|
+
return resolveAddress(overrideUrl, DASHBOARD_ENV_NAMES, "dashboard_url", readCredentials()?.dashboard_url, DASHBOARD_URL_FALLBACK);
|
|
54
|
+
}
|
|
34
55
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
56
|
+
* The dashboard for the environment this login is going to.
|
|
57
|
+
*
|
|
58
|
+
* The two addresses describe one environment, so the stored rung is read only
|
|
59
|
+
* where the gateway being logged into is the one the credentials already name.
|
|
60
|
+
* Carrying a dashboard across a change of gateway is the wrong workspace, and
|
|
61
|
+
* so is dropping one where the gateway did not change at all.
|
|
41
62
|
*/
|
|
42
|
-
function
|
|
43
|
-
if (overrideUrl) return overrideUrl;
|
|
44
|
-
if (process.env.GATEWAY_URL) return process.env.GATEWAY_URL;
|
|
45
|
-
if (process.env.WAWESOME_GATEWAY_URL) return process.env.WAWESOME_GATEWAY_URL;
|
|
46
|
-
const settings = readSettings();
|
|
47
|
-
if (settings.gateway_url && typeof settings.gateway_url === "string") return settings.gateway_url;
|
|
63
|
+
function resolveDashboardUrlForLogin(overrideUrl, gatewayUrl) {
|
|
48
64
|
const creds = readCredentials();
|
|
49
|
-
|
|
50
|
-
return "
|
|
65
|
+
const sameEnvironment = creds?.gateway_url === gatewayUrl;
|
|
66
|
+
return resolveAddress(overrideUrl, DASHBOARD_ENV_NAMES, "dashboard_url", sameEnvironment ? creds?.dashboard_url : void 0, DASHBOARD_URL_FALLBACK);
|
|
51
67
|
}
|
|
52
|
-
getGatewayUrl();
|
|
53
68
|
/** Localhost port used during OAuth callback */
|
|
54
69
|
const OAUTH_CALLBACK_PORT = 9999;
|
|
55
70
|
/** Path to the user-level credentials file */
|
|
@@ -166,7 +181,7 @@ async function buildJs(entryInput, options) {
|
|
|
166
181
|
* that has to name this version — `--version`, the dependency a scaffolded
|
|
167
182
|
* project pins — reads it here, so a release bumps one file.
|
|
168
183
|
*/
|
|
169
|
-
const CLI_VERSION = "0.0.
|
|
184
|
+
const CLI_VERSION = "0.0.14";
|
|
170
185
|
//#endregion
|
|
171
186
|
//#region src/prompt.ts
|
|
172
187
|
/**
|
|
@@ -247,14 +262,21 @@ var GatewayError = class extends Error {
|
|
|
247
262
|
* was doing, so callers handle it before reaching this.
|
|
248
263
|
*/
|
|
249
264
|
async function asGatewayError(res, fallback) {
|
|
250
|
-
|
|
265
|
+
return rejectionOf(await res.text().catch(() => ""), res.status, fallback);
|
|
266
|
+
}
|
|
267
|
+
/** The same reading, for a caller that has already taken the body off the wire. */
|
|
268
|
+
function rejectionOf(body, status, fallback) {
|
|
251
269
|
try {
|
|
252
270
|
const parsed = JSON.parse(body);
|
|
253
|
-
return new GatewayError(parsed.error || fallback,
|
|
271
|
+
return new GatewayError(parsed.error || fallback, status, parsed.reason, body);
|
|
254
272
|
} catch {
|
|
255
|
-
return new GatewayError(fallback,
|
|
273
|
+
return new GatewayError(fallback, status, void 0, body);
|
|
256
274
|
}
|
|
257
275
|
}
|
|
276
|
+
/** Why the gateway refused, where what was thrown might not be a refusal at all. */
|
|
277
|
+
function reasonOf(err) {
|
|
278
|
+
return err instanceof GatewayError ? err.reason : void 0;
|
|
279
|
+
}
|
|
258
280
|
/** What went wrong, as text, whatever was thrown. */
|
|
259
281
|
function errorText(err) {
|
|
260
282
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -306,6 +328,10 @@ function renameAdvice(reason) {
|
|
|
306
328
|
text: "Use lowercase letters, numbers and single hyphens.",
|
|
307
329
|
retryable: true
|
|
308
330
|
};
|
|
331
|
+
case "too-short": return {
|
|
332
|
+
text: "Use at least three characters.",
|
|
333
|
+
retryable: true
|
|
334
|
+
};
|
|
309
335
|
case "locked": return {
|
|
310
336
|
text: "A deploy landed while this was running, which fixed the address for good.",
|
|
311
337
|
retryable: false
|
|
@@ -377,6 +403,7 @@ async function promptForWorkspaceName(options) {
|
|
|
377
403
|
*/
|
|
378
404
|
async function login(options) {
|
|
379
405
|
const gatewayUrl = getGatewayUrl(options.gateway || options.api);
|
|
406
|
+
const dashboardUrl = resolveDashboardUrlForLogin(options.dashboard, gatewayUrl);
|
|
380
407
|
const provider = options.provider || "github";
|
|
381
408
|
const isVerbose = Boolean(options.verbose);
|
|
382
409
|
const authUrl = `${SUPABASE_URL}/auth/v1/authorize?provider=${provider}&redirect_to=${encodeURIComponent(`http://localhost:${OAUTH_CALLBACK_PORT}/callback`)}`;
|
|
@@ -476,6 +503,7 @@ async function login(options) {
|
|
|
476
503
|
} catch {}
|
|
477
504
|
writeCredentials({
|
|
478
505
|
gateway_url: gatewayUrl,
|
|
506
|
+
dashboard_url: dashboardUrl,
|
|
479
507
|
tenant_jwt: exchangeData.tenant_jwt,
|
|
480
508
|
tenant_id: primaryTenantId,
|
|
481
509
|
user_email: userEmail,
|
|
@@ -549,6 +577,155 @@ async function whoami() {
|
|
|
549
577
|
console.log(` Gateway: ${creds.gateway_url}\n`);
|
|
550
578
|
}
|
|
551
579
|
//#endregion
|
|
580
|
+
//#region src/usage.ts
|
|
581
|
+
const USAGE_TIMEOUT_MS = 2e3;
|
|
582
|
+
/**
|
|
583
|
+
* Bounded by its own timeout: every caller treats this as an aside to something
|
|
584
|
+
* that already succeeded, so a management API that hangs must not be able to
|
|
585
|
+
* hold that something open.
|
|
586
|
+
*/
|
|
587
|
+
async function fetchTenantUsage(creds, timeoutMs = USAGE_TIMEOUT_MS) {
|
|
588
|
+
const controller = new AbortController();
|
|
589
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
590
|
+
try {
|
|
591
|
+
const res = await fetch(`${creds.gateway_url}/v1/tenant/usage`, {
|
|
592
|
+
headers: { Authorization: `Bearer ${creds.tenant_jwt}` },
|
|
593
|
+
signal: controller.signal
|
|
594
|
+
});
|
|
595
|
+
if (!res.ok) throw await asGatewayError(res, `Failed to read usage (HTTP ${res.status}).`);
|
|
596
|
+
return asTenantUsage(await res.json());
|
|
597
|
+
} finally {
|
|
598
|
+
clearTimeout(timer);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* A payload that is missing any of this is one the plan could not be resolved
|
|
603
|
+
* for, and is refused here rather than rendered as half a block.
|
|
604
|
+
*/
|
|
605
|
+
function asTenantUsage(value) {
|
|
606
|
+
const usage = value;
|
|
607
|
+
if (!(!!usage && typeof usage.plan?.name === "string" && typeof usage.plan?.limits?.app_slots === "number" && typeof usage.occupied_app_slots === "number" && !!usage.allowances && typeof usage.allowances === "object" && isInstant(usage.period?.start) && isInstant(usage.period?.end))) throw new Error("Usage payload does not carry a resolved plan.");
|
|
608
|
+
return usage;
|
|
609
|
+
}
|
|
610
|
+
function isInstant(value) {
|
|
611
|
+
return typeof value === "string" && !isNaN(new Date(value).getTime());
|
|
612
|
+
}
|
|
613
|
+
//#endregion
|
|
614
|
+
//#region src/headroom.ts
|
|
615
|
+
const KNOWN_LABELS = {
|
|
616
|
+
invocations: "Invocations",
|
|
617
|
+
caller_facing_bytes: "Caller-facing bytes"
|
|
618
|
+
};
|
|
619
|
+
const COUNT_UNITS = [
|
|
620
|
+
{
|
|
621
|
+
threshold: 1e9,
|
|
622
|
+
suffix: "B"
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
threshold: 1e6,
|
|
626
|
+
suffix: "M"
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
threshold: 1e3,
|
|
630
|
+
suffix: "k"
|
|
631
|
+
}
|
|
632
|
+
];
|
|
633
|
+
const BYTE_UNITS = [
|
|
634
|
+
"B",
|
|
635
|
+
"KiB",
|
|
636
|
+
"MiB",
|
|
637
|
+
"GiB",
|
|
638
|
+
"TiB"
|
|
639
|
+
];
|
|
640
|
+
const MONTHS = [
|
|
641
|
+
"January",
|
|
642
|
+
"February",
|
|
643
|
+
"March",
|
|
644
|
+
"April",
|
|
645
|
+
"May",
|
|
646
|
+
"June",
|
|
647
|
+
"July",
|
|
648
|
+
"August",
|
|
649
|
+
"September",
|
|
650
|
+
"October",
|
|
651
|
+
"November",
|
|
652
|
+
"December"
|
|
653
|
+
];
|
|
654
|
+
/** Indented to sit inside the receipt the deploy already prints. */
|
|
655
|
+
function headroomLines(usage) {
|
|
656
|
+
const lines = [
|
|
657
|
+
` Plan: ${usage.plan.name} — this deploy does not change your bill.`,
|
|
658
|
+
` Apps: ${usage.occupied_app_slots} / ${usage.plan.limits.app_slots} slots`,
|
|
659
|
+
` Usage: ${periodLabel(usage.period.start, usage.period.end)}`
|
|
660
|
+
];
|
|
661
|
+
const entries = Object.entries(usage.allowances);
|
|
662
|
+
const labelWidth = widest(entries.map(([key]) => allowanceLabel(key)));
|
|
663
|
+
const amountWidth = widest(entries.map(([key, allowance]) => amount(key, allowance.used, allowance.limit)));
|
|
664
|
+
for (const [key, allowance] of entries) {
|
|
665
|
+
const percent = allowance.consumed_percent === null ? "" : ` (${allowance.consumed_percent}%)`;
|
|
666
|
+
const line = ` ${allowanceLabel(key).padEnd(labelWidth)} ${amount(key, allowance.used, allowance.limit).padEnd(amountWidth)}${percent}`;
|
|
667
|
+
lines.push(line.trimEnd());
|
|
668
|
+
}
|
|
669
|
+
const refused = refusedAtShare(usage);
|
|
670
|
+
if (refused !== null) lines.push(` Refused: ${formatCount(refused)} request${refused === 1 ? "" : "s"} at your share`);
|
|
671
|
+
return lines;
|
|
672
|
+
}
|
|
673
|
+
function refusedAtShare(usage) {
|
|
674
|
+
const refused = usage.refusals?.at_granted_share;
|
|
675
|
+
if (typeof refused !== "number" || !Number.isFinite(refused) || refused <= 0) return null;
|
|
676
|
+
return refused;
|
|
677
|
+
}
|
|
678
|
+
function allowanceLabel(key) {
|
|
679
|
+
if (KNOWN_LABELS[key]) return KNOWN_LABELS[key];
|
|
680
|
+
const words = key.replace(/[_-]+/g, " ").trim();
|
|
681
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
682
|
+
}
|
|
683
|
+
function formatCount(value) {
|
|
684
|
+
for (let i = 0; i < COUNT_UNITS.length; i++) {
|
|
685
|
+
const { threshold, suffix } = COUNT_UNITS[i];
|
|
686
|
+
if (value < threshold) continue;
|
|
687
|
+
const scaled = round(value / threshold, 1);
|
|
688
|
+
if (scaled >= 1e3 && i > 0) {
|
|
689
|
+
const bigger = COUNT_UNITS[i - 1];
|
|
690
|
+
return `${round(value / bigger.threshold, 1)}${bigger.suffix}`;
|
|
691
|
+
}
|
|
692
|
+
return `${scaled}${suffix}`;
|
|
693
|
+
}
|
|
694
|
+
return String(Math.round(value));
|
|
695
|
+
}
|
|
696
|
+
function formatBytes(value) {
|
|
697
|
+
let scaled = value;
|
|
698
|
+
let unit = 0;
|
|
699
|
+
while (scaled >= 1024 && unit < BYTE_UNITS.length - 1) {
|
|
700
|
+
scaled /= 1024;
|
|
701
|
+
unit++;
|
|
702
|
+
}
|
|
703
|
+
return `${unit === 0 ? Math.round(scaled) : round(scaled, 2)} ${BYTE_UNITS[unit]}`;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Name the period rather than print its bounds. The end is exclusive, so a
|
|
707
|
+
* partial period names the last day inside it and not the instant after it.
|
|
708
|
+
*/
|
|
709
|
+
function periodLabel(start, end) {
|
|
710
|
+
const from = new Date(start);
|
|
711
|
+
const to = new Date(end);
|
|
712
|
+
if (from.getTime() === Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), 1) && to.getTime() === Date.UTC(from.getUTCFullYear(), from.getUTCMonth() + 1, 1)) return `${MONTHS[from.getUTCMonth()]} ${from.getUTCFullYear()} (UTC)`;
|
|
713
|
+
return `${dayLabel(from)} – ${dayLabel(/* @__PURE__ */ new Date(to.getTime() - 1))} (UTC)`;
|
|
714
|
+
}
|
|
715
|
+
function dayLabel(date) {
|
|
716
|
+
return `${date.getUTCDate()} ${MONTHS[date.getUTCMonth()].slice(0, 3)} ${date.getUTCFullYear()}`;
|
|
717
|
+
}
|
|
718
|
+
function amount(key, used, limit) {
|
|
719
|
+
const format = key.includes("bytes") ? formatBytes : formatCount;
|
|
720
|
+
return `${format(used)} / ${format(limit)}`;
|
|
721
|
+
}
|
|
722
|
+
function round(value, places) {
|
|
723
|
+
return Number(value.toFixed(places));
|
|
724
|
+
}
|
|
725
|
+
function widest(values) {
|
|
726
|
+
return values.reduce((longest, value) => Math.max(longest, value.length), 0);
|
|
727
|
+
}
|
|
728
|
+
//#endregion
|
|
552
729
|
//#region ../shared/public-address.ts
|
|
553
730
|
const INVOCATION_PREFIX = "/x";
|
|
554
731
|
const SUBTREE_NOTE = "Every path beneath this address reaches the Function.";
|
|
@@ -623,12 +800,8 @@ async function deploy(entryInput, options) {
|
|
|
623
800
|
const errorBody = await uploadRes.text();
|
|
624
801
|
if (uploadRes.status === 401) console.error("[wawesome] Error: Authentication expired. Run 'wawesome login' again.");
|
|
625
802
|
else if (uploadRes.status === 409) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
const parsed = JSON.parse(errorBody);
|
|
629
|
-
if (parsed.error) msg = parsed.error;
|
|
630
|
-
} catch {}
|
|
631
|
-
console.error(`\n[wawesome] \x1b[31mError: ${msg}\x1b[0m`);
|
|
803
|
+
const refusal = rejectionOf(errorBody, uploadRes.status, "Version with this code bundle already exists.");
|
|
804
|
+
console.error(`\n[wawesome] \x1b[31mError: ${refusal.message}\x1b[0m`);
|
|
632
805
|
console.error("[wawesome] Code versions are immutable and cannot be overwritten.");
|
|
633
806
|
console.error("[wawesome] To switch active version, run: \x1B[36mwawesome version switch\x1B[0m\n");
|
|
634
807
|
} else {
|
|
@@ -668,6 +841,12 @@ async function deploy(entryInput, options) {
|
|
|
668
841
|
} catch (err) {
|
|
669
842
|
if (isVerbose) console.log(`[wawesome:verbose] Could not resolve the workspace address: ${err instanceof Error ? err.message : err}`);
|
|
670
843
|
}
|
|
844
|
+
let headroom = null;
|
|
845
|
+
try {
|
|
846
|
+
headroom = headroomLines(await fetchTenantUsage(creds));
|
|
847
|
+
} catch (err) {
|
|
848
|
+
if (isVerbose) console.log(`[wawesome:verbose] Could not read the plan's usage: ${errorText(err)}`);
|
|
849
|
+
}
|
|
671
850
|
console.log("\n======================================================");
|
|
672
851
|
console.log("🚀 \x1B[32mDEPLOYED SUCCESSFULLY!\x1B[0m");
|
|
673
852
|
console.log("======================================================");
|
|
@@ -678,6 +857,10 @@ async function deploy(entryInput, options) {
|
|
|
678
857
|
console.log(`\n URL: \x1b[36m${address}\x1b[0m`);
|
|
679
858
|
console.log(` ${SUBTREE_NOTE}`);
|
|
680
859
|
}
|
|
860
|
+
if (headroom) {
|
|
861
|
+
console.log("");
|
|
862
|
+
for (const line of headroom) console.log(line);
|
|
863
|
+
}
|
|
681
864
|
console.log("======================================================\n");
|
|
682
865
|
return {
|
|
683
866
|
app,
|
|
@@ -687,6 +870,20 @@ async function deploy(entryInput, options) {
|
|
|
687
870
|
};
|
|
688
871
|
}
|
|
689
872
|
//#endregion
|
|
873
|
+
//#region src/billing.ts
|
|
874
|
+
function billingPageUrl() {
|
|
875
|
+
const base = getDashboardUrl().replace(/\/+$/, "");
|
|
876
|
+
try {
|
|
877
|
+
return new URL("billing", `${base}/`).toString();
|
|
878
|
+
} catch {
|
|
879
|
+
return `${base}/billing`;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
function appSlotAdvice(reason) {
|
|
883
|
+
if (reason !== "app-slots-exhausted") return "";
|
|
884
|
+
return `Where to resolve it: ${billingPageUrl()}`;
|
|
885
|
+
}
|
|
886
|
+
//#endregion
|
|
690
887
|
//#region src/env.ts
|
|
691
888
|
const STANDARD_SECRET_MESSAGES = [
|
|
692
889
|
"Encrypted at rest using AES-256",
|
|
@@ -1523,6 +1720,7 @@ async function ensureSession(options) {
|
|
|
1523
1720
|
try {
|
|
1524
1721
|
await login({
|
|
1525
1722
|
api: options.api,
|
|
1723
|
+
dashboard: options.dashboard,
|
|
1526
1724
|
verbose: options.verbose
|
|
1527
1725
|
});
|
|
1528
1726
|
} catch (err) {
|
|
@@ -1585,7 +1783,7 @@ async function offerWorkspaceAddress(session, creds, tenant, appSlug, functionNa
|
|
|
1585
1783
|
return;
|
|
1586
1784
|
} catch (err) {
|
|
1587
1785
|
console.log(`[wawesome] ${errorText(err)}`);
|
|
1588
|
-
refusal = err
|
|
1786
|
+
refusal = reasonOf(err);
|
|
1589
1787
|
const { text, retryable } = renameAdvice(refusal);
|
|
1590
1788
|
if (text) console.log(`[wawesome] ${text}`);
|
|
1591
1789
|
if (!retryable) break;
|
|
@@ -1605,7 +1803,7 @@ async function wireUp(creds, appSlug, manifest, answers) {
|
|
|
1605
1803
|
try {
|
|
1606
1804
|
await ensureApp(creds, appSlug);
|
|
1607
1805
|
} catch (err) {
|
|
1608
|
-
fail(errorText(err), scaffolded);
|
|
1806
|
+
fail(errorText(err), ...[appSlotAdvice(reasonOf(err)), scaffolded].filter(Boolean));
|
|
1609
1807
|
}
|
|
1610
1808
|
for (const { declared, value } of answers) {
|
|
1611
1809
|
if (!value) {
|
|
@@ -2595,12 +2793,12 @@ cli.command("env [action] [key] [value]", "Manage environment variables (set, li
|
|
|
2595
2793
|
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));
|
|
2596
2794
|
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));
|
|
2597
2795
|
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));
|
|
2598
|
-
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("--provider <name>", "OAuth provider (default: github)").option("--workspace <name>", "Name for the workspace, when signing up without a terminal to prompt").option("-v, --verbose", "Enable verbose debug output").action((options) => login(options));
|
|
2796
|
+
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("-v, --verbose", "Enable verbose debug output").action((options) => login(options));
|
|
2599
2797
|
cli.command("logout", "Clear stored authentication credentials").action(() => logout());
|
|
2600
2798
|
cli.command("whoami", "Show current login session info").action(() => whoami());
|
|
2601
2799
|
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));
|
|
2602
2800
|
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));
|
|
2603
|
-
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("--root", "Generate a root function router template").option("-v, --verbose", "Enable verbose debug output").action((options) => init(options));
|
|
2801
|
+
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));
|
|
2604
2802
|
cli.command("logs [function-name-or-invocation-id]", "View invocation history, fetch log output, or follow live").usage(`logs [target] [options]
|
|
2605
2803
|
|
|
2606
2804
|
The target argument determines what the command does:
|