import { sdk } from '../sdk' import { configFile } from '../file-models/config.json' const { InputSpec, Value } = sdk // Operator-editable bundle pricing for credit purchases. The bundle // SIZES are fixed (5, 10, 20 credits — three bundles fit cleanly in // the buyer modal's single row). Only their sats prices move via // this action. If you need different bundle sizes, edit // relay_credit_packages_json directly on the relay's data volume. const inputSpec = InputSpec.of({ pkg_5_sats: Value.number({ name: '5 credit bundle — price (sats)', description: 'Price the buyer pays for a 5-credit top-up.', required: true, default: 4000, min: 1, max: 100_000_000, integer: true, step: 1, units: 'sats', placeholder: null, }), pkg_10_sats: Value.number({ name: '10 credit bundle — price (sats)', description: 'Price the buyer pays for a 10-credit top-up.', required: true, default: 6000, min: 1, max: 100_000_000, integer: true, step: 1, units: 'sats', placeholder: null, }), pkg_20_sats: Value.number({ name: '20 credit bundle — price (sats)', description: 'Price the buyer pays for a 20-credit top-up.', required: true, default: 10000, min: 1, max: 100_000_000, integer: true, step: 1, units: 'sats', placeholder: null, }), }) const FIXED_CREDIT_SIZES = [5, 10, 20] as const export const setCreditPackages = sdk.Action.withInput( 'set-credit-packages', async ({ effects }) => ({ name: 'Set Credit Bundle Prices', description: 'Per-bundle sats prices shown to buyers in the Recap credit-purchase modal. Bundle sizes (5, 10, 20 credits) are fixed by this action; edit relay_credit_packages_json directly if you need different sizes. Changes apply to the next buyer immediately — no daemon restart.', warning: null, allowedStatuses: 'any', group: 'Tiers', visibility: 'enabled', }), inputSpec, async ({ effects }) => { const config = await configFile.read().once() // Translate the stored JSON array back into per-bundle inputs. // We only care about the four fixed sizes; anything else in the // stored array is preserved on save (see merge step below) but // not exposed in the form. let parsed: Array<{ credits: number; sats: number }> = [] try { const raw = JSON.parse(config?.relay_credit_packages_json || '[]') if (Array.isArray(raw)) { parsed = raw .map((p: any) => ({ credits: Number(p?.credits), sats: Number(p?.sats), })) .filter((p) => Number.isFinite(p.credits) && Number.isFinite(p.sats)) } } catch { // ignored — fall back to defaults } const lookup = (n: number, fallback: number) => parsed.find((p) => p.credits === n)?.sats ?? fallback return { pkg_5_sats: lookup(5, 4000), pkg_10_sats: lookup(10, 6000), pkg_20_sats: lookup(20, 10000), } }, async ({ effects, input }) => { const packages = [ { credits: 5, sats: Number(input.pkg_5_sats) }, { credits: 10, sats: Number(input.pkg_10_sats) }, { credits: 20, sats: Number(input.pkg_20_sats) }, ].filter( (p) => Number.isFinite(p.sats) && p.sats > 0 && FIXED_CREDIT_SIZES.includes(p.credits as 5 | 10 | 20) ) await configFile.merge(effects, { relay_credit_packages_json: JSON.stringify(packages), }) return null }, )