Fix Dockerfile to copy all server/*.js modules; refresh vendor to v0.2.0
The runtime crash on v0.2.3:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/app/server/util.js'
imported from /app/server/index.js
happened because the Dockerfile's stage-2 COPY only listed server/
index.js + server/license.js explicitly. When I started extracting
modules in v0.2.3 (util.js, gemini-helpers.js, audio.js, ytdlp.js,
cookies.js, config.js, license-middleware.js, history.js, library.js)
I forgot to update the COPY list, so those files were never copied
into the runner image. Local 'node' tests passed because the modules
exist on disk; the .s9pk container had only the two original files
and crashed on first import.
Fix:
COPY server/*.js ./server/
Glob picks up all top-level .js files automatically, including any
future extractions, while still skipping server/test/ and server/
node_modules/. This is the simplest forward-compatible form.
Bonus: refresh the vendored @keysat/licensing-client from 0.1.0 to
0.2.0. The new SDK adds:
• policySlug field on StartPurchaseOptions (so we can drive Core/
Pro tier selection programmatically from our backend)
• client.listPublicPolicies(productSlug) for fetching the tier
cards' data without auth
Both are prerequisites for the in-app buy flow planned in
~/.claude/plans/in-app-buy-flow.md. The vendor's own node_modules
(@noble/ed25519, @noble/hashes) is gitignored as before — Docker
builds re-install via `npm install --omit=dev --ignore-scripts` in
the vendor dir during stage 1.
Also includes the license-middleware update from earlier in the day:
a 30s license-file poll so a key set via the "Set Recap License"
StartOS action is picked up within seconds (instead of waiting for
the 6h scheduled validateOnline tick).
This commit is contained in:
+72
-1
@@ -223,6 +223,65 @@ interface StartPurchaseOptions {
|
||||
code?: string;
|
||||
/** Optional buyer note recorded on the invoice (admin-visible). */
|
||||
buyerNote?: string;
|
||||
/**
|
||||
* Optional tier slug (the policy the buyer chose). When set, the
|
||||
* licensing service prices the invoice at the policy's
|
||||
* `price_sats_override` and remembers the chosen policy on the
|
||||
* invoice so the issued license carries that policy's
|
||||
* entitlements / duration / max_machines / trial flag.
|
||||
*
|
||||
* When omitted, the service falls back to the product's default
|
||||
* policy (the policy slugged "default", or the first active one).
|
||||
*
|
||||
* To list available tiers for a product without auth, see
|
||||
* {@link Client.listPublicPolicies}.
|
||||
*/
|
||||
policySlug?: string;
|
||||
}
|
||||
/**
|
||||
* One tier on the buyer-facing tier picker. Returned by
|
||||
* {@link Client.listPublicPolicies}. The shape mirrors what the
|
||||
* licensing service's `/buy/<slug>` page reads server-side, so an
|
||||
* in-app tier picker can render identical text and pricing without
|
||||
* the buyer ever leaving the app.
|
||||
*/
|
||||
interface PublicPolicy {
|
||||
slug: string;
|
||||
name: string;
|
||||
/** Free-form per-tier blurb (operator-set in admin UI). May be empty. */
|
||||
description: string;
|
||||
/**
|
||||
* Effective price in the smallest unit of the product's listed
|
||||
* currency: sats for SAT-priced products, cents for USD/EUR-priced
|
||||
* products. The product-level currency is on the parent
|
||||
* {@link PublicPoliciesResponse.product.basePriceSats} (sats only) and
|
||||
* via the daemon's `/v1/products/<slug>` endpoint for the full
|
||||
* currency-typed view.
|
||||
*/
|
||||
priceSats: number;
|
||||
/** 0 = perpetual; otherwise license lifetime in seconds. */
|
||||
durationSeconds: number;
|
||||
/** Seat cap. 0 = unlimited, 1 = single-seat, n = n-seat. */
|
||||
maxMachines: number;
|
||||
isTrial: boolean;
|
||||
entitlements: string[];
|
||||
/** True if the operator marked this tier as "Most popular". */
|
||||
highlighted: boolean;
|
||||
/** True if the policy is a recurring subscription. */
|
||||
isRecurring: boolean;
|
||||
/** Renewal cadence in days (0 for non-recurring). */
|
||||
renewalPeriodDays: number;
|
||||
/** First-cycle free-trial length (0 for none). */
|
||||
trialDays: number;
|
||||
}
|
||||
interface PublicPoliciesResponse {
|
||||
product: {
|
||||
slug: string;
|
||||
name: string;
|
||||
description: string;
|
||||
basePriceSats: number;
|
||||
};
|
||||
policies: PublicPolicy[];
|
||||
}
|
||||
interface RedeemFreeOptions {
|
||||
/** Optional email recorded on the synthetic invoice + license. */
|
||||
@@ -264,6 +323,18 @@ declare class Client {
|
||||
deactivate(key: string, fingerprint: string, reason?: string): Promise<MachineResponse>;
|
||||
/** Start a purchase. Returns the checkout URL and invoice id. */
|
||||
startPurchase(productSlug: string, opts?: StartPurchaseOptions): Promise<PurchaseSession>;
|
||||
/**
|
||||
* List public, buyer-visible policies (tiers) for a product. No
|
||||
* auth — same data the licensing service's `/buy/<slug>` page
|
||||
* uses server-side. Use this to render an in-app tier picker
|
||||
* that stays in sync with the operator's admin-side tier setup.
|
||||
*
|
||||
* Returns each policy's slug, display name, price (in the
|
||||
* product's listed currency's smallest unit — sats or cents),
|
||||
* entitlements, recurring/trial flags. Internal fields (id,
|
||||
* tip recipients, raw metadata) are deliberately omitted.
|
||||
*/
|
||||
listPublicPolicies(productSlug: string): Promise<PublicPoliciesResponse>;
|
||||
/**
|
||||
* Redeem a `free_license` code: bypass BTCPay entirely and receive the
|
||||
* signed license key directly. Throws if the code is unknown / disabled
|
||||
@@ -303,4 +374,4 @@ declare class LicensingError extends Error {
|
||||
constructor(code: string, message: string);
|
||||
}
|
||||
|
||||
export { Client, FLAG_FINGERPRINT_BOUND, FLAG_TRIAL, KEY_PREFIX, KEY_VERSION, KEY_VERSION_V1, KEY_VERSION_V2, type LicenseKey, type LicensePayload, LicensingError, type MachineResponse, type PollResponse, PublicKey, type PurchaseSession, type RedeemFreeOptions, type RedeemFreeResponse, type StartPurchaseOptions, type ValidateOptions, type ValidateResponse, Verifier, type VerifyOk, hasEntitlement, hashFingerprint, isExpiredAt, parseLicenseKey };
|
||||
export { Client, FLAG_FINGERPRINT_BOUND, FLAG_TRIAL, KEY_PREFIX, KEY_VERSION, KEY_VERSION_V1, KEY_VERSION_V2, type LicenseKey, type LicensePayload, LicensingError, type MachineResponse, type PollResponse, PublicKey, type PublicPoliciesResponse, type PublicPolicy, type PurchaseSession, type RedeemFreeOptions, type RedeemFreeResponse, type StartPurchaseOptions, type ValidateOptions, type ValidateResponse, Verifier, type VerifyOk, hasEntitlement, hashFingerprint, isExpiredAt, parseLicenseKey };
|
||||
|
||||
Reference in New Issue
Block a user