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
- 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>
109 lines
3.2 KiB
TypeScript
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}`);
|
|
};
|
|
}
|