Files
timmy-tower/artifacts/api-server/src/lib/lnbits.ts

92 lines
2.8 KiB
TypeScript
Raw Normal View History

import { randomBytes } from "crypto";
export interface LNbitsInvoice {
paymentHash: string;
paymentRequest: string;
}
Task #2: MVP Foundation — injectable services, DB schema, smoke test DB schema - jobs and invoices tables added to lib/db/src/schema/ - schema barrel updated (jobs, invoices, conversations, messages) - pnpm --filter @workspace/db run push applied successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class accepting optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials are absent; logs warning - createInvoice() -> { paymentHash, paymentRequest } - checkInvoicePaid() -> boolean - stubMarkPaid() helper for dev/test flows - Real LNbits REST v1 calls wired behind the stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel / workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - Wired via Replit Anthropic AI Integration (no user API key) PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Zero LLM involvement; fully deterministic Smoke test (scripts/src/smoke.ts) - pnpm --filter @workspace/scripts run smoke - Verifies LNbits stub: create, check unpaid, mark paid, check paid - Verifies Anthropic: evaluateRequest round-trip - Both checks passed replit.md - Documented required (LNBITS_URL, LNBITS_API_KEY) and auto-provisioned secrets - Stub-mode behaviour explained
2026-03-18 15:14:23 +00:00
export interface LNbitsConfig {
url: string;
apiKey: string;
}
Task #2: MVP Foundation — injectable services, DB schema, smoke test DB schema - jobs and invoices tables added to lib/db/src/schema/ - schema barrel updated (jobs, invoices, conversations, messages) - pnpm --filter @workspace/db run push applied successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class accepting optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials are absent; logs warning - createInvoice() -> { paymentHash, paymentRequest } - checkInvoicePaid() -> boolean - stubMarkPaid() helper for dev/test flows - Real LNbits REST v1 calls wired behind the stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel / workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - Wired via Replit Anthropic AI Integration (no user API key) PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Zero LLM involvement; fully deterministic Smoke test (scripts/src/smoke.ts) - pnpm --filter @workspace/scripts run smoke - Verifies LNbits stub: create, check unpaid, mark paid, check paid - Verifies Anthropic: evaluateRequest round-trip - Both checks passed replit.md - Documented required (LNBITS_URL, LNBITS_API_KEY) and auto-provisioned secrets - Stub-mode behaviour explained
2026-03-18 15:14:23 +00:00
const stubPaidInvoices = new Set<string>();
Task #2: MVP Foundation — injectable services, DB schema, smoke test DB schema - jobs and invoices tables added to lib/db/src/schema/ - schema barrel updated (jobs, invoices, conversations, messages) - pnpm --filter @workspace/db run push applied successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class accepting optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials are absent; logs warning - createInvoice() -> { paymentHash, paymentRequest } - checkInvoicePaid() -> boolean - stubMarkPaid() helper for dev/test flows - Real LNbits REST v1 calls wired behind the stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel / workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - Wired via Replit Anthropic AI Integration (no user API key) PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Zero LLM involvement; fully deterministic Smoke test (scripts/src/smoke.ts) - pnpm --filter @workspace/scripts run smoke - Verifies LNbits stub: create, check unpaid, mark paid, check paid - Verifies Anthropic: evaluateRequest round-trip - Both checks passed replit.md - Documented required (LNBITS_URL, LNBITS_API_KEY) and auto-provisioned secrets - Stub-mode behaviour explained
2026-03-18 15:14:23 +00:00
export class LNbitsService {
private readonly url: string;
private readonly apiKey: string;
readonly stubMode: boolean;
Task #2: MVP Foundation — injectable services, DB schema, smoke test DB schema - jobs and invoices tables added to lib/db/src/schema/ - schema barrel updated (jobs, invoices, conversations, messages) - pnpm --filter @workspace/db run push applied successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class accepting optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials are absent; logs warning - createInvoice() -> { paymentHash, paymentRequest } - checkInvoicePaid() -> boolean - stubMarkPaid() helper for dev/test flows - Real LNbits REST v1 calls wired behind the stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel / workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - Wired via Replit Anthropic AI Integration (no user API key) PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Zero LLM involvement; fully deterministic Smoke test (scripts/src/smoke.ts) - pnpm --filter @workspace/scripts run smoke - Verifies LNbits stub: create, check unpaid, mark paid, check paid - Verifies Anthropic: evaluateRequest round-trip - Both checks passed replit.md - Documented required (LNBITS_URL, LNBITS_API_KEY) and auto-provisioned secrets - Stub-mode behaviour explained
2026-03-18 15:14:23 +00:00
constructor(config?: Partial<LNbitsConfig>) {
this.url = config?.url ?? process.env.LNBITS_URL ?? "";
this.apiKey = config?.apiKey ?? process.env.LNBITS_API_KEY ?? "";
this.stubMode = !this.url || !this.apiKey;
if (this.stubMode) {
console.warn("[LNbitsService] No LNBITS_URL/LNBITS_API_KEY — running in STUB mode. Invoices are simulated.");
}
}
Task #2: MVP Foundation — injectable services, DB schema, smoke test DB schema - jobs and invoices tables added to lib/db/src/schema/ - schema barrel updated (jobs, invoices, conversations, messages) - pnpm --filter @workspace/db run push applied successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class accepting optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials are absent; logs warning - createInvoice() -> { paymentHash, paymentRequest } - checkInvoicePaid() -> boolean - stubMarkPaid() helper for dev/test flows - Real LNbits REST v1 calls wired behind the stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel / workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - Wired via Replit Anthropic AI Integration (no user API key) PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Zero LLM involvement; fully deterministic Smoke test (scripts/src/smoke.ts) - pnpm --filter @workspace/scripts run smoke - Verifies LNbits stub: create, check unpaid, mark paid, check paid - Verifies Anthropic: evaluateRequest round-trip - Both checks passed replit.md - Documented required (LNBITS_URL, LNBITS_API_KEY) and auto-provisioned secrets - Stub-mode behaviour explained
2026-03-18 15:14:23 +00:00
async createInvoice(amountSats: number, memo: string): Promise<LNbitsInvoice> {
if (this.stubMode) {
const paymentHash = randomBytes(32).toString("hex");
const paymentRequest = `lnbcrt${amountSats}u1stub_${paymentHash.slice(0, 16)}`;
console.log(`[stub] Created invoice: ${amountSats} sats — "${memo}" — hash=${paymentHash}`);
return { paymentHash, paymentRequest };
}
const response = await fetch(`${this.url.replace(/\/$/, "")}/api/v1/payments`, {
method: "POST",
headers: this.headers(),
body: JSON.stringify({ out: false, amount: amountSats, memo }),
});
if (!response.ok) {
const body = await response.text();
throw new Error(`LNbits createInvoice failed (${response.status}): ${body}`);
}
const data = (await response.json()) as {
payment_hash: string;
payment_request: string;
};
return { paymentHash: data.payment_hash, paymentRequest: data.payment_request };
}
async checkInvoicePaid(paymentHash: string): Promise<boolean> {
if (this.stubMode) {
return stubPaidInvoices.has(paymentHash);
}
const response = await fetch(
`${this.url.replace(/\/$/, "")}/api/v1/payments/${paymentHash}`,
{ method: "GET", headers: this.headers() },
);
if (!response.ok) {
const body = await response.text();
throw new Error(`LNbits checkInvoice failed (${response.status}): ${body}`);
}
const data = (await response.json()) as { paid: boolean };
return data.paid;
}
/** Stub-only helper: mark an invoice as paid for testing/dev flows. */
stubMarkPaid(paymentHash: string): void {
if (!this.stubMode) {
throw new Error("stubMarkPaid called on a real LNbitsService instance");
}
stubPaidInvoices.add(paymentHash);
console.log(`[stub] Marked invoice paid: hash=${paymentHash}`);
}
private headers(): Record<string, string> {
return {
"Content-Type": "application/json",
"X-Api-Key": this.apiKey,
};
}
}
Task #2: MVP Foundation — injectable services, DB schema, smoke test DB schema - jobs and invoices tables added to lib/db/src/schema/ - schema barrel updated (jobs, invoices, conversations, messages) - pnpm --filter @workspace/db run push applied successfully LNbitsService (artifacts/api-server/src/lib/lnbits.ts) - Injectable class accepting optional { url, apiKey } config - Falls back to LNBITS_URL / LNBITS_API_KEY env vars - Auto-detects stub mode when credentials are absent; logs warning - createInvoice() -> { paymentHash, paymentRequest } - checkInvoicePaid() -> boolean - stubMarkPaid() helper for dev/test flows - Real LNbits REST v1 calls wired behind the stub guard AgentService (artifacts/api-server/src/lib/agent.ts) - Injectable class with configurable evalModel / workModel - evaluateRequest(text) -> { accepted: boolean, reason: string } uses claude-haiku-4-5; strips markdown fences before JSON parse - executeWork(text) -> { result: string } uses claude-sonnet-4-6 - Wired via Replit Anthropic AI Integration (no user API key) PricingService (artifacts/api-server/src/lib/pricing.ts) - Injectable class with configurable fee/bucket thresholds - calculateEvalFeeSats() -> 10 sats (fixed) - calculateWorkFeeSats(text) -> 50/100/250 by char-length bucket - Zero LLM involvement; fully deterministic Smoke test (scripts/src/smoke.ts) - pnpm --filter @workspace/scripts run smoke - Verifies LNbits stub: create, check unpaid, mark paid, check paid - Verifies Anthropic: evaluateRequest round-trip - Both checks passed replit.md - Documented required (LNBITS_URL, LNBITS_API_KEY) and auto-provisioned secrets - Stub-mode behaviour explained
2026-03-18 15:14:23 +00:00
export const lnbitsService = new LNbitsService();