Files
zclaw_openfang/desktop/src/lib/saas-billing.ts
iven f846f3d632
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
fix(tauri): update @reserved annotations + remove dead SaaS client methods
- Update 9 @reserved → @connected for commands with frontend consumers:
  zclaw_status, zclaw_start, zclaw_stop, zclaw_restart, zclaw_doctor,
  viking_add_with_metadata, viking_store_with_summaries,
  trigger_execute, scheduled_task_create
- Remove 10 dead SaaS client methods with zero callers:
  healthCheck, listDevices (saas-client.ts)
  getRelayTask, getUsage/relay (saas-relay.ts)
  listPrompts, getPrompt, listPromptVersions, getPromptVersion (saas-prompt.ts)
  getPlan, getUsage/billing (saas-billing.ts)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 00:22:45 +08:00

109 lines
3.2 KiB
TypeScript

/**
* SaaS Billing Methods — Mixin
*
* Installs billing-related methods onto SaaSClient.prototype.
* Covers usage increment (fire-and-forget) and full billing CRUD
* (plans, subscription, usage, payments).
*/
import type {
BillingPlan,
SubscriptionInfo,
CreatePaymentRequest,
PaymentResult,
PaymentStatus,
} from './saas-types';
export interface UsageIncrementResult {
dimension: string;
incremented: number;
usage: {
relay_requests: number;
hand_executions: number;
pipeline_runs: number;
input_tokens: number;
output_tokens: number;
[key: string]: unknown;
};
}
type RequestFn = <T>(method: string, path: string, body?: unknown) => Promise<T>;
export function installBillingMethods(ClientClass: { prototype: any }): void {
const proto = ClientClass.prototype;
// --- Usage Increment ---
/**
* Report a usage increment for a specific dimension.
*
* Called after hand execution, pipeline run, or other metered operations.
* Non-blocking — failures are logged but don't affect the caller.
*
* @param dimension - One of: hand_executions, pipeline_runs, relay_requests
* @param count - How many units to increment (default 1, max 100)
*/
proto.incrementUsageDimension = async function (
this: { request<T>(method: string, path: string, body?: unknown): Promise<T> },
dimension: string,
count: number = 1,
): Promise<UsageIncrementResult> {
return this.request<UsageIncrementResult>(
'POST',
'/api/v1/billing/usage/increment',
{ dimension, count },
);
};
/**
* Fire-and-forget version of incrementUsageDimension.
* Logs errors but never throws — safe to call in finally blocks.
*/
proto.reportUsageFireAndForget = function (
this: { incrementUsageDimension(dimension: string, count?: number): Promise<UsageIncrementResult> },
dimension: string,
count: number = 1,
): void {
this.incrementUsageDimension(dimension, count).catch((err: unknown) => {
const msg = err instanceof Error ? err.message : String(err);
console.warn(`[Billing] Failed to report ${dimension} usage (+${count}): ${msg}`);
});
};
// --- Plans ---
/** List all active billing plans (public, no auth required) */
proto.listPlans = async function (
this: { request: RequestFn },
): Promise<BillingPlan[]> {
return this.request<BillingPlan[]>('GET', '/api/v1/billing/plans');
};
// --- Subscription ---
/** Get current subscription info (plan + subscription + usage) */
proto.getSubscription = async function (
this: { request: RequestFn },
): Promise<SubscriptionInfo> {
return this.request<SubscriptionInfo>('GET', '/api/v1/billing/subscription');
};
// --- Payments ---
/** Create a payment order for a plan upgrade */
proto.createPayment = async function (
this: { request: RequestFn },
data: CreatePaymentRequest,
): Promise<PaymentResult> {
return this.request<PaymentResult>('POST', '/api/v1/billing/payments', data);
};
/** Check payment status by payment ID */
proto.getPaymentStatus = async function (
this: { request: RequestFn },
paymentId: string,
): Promise<PaymentStatus> {
return this.request<PaymentStatus>('GET', `/api/v1/billing/payments/${paymentId}`);
};
}