@lizard-build/cli 4.0.23 → 4.0.24
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 +14 -0
- package/dist/commands/billing.d.ts +68 -0
- package/dist/commands/billing.js +435 -0
- package/dist/commands/billing.js.map +1 -0
- package/dist/commands/metrics.d.ts +13 -0
- package/dist/commands/metrics.js +33 -2
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/up.js +10 -3
- package/dist/commands/up.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +25 -0
- package/dist/lib/api.js +81 -26
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/skills-data.generated.js +1 -1
- package/dist/lib/skills-data.generated.js.map +1 -1
- package/dist/lib/updater.d.ts +1 -1
- package/dist/lib/updater.js +1 -1
- package/package.json +1 -4
- package/skill-data/core/SKILL.md +37 -29
- package/src/commands/billing.ts +571 -0
- package/src/commands/metrics.ts +49 -2
- package/src/commands/up.ts +9 -8
- package/src/index.ts +2 -2
- package/src/lib/api.ts +81 -26
- package/src/lib/updater.ts +1 -1
- package/test/unit/billing.test.ts +347 -0
- package/test/unit/metrics.test.ts +72 -0
- package/test/unit/payment-required.test.ts +110 -0
- package/dist/commands/credits.d.ts +0 -2
- package/dist/commands/credits.js +0 -331
- package/dist/commands/credits.js.map +0 -1
- package/dist/lib/x402.d.ts +0 -23
- package/dist/lib/x402.js +0 -148
- package/dist/lib/x402.js.map +0 -1
- package/src/commands/credits.ts +0 -379
- package/src/lib/x402.ts +0 -131
- package/test/unit/x402.test.ts +0 -61
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ lizard ps # list services
|
|
|
63
63
|
lizard status # show the linked workspace, project and service
|
|
64
64
|
lizard logs # stream runtime logs
|
|
65
65
|
lizard metrics # resource metrics (CPU, memory, network, disk); --cost for spend
|
|
66
|
+
lizard billing # plan, trial, credits used this month; start, cancel or resume Pro
|
|
66
67
|
lizard metrics --all # all services in the project, even with a linked service
|
|
67
68
|
lizard scale # scale replicas / CPU / memory / storage
|
|
68
69
|
lizard secrets # manage secrets
|
|
@@ -93,6 +94,19 @@ lizard ssh -s my-app -- printenv
|
|
|
93
94
|
|
|
94
95
|
Run `lizard --help` for the full list.
|
|
95
96
|
|
|
97
|
+
## Billing
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
lizard billing # plan, trial, credits used this month, next charge
|
|
101
|
+
lizard billing start # start the Pro trial in Stripe Checkout
|
|
102
|
+
lizard billing promo X # redeem a promo code for a longer trial
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Pro costs $19/month, taxes included, with $19 in credits each month. Usage
|
|
106
|
+
above that is pay as you go. A new account starts with a 7-day trial with $5 in
|
|
107
|
+
credits. When the account has no plan, commands that create something fail
|
|
108
|
+
with the reason and the link to open next.
|
|
109
|
+
|
|
96
110
|
## For agents and scripts
|
|
97
111
|
|
|
98
112
|
Add `--json` to any command for structured output. The CLI also switches to JSON
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/** Old prepaid credits (`plan: "payg"`) keep working until this date. */
|
|
3
|
+
export declare const CREDITS_END = "November 1, 2026";
|
|
4
|
+
export declare function billingUrl(): string;
|
|
5
|
+
export interface Subscription {
|
|
6
|
+
plan: "none" | "pro" | "payg" | "enterprise" | string;
|
|
7
|
+
status: "none" | "trialing" | "active" | "past_due" | "canceled" | string;
|
|
8
|
+
isOwner?: boolean;
|
|
9
|
+
priceCents: number;
|
|
10
|
+
taxIncluded: boolean;
|
|
11
|
+
includedCents: number;
|
|
12
|
+
trial: {
|
|
13
|
+
eligible: boolean;
|
|
14
|
+
days: number | null;
|
|
15
|
+
creditCents: number | null;
|
|
16
|
+
promoCode: string | null;
|
|
17
|
+
endsAt: number | null;
|
|
18
|
+
usedCents: number | null;
|
|
19
|
+
remainingCents: number | null;
|
|
20
|
+
};
|
|
21
|
+
period: {
|
|
22
|
+
kind: "trial" | "paid";
|
|
23
|
+
start: number;
|
|
24
|
+
end: number;
|
|
25
|
+
includedCents: number;
|
|
26
|
+
usedCents: number;
|
|
27
|
+
overageCents: number;
|
|
28
|
+
billedOverageCents: number;
|
|
29
|
+
unbilledOverageCents: number;
|
|
30
|
+
nextOverageChargeAtCents: number | null;
|
|
31
|
+
} | null;
|
|
32
|
+
nextCharge: {
|
|
33
|
+
at: number | null;
|
|
34
|
+
amountCents: number;
|
|
35
|
+
} | null;
|
|
36
|
+
cancelAt: number | null;
|
|
37
|
+
pastDue: boolean;
|
|
38
|
+
openInvoiceUrl: string | null;
|
|
39
|
+
paymentMethod: {
|
|
40
|
+
brand: string;
|
|
41
|
+
last4: string;
|
|
42
|
+
expMonth: number;
|
|
43
|
+
expYear: number;
|
|
44
|
+
} | null;
|
|
45
|
+
limits: {
|
|
46
|
+
tier: string;
|
|
47
|
+
replicasPerApp: number;
|
|
48
|
+
} | null;
|
|
49
|
+
checkoutAvailable: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** New servers add the trial fields; older ones send only the first four. */
|
|
52
|
+
export interface PromoResult {
|
|
53
|
+
status: string;
|
|
54
|
+
code?: string;
|
|
55
|
+
creditCents: number;
|
|
56
|
+
expiresAt: number | null;
|
|
57
|
+
balanceCents: number;
|
|
58
|
+
trialDays?: number | null;
|
|
59
|
+
trialCreditCents?: number | null;
|
|
60
|
+
appliesTo?: "next_checkout" | "current_trial";
|
|
61
|
+
}
|
|
62
|
+
/** $19, $4.10: whole dollars without cents. */
|
|
63
|
+
export declare function usd(cents: number): string;
|
|
64
|
+
/** Lines that describe a plan, for people. Pure, so the tests can read it. */
|
|
65
|
+
export declare function describeSubscription(sub: Subscription, now?: number): string[];
|
|
66
|
+
/** One line for a redeemed promo code. Works with servers before and after Pro. */
|
|
67
|
+
export declare function describePromo(out: PromoResult): string;
|
|
68
|
+
export declare function registerBilling(program: Command): void;
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import open from "open";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import { api, withQuery, getBaseURL, isNotFound } from "../lib/api.js";
|
|
5
|
+
import { success, isJSONMode, printJSON, table, isTTY, timeAgo, info, warn } from "../lib/format.js";
|
|
6
|
+
// `lizard billing` — the account's plan (docs/pro-billing-api.md on the platform
|
|
7
|
+
// repo). Pro is $19/month, taxes included, with $19 of monthly credits; usage above
|
|
8
|
+
// that is pay as you go. Unlike most commands this never takes --project/--service:
|
|
9
|
+
// billing belongs to the account, not a workspace. `lizard credits` is the old name.
|
|
10
|
+
/** Old prepaid credits (`plan: "payg"`) keep working until this date. */
|
|
11
|
+
export const CREDITS_END = "November 1, 2026";
|
|
12
|
+
const BILLING_PATH = "/profile/account-billing";
|
|
13
|
+
export function billingUrl() {
|
|
14
|
+
return `${getBaseURL()}${BILLING_PATH}`;
|
|
15
|
+
}
|
|
16
|
+
function fmtCents(cents) {
|
|
17
|
+
const sign = cents < 0 ? "-" : "";
|
|
18
|
+
return `${sign}$${(Math.abs(cents) / 100).toFixed(2)}`;
|
|
19
|
+
}
|
|
20
|
+
/** $19, $4.10: whole dollars without cents. */
|
|
21
|
+
export function usd(cents) {
|
|
22
|
+
return cents % 100 === 0 ? `$${cents / 100}` : fmtCents(cents);
|
|
23
|
+
}
|
|
24
|
+
function fmtDate(ms) {
|
|
25
|
+
return new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
26
|
+
}
|
|
27
|
+
function daysLeft(endMs, now = Date.now()) {
|
|
28
|
+
const days = Math.max(0, Math.ceil((endMs - now) / 86_400_000));
|
|
29
|
+
return days === 1 ? "1 day left" : `${days} days left`;
|
|
30
|
+
}
|
|
31
|
+
function statusLabel(status) {
|
|
32
|
+
switch (status) {
|
|
33
|
+
case "active": return chalk.green(status);
|
|
34
|
+
case "trialing": return chalk.green("trial");
|
|
35
|
+
case "grace":
|
|
36
|
+
case "past_due": return chalk.yellow(status.replace("_", " "));
|
|
37
|
+
case "frozen":
|
|
38
|
+
case "canceled": return chalk.red(status);
|
|
39
|
+
default: return status;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const creditsNotice = () => `Prepaid credits end on ${CREDITS_END}. Start Pro before then: lizard billing start`;
|
|
43
|
+
/** Lines that describe a plan, for people. Pure, so the tests can read it. */
|
|
44
|
+
export function describeSubscription(sub, now = Date.now()) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
const label = (s) => chalk.dim(s);
|
|
47
|
+
const owner = sub.isOwner === false ? chalk.dim(" (workspace owner's plan)") : "";
|
|
48
|
+
if (sub.plan === "payg") {
|
|
49
|
+
lines.push(`${chalk.bold("Prepaid credits")}${owner}`);
|
|
50
|
+
lines.push(chalk.yellow(creditsNotice()));
|
|
51
|
+
return lines;
|
|
52
|
+
}
|
|
53
|
+
if (sub.plan === "enterprise") {
|
|
54
|
+
lines.push(`${chalk.bold("Enterprise")}${owner}`);
|
|
55
|
+
lines.push("Pay as you go, invoiced monthly.");
|
|
56
|
+
return lines;
|
|
57
|
+
}
|
|
58
|
+
if (sub.plan !== "pro") {
|
|
59
|
+
lines.push(`${chalk.bold("No plan")}${owner}`);
|
|
60
|
+
if (sub.status === "canceled")
|
|
61
|
+
lines.push("Pro has ended.");
|
|
62
|
+
if (sub.isOwner === false) {
|
|
63
|
+
lines.push("The workspace owner has to start Pro.");
|
|
64
|
+
}
|
|
65
|
+
else if (!sub.checkoutAvailable) {
|
|
66
|
+
lines.push("Pro is not open for this account yet.");
|
|
67
|
+
}
|
|
68
|
+
else if (sub.trial.eligible && sub.trial.days) {
|
|
69
|
+
lines.push(`Start a ${sub.trial.days}-day Pro trial with ${usd(sub.trial.creditCents ?? 0)} in credits: ` +
|
|
70
|
+
chalk.cyan("lizard billing start"));
|
|
71
|
+
lines.push(chalk.dim(`No charge today, then ${usd(sub.priceCents)}/month, taxes included.`));
|
|
72
|
+
if (sub.trial.promoCode)
|
|
73
|
+
lines.push(label("Promo code: ") + `${sub.trial.promoCode} (saved for the trial)`);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
lines.push(`Start Pro: ${usd(sub.priceCents)}/month, taxes included, with ${usd(sub.includedCents)} in credits each month: ` +
|
|
77
|
+
chalk.cyan("lizard billing start"));
|
|
78
|
+
}
|
|
79
|
+
return lines;
|
|
80
|
+
}
|
|
81
|
+
lines.push(`${chalk.bold("Pro")} ${statusLabel(sub.status)}${owner} ` +
|
|
82
|
+
chalk.dim(`${usd(sub.priceCents)}/month, taxes included`));
|
|
83
|
+
const period = sub.period;
|
|
84
|
+
if (sub.status === "trialing") {
|
|
85
|
+
if (sub.trial.endsAt)
|
|
86
|
+
lines.push(label("Trial: ") + `${daysLeft(sub.trial.endsAt, now)}, ends ${fmtDate(sub.trial.endsAt)}`);
|
|
87
|
+
const credit = sub.trial.creditCents ?? period?.includedCents ?? 0;
|
|
88
|
+
const used = sub.trial.usedCents ?? period?.usedCents ?? 0;
|
|
89
|
+
const left = sub.trial.remainingCents ?? Math.max(0, credit - used);
|
|
90
|
+
lines.push(label("Trial credits: ") + `${usd(used)} of ${usd(credit)} used, ${usd(left)} left`);
|
|
91
|
+
}
|
|
92
|
+
else if (period) {
|
|
93
|
+
lines.push(label("This month: ") +
|
|
94
|
+
`${usd(period.usedCents)} of ${usd(period.includedCents)} used` +
|
|
95
|
+
chalk.dim(` (${fmtDate(period.start)} to ${fmtDate(period.end)})`));
|
|
96
|
+
if (period.kind === "paid") {
|
|
97
|
+
let overage = `${usd(period.overageCents)} this month`;
|
|
98
|
+
if (period.overageCents > 0) {
|
|
99
|
+
overage += `, ${usd(period.billedOverageCents)} invoiced so far`;
|
|
100
|
+
if (period.nextOverageChargeAtCents) {
|
|
101
|
+
overage += `; the next invoice goes out at ${usd(period.nextOverageChargeAtCents)}`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
lines.push(label("Overage: ") + overage);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (sub.nextCharge?.at) {
|
|
108
|
+
lines.push(label("Next charge: ") + `${usd(sub.nextCharge.amountCents)} on ${fmtDate(sub.nextCharge.at)}`);
|
|
109
|
+
}
|
|
110
|
+
if (sub.cancelAt) {
|
|
111
|
+
lines.push(chalk.yellow(`Ends on ${fmtDate(sub.cancelAt)}.`) + " Run " + chalk.cyan("lizard billing resume") + " to keep Pro.");
|
|
112
|
+
}
|
|
113
|
+
if (sub.pastDue) {
|
|
114
|
+
lines.push(chalk.red("A payment failed. New resources are blocked until the open invoice is paid."));
|
|
115
|
+
if (sub.openInvoiceUrl)
|
|
116
|
+
lines.push(label("Pay it: ") + chalk.cyan(sub.openInvoiceUrl));
|
|
117
|
+
}
|
|
118
|
+
if (sub.limits) {
|
|
119
|
+
const n = sub.limits.replicasPerApp;
|
|
120
|
+
lines.push(label("Replicas: ") + `up to ${n} per service`);
|
|
121
|
+
}
|
|
122
|
+
if (sub.paymentMethod) {
|
|
123
|
+
const pm = sub.paymentMethod;
|
|
124
|
+
lines.push(label("Card: ") + `${pm.brand} •••• ${pm.last4}` + chalk.dim(` (expires ${pm.expMonth}/${pm.expYear})`));
|
|
125
|
+
}
|
|
126
|
+
return lines;
|
|
127
|
+
}
|
|
128
|
+
async function fetchBalance() {
|
|
129
|
+
return api.get("/api/billing/balance");
|
|
130
|
+
}
|
|
131
|
+
/** The prepaid credits view (old `payg` accounts until November 1, and enterprise usage). */
|
|
132
|
+
function printBalanceView(view) {
|
|
133
|
+
console.log(`${chalk.bold(fmtCents(view.balanceCents))} ${statusLabel(view.status)} ${chalk.dim(`(${view.plan})`)}`);
|
|
134
|
+
if (view.neverFreeze) {
|
|
135
|
+
console.log(chalk.dim("Exempt from billing freeze — balance can go negative with no enforcement."));
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
if (view.overdraftLimitCents != null) {
|
|
139
|
+
console.log(chalk.dim(`Overdraft limit: `) + fmtCents(-view.overdraftLimitCents));
|
|
140
|
+
}
|
|
141
|
+
if (view.availableCents != null) {
|
|
142
|
+
console.log(chalk.dim(`Available before freeze: `) + fmtCents(view.availableCents));
|
|
143
|
+
}
|
|
144
|
+
if (view.graceSince) {
|
|
145
|
+
const deadline = view.graceSince + view.graceHours * 3_600_000;
|
|
146
|
+
const hoursLeft = Math.max(0, Math.round((deadline - Date.now()) / 3_600_000));
|
|
147
|
+
console.log(chalk.yellow(`In grace since ${timeAgo(view.graceSince)} — freezes in ~${hoursLeft}h if unpaid.`));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (view.expiringCents > 0 && view.expiringAt) {
|
|
151
|
+
console.log(chalk.dim(`Expiring: `) + `${fmtCents(view.expiringCents)} by ${new Date(view.expiringAt).toLocaleDateString()}`);
|
|
152
|
+
}
|
|
153
|
+
if (view.hourlyRateCents > 0) {
|
|
154
|
+
const rate = chalk.dim(`(~${fmtCents(view.hourlyRateCents)}/hr)`);
|
|
155
|
+
if (view.runwayHours != null) {
|
|
156
|
+
const days = (view.runwayHours / 24).toFixed(1);
|
|
157
|
+
console.log(chalk.dim(`Runway: `) + `~${view.runwayHours}h (${days}d) at current usage ${rate}`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.log(chalk.dim(`Current usage rate: `) + rate);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (view.autoTopup) {
|
|
164
|
+
console.log(chalk.dim("Auto top-up: ") +
|
|
165
|
+
(view.autoTopup.enabled
|
|
166
|
+
? `on — tops up ${fmtCents(view.autoTopup.amountCents)} when balance drops below ${fmtCents(view.autoTopup.thresholdCents)}`
|
|
167
|
+
: "off"));
|
|
168
|
+
if (view.autoTopup.disabledReason) {
|
|
169
|
+
console.log(chalk.yellow(` disabled: ${view.autoTopup.disabledReason}`));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (view.pendingPromo) {
|
|
173
|
+
console.log(chalk.dim("Pending promo: ") + `${view.pendingPromo.code} (${fmtCents(view.pendingPromo.creditCents)})`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function showStatus(opts) {
|
|
177
|
+
let sub;
|
|
178
|
+
try {
|
|
179
|
+
sub = await api.get(withQuery("/api/billing/subscription", { workspaceId: opts.workspace }));
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
// A server without Pro yet: the prepaid credits view is all there is.
|
|
183
|
+
if (!isNotFound(e) || opts.workspace)
|
|
184
|
+
throw e;
|
|
185
|
+
const view = await fetchBalance();
|
|
186
|
+
if (isJSONMode())
|
|
187
|
+
printJSON(view);
|
|
188
|
+
else
|
|
189
|
+
printBalanceView(view);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const url = billingUrl();
|
|
193
|
+
const ownPrepaid = sub.plan === "payg" && sub.isOwner !== false;
|
|
194
|
+
const balance = ownPrepaid ? await fetchBalance() : null;
|
|
195
|
+
if (isJSONMode()) {
|
|
196
|
+
printJSON({
|
|
197
|
+
...sub,
|
|
198
|
+
billingUrl: url,
|
|
199
|
+
...(balance ? { balance, notice: `Prepaid credits end on ${CREDITS_END}.` } : {}),
|
|
200
|
+
});
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (sub.plan === "payg") {
|
|
204
|
+
if (balance)
|
|
205
|
+
printBalanceView(balance);
|
|
206
|
+
else
|
|
207
|
+
console.log(`${chalk.bold("Prepaid credits")}${chalk.dim(" (workspace owner's plan)")}`);
|
|
208
|
+
console.log(chalk.yellow(creditsNotice()));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
for (const line of describeSubscription(sub))
|
|
212
|
+
console.log(line);
|
|
213
|
+
}
|
|
214
|
+
console.log(chalk.dim("Billing: ") + chalk.cyan(url));
|
|
215
|
+
}
|
|
216
|
+
async function confirmOrExit(message, yes) {
|
|
217
|
+
if (yes)
|
|
218
|
+
return;
|
|
219
|
+
if (!isTTY())
|
|
220
|
+
throw new Error("Use -y to confirm in non-interactive mode");
|
|
221
|
+
const confirmed = await p.confirm({ message });
|
|
222
|
+
if (p.isCancel(confirmed) || !confirmed)
|
|
223
|
+
process.exit(5);
|
|
224
|
+
}
|
|
225
|
+
async function openInBrowser(url, wanted) {
|
|
226
|
+
if (wanted && isTTY() && !isJSONMode())
|
|
227
|
+
await open(url).catch(() => { });
|
|
228
|
+
}
|
|
229
|
+
/** One line for a redeemed promo code. Works with servers before and after Pro. */
|
|
230
|
+
export function describePromo(out) {
|
|
231
|
+
if (out.trialDays != null) {
|
|
232
|
+
const credits = usd(out.trialCreditCents ?? out.creditCents);
|
|
233
|
+
if (out.appliesTo === "current_trial") {
|
|
234
|
+
const until = out.expiresAt ? `, until ${fmtDate(out.expiresAt)}` : "";
|
|
235
|
+
return `Your trial now runs ${out.trialDays} days with ${credits} in credits${until}.`;
|
|
236
|
+
}
|
|
237
|
+
const code = out.code ? `Code ${out.code} saved: ` : "Code saved: ";
|
|
238
|
+
return `${code}a ${out.trialDays}-day Pro trial with ${credits} in credits. Start it with \`lizard billing start\`.`;
|
|
239
|
+
}
|
|
240
|
+
if (out.status === "pending_payment_method") {
|
|
241
|
+
return `Code saved: ${fmtCents(out.creditCents)} in credits. They unlock when you start your trial in Billing.`;
|
|
242
|
+
}
|
|
243
|
+
return `Redeemed ${fmtCents(out.creditCents)}. New balance: ${fmtCents(out.balanceCents)}.`;
|
|
244
|
+
}
|
|
245
|
+
export function registerBilling(program) {
|
|
246
|
+
const cmd = program
|
|
247
|
+
.command("billing")
|
|
248
|
+
.alias("credits")
|
|
249
|
+
.description("Your plan: Pro trial, this month's credits, next charge, cancel or resume (`credits` is the old name)")
|
|
250
|
+
.option("-w, --workspace <id>", "Show the plan of this workspace's owner")
|
|
251
|
+
.action(async () => {
|
|
252
|
+
await showStatus(cmd.opts());
|
|
253
|
+
});
|
|
254
|
+
cmd.hook("preAction", (thisCommand) => {
|
|
255
|
+
if (thisCommand.parent?.args[0] === "credits") {
|
|
256
|
+
warn("`lizard credits` is now `lizard billing`. The old name will go away in a later release.");
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
cmd
|
|
260
|
+
.command("status")
|
|
261
|
+
.description("Show the plan, trial, this month's credits and the next charge")
|
|
262
|
+
.option("-w, --workspace <id>", "Show the plan of this workspace's owner")
|
|
263
|
+
.action(async (opts) => {
|
|
264
|
+
// `billing` takes -w too and claims it wherever it appears on the line.
|
|
265
|
+
await showStatus({ ...cmd.opts(), ...opts });
|
|
266
|
+
});
|
|
267
|
+
cmd
|
|
268
|
+
.command("open")
|
|
269
|
+
.description("Open the Billing page in the browser")
|
|
270
|
+
.option("--no-open", "Print the link instead of opening a browser")
|
|
271
|
+
.action(async (opts) => {
|
|
272
|
+
const url = billingUrl();
|
|
273
|
+
if (isJSONMode()) {
|
|
274
|
+
printJSON({ url, opened: false });
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
console.log(chalk.cyan(url));
|
|
278
|
+
await openInBrowser(url, opts.open !== false);
|
|
279
|
+
});
|
|
280
|
+
cmd
|
|
281
|
+
.command("start")
|
|
282
|
+
.description("Start Pro in Stripe Checkout: the trial if the account can have one, otherwise Pro at once")
|
|
283
|
+
.option("--return-url <path>", "Dashboard path to come back to after Checkout, e.g. /projects/abc")
|
|
284
|
+
.option("--no-open", "Print the Checkout link instead of opening a browser")
|
|
285
|
+
.action(async (opts) => {
|
|
286
|
+
const out = await api.post("/api/billing/subscription/checkout", { returnUrl: opts.returnUrl });
|
|
287
|
+
if (isJSONMode()) {
|
|
288
|
+
printJSON(out);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (out.trialDays) {
|
|
292
|
+
console.log(`Your ${out.trialDays}-day trial includes ${usd(out.trialCreditCents ?? 0)} in credits. ` +
|
|
293
|
+
"No charge today, then $19/month, taxes included.");
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
console.log("Pro starts today: $19/month, taxes included, with $19 in credits each month.");
|
|
297
|
+
}
|
|
298
|
+
console.log(`Finish in Stripe Checkout:\n${chalk.cyan(out.url)}`);
|
|
299
|
+
await openInBrowser(out.url, opts.open !== false);
|
|
300
|
+
info(chalk.dim("Run `lizard billing` when you are done."));
|
|
301
|
+
});
|
|
302
|
+
cmd
|
|
303
|
+
.command("start-now")
|
|
304
|
+
.description("End the trial now: charge $19 and start the first paid month with $19 in credits")
|
|
305
|
+
.option("-y, --yes", "Skip confirmation")
|
|
306
|
+
.option("--no-open", "Print the payment link instead of opening a browser")
|
|
307
|
+
.action(async (opts) => {
|
|
308
|
+
await confirmOrExit("End the trial and charge $19 now?", opts.yes);
|
|
309
|
+
const out = await api.post("/api/billing/subscription/start-now", {});
|
|
310
|
+
if (out.status === "failed") {
|
|
311
|
+
throw Object.assign(new Error(`The charge failed, so your trial continues. Check your card in Billing:\n ${billingUrl()}`), { code: "PAYMENT_FAILED" });
|
|
312
|
+
}
|
|
313
|
+
if (isJSONMode()) {
|
|
314
|
+
printJSON(out);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
if (out.status === "requires_action" && out.invoiceUrl) {
|
|
318
|
+
console.log(`Your bank needs you to confirm the payment. Your trial continues until you do:\n${chalk.cyan(out.invoiceUrl)}`);
|
|
319
|
+
await openInBrowser(out.invoiceUrl, opts.open !== false);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
success("Pro is active. We charged $19 and added $19 in credits for this month.");
|
|
323
|
+
});
|
|
324
|
+
cmd
|
|
325
|
+
.command("cancel")
|
|
326
|
+
.description("Cancel Pro at the end of this month or trial; nothing is charged after that")
|
|
327
|
+
.option("-y, --yes", "Skip confirmation")
|
|
328
|
+
.action(async (opts) => {
|
|
329
|
+
await confirmOrExit("Cancel Pro at the end of the current period?", opts.yes);
|
|
330
|
+
const out = await api.post("/api/billing/subscription/cancel", {});
|
|
331
|
+
if (isJSONMode()) {
|
|
332
|
+
printJSON(out);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
success(out.cancelAt
|
|
336
|
+
? `Pro ends on ${fmtDate(out.cancelAt)}. Run \`lizard billing resume\` to keep it.`
|
|
337
|
+
: "Pro is cancelled.");
|
|
338
|
+
});
|
|
339
|
+
cmd
|
|
340
|
+
.command("resume")
|
|
341
|
+
.description("Undo a cancel: keep Pro after the current period")
|
|
342
|
+
.action(async () => {
|
|
343
|
+
const out = await api.post("/api/billing/subscription/resume", {});
|
|
344
|
+
if (isJSONMode()) {
|
|
345
|
+
printJSON(out);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
success("Pro continues. It renews as usual.");
|
|
349
|
+
});
|
|
350
|
+
cmd
|
|
351
|
+
.command("promo <code>")
|
|
352
|
+
.description("Redeem a promo code: a longer Pro trial with more trial credits")
|
|
353
|
+
.action(async (code) => {
|
|
354
|
+
const out = await api.post("/api/billing/promo/redeem", { code });
|
|
355
|
+
if (isJSONMode()) {
|
|
356
|
+
printJSON(out);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
success(describePromo(out));
|
|
360
|
+
});
|
|
361
|
+
cmd
|
|
362
|
+
.command("balance")
|
|
363
|
+
.description(`Prepaid credits balance (old credits accounts until ${CREDITS_END}, and enterprise)`)
|
|
364
|
+
.action(async () => {
|
|
365
|
+
const view = await fetchBalance();
|
|
366
|
+
if (isJSONMode()) {
|
|
367
|
+
printJSON(view);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
printBalanceView(view);
|
|
371
|
+
});
|
|
372
|
+
cmd
|
|
373
|
+
.command("transactions")
|
|
374
|
+
.alias("ledger")
|
|
375
|
+
.description("List recent balance transactions (prepaid credits and enterprise accounts)")
|
|
376
|
+
.option("-l, --limit <n>", "Max results (1-100)", "20")
|
|
377
|
+
.option("-c, --cursor <cursor>", "Pagination cursor from a previous call")
|
|
378
|
+
.option("-u, --usage", "Include daily usage-deduction rows")
|
|
379
|
+
.action(async (opts) => {
|
|
380
|
+
const page = await api.get(withQuery("/api/billing/transactions", {
|
|
381
|
+
limit: opts.limit,
|
|
382
|
+
cursor: opts.cursor,
|
|
383
|
+
includeUsage: opts.usage ? "1" : undefined,
|
|
384
|
+
}));
|
|
385
|
+
if (isJSONMode()) {
|
|
386
|
+
printJSON(page);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (page.items.length === 0) {
|
|
390
|
+
console.log("No transactions.");
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
table(["When", "Kind", "Amount", "Balance after", "Description"], page.items.map((t) => [
|
|
394
|
+
timeAgo(t.createdAt),
|
|
395
|
+
t.kind,
|
|
396
|
+
t.amountCents >= 0 ? chalk.green(fmtCents(t.amountCents)) : chalk.red(fmtCents(t.amountCents)),
|
|
397
|
+
t.balanceAfterCents != null ? fmtCents(t.balanceAfterCents) : "—",
|
|
398
|
+
t.description ?? "",
|
|
399
|
+
]));
|
|
400
|
+
if (page.nextCursor) {
|
|
401
|
+
console.log(chalk.dim(`\nMore results: lizard billing transactions --cursor ${page.nextCursor}`));
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
// ── Payment methods ───────────────────────────────────────────────
|
|
405
|
+
const pm = cmd.command("payment-methods").alias("pm").description("List or remove saved cards (add a card in Billing)");
|
|
406
|
+
pm.command("list")
|
|
407
|
+
.description("List saved cards")
|
|
408
|
+
.action(async () => {
|
|
409
|
+
const { items } = await api.get("/api/billing/payment-methods");
|
|
410
|
+
if (isJSONMode()) {
|
|
411
|
+
printJSON(items);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (items.length === 0) {
|
|
415
|
+
console.log(`No saved cards. Add one in Billing: ${chalk.cyan(billingUrl())}`);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
table(["ID", "Card", "Expires", "Default"], items.map((m) => [m.id, `${m.brand} •••• ${m.last4}`, `${m.expMonth}/${m.expYear}`, m.isDefault ? "yes" : ""]));
|
|
419
|
+
});
|
|
420
|
+
pm.command("remove <id>")
|
|
421
|
+
.alias("rm")
|
|
422
|
+
.description("Remove a saved card")
|
|
423
|
+
.option("-y, --yes", "Skip confirmation")
|
|
424
|
+
.action(async (id, opts) => {
|
|
425
|
+
await confirmOrExit(`Remove payment method ${chalk.bold(id)}?`, opts.yes);
|
|
426
|
+
const out = await api.delete(`/api/billing/payment-methods/${encodeURIComponent(id)}`);
|
|
427
|
+
if (isJSONMode()) {
|
|
428
|
+
printJSON(out);
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
success("Payment method removed");
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/commands/billing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
export declare function registerMetrics(program: Command): void;
|
|
3
|
+
/** This month's Pro credits for the workspace owner's account; null for other plans. */
|
|
4
|
+
interface ProCredits {
|
|
5
|
+
status: string;
|
|
6
|
+
kind: "trial" | "paid";
|
|
7
|
+
periodStart: number;
|
|
8
|
+
periodEnd: number;
|
|
9
|
+
includedCents: number;
|
|
10
|
+
usedCents: number;
|
|
11
|
+
overageCents: number;
|
|
12
|
+
}
|
|
13
|
+
/** "$4.10 of $19 in monthly credits used, overage $0" — the account-wide line for Pro. */
|
|
14
|
+
export declare function proCreditsLine(pro: ProCredits): string;
|
|
15
|
+
export {};
|
package/dist/commands/metrics.js
CHANGED
|
@@ -13,7 +13,7 @@ export function registerMetrics(program) {
|
|
|
13
13
|
.option("--all", "Show all services in the project, ignoring the linked service")
|
|
14
14
|
.option("-r, --range <range>", `Time range: ${RANGES.join("|")}`, "1h")
|
|
15
15
|
.option("-w, --watch", "Live view, refreshed every 3s (Ctrl+C to stop)")
|
|
16
|
-
.option("--cost", "Show running resources, cost per hour,
|
|
16
|
+
.option("--cost", "Show running resources, cost per hour, current billing-period usage (incl. egress) and Pro credits used this month")
|
|
17
17
|
.action(async (opts) => {
|
|
18
18
|
if (opts.all && opts.service) {
|
|
19
19
|
fail("--all cannot be combined with --service. Choose all services or one service.");
|
|
@@ -364,6 +364,31 @@ async function fetchPeriodUsage(projectId, workspaceId) {
|
|
|
364
364
|
totalEstimatedUsd: rows.reduce((s, r) => s + r.estimatedUsd, 0),
|
|
365
365
|
};
|
|
366
366
|
}
|
|
367
|
+
async function fetchProCredits(workspaceId) {
|
|
368
|
+
const sub = await api
|
|
369
|
+
.get(withQuery("/api/billing/subscription", { workspaceId }))
|
|
370
|
+
.catch(() => null);
|
|
371
|
+
if (!sub || sub.plan !== "pro" || !sub.period)
|
|
372
|
+
return null;
|
|
373
|
+
return {
|
|
374
|
+
status: sub.status,
|
|
375
|
+
kind: sub.period.kind,
|
|
376
|
+
periodStart: sub.period.start,
|
|
377
|
+
periodEnd: sub.period.end,
|
|
378
|
+
includedCents: sub.period.includedCents,
|
|
379
|
+
usedCents: sub.period.usedCents,
|
|
380
|
+
overageCents: sub.period.overageCents,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
const dollars = (cents) => (cents % 100 === 0 ? `$${cents / 100}` : `$${(cents / 100).toFixed(2)}`);
|
|
384
|
+
/** "$4.10 of $19 in monthly credits used, overage $0" — the account-wide line for Pro. */
|
|
385
|
+
export function proCreditsLine(pro) {
|
|
386
|
+
if (pro.kind === "trial") {
|
|
387
|
+
const left = Math.max(0, pro.includedCents - pro.usedCents);
|
|
388
|
+
return `${dollars(pro.usedCents)} of ${dollars(pro.includedCents)} in trial credits used, ${dollars(left)} left`;
|
|
389
|
+
}
|
|
390
|
+
return `${dollars(pro.usedCents)} of ${dollars(pro.includedCents)} in monthly credits used, overage ${dollars(pro.overageCents)}`;
|
|
391
|
+
}
|
|
367
392
|
function fmtPeriodDate(ms) {
|
|
368
393
|
return new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
369
394
|
}
|
|
@@ -373,10 +398,12 @@ async function showCost(projectId, scope) {
|
|
|
373
398
|
}
|
|
374
399
|
let data;
|
|
375
400
|
let usage;
|
|
401
|
+
let pro;
|
|
376
402
|
try {
|
|
377
|
-
[data, usage] = await Promise.all([
|
|
403
|
+
[data, usage, pro] = await Promise.all([
|
|
378
404
|
api.get(withQuery("/api/billing/live", { workspaceId: scope.workspaceId })),
|
|
379
405
|
fetchPeriodUsage(projectId, scope.workspaceId),
|
|
406
|
+
fetchProCredits(scope.workspaceId),
|
|
380
407
|
]);
|
|
381
408
|
}
|
|
382
409
|
catch (e) {
|
|
@@ -394,6 +421,8 @@ async function showCost(projectId, scope) {
|
|
|
394
421
|
projectCostPerHour: projectCost,
|
|
395
422
|
workspaceCostPerHour: data.costPerHour,
|
|
396
423
|
currentPeriod: usage,
|
|
424
|
+
// Pro only: the account's credits this month (the trial's while trialing).
|
|
425
|
+
...(pro ? { proCredits: pro } : {}),
|
|
397
426
|
});
|
|
398
427
|
return;
|
|
399
428
|
}
|
|
@@ -415,6 +444,8 @@ async function showCost(projectId, scope) {
|
|
|
415
444
|
chalk.dim(` (~$${(projectCost * 730).toFixed(2)}/mo at current usage)`));
|
|
416
445
|
}
|
|
417
446
|
console.log(chalk.dim("Workspace ") + `$${data.costPerHour.toFixed(4)}/hr`);
|
|
447
|
+
if (pro)
|
|
448
|
+
console.log(chalk.dim(pro.kind === "trial" ? "Pro trial " : "Pro ") + proCreditsLine(pro));
|
|
418
449
|
if (usage && usage.rows.length > 0) {
|
|
419
450
|
console.log();
|
|
420
451
|
console.log(chalk.bold("This billing period") +
|