@lizard-build/sdk 0.1.51 → 0.1.52

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/dist/index.d.mts CHANGED
@@ -1520,6 +1520,7 @@ declare class RegionsAPI {
1520
1520
  list(): Promise<Region[]>;
1521
1521
  }
1522
1522
 
1523
+ /** Prepaid credits balance: old credits accounts (`plan: 'payg'`) until 1 November 2026, and enterprise usage. */
1523
1524
  interface Balance {
1524
1525
  plan: string;
1525
1526
  status: 'active' | 'grace' | 'frozen' | string;
@@ -1558,61 +1559,180 @@ interface ListTransactionsOpts {
1558
1559
  includeUsage?: boolean;
1559
1560
  }
1560
1561
  /**
1561
- * Account balance and usage.
1562
+ * `none`: no plan yet (creating anything answers 402). `pro`: the Pro subscription, see
1563
+ * {@link Subscription.status}. `payg`: old prepaid credits, until 1 November 2026.
1564
+ * `enterprise`: pay as you go, invoiced monthly.
1565
+ */
1566
+ type Plan = 'none' | 'pro' | 'payg' | 'enterprise';
1567
+ /** Pro subscription status; `none` when there is no subscription. */
1568
+ type SubscriptionStatus = 'trialing' | 'active' | 'past_due' | 'canceled' | 'none';
1569
+ interface Subscription {
1570
+ plan: Plan | (string & {});
1571
+ status: SubscriptionStatus | (string & {});
1572
+ /** False when read for a workspace the caller does not own; card and invoice fields are then null. */
1573
+ isOwner: boolean;
1574
+ /** 1900: $19/month. */
1575
+ priceCents: number;
1576
+ /** The price includes taxes. */
1577
+ taxIncluded: boolean;
1578
+ /** Credits included each paid month, in cents (1900). */
1579
+ includedCents: number;
1580
+ trial: {
1581
+ /** The account can start a trial now. */
1582
+ eligible: boolean;
1583
+ /** Trial length it would get, or got. */
1584
+ days: number | null;
1585
+ /** Trial credits, in cents. */
1586
+ creditCents: number | null;
1587
+ /** Promo code held for the trial, if any. */
1588
+ promoCode: string | null;
1589
+ /** Trial end (ms); null when not trialing. */
1590
+ endsAt: number | null;
1591
+ /** Trial credits used (trialing only). */
1592
+ usedCents: number | null;
1593
+ remainingCents: number | null;
1594
+ };
1595
+ /** The current Pro period; null when there is none. */
1596
+ period: {
1597
+ kind: 'trial' | 'paid';
1598
+ start: number;
1599
+ end: number;
1600
+ /** 500 in the trial, 1900 in a paid month. */
1601
+ includedCents: number;
1602
+ usedCents: number;
1603
+ /** Paid month: usage above the included credits. */
1604
+ overageCents: number;
1605
+ /** Of the overage, already invoiced. */
1606
+ billedOverageCents: number;
1607
+ unbilledOverageCents: number;
1608
+ /** Paid month: unbilled overage at which the next invoice goes out. */
1609
+ nextOverageChargeAtCents: number | null;
1610
+ } | null;
1611
+ /** Null when there is none, or the subscription is cancelling. */
1612
+ nextCharge: {
1613
+ at: number | null;
1614
+ amountCents: number;
1615
+ } | null;
1616
+ /** When a cancelled subscription ends (ms). */
1617
+ cancelAt: number | null;
1618
+ /** An invoice is unpaid. */
1619
+ pastDue: boolean;
1620
+ /** Stripe page that pays it (owner only). */
1621
+ openInvoiceUrl: string | null;
1622
+ paymentMethod: {
1623
+ brand: string;
1624
+ last4: string;
1625
+ expMonth: number;
1626
+ expYear: number;
1627
+ } | null;
1628
+ /** Pro only: `tier` is `trial` or `pro`. */
1629
+ limits: {
1630
+ tier: string;
1631
+ replicasPerApp: number;
1632
+ } | null;
1633
+ /** Pro Checkout is open for this account. */
1634
+ checkoutAvailable: boolean;
1635
+ }
1636
+ interface StartCheckoutOpts {
1637
+ /** Dashboard path to come back to after Checkout, e.g. `/projects/abc`. */
1638
+ returnUrl?: string;
1639
+ }
1640
+ interface CheckoutSession {
1641
+ /** Stripe Checkout page. The user finishes it in a browser. */
1642
+ url: string;
1643
+ sessionId: string;
1644
+ /** Null when the account already had a trial: Pro starts at once and charges $19. */
1645
+ trialDays: number | null;
1646
+ trialCreditCents: number | null;
1647
+ }
1648
+ interface StartProNowResult {
1649
+ /**
1650
+ * `active`: charged $19, the first paid month started. `requires_action`: the bank
1651
+ * wants a confirmation (or declined); open `invoiceUrl`, the trial continues.
1652
+ * `failed`: nothing changed, the trial continues.
1653
+ */
1654
+ status: 'active' | 'requires_action' | 'failed';
1655
+ invoiceUrl?: string | null;
1656
+ }
1657
+ interface PromoRedemption {
1658
+ /** `pending_payment_method`: held for the trial Checkout starts. `applied`: applied now. */
1659
+ status: string;
1660
+ code?: string;
1661
+ /** The trial credits (older servers: the credit amount). */
1662
+ creditCents: number;
1663
+ expiresAt: number | null;
1664
+ /** Kept for older clients. */
1665
+ balanceCents: number;
1666
+ /** Trial length the code gives. Missing from servers before Pro. */
1667
+ trialDays?: number;
1668
+ trialCreditCents?: number;
1669
+ /** `next_checkout`: held for the trial `startCheckout` opens. `current_trial`: extended the running trial. */
1670
+ appliesTo?: 'next_checkout' | 'current_trial';
1671
+ }
1672
+ /**
1673
+ * The account's plan, balance and usage.
1674
+ *
1675
+ * Pro costs $19/month, taxes included, with $19 of credits each month; usage above that
1676
+ * is pay as you go, invoiced as it builds up. A new account starts with a 7-day trial
1677
+ * with $5 of credits. Enterprise is pay as you go, invoiced monthly. Old prepaid
1678
+ * credits accounts (`plan: 'payg'`) keep {@link balance} and {@link transactions}
1679
+ * until 1 November 2026.
1562
1680
  *
1563
1681
  * Billing is **account-scoped, not workspace-scoped**: every workspace you create for
1564
1682
  * a user bills to the account that owns the key. That is what makes per-user
1565
- * workspaces a safe pattern — your users get isolation, you keep one bill — and also
1566
- * what makes {@link Balance.runwayHours} worth watching before you provision more.
1683
+ * workspaces a safe pattern — your users get isolation, you keep one bill.
1567
1684
  *
1568
- * **Requires an unscoped key.** A scoped key is refused with 403
1569
- * `ACCOUNT_SCOPE_REQUIRED`, because there is no workspace-scoped view of one shared
1570
- * balance, ledger and set of saved cards — and because a scoped key is meant to be
1571
- * handed to an end user, who should not be reading your card details or spending
1572
- * against them. For per-workspace spend, use {@link MetricsAPI.cost} instead.
1685
+ * **Requires an unscoped key**, except {@link subscription} with a `workspaceId` the
1686
+ * key can reach. A scoped key is refused with 403 `ACCOUNT_SCOPE_REQUIRED`, because a
1687
+ * scoped key is meant to be handed to an end user, who should not be reading your
1688
+ * card details or changing your plan. For per-workspace spend, use
1689
+ * {@link MetricsAPI.cost} instead.
1690
+ *
1691
+ * Checkout and invoices are web pages: methods return their URL for the user to open.
1692
+ * Nothing here retries a payment.
1573
1693
  */
1574
1694
  declare class BillingAPI {
1575
1695
  private readonly client;
1576
1696
  constructor(client: PlatformClient);
1577
- /** Current balance, status, burn rate, and runway. */
1697
+ /**
1698
+ * The plan: trial, this month's credits, overage, next charge, cancel date. With
1699
+ * `workspaceId`, the plan of that workspace's owner (`isOwner: false`, no card or
1700
+ * invoice fields).
1701
+ */
1702
+ subscription(opts?: {
1703
+ workspaceId?: string;
1704
+ }): Promise<Subscription>;
1705
+ /**
1706
+ * Opens Stripe Checkout for Pro: the trial when the account can have one, otherwise
1707
+ * Pro at once ($19 today). Returns the page for the user to finish; a second call
1708
+ * while it is open returns the same session.
1709
+ */
1710
+ startCheckout(opts?: StartCheckoutOpts): Promise<CheckoutSession>;
1711
+ /** Ends the trial now: charges $19 and starts the first paid month with $19 of credits. */
1712
+ startProNow(): Promise<StartProNowResult>;
1713
+ /** Cancels Pro at the end of the current month or trial. Cancelling in the trial costs nothing. */
1714
+ cancel(): Promise<{
1715
+ cancelAt: number | null;
1716
+ }>;
1717
+ /** Undoes {@link cancel}: Pro renews as usual. */
1718
+ resume(): Promise<{
1719
+ cancelAt: null;
1720
+ }>;
1721
+ /** Redeems a promo code: a longer trial with more trial credits. Works only before the first payment. */
1722
+ redeemPromo(code: string): Promise<PromoRedemption>;
1723
+ /** Prepaid credits balance, status, burn rate and runway (old `payg` accounts and enterprise). */
1578
1724
  balance(): Promise<Balance>;
1579
- /** A page of balance transactions, newest first. */
1725
+ /** A page of balance transactions, newest first (old `payg` accounts and enterprise). */
1580
1726
  transactions(opts?: ListTransactionsOpts): Promise<TransactionPage>;
1581
1727
  /** Cost summary for the current billing period, broken down by resource. */
1582
1728
  summary(): Promise<unknown>;
1583
1729
  /** Live (not-yet-invoiced) usage accumulating right now. */
1584
1730
  live(): Promise<unknown>;
1731
+ /** Saved cards. Cards are added in Checkout or on the Billing page. */
1585
1732
  paymentMethods(): Promise<{
1586
1733
  items: unknown[];
1587
1734
  }>;
1588
- setupPaymentMethod(returnUrl?: string): Promise<{
1589
- url: string;
1590
- }>;
1591
1735
  removePaymentMethod(id: string): Promise<void>;
1592
- purchase(creditCents: number, opts?: {
1593
- paymentMethod?: 'card' | 'crypto';
1594
- returnUrl?: string;
1595
- }): Promise<{
1596
- url: string;
1597
- sessionId?: string;
1598
- }>;
1599
- autoTopup(): Promise<unknown>;
1600
- setAutoTopup(opts: {
1601
- enabled: boolean;
1602
- thresholdCents: number;
1603
- amountCents: number;
1604
- paymentMethodIds: string[];
1605
- }): Promise<unknown>;
1606
- runAutoTopup(): Promise<unknown>;
1607
- redeemPromo(code: string): Promise<unknown>;
1608
- x402Quote(creditCents: number): Promise<unknown>;
1609
- paymentStatus(attemptId: string): Promise<unknown>;
1610
- /** Pay with the CLI's durable x402 journal. Requires CLI >= 4.0.8. Amounts are cents. */
1611
- payX402(creditCents: number, opts: {
1612
- maxTotalCents: number;
1613
- requestId?: string;
1614
- executable?: string;
1615
- }): Promise<unknown>;
1616
1736
  }
1617
1737
 
1618
1738
  interface VolumeInfo {
@@ -1896,6 +2016,34 @@ declare class ConflictError extends LizardError {
1896
2016
  declare class TimeoutError extends LizardError {
1897
2017
  constructor(message?: string);
1898
2018
  }
2019
+ /**
2020
+ * The account has to pay before it can do this (HTTP 402). Every create call answers
2021
+ * 402 when the account has no plan yet, its trial credits are used up, or an invoice
2022
+ * is unpaid. Show {@link message} to the user as is and send them to {@link url}.
2023
+ *
2024
+ * Still a {@link LizardError}, so existing `catch` blocks keep working.
2025
+ */
2026
+ declare class PaymentRequiredError extends LizardError {
2027
+ /**
2028
+ * Why payment is needed: `trial_available` (start the Pro trial),
2029
+ * `subscription_required` (the trial was used: start Pro), `trial_credits_used`
2030
+ * (start Pro now), `past_due` or `paused` (pay the open invoice), or, for accounts
2031
+ * on the old prepaid credits, `grace`, `frozen`, `card_required` or
2032
+ * `credits_required`. {@link LizardError.status} stays the HTTP status, 402.
2033
+ */
2034
+ paymentStatus?: string;
2035
+ /** Billing page that starts the Pro trial, or Pro when the trial was used. */
2036
+ subscribeUrl?: string;
2037
+ /** The Billing page. */
2038
+ billingUrl?: string;
2039
+ /** The Credits page, for accounts on the old prepaid credits. */
2040
+ topupUrl?: string;
2041
+ /** Stripe page that pays an unpaid invoice (`code` `UNPAID_INVOICE`). */
2042
+ invoiceUrl?: string;
2043
+ constructor(message?: string);
2044
+ /** The one page to open next for this {@link paymentStatus}. */
2045
+ get url(): string | undefined;
2046
+ }
1899
2047
 
1900
2048
  /** A single output item produced during code execution. */
1901
2049
  type OutputItem = {
@@ -2070,4 +2218,4 @@ declare class LizardCLI {
2070
2218
  }): Promise<CLIResult>;
2071
2219
  }
2072
2220
 
2073
- export { type Account, type Addon, type AddonType, AddonsAPI, type ApiKey, type ApiKeyScope, ApiKeysAPI, AuthenticationError, type Balance, BillingAPI, type CLIOptions, type CLIResult, type ClickOpts, type CodeContext, CodeSandbox, ConfigApplyError, type ConfigResult, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateApiKeyOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type CreateWorkspaceOpts, type CreatedApiKey, type DeployEvent, DeployHandle, Desktop, type DesktopInfo, type DesktopStartOpts, type DomainInfo, type DomainStatus, DomainsAPI, Execution, ExecutionError, type ExposedPort, type FileInfo, type ForkResult, type FsEvent, type FsOpts, GitHubAPI, type ListTransactionsOpts, Lizard, LizardCLI, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, MetricsAPI, type MouseButton, NotFoundError, PlatformClient, type ProcessInfo, type ProcessOpts, type ProcessResult, type ProcessSignal, type Project, type ProjectConfig, type ProjectCost, ProjectsAPI, type RawMetrics, type Region, RegionsAPI, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type SandboxSnapshot, type ScaleOpts, type ScrollDirection, type Secret, SecretsAPI, type Service, type ServiceMetrics, type ServiceUpdate, ServicesAPI, type SetSecretOpts, type SnapshotOpts, type SnapshotWaitOpts, StorageAPI, type StreamEvent, TimeoutError, type Transaction, type TransactionPage, Volume, type VolumeInfo, type Workspace, WorkspacesAPI, resolveProjectId };
2221
+ export { type Account, type Addon, type AddonType, AddonsAPI, type ApiKey, type ApiKeyScope, ApiKeysAPI, AuthenticationError, type Balance, BillingAPI, type CLIOptions, type CLIResult, type CheckoutSession, type ClickOpts, type CodeContext, CodeSandbox, ConfigApplyError, type ConfigResult, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateApiKeyOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type CreateWorkspaceOpts, type CreatedApiKey, type DeployEvent, DeployHandle, Desktop, type DesktopInfo, type DesktopStartOpts, type DomainInfo, type DomainStatus, DomainsAPI, Execution, ExecutionError, type ExposedPort, type FileInfo, type ForkResult, type FsEvent, type FsOpts, GitHubAPI, type ListTransactionsOpts, Lizard, LizardCLI, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, MetricsAPI, type MouseButton, NotFoundError, PaymentRequiredError, type Plan, PlatformClient, type ProcessInfo, type ProcessOpts, type ProcessResult, type ProcessSignal, type Project, type ProjectConfig, type ProjectCost, ProjectsAPI, type PromoRedemption, type RawMetrics, type Region, RegionsAPI, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type SandboxSnapshot, type ScaleOpts, type ScrollDirection, type Secret, SecretsAPI, type Service, type ServiceMetrics, type ServiceUpdate, ServicesAPI, type SetSecretOpts, type SnapshotOpts, type SnapshotWaitOpts, type StartCheckoutOpts, type StartProNowResult, StorageAPI, type StreamEvent, type Subscription, type SubscriptionStatus, TimeoutError, type Transaction, type TransactionPage, Volume, type VolumeInfo, type Workspace, WorkspacesAPI, resolveProjectId };
package/dist/index.d.ts CHANGED
@@ -1520,6 +1520,7 @@ declare class RegionsAPI {
1520
1520
  list(): Promise<Region[]>;
1521
1521
  }
1522
1522
 
1523
+ /** Prepaid credits balance: old credits accounts (`plan: 'payg'`) until 1 November 2026, and enterprise usage. */
1523
1524
  interface Balance {
1524
1525
  plan: string;
1525
1526
  status: 'active' | 'grace' | 'frozen' | string;
@@ -1558,61 +1559,180 @@ interface ListTransactionsOpts {
1558
1559
  includeUsage?: boolean;
1559
1560
  }
1560
1561
  /**
1561
- * Account balance and usage.
1562
+ * `none`: no plan yet (creating anything answers 402). `pro`: the Pro subscription, see
1563
+ * {@link Subscription.status}. `payg`: old prepaid credits, until 1 November 2026.
1564
+ * `enterprise`: pay as you go, invoiced monthly.
1565
+ */
1566
+ type Plan = 'none' | 'pro' | 'payg' | 'enterprise';
1567
+ /** Pro subscription status; `none` when there is no subscription. */
1568
+ type SubscriptionStatus = 'trialing' | 'active' | 'past_due' | 'canceled' | 'none';
1569
+ interface Subscription {
1570
+ plan: Plan | (string & {});
1571
+ status: SubscriptionStatus | (string & {});
1572
+ /** False when read for a workspace the caller does not own; card and invoice fields are then null. */
1573
+ isOwner: boolean;
1574
+ /** 1900: $19/month. */
1575
+ priceCents: number;
1576
+ /** The price includes taxes. */
1577
+ taxIncluded: boolean;
1578
+ /** Credits included each paid month, in cents (1900). */
1579
+ includedCents: number;
1580
+ trial: {
1581
+ /** The account can start a trial now. */
1582
+ eligible: boolean;
1583
+ /** Trial length it would get, or got. */
1584
+ days: number | null;
1585
+ /** Trial credits, in cents. */
1586
+ creditCents: number | null;
1587
+ /** Promo code held for the trial, if any. */
1588
+ promoCode: string | null;
1589
+ /** Trial end (ms); null when not trialing. */
1590
+ endsAt: number | null;
1591
+ /** Trial credits used (trialing only). */
1592
+ usedCents: number | null;
1593
+ remainingCents: number | null;
1594
+ };
1595
+ /** The current Pro period; null when there is none. */
1596
+ period: {
1597
+ kind: 'trial' | 'paid';
1598
+ start: number;
1599
+ end: number;
1600
+ /** 500 in the trial, 1900 in a paid month. */
1601
+ includedCents: number;
1602
+ usedCents: number;
1603
+ /** Paid month: usage above the included credits. */
1604
+ overageCents: number;
1605
+ /** Of the overage, already invoiced. */
1606
+ billedOverageCents: number;
1607
+ unbilledOverageCents: number;
1608
+ /** Paid month: unbilled overage at which the next invoice goes out. */
1609
+ nextOverageChargeAtCents: number | null;
1610
+ } | null;
1611
+ /** Null when there is none, or the subscription is cancelling. */
1612
+ nextCharge: {
1613
+ at: number | null;
1614
+ amountCents: number;
1615
+ } | null;
1616
+ /** When a cancelled subscription ends (ms). */
1617
+ cancelAt: number | null;
1618
+ /** An invoice is unpaid. */
1619
+ pastDue: boolean;
1620
+ /** Stripe page that pays it (owner only). */
1621
+ openInvoiceUrl: string | null;
1622
+ paymentMethod: {
1623
+ brand: string;
1624
+ last4: string;
1625
+ expMonth: number;
1626
+ expYear: number;
1627
+ } | null;
1628
+ /** Pro only: `tier` is `trial` or `pro`. */
1629
+ limits: {
1630
+ tier: string;
1631
+ replicasPerApp: number;
1632
+ } | null;
1633
+ /** Pro Checkout is open for this account. */
1634
+ checkoutAvailable: boolean;
1635
+ }
1636
+ interface StartCheckoutOpts {
1637
+ /** Dashboard path to come back to after Checkout, e.g. `/projects/abc`. */
1638
+ returnUrl?: string;
1639
+ }
1640
+ interface CheckoutSession {
1641
+ /** Stripe Checkout page. The user finishes it in a browser. */
1642
+ url: string;
1643
+ sessionId: string;
1644
+ /** Null when the account already had a trial: Pro starts at once and charges $19. */
1645
+ trialDays: number | null;
1646
+ trialCreditCents: number | null;
1647
+ }
1648
+ interface StartProNowResult {
1649
+ /**
1650
+ * `active`: charged $19, the first paid month started. `requires_action`: the bank
1651
+ * wants a confirmation (or declined); open `invoiceUrl`, the trial continues.
1652
+ * `failed`: nothing changed, the trial continues.
1653
+ */
1654
+ status: 'active' | 'requires_action' | 'failed';
1655
+ invoiceUrl?: string | null;
1656
+ }
1657
+ interface PromoRedemption {
1658
+ /** `pending_payment_method`: held for the trial Checkout starts. `applied`: applied now. */
1659
+ status: string;
1660
+ code?: string;
1661
+ /** The trial credits (older servers: the credit amount). */
1662
+ creditCents: number;
1663
+ expiresAt: number | null;
1664
+ /** Kept for older clients. */
1665
+ balanceCents: number;
1666
+ /** Trial length the code gives. Missing from servers before Pro. */
1667
+ trialDays?: number;
1668
+ trialCreditCents?: number;
1669
+ /** `next_checkout`: held for the trial `startCheckout` opens. `current_trial`: extended the running trial. */
1670
+ appliesTo?: 'next_checkout' | 'current_trial';
1671
+ }
1672
+ /**
1673
+ * The account's plan, balance and usage.
1674
+ *
1675
+ * Pro costs $19/month, taxes included, with $19 of credits each month; usage above that
1676
+ * is pay as you go, invoiced as it builds up. A new account starts with a 7-day trial
1677
+ * with $5 of credits. Enterprise is pay as you go, invoiced monthly. Old prepaid
1678
+ * credits accounts (`plan: 'payg'`) keep {@link balance} and {@link transactions}
1679
+ * until 1 November 2026.
1562
1680
  *
1563
1681
  * Billing is **account-scoped, not workspace-scoped**: every workspace you create for
1564
1682
  * a user bills to the account that owns the key. That is what makes per-user
1565
- * workspaces a safe pattern — your users get isolation, you keep one bill — and also
1566
- * what makes {@link Balance.runwayHours} worth watching before you provision more.
1683
+ * workspaces a safe pattern — your users get isolation, you keep one bill.
1567
1684
  *
1568
- * **Requires an unscoped key.** A scoped key is refused with 403
1569
- * `ACCOUNT_SCOPE_REQUIRED`, because there is no workspace-scoped view of one shared
1570
- * balance, ledger and set of saved cards — and because a scoped key is meant to be
1571
- * handed to an end user, who should not be reading your card details or spending
1572
- * against them. For per-workspace spend, use {@link MetricsAPI.cost} instead.
1685
+ * **Requires an unscoped key**, except {@link subscription} with a `workspaceId` the
1686
+ * key can reach. A scoped key is refused with 403 `ACCOUNT_SCOPE_REQUIRED`, because a
1687
+ * scoped key is meant to be handed to an end user, who should not be reading your
1688
+ * card details or changing your plan. For per-workspace spend, use
1689
+ * {@link MetricsAPI.cost} instead.
1690
+ *
1691
+ * Checkout and invoices are web pages: methods return their URL for the user to open.
1692
+ * Nothing here retries a payment.
1573
1693
  */
1574
1694
  declare class BillingAPI {
1575
1695
  private readonly client;
1576
1696
  constructor(client: PlatformClient);
1577
- /** Current balance, status, burn rate, and runway. */
1697
+ /**
1698
+ * The plan: trial, this month's credits, overage, next charge, cancel date. With
1699
+ * `workspaceId`, the plan of that workspace's owner (`isOwner: false`, no card or
1700
+ * invoice fields).
1701
+ */
1702
+ subscription(opts?: {
1703
+ workspaceId?: string;
1704
+ }): Promise<Subscription>;
1705
+ /**
1706
+ * Opens Stripe Checkout for Pro: the trial when the account can have one, otherwise
1707
+ * Pro at once ($19 today). Returns the page for the user to finish; a second call
1708
+ * while it is open returns the same session.
1709
+ */
1710
+ startCheckout(opts?: StartCheckoutOpts): Promise<CheckoutSession>;
1711
+ /** Ends the trial now: charges $19 and starts the first paid month with $19 of credits. */
1712
+ startProNow(): Promise<StartProNowResult>;
1713
+ /** Cancels Pro at the end of the current month or trial. Cancelling in the trial costs nothing. */
1714
+ cancel(): Promise<{
1715
+ cancelAt: number | null;
1716
+ }>;
1717
+ /** Undoes {@link cancel}: Pro renews as usual. */
1718
+ resume(): Promise<{
1719
+ cancelAt: null;
1720
+ }>;
1721
+ /** Redeems a promo code: a longer trial with more trial credits. Works only before the first payment. */
1722
+ redeemPromo(code: string): Promise<PromoRedemption>;
1723
+ /** Prepaid credits balance, status, burn rate and runway (old `payg` accounts and enterprise). */
1578
1724
  balance(): Promise<Balance>;
1579
- /** A page of balance transactions, newest first. */
1725
+ /** A page of balance transactions, newest first (old `payg` accounts and enterprise). */
1580
1726
  transactions(opts?: ListTransactionsOpts): Promise<TransactionPage>;
1581
1727
  /** Cost summary for the current billing period, broken down by resource. */
1582
1728
  summary(): Promise<unknown>;
1583
1729
  /** Live (not-yet-invoiced) usage accumulating right now. */
1584
1730
  live(): Promise<unknown>;
1731
+ /** Saved cards. Cards are added in Checkout or on the Billing page. */
1585
1732
  paymentMethods(): Promise<{
1586
1733
  items: unknown[];
1587
1734
  }>;
1588
- setupPaymentMethod(returnUrl?: string): Promise<{
1589
- url: string;
1590
- }>;
1591
1735
  removePaymentMethod(id: string): Promise<void>;
1592
- purchase(creditCents: number, opts?: {
1593
- paymentMethod?: 'card' | 'crypto';
1594
- returnUrl?: string;
1595
- }): Promise<{
1596
- url: string;
1597
- sessionId?: string;
1598
- }>;
1599
- autoTopup(): Promise<unknown>;
1600
- setAutoTopup(opts: {
1601
- enabled: boolean;
1602
- thresholdCents: number;
1603
- amountCents: number;
1604
- paymentMethodIds: string[];
1605
- }): Promise<unknown>;
1606
- runAutoTopup(): Promise<unknown>;
1607
- redeemPromo(code: string): Promise<unknown>;
1608
- x402Quote(creditCents: number): Promise<unknown>;
1609
- paymentStatus(attemptId: string): Promise<unknown>;
1610
- /** Pay with the CLI's durable x402 journal. Requires CLI >= 4.0.8. Amounts are cents. */
1611
- payX402(creditCents: number, opts: {
1612
- maxTotalCents: number;
1613
- requestId?: string;
1614
- executable?: string;
1615
- }): Promise<unknown>;
1616
1736
  }
1617
1737
 
1618
1738
  interface VolumeInfo {
@@ -1896,6 +2016,34 @@ declare class ConflictError extends LizardError {
1896
2016
  declare class TimeoutError extends LizardError {
1897
2017
  constructor(message?: string);
1898
2018
  }
2019
+ /**
2020
+ * The account has to pay before it can do this (HTTP 402). Every create call answers
2021
+ * 402 when the account has no plan yet, its trial credits are used up, or an invoice
2022
+ * is unpaid. Show {@link message} to the user as is and send them to {@link url}.
2023
+ *
2024
+ * Still a {@link LizardError}, so existing `catch` blocks keep working.
2025
+ */
2026
+ declare class PaymentRequiredError extends LizardError {
2027
+ /**
2028
+ * Why payment is needed: `trial_available` (start the Pro trial),
2029
+ * `subscription_required` (the trial was used: start Pro), `trial_credits_used`
2030
+ * (start Pro now), `past_due` or `paused` (pay the open invoice), or, for accounts
2031
+ * on the old prepaid credits, `grace`, `frozen`, `card_required` or
2032
+ * `credits_required`. {@link LizardError.status} stays the HTTP status, 402.
2033
+ */
2034
+ paymentStatus?: string;
2035
+ /** Billing page that starts the Pro trial, or Pro when the trial was used. */
2036
+ subscribeUrl?: string;
2037
+ /** The Billing page. */
2038
+ billingUrl?: string;
2039
+ /** The Credits page, for accounts on the old prepaid credits. */
2040
+ topupUrl?: string;
2041
+ /** Stripe page that pays an unpaid invoice (`code` `UNPAID_INVOICE`). */
2042
+ invoiceUrl?: string;
2043
+ constructor(message?: string);
2044
+ /** The one page to open next for this {@link paymentStatus}. */
2045
+ get url(): string | undefined;
2046
+ }
1899
2047
 
1900
2048
  /** A single output item produced during code execution. */
1901
2049
  type OutputItem = {
@@ -2070,4 +2218,4 @@ declare class LizardCLI {
2070
2218
  }): Promise<CLIResult>;
2071
2219
  }
2072
2220
 
2073
- export { type Account, type Addon, type AddonType, AddonsAPI, type ApiKey, type ApiKeyScope, ApiKeysAPI, AuthenticationError, type Balance, BillingAPI, type CLIOptions, type CLIResult, type ClickOpts, type CodeContext, CodeSandbox, ConfigApplyError, type ConfigResult, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateApiKeyOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type CreateWorkspaceOpts, type CreatedApiKey, type DeployEvent, DeployHandle, Desktop, type DesktopInfo, type DesktopStartOpts, type DomainInfo, type DomainStatus, DomainsAPI, Execution, ExecutionError, type ExposedPort, type FileInfo, type ForkResult, type FsEvent, type FsOpts, GitHubAPI, type ListTransactionsOpts, Lizard, LizardCLI, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, MetricsAPI, type MouseButton, NotFoundError, PlatformClient, type ProcessInfo, type ProcessOpts, type ProcessResult, type ProcessSignal, type Project, type ProjectConfig, type ProjectCost, ProjectsAPI, type RawMetrics, type Region, RegionsAPI, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type SandboxSnapshot, type ScaleOpts, type ScrollDirection, type Secret, SecretsAPI, type Service, type ServiceMetrics, type ServiceUpdate, ServicesAPI, type SetSecretOpts, type SnapshotOpts, type SnapshotWaitOpts, StorageAPI, type StreamEvent, TimeoutError, type Transaction, type TransactionPage, Volume, type VolumeInfo, type Workspace, WorkspacesAPI, resolveProjectId };
2221
+ export { type Account, type Addon, type AddonType, AddonsAPI, type ApiKey, type ApiKeyScope, ApiKeysAPI, AuthenticationError, type Balance, BillingAPI, type CLIOptions, type CLIResult, type CheckoutSession, type ClickOpts, type CodeContext, CodeSandbox, ConfigApplyError, type ConfigResult, ConflictError, type ConnectionOpts, type CostMetrics, type CreateAddonOpts, type CreateApiKeyOpts, type CreateContextOpts, type CreateProjectOpts, type CreateServiceOpts, type CreateVolumeOpts, type CreateWorkspaceOpts, type CreatedApiKey, type DeployEvent, DeployHandle, Desktop, type DesktopInfo, type DesktopStartOpts, type DomainInfo, type DomainStatus, DomainsAPI, Execution, ExecutionError, type ExposedPort, type FileInfo, type ForkResult, type FsEvent, type FsOpts, GitHubAPI, type ListTransactionsOpts, Lizard, LizardCLI, LizardError, type LizardOpts, type LogLine, type MetricPoint, type MetricRange, MetricsAPI, type MouseButton, NotFoundError, PaymentRequiredError, type Plan, PlatformClient, type ProcessInfo, type ProcessOpts, type ProcessResult, type ProcessSignal, type Project, type ProjectConfig, type ProjectCost, ProjectsAPI, type PromoRedemption, type RawMetrics, type Region, RegionsAPI, type RunCodeLanguage, type RunCodeOpts, Sandbox, type SandboxInfo, type SandboxOpts, type SandboxSnapshot, type ScaleOpts, type ScrollDirection, type Secret, SecretsAPI, type Service, type ServiceMetrics, type ServiceUpdate, ServicesAPI, type SetSecretOpts, type SnapshotOpts, type SnapshotWaitOpts, type StartCheckoutOpts, type StartProNowResult, StorageAPI, type StreamEvent, type Subscription, type SubscriptionStatus, TimeoutError, type Transaction, type TransactionPage, Volume, type VolumeInfo, type Workspace, WorkspacesAPI, resolveProjectId };
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  LizardError: () => LizardError,
37
37
  MetricsAPI: () => MetricsAPI,
38
38
  NotFoundError: () => NotFoundError,
39
+ PaymentRequiredError: () => PaymentRequiredError,
39
40
  PlatformClient: () => PlatformClient,
40
41
  ProjectsAPI: () => ProjectsAPI,
41
42
  RegionsAPI: () => RegionsAPI,
@@ -111,18 +112,44 @@ var TimeoutError = class extends LizardError {
111
112
  this.name = "TimeoutError";
112
113
  }
113
114
  };
115
+ var PaymentRequiredError = class extends LizardError {
116
+ constructor(message = "Payment required") {
117
+ super(message);
118
+ this.name = "PaymentRequiredError";
119
+ }
120
+ /** The one page to open next for this {@link paymentStatus}. */
121
+ get url() {
122
+ const s = this.paymentStatus;
123
+ if (this.invoiceUrl) return this.invoiceUrl;
124
+ if ((s === "trial_available" || s === "subscription_required") && this.subscribeUrl) return this.subscribeUrl;
125
+ if (s && PREPAID_STATUSES.has(s) && this.topupUrl) return this.topupUrl;
126
+ return this.billingUrl ?? this.subscribeUrl ?? this.topupUrl;
127
+ }
128
+ };
129
+ var PREPAID_STATUSES = /* @__PURE__ */ new Set(["grace", "frozen", "card_required", "credits_required"]);
130
+ var ERROR_CODE = /^[A-Z][A-Z0-9_]*$/;
114
131
  async function handleApiError(res) {
115
- let message;
116
- let code;
132
+ let body = {};
117
133
  try {
118
- const body = await res.json();
119
- message = body.error ?? res.statusText;
120
- if (typeof body.code === "string") code = body.code;
134
+ const parsed = await res.json();
135
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) body = parsed;
121
136
  } catch {
122
- message = res.statusText;
123
137
  }
138
+ const text = (key) => typeof body[key] === "string" && body[key] ? body[key] : void 0;
139
+ const error = text("error");
140
+ const errorIsCode = !!error && ERROR_CODE.test(error);
141
+ const message = (errorIsCode ? text("message") ?? error : error ?? text("message")) ?? res.statusText;
142
+ const code = text("code") ?? (errorIsCode ? error : void 0);
124
143
  let err;
125
- if (res.status === 401 || res.status === 403) err = new AuthenticationError(message);
144
+ if (res.status === 402) {
145
+ const paid = new PaymentRequiredError(message);
146
+ paid.paymentStatus = text("status");
147
+ paid.subscribeUrl = text("subscribeUrl");
148
+ paid.billingUrl = text("billingUrl");
149
+ paid.topupUrl = text("topupUrl");
150
+ paid.invoiceUrl = text("invoiceUrl");
151
+ err = paid;
152
+ } else if (res.status === 401 || res.status === 403) err = new AuthenticationError(message);
126
153
  else if (res.status === 404) err = new NotFoundError(message);
127
154
  else if (res.status === 409) err = new ConflictError(message);
128
155
  else if (res.status === 408 || res.status === 504) err = new TimeoutError(message);
@@ -1672,88 +1699,54 @@ var RegionsAPI = class {
1672
1699
  }
1673
1700
  };
1674
1701
 
1675
- // src/cli.ts
1676
- var import_node_child_process = require("child_process");
1677
- var LizardCLI = class {
1678
- constructor(opts = {}) {
1679
- this.opts = opts;
1680
- }
1681
- run(args, opts = {}) {
1682
- return new Promise((resolve, reject) => {
1683
- const env = { ...process.env };
1684
- if (this.opts.apiKey) {
1685
- env.LIZARD_API_KEY = this.opts.apiKey;
1686
- env.LIZARD_TOKEN = this.opts.apiKey;
1687
- }
1688
- if (this.opts.apiUrl) env.LIZARD_API_URL = this.opts.apiUrl;
1689
- const child = (0, import_node_child_process.spawn)(this.opts.executable ?? "lizard", ["--json", ...args], {
1690
- cwd: this.opts.cwd,
1691
- env,
1692
- shell: false,
1693
- stdio: "pipe",
1694
- signal: opts.signal,
1695
- timeout: this.opts.timeoutMs ?? 6e5
1696
- });
1697
- child.stdout.setEncoding("utf8");
1698
- child.stderr.setEncoding("utf8");
1699
- let stdout = "", stderr = "", bytes = 0;
1700
- const collect = (stream, data) => {
1701
- bytes += Buffer.byteLength(data);
1702
- if (bytes > 16 * 1024 * 1024) {
1703
- child.kill();
1704
- reject(new Error("CLI output exceeds 16 MiB"));
1705
- return;
1706
- }
1707
- if (stream === "stdout") stdout += data;
1708
- else stderr += data;
1709
- };
1710
- child.stdout.on("data", (data) => collect("stdout", data));
1711
- child.stderr.on("data", (data) => collect("stderr", data));
1712
- child.on("error", reject);
1713
- child.on("close", (code, signal) => {
1714
- if (signal) {
1715
- reject(new Error(`CLI ended with signal ${signal}`));
1716
- return;
1717
- }
1718
- let events = [];
1719
- try {
1720
- events = [JSON.parse(stdout)];
1721
- } catch {
1722
- events = stdout.split("\n").filter(Boolean).flatMap((line) => {
1723
- try {
1724
- return [JSON.parse(line)];
1725
- } catch {
1726
- return [];
1727
- }
1728
- });
1729
- }
1730
- resolve({ code: code ?? 1, stdout, stderr, events });
1731
- });
1732
- child.stdin.on("error", (error) => {
1733
- if (error.code !== "EPIPE") reject(error);
1734
- });
1735
- child.stdin.end(opts.stdin);
1736
- });
1737
- }
1738
- };
1739
-
1740
1702
  // src/platform/billing.ts
1741
1703
  var BillingAPI = class {
1742
1704
  constructor(client) {
1743
1705
  this.client = client;
1744
1706
  }
1745
- /** Current balance, status, burn rate, and runway. */
1707
+ /**
1708
+ * The plan: trial, this month's credits, overage, next charge, cancel date. With
1709
+ * `workspaceId`, the plan of that workspace's owner (`isOwner: false`, no card or
1710
+ * invoice fields).
1711
+ */
1712
+ subscription(opts) {
1713
+ return this.client.get(query("/api/billing/subscription", { workspaceId: opts?.workspaceId }));
1714
+ }
1715
+ /**
1716
+ * Opens Stripe Checkout for Pro: the trial when the account can have one, otherwise
1717
+ * Pro at once ($19 today). Returns the page for the user to finish; a second call
1718
+ * while it is open returns the same session.
1719
+ */
1720
+ startCheckout(opts = {}) {
1721
+ return this.client.post("/api/billing/subscription/checkout", { returnUrl: opts.returnUrl });
1722
+ }
1723
+ /** Ends the trial now: charges $19 and starts the first paid month with $19 of credits. */
1724
+ startProNow() {
1725
+ return this.client.post("/api/billing/subscription/start-now", {});
1726
+ }
1727
+ /** Cancels Pro at the end of the current month or trial. Cancelling in the trial costs nothing. */
1728
+ cancel() {
1729
+ return this.client.post("/api/billing/subscription/cancel", {});
1730
+ }
1731
+ /** Undoes {@link cancel}: Pro renews as usual. */
1732
+ resume() {
1733
+ return this.client.post("/api/billing/subscription/resume", {});
1734
+ }
1735
+ /** Redeems a promo code: a longer trial with more trial credits. Works only before the first payment. */
1736
+ redeemPromo(code) {
1737
+ return this.client.post("/api/billing/promo/redeem", { code });
1738
+ }
1739
+ /** Prepaid credits balance, status, burn rate and runway (old `payg` accounts and enterprise). */
1746
1740
  balance() {
1747
1741
  return this.client.get("/api/billing/balance");
1748
1742
  }
1749
- /** A page of balance transactions, newest first. */
1743
+ /** A page of balance transactions, newest first (old `payg` accounts and enterprise). */
1750
1744
  transactions(opts) {
1751
- const qs = new URLSearchParams();
1752
- if (opts?.limit !== void 0) qs.set("limit", String(opts.limit));
1753
- if (opts?.cursor) qs.set("cursor", opts.cursor);
1754
- if (opts?.includeUsage) qs.set("includeUsage", "1");
1755
- const q = qs.toString();
1756
- return this.client.get(`/api/billing/transactions${q ? `?${q}` : ""}`);
1745
+ return this.client.get(query("/api/billing/transactions", {
1746
+ limit: opts?.limit,
1747
+ cursor: opts?.cursor || void 0,
1748
+ includeUsage: opts?.includeUsage ? "1" : void 0
1749
+ }));
1757
1750
  }
1758
1751
  /** Cost summary for the current billing period, broken down by resource. */
1759
1752
  summary() {
@@ -1763,46 +1756,13 @@ var BillingAPI = class {
1763
1756
  live() {
1764
1757
  return this.client.get("/api/billing/live");
1765
1758
  }
1759
+ /** Saved cards. Cards are added in Checkout or on the Billing page. */
1766
1760
  paymentMethods() {
1767
1761
  return this.client.get("/api/billing/payment-methods");
1768
1762
  }
1769
- setupPaymentMethod(returnUrl) {
1770
- return this.client.post("/api/billing/payment-methods/setup", { returnUrl });
1771
- }
1772
1763
  removePaymentMethod(id) {
1773
1764
  return this.client.delete(`/api/billing/payment-methods/${encodeURIComponent(id)}`);
1774
1765
  }
1775
- purchase(creditCents, opts = {}) {
1776
- if (!Number.isSafeInteger(creditCents) || creditCents <= 0) throw new Error("creditCents must be a positive integer");
1777
- return this.client.post("/api/billing/purchase", { creditCents, paymentMethod: opts.paymentMethod ?? "card", returnUrl: opts.returnUrl });
1778
- }
1779
- autoTopup() {
1780
- return this.client.get("/api/billing/auto-topup");
1781
- }
1782
- setAutoTopup(opts) {
1783
- return this.client.put("/api/billing/auto-topup", opts);
1784
- }
1785
- runAutoTopup() {
1786
- return this.client.post("/api/billing/auto-topup/run", {});
1787
- }
1788
- redeemPromo(code) {
1789
- return this.client.post("/api/billing/promo/redeem", { code });
1790
- }
1791
- x402Quote(creditCents) {
1792
- return this.client.post("/api/billing/purchase/x402/quote", { creditCents });
1793
- }
1794
- paymentStatus(attemptId) {
1795
- return this.client.get(`/api/billing/purchase/x402/${encodeURIComponent(attemptId)}`);
1796
- }
1797
- /** Pay with the CLI's durable x402 journal. Requires CLI >= 4.0.8. Amounts are cents. */
1798
- async payX402(creditCents, opts) {
1799
- if (![creditCents, opts.maxTotalCents].every((n) => Number.isSafeInteger(n) && n > 0)) throw new Error("Payment amounts must be positive integer cents");
1800
- const args = ["credits", "topup", (creditCents / 100).toFixed(2), "--method", "x402", "--max-total", (opts.maxTotalCents / 100).toFixed(2), "--yes"];
1801
- if (opts.requestId) args.push("--request-id", opts.requestId);
1802
- const result = await new LizardCLI({ apiKey: this.client.config.apiKey, apiUrl: this.client.config.apiUrl, executable: opts.executable }).run(args);
1803
- if (result.code !== 0) throw new Error(`x402 CLI exited with code ${result.code}; inspect the payment journal or query paymentStatus before retrying`);
1804
- return result.events[0];
1805
- }
1806
1766
  };
1807
1767
 
1808
1768
  // src/volume/index.ts
@@ -2507,6 +2467,71 @@ function toBase64(text) {
2507
2467
  for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode(...bytes.subarray(i, i + 32768));
2508
2468
  return btoa(s);
2509
2469
  }
2470
+
2471
+ // src/cli.ts
2472
+ var import_node_child_process = require("child_process");
2473
+ var LizardCLI = class {
2474
+ constructor(opts = {}) {
2475
+ this.opts = opts;
2476
+ }
2477
+ run(args, opts = {}) {
2478
+ return new Promise((resolve, reject) => {
2479
+ const env = { ...process.env };
2480
+ if (this.opts.apiKey) {
2481
+ env.LIZARD_API_KEY = this.opts.apiKey;
2482
+ env.LIZARD_TOKEN = this.opts.apiKey;
2483
+ }
2484
+ if (this.opts.apiUrl) env.LIZARD_API_URL = this.opts.apiUrl;
2485
+ const child = (0, import_node_child_process.spawn)(this.opts.executable ?? "lizard", ["--json", ...args], {
2486
+ cwd: this.opts.cwd,
2487
+ env,
2488
+ shell: false,
2489
+ stdio: "pipe",
2490
+ signal: opts.signal,
2491
+ timeout: this.opts.timeoutMs ?? 6e5
2492
+ });
2493
+ child.stdout.setEncoding("utf8");
2494
+ child.stderr.setEncoding("utf8");
2495
+ let stdout = "", stderr = "", bytes = 0;
2496
+ const collect = (stream, data) => {
2497
+ bytes += Buffer.byteLength(data);
2498
+ if (bytes > 16 * 1024 * 1024) {
2499
+ child.kill();
2500
+ reject(new Error("CLI output exceeds 16 MiB"));
2501
+ return;
2502
+ }
2503
+ if (stream === "stdout") stdout += data;
2504
+ else stderr += data;
2505
+ };
2506
+ child.stdout.on("data", (data) => collect("stdout", data));
2507
+ child.stderr.on("data", (data) => collect("stderr", data));
2508
+ child.on("error", reject);
2509
+ child.on("close", (code, signal) => {
2510
+ if (signal) {
2511
+ reject(new Error(`CLI ended with signal ${signal}`));
2512
+ return;
2513
+ }
2514
+ let events = [];
2515
+ try {
2516
+ events = [JSON.parse(stdout)];
2517
+ } catch {
2518
+ events = stdout.split("\n").filter(Boolean).flatMap((line) => {
2519
+ try {
2520
+ return [JSON.parse(line)];
2521
+ } catch {
2522
+ return [];
2523
+ }
2524
+ });
2525
+ }
2526
+ resolve({ code: code ?? 1, stdout, stderr, events });
2527
+ });
2528
+ child.stdin.on("error", (error) => {
2529
+ if (error.code !== "EPIPE") reject(error);
2530
+ });
2531
+ child.stdin.end(opts.stdin);
2532
+ });
2533
+ }
2534
+ };
2510
2535
  // Annotate the CommonJS export names for ESM import in node:
2511
2536
  0 && (module.exports = {
2512
2537
  AddonsAPI,
@@ -2525,6 +2550,7 @@ function toBase64(text) {
2525
2550
  LizardError,
2526
2551
  MetricsAPI,
2527
2552
  NotFoundError,
2553
+ PaymentRequiredError,
2528
2554
  PlatformClient,
2529
2555
  ProjectsAPI,
2530
2556
  RegionsAPI,
package/dist/index.mjs CHANGED
@@ -59,18 +59,44 @@ var TimeoutError = class extends LizardError {
59
59
  this.name = "TimeoutError";
60
60
  }
61
61
  };
62
+ var PaymentRequiredError = class extends LizardError {
63
+ constructor(message = "Payment required") {
64
+ super(message);
65
+ this.name = "PaymentRequiredError";
66
+ }
67
+ /** The one page to open next for this {@link paymentStatus}. */
68
+ get url() {
69
+ const s = this.paymentStatus;
70
+ if (this.invoiceUrl) return this.invoiceUrl;
71
+ if ((s === "trial_available" || s === "subscription_required") && this.subscribeUrl) return this.subscribeUrl;
72
+ if (s && PREPAID_STATUSES.has(s) && this.topupUrl) return this.topupUrl;
73
+ return this.billingUrl ?? this.subscribeUrl ?? this.topupUrl;
74
+ }
75
+ };
76
+ var PREPAID_STATUSES = /* @__PURE__ */ new Set(["grace", "frozen", "card_required", "credits_required"]);
77
+ var ERROR_CODE = /^[A-Z][A-Z0-9_]*$/;
62
78
  async function handleApiError(res) {
63
- let message;
64
- let code;
79
+ let body = {};
65
80
  try {
66
- const body = await res.json();
67
- message = body.error ?? res.statusText;
68
- if (typeof body.code === "string") code = body.code;
81
+ const parsed = await res.json();
82
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) body = parsed;
69
83
  } catch {
70
- message = res.statusText;
71
84
  }
85
+ const text = (key) => typeof body[key] === "string" && body[key] ? body[key] : void 0;
86
+ const error = text("error");
87
+ const errorIsCode = !!error && ERROR_CODE.test(error);
88
+ const message = (errorIsCode ? text("message") ?? error : error ?? text("message")) ?? res.statusText;
89
+ const code = text("code") ?? (errorIsCode ? error : void 0);
72
90
  let err;
73
- if (res.status === 401 || res.status === 403) err = new AuthenticationError(message);
91
+ if (res.status === 402) {
92
+ const paid = new PaymentRequiredError(message);
93
+ paid.paymentStatus = text("status");
94
+ paid.subscribeUrl = text("subscribeUrl");
95
+ paid.billingUrl = text("billingUrl");
96
+ paid.topupUrl = text("topupUrl");
97
+ paid.invoiceUrl = text("invoiceUrl");
98
+ err = paid;
99
+ } else if (res.status === 401 || res.status === 403) err = new AuthenticationError(message);
74
100
  else if (res.status === 404) err = new NotFoundError(message);
75
101
  else if (res.status === 409) err = new ConflictError(message);
76
102
  else if (res.status === 408 || res.status === 504) err = new TimeoutError(message);
@@ -1620,88 +1646,54 @@ var RegionsAPI = class {
1620
1646
  }
1621
1647
  };
1622
1648
 
1623
- // src/cli.ts
1624
- import { spawn } from "child_process";
1625
- var LizardCLI = class {
1626
- constructor(opts = {}) {
1627
- this.opts = opts;
1628
- }
1629
- run(args, opts = {}) {
1630
- return new Promise((resolve, reject) => {
1631
- const env = { ...process.env };
1632
- if (this.opts.apiKey) {
1633
- env.LIZARD_API_KEY = this.opts.apiKey;
1634
- env.LIZARD_TOKEN = this.opts.apiKey;
1635
- }
1636
- if (this.opts.apiUrl) env.LIZARD_API_URL = this.opts.apiUrl;
1637
- const child = spawn(this.opts.executable ?? "lizard", ["--json", ...args], {
1638
- cwd: this.opts.cwd,
1639
- env,
1640
- shell: false,
1641
- stdio: "pipe",
1642
- signal: opts.signal,
1643
- timeout: this.opts.timeoutMs ?? 6e5
1644
- });
1645
- child.stdout.setEncoding("utf8");
1646
- child.stderr.setEncoding("utf8");
1647
- let stdout = "", stderr = "", bytes = 0;
1648
- const collect = (stream, data) => {
1649
- bytes += Buffer.byteLength(data);
1650
- if (bytes > 16 * 1024 * 1024) {
1651
- child.kill();
1652
- reject(new Error("CLI output exceeds 16 MiB"));
1653
- return;
1654
- }
1655
- if (stream === "stdout") stdout += data;
1656
- else stderr += data;
1657
- };
1658
- child.stdout.on("data", (data) => collect("stdout", data));
1659
- child.stderr.on("data", (data) => collect("stderr", data));
1660
- child.on("error", reject);
1661
- child.on("close", (code, signal) => {
1662
- if (signal) {
1663
- reject(new Error(`CLI ended with signal ${signal}`));
1664
- return;
1665
- }
1666
- let events = [];
1667
- try {
1668
- events = [JSON.parse(stdout)];
1669
- } catch {
1670
- events = stdout.split("\n").filter(Boolean).flatMap((line) => {
1671
- try {
1672
- return [JSON.parse(line)];
1673
- } catch {
1674
- return [];
1675
- }
1676
- });
1677
- }
1678
- resolve({ code: code ?? 1, stdout, stderr, events });
1679
- });
1680
- child.stdin.on("error", (error) => {
1681
- if (error.code !== "EPIPE") reject(error);
1682
- });
1683
- child.stdin.end(opts.stdin);
1684
- });
1685
- }
1686
- };
1687
-
1688
1649
  // src/platform/billing.ts
1689
1650
  var BillingAPI = class {
1690
1651
  constructor(client) {
1691
1652
  this.client = client;
1692
1653
  }
1693
- /** Current balance, status, burn rate, and runway. */
1654
+ /**
1655
+ * The plan: trial, this month's credits, overage, next charge, cancel date. With
1656
+ * `workspaceId`, the plan of that workspace's owner (`isOwner: false`, no card or
1657
+ * invoice fields).
1658
+ */
1659
+ subscription(opts) {
1660
+ return this.client.get(query("/api/billing/subscription", { workspaceId: opts?.workspaceId }));
1661
+ }
1662
+ /**
1663
+ * Opens Stripe Checkout for Pro: the trial when the account can have one, otherwise
1664
+ * Pro at once ($19 today). Returns the page for the user to finish; a second call
1665
+ * while it is open returns the same session.
1666
+ */
1667
+ startCheckout(opts = {}) {
1668
+ return this.client.post("/api/billing/subscription/checkout", { returnUrl: opts.returnUrl });
1669
+ }
1670
+ /** Ends the trial now: charges $19 and starts the first paid month with $19 of credits. */
1671
+ startProNow() {
1672
+ return this.client.post("/api/billing/subscription/start-now", {});
1673
+ }
1674
+ /** Cancels Pro at the end of the current month or trial. Cancelling in the trial costs nothing. */
1675
+ cancel() {
1676
+ return this.client.post("/api/billing/subscription/cancel", {});
1677
+ }
1678
+ /** Undoes {@link cancel}: Pro renews as usual. */
1679
+ resume() {
1680
+ return this.client.post("/api/billing/subscription/resume", {});
1681
+ }
1682
+ /** Redeems a promo code: a longer trial with more trial credits. Works only before the first payment. */
1683
+ redeemPromo(code) {
1684
+ return this.client.post("/api/billing/promo/redeem", { code });
1685
+ }
1686
+ /** Prepaid credits balance, status, burn rate and runway (old `payg` accounts and enterprise). */
1694
1687
  balance() {
1695
1688
  return this.client.get("/api/billing/balance");
1696
1689
  }
1697
- /** A page of balance transactions, newest first. */
1690
+ /** A page of balance transactions, newest first (old `payg` accounts and enterprise). */
1698
1691
  transactions(opts) {
1699
- const qs = new URLSearchParams();
1700
- if (opts?.limit !== void 0) qs.set("limit", String(opts.limit));
1701
- if (opts?.cursor) qs.set("cursor", opts.cursor);
1702
- if (opts?.includeUsage) qs.set("includeUsage", "1");
1703
- const q = qs.toString();
1704
- return this.client.get(`/api/billing/transactions${q ? `?${q}` : ""}`);
1692
+ return this.client.get(query("/api/billing/transactions", {
1693
+ limit: opts?.limit,
1694
+ cursor: opts?.cursor || void 0,
1695
+ includeUsage: opts?.includeUsage ? "1" : void 0
1696
+ }));
1705
1697
  }
1706
1698
  /** Cost summary for the current billing period, broken down by resource. */
1707
1699
  summary() {
@@ -1711,46 +1703,13 @@ var BillingAPI = class {
1711
1703
  live() {
1712
1704
  return this.client.get("/api/billing/live");
1713
1705
  }
1706
+ /** Saved cards. Cards are added in Checkout or on the Billing page. */
1714
1707
  paymentMethods() {
1715
1708
  return this.client.get("/api/billing/payment-methods");
1716
1709
  }
1717
- setupPaymentMethod(returnUrl) {
1718
- return this.client.post("/api/billing/payment-methods/setup", { returnUrl });
1719
- }
1720
1710
  removePaymentMethod(id) {
1721
1711
  return this.client.delete(`/api/billing/payment-methods/${encodeURIComponent(id)}`);
1722
1712
  }
1723
- purchase(creditCents, opts = {}) {
1724
- if (!Number.isSafeInteger(creditCents) || creditCents <= 0) throw new Error("creditCents must be a positive integer");
1725
- return this.client.post("/api/billing/purchase", { creditCents, paymentMethod: opts.paymentMethod ?? "card", returnUrl: opts.returnUrl });
1726
- }
1727
- autoTopup() {
1728
- return this.client.get("/api/billing/auto-topup");
1729
- }
1730
- setAutoTopup(opts) {
1731
- return this.client.put("/api/billing/auto-topup", opts);
1732
- }
1733
- runAutoTopup() {
1734
- return this.client.post("/api/billing/auto-topup/run", {});
1735
- }
1736
- redeemPromo(code) {
1737
- return this.client.post("/api/billing/promo/redeem", { code });
1738
- }
1739
- x402Quote(creditCents) {
1740
- return this.client.post("/api/billing/purchase/x402/quote", { creditCents });
1741
- }
1742
- paymentStatus(attemptId) {
1743
- return this.client.get(`/api/billing/purchase/x402/${encodeURIComponent(attemptId)}`);
1744
- }
1745
- /** Pay with the CLI's durable x402 journal. Requires CLI >= 4.0.8. Amounts are cents. */
1746
- async payX402(creditCents, opts) {
1747
- if (![creditCents, opts.maxTotalCents].every((n) => Number.isSafeInteger(n) && n > 0)) throw new Error("Payment amounts must be positive integer cents");
1748
- const args = ["credits", "topup", (creditCents / 100).toFixed(2), "--method", "x402", "--max-total", (opts.maxTotalCents / 100).toFixed(2), "--yes"];
1749
- if (opts.requestId) args.push("--request-id", opts.requestId);
1750
- const result = await new LizardCLI({ apiKey: this.client.config.apiKey, apiUrl: this.client.config.apiUrl, executable: opts.executable }).run(args);
1751
- if (result.code !== 0) throw new Error(`x402 CLI exited with code ${result.code}; inspect the payment journal or query paymentStatus before retrying`);
1752
- return result.events[0];
1753
- }
1754
1713
  };
1755
1714
 
1756
1715
  // src/volume/index.ts
@@ -2455,6 +2414,71 @@ function toBase64(text) {
2455
2414
  for (let i = 0; i < bytes.length; i += 32768) s += String.fromCharCode(...bytes.subarray(i, i + 32768));
2456
2415
  return btoa(s);
2457
2416
  }
2417
+
2418
+ // src/cli.ts
2419
+ import { spawn } from "child_process";
2420
+ var LizardCLI = class {
2421
+ constructor(opts = {}) {
2422
+ this.opts = opts;
2423
+ }
2424
+ run(args, opts = {}) {
2425
+ return new Promise((resolve, reject) => {
2426
+ const env = { ...process.env };
2427
+ if (this.opts.apiKey) {
2428
+ env.LIZARD_API_KEY = this.opts.apiKey;
2429
+ env.LIZARD_TOKEN = this.opts.apiKey;
2430
+ }
2431
+ if (this.opts.apiUrl) env.LIZARD_API_URL = this.opts.apiUrl;
2432
+ const child = spawn(this.opts.executable ?? "lizard", ["--json", ...args], {
2433
+ cwd: this.opts.cwd,
2434
+ env,
2435
+ shell: false,
2436
+ stdio: "pipe",
2437
+ signal: opts.signal,
2438
+ timeout: this.opts.timeoutMs ?? 6e5
2439
+ });
2440
+ child.stdout.setEncoding("utf8");
2441
+ child.stderr.setEncoding("utf8");
2442
+ let stdout = "", stderr = "", bytes = 0;
2443
+ const collect = (stream, data) => {
2444
+ bytes += Buffer.byteLength(data);
2445
+ if (bytes > 16 * 1024 * 1024) {
2446
+ child.kill();
2447
+ reject(new Error("CLI output exceeds 16 MiB"));
2448
+ return;
2449
+ }
2450
+ if (stream === "stdout") stdout += data;
2451
+ else stderr += data;
2452
+ };
2453
+ child.stdout.on("data", (data) => collect("stdout", data));
2454
+ child.stderr.on("data", (data) => collect("stderr", data));
2455
+ child.on("error", reject);
2456
+ child.on("close", (code, signal) => {
2457
+ if (signal) {
2458
+ reject(new Error(`CLI ended with signal ${signal}`));
2459
+ return;
2460
+ }
2461
+ let events = [];
2462
+ try {
2463
+ events = [JSON.parse(stdout)];
2464
+ } catch {
2465
+ events = stdout.split("\n").filter(Boolean).flatMap((line) => {
2466
+ try {
2467
+ return [JSON.parse(line)];
2468
+ } catch {
2469
+ return [];
2470
+ }
2471
+ });
2472
+ }
2473
+ resolve({ code: code ?? 1, stdout, stderr, events });
2474
+ });
2475
+ child.stdin.on("error", (error) => {
2476
+ if (error.code !== "EPIPE") reject(error);
2477
+ });
2478
+ child.stdin.end(opts.stdin);
2479
+ });
2480
+ }
2481
+ };
2458
2482
  export {
2459
2483
  AddonsAPI,
2460
2484
  ApiKeysAPI,
@@ -2472,6 +2496,7 @@ export {
2472
2496
  LizardError,
2473
2497
  MetricsAPI,
2474
2498
  NotFoundError,
2499
+ PaymentRequiredError,
2475
2500
  PlatformClient,
2476
2501
  ProjectsAPI,
2477
2502
  RegionsAPI,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lizard-build/sdk",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "description": "TypeScript SDK for Lizard: Firecracker microVM sandboxes, code execution and cloud apps.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",