import { sdk } from '../sdk' import { configFile } from '../file-models/config.json' const { InputSpec, Value } = sdk // Operator-facing knob for tier-quota tuning without a code change or // redeploy. The schema is { core: TierConfig, pro: TierConfig, // max: TierConfig } where TierConfig is // { lifetime: number|null, monthly: number|null, geminiCapMonthly: number|null } // null means "no cap on this dimension." The relay reads this on every // request via configFile's live-reload. const inputSpec = InputSpec.of({ // Core tier knobs. core_lifetime: Value.number({ name: 'Core — Lifetime Credits', description: 'Total credits a Core (unlicensed) install can ever spend. Default 5.', required: true, default: 5, min: 0, max: 1_000_000, integer: true, step: 1, units: 'credits', placeholder: null, }), // Pro tier knobs. pro_monthly: Value.number({ name: 'Pro — Monthly Credits', description: 'Total credits a Pro user gets each calendar month. Resets on the 1st. Default 50.', required: true, default: 50, min: 0, max: 1_000_000, integer: true, step: 1, units: 'credits', placeholder: null, }), pro_gemini_cap: Value.number({ name: 'Pro — Gemini Cap (monthly)', description: 'Within the Pro monthly allowance, how many credits may be served via Gemini (the rest spill to the operator-hardware fallback). Default 25.', required: true, default: 25, min: 0, max: 1_000_000, integer: true, step: 1, units: 'credits', placeholder: null, }), // Max tier knobs. max_gemini_cap: Value.number({ name: 'Max — Gemini Cap (monthly)', description: 'Max-tier users get unlimited total credits but a capped slice goes via Gemini. Default 50.', required: true, default: 50, min: 0, max: 1_000_000, integer: true, step: 1, units: 'credits', placeholder: null, }), }) export const adjustTierQuotas = sdk.Action.withInput( 'adjust-tier-quotas', async ({ effects }) => ({ name: 'Adjust Tier Quotas', description: 'Tune the per-tier monthly credit caps and Gemini exposure without redeploying. Changes apply to the next request — no restart needed.', warning: null, allowedStatuses: 'any', group: null, visibility: 'enabled', }), inputSpec, async ({ effects }) => { const config = await configFile.read().once() let parsed: any = {} try { parsed = JSON.parse(config?.relay_tier_quotas_json || '{}') } catch { parsed = {} } return { core_lifetime: parsed?.core?.lifetime ?? 5, pro_monthly: parsed?.pro?.monthly ?? 50, pro_gemini_cap: parsed?.pro?.geminiCapMonthly ?? 25, max_gemini_cap: parsed?.max?.geminiCapMonthly ?? 50, } }, async ({ effects, input }) => { const quotas = { core: { lifetime: input.core_lifetime ?? 5, monthly: null, geminiCapMonthly: null, }, pro: { lifetime: null, monthly: input.pro_monthly ?? 50, geminiCapMonthly: input.pro_gemini_cap ?? 25, }, max: { lifetime: null, monthly: null, geminiCapMonthly: input.max_gemini_cap ?? 50, }, } await configFile.merge(effects, { relay_tier_quotas_json: JSON.stringify(quotas), }) return null }, )