@hachej/boring-core 0.1.42 → 0.1.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PostgresMeteringStore-CzNv6xil.d.ts +224 -0
- package/dist/app/front/index.d.ts +216 -3
- package/dist/app/front/index.js +834 -43
- package/dist/app/server/index.d.ts +3 -3
- package/dist/app/server/index.js +33 -8
- package/dist/{authHook-DUqyxueY.d.ts → authHook-CzBsMwwM.d.ts} +2 -2
- package/dist/{chunk-C3YMOITB.js → chunk-I56OTSPB.js} +649 -6
- package/dist/{chunk-H5KU6R6Y.js → chunk-LIBHVT7V.js} +5 -1
- package/dist/{chunk-GZVKZD4P.js → chunk-UM5SHYIS.js} +11 -2
- package/dist/{chunk-MLTJKZL4.js → chunk-VYXEXOCO.js} +21 -10
- package/dist/{connection-AL8KSENV.d.ts → connection-C5SiqoNc.d.ts} +1 -1
- package/dist/front/index.d.ts +15 -2
- package/dist/front/index.js +2 -2
- package/dist/server/db/index.d.ts +4 -4
- package/dist/server/db/index.js +6 -2
- package/dist/server/index.d.ts +594 -7
- package/dist/server/index.js +1467 -4
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/{types-CbMOXLBf.d.ts → types-CWtJ4kgd.d.ts} +3 -0
- package/drizzle/0011_usage_metering.sql +57 -0
- package/drizzle/0012_credit_purchases.sql +9 -0
- package/drizzle/0013_credit_purchase_lifecycle.sql +28 -0
- package/drizzle/0014_reservation_charge_on_expire.sql +7 -0
- package/drizzle/meta/_journal.json +28 -0
- package/package.json +4 -4
- package/dist/migrate-B4dwdtGP.d.ts +0 -8
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { C as CoreConfig } from './types-CWtJ4kgd.js';
|
|
2
|
+
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
3
|
+
|
|
4
|
+
interface RunMigrationsOptions {
|
|
5
|
+
migrationsFolder?: string;
|
|
6
|
+
}
|
|
7
|
+
declare function runMigrations(config: CoreConfig, options?: RunMigrationsOptions): Promise<void>;
|
|
8
|
+
|
|
9
|
+
declare class InsufficientCreditError extends Error {
|
|
10
|
+
readonly availableMicros: number;
|
|
11
|
+
readonly requiredMicros: number;
|
|
12
|
+
readonly statusCode = 402;
|
|
13
|
+
readonly code = "PAYMENT_REQUIRED";
|
|
14
|
+
constructor(availableMicros: number, requiredMicros: number);
|
|
15
|
+
}
|
|
16
|
+
interface MeteringBalance {
|
|
17
|
+
userId: string;
|
|
18
|
+
grantedMicros: number;
|
|
19
|
+
usedMicros: number;
|
|
20
|
+
remainingMicros: number;
|
|
21
|
+
activeReservedMicros: number;
|
|
22
|
+
/** remainingMicros minus activeReservedMicros; what a new reservation sees. */
|
|
23
|
+
availableMicros: number;
|
|
24
|
+
}
|
|
25
|
+
type CreditLedgerKind = 'grant' | 'purchase' | 'usage' | 'refund' | 'fallback';
|
|
26
|
+
/** One normalized, display-safe credit-ledger row for the account activity view.
|
|
27
|
+
* `amountMicros` is SIGNED: positive = credits added (grant/purchase), negative =
|
|
28
|
+
* consumed/removed (usage/refund). `description` is generic — no prompt/repo/model/
|
|
29
|
+
* provider/order/session details. */
|
|
30
|
+
interface CreditLedgerEntry {
|
|
31
|
+
id: string;
|
|
32
|
+
kind: CreditLedgerKind;
|
|
33
|
+
amountMicros: number;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
description: string;
|
|
36
|
+
}
|
|
37
|
+
interface GrantOnceInput {
|
|
38
|
+
userId: string;
|
|
39
|
+
/** Unique per (userId, reason); repeat calls with the same reason are no-ops. */
|
|
40
|
+
reason: string;
|
|
41
|
+
amountMicros: number;
|
|
42
|
+
expiresAt?: Date;
|
|
43
|
+
}
|
|
44
|
+
interface ReserveInput {
|
|
45
|
+
userId: string;
|
|
46
|
+
workspaceId?: string;
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
/** Stable run id; at most one active reservation may exist per runId. */
|
|
49
|
+
runId: string;
|
|
50
|
+
source?: string;
|
|
51
|
+
amountMicros: number;
|
|
52
|
+
ttlSeconds: number;
|
|
53
|
+
/**
|
|
54
|
+
* Reject when the user's available balance (remaining minus active
|
|
55
|
+
* reservations) is below this floor. Defaults to amountMicros, i.e. the
|
|
56
|
+
* reservation must fit entirely.
|
|
57
|
+
*/
|
|
58
|
+
minAvailableMicros?: number;
|
|
59
|
+
}
|
|
60
|
+
interface ReserveResult {
|
|
61
|
+
reservationId: string;
|
|
62
|
+
}
|
|
63
|
+
interface RecordUsageInput {
|
|
64
|
+
/** Stable idempotency key; a second insert with the same id is a no-op. */
|
|
65
|
+
usageId: string;
|
|
66
|
+
userId: string;
|
|
67
|
+
workspaceId?: string;
|
|
68
|
+
sessionId?: string;
|
|
69
|
+
runId?: string;
|
|
70
|
+
messageId?: string;
|
|
71
|
+
source?: string;
|
|
72
|
+
provider?: string;
|
|
73
|
+
model?: string;
|
|
74
|
+
inputTokens?: number;
|
|
75
|
+
outputTokens?: number;
|
|
76
|
+
cacheReadTokens?: number;
|
|
77
|
+
cacheWriteTokens?: number;
|
|
78
|
+
/** Provider-reported cost, in micros, before host pricing policy. */
|
|
79
|
+
providerCostMicros?: number;
|
|
80
|
+
/** Host-priced cost actually charged against the balance. */
|
|
81
|
+
billedCostMicros: number;
|
|
82
|
+
stopReason?: string;
|
|
83
|
+
metadata?: Record<string, unknown>;
|
|
84
|
+
}
|
|
85
|
+
interface RecordUsageResult {
|
|
86
|
+
inserted: boolean;
|
|
87
|
+
}
|
|
88
|
+
type ReservationFinalStatus = 'settled' | 'released';
|
|
89
|
+
interface FinishReservationInput {
|
|
90
|
+
/** Preferred key: the id returned by reserve(). */
|
|
91
|
+
reservationId?: string;
|
|
92
|
+
/** Fallback key; pair with userId to scope across tenants. */
|
|
93
|
+
runId?: string;
|
|
94
|
+
userId?: string;
|
|
95
|
+
}
|
|
96
|
+
declare class PostgresMeteringStore {
|
|
97
|
+
private db;
|
|
98
|
+
constructor(db: PostgresJsDatabase);
|
|
99
|
+
/** Idempotently create a grant keyed by (userId, reason). */
|
|
100
|
+
grantOnce(input: GrantOnceInput): Promise<{
|
|
101
|
+
created: boolean;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Credit a purchase exactly once GLOBALLY per order id. The order id is the
|
|
105
|
+
* primary key, so a webhook retry or a delivery misrouted to a different user
|
|
106
|
+
* can never double-credit. A per-order advisory lock serializes this against
|
|
107
|
+
* revokePurchase so a refund that arrives BEFORE order_created (out-of-order
|
|
108
|
+
* delivery) leaves a 'refunded' tombstone that blocks this grant — the user
|
|
109
|
+
* never keeps credits for a refunded order. Returns `granted: false` when the
|
|
110
|
+
* order was already processed or has been refunded.
|
|
111
|
+
*/
|
|
112
|
+
grantPurchaseOnce(input: {
|
|
113
|
+
orderId: string;
|
|
114
|
+
userId: string;
|
|
115
|
+
amountMicros: number;
|
|
116
|
+
source?: string;
|
|
117
|
+
storeId?: string;
|
|
118
|
+
testMode?: boolean;
|
|
119
|
+
currency?: string;
|
|
120
|
+
variantId?: string;
|
|
121
|
+
}): Promise<{
|
|
122
|
+
granted: boolean;
|
|
123
|
+
}>;
|
|
124
|
+
/**
|
|
125
|
+
* Revoke a refunded/disputed purchase. Under the same per-order advisory lock
|
|
126
|
+
* as grantPurchaseOnce, supports repeated PARTIAL refunds:
|
|
127
|
+
* - `refundFraction` is the cumulative fraction of the order (by money) that
|
|
128
|
+
* has been refunded — i.e. LS `refunded_amount / total` (both tax-inclusive),
|
|
129
|
+
* which maps the refund onto the same basis as the credited amount. The
|
|
130
|
+
* revoked credits = round(creditedMicros × fraction), capped at credited.
|
|
131
|
+
* The method debits only the delta since the last refund. Omit (undefined)
|
|
132
|
+
* for a full refund of the entire credited amount.
|
|
133
|
+
* - granted order → debit the delta, track cumulative `refunded_micros`, and
|
|
134
|
+
* mark 'refunded' once fully revoked; returns revoked=true when a debit was
|
|
135
|
+
* posted.
|
|
136
|
+
* - not yet seen → write a 'refunded' tombstone so a later order_created
|
|
137
|
+
* cannot grant; returns revoked=false (nothing was credited yet).
|
|
138
|
+
* - already fully refunded / no new delta → no-op; returns revoked=false.
|
|
139
|
+
*/
|
|
140
|
+
revokePurchase(orderId: string, opts?: {
|
|
141
|
+
refundFraction?: number;
|
|
142
|
+
source?: string;
|
|
143
|
+
allowTombstone?: boolean;
|
|
144
|
+
expectedStoreId?: string;
|
|
145
|
+
expectedTestMode?: boolean;
|
|
146
|
+
expectedCurrency?: string;
|
|
147
|
+
}): Promise<{
|
|
148
|
+
revoked: boolean;
|
|
149
|
+
}>;
|
|
150
|
+
/**
|
|
151
|
+
* Insert a refund debit idempotently by id. If the id already exists (manual
|
|
152
|
+
* repair / corrupted retry), VERIFY the existing row is the same debit (user +
|
|
153
|
+
* amount) — throw on mismatch so a caller never records a refund the balance was
|
|
154
|
+
* not actually debited for. Returns true iff a new debit row was written.
|
|
155
|
+
*/
|
|
156
|
+
private insertVerifiedLedgerDebit;
|
|
157
|
+
/** Total billed micros already recorded for a run (for fallback top-up so a
|
|
158
|
+
* partial-success run isn't charged the hold ON TOP of its real usage). */
|
|
159
|
+
billedMicrosForRun(userId: string, runId: string): Promise<number>;
|
|
160
|
+
/** Total billed micros for a specific RESERVATION (run attempt). Preferred over
|
|
161
|
+
* billedMicrosForRun for fallback top-up: runId is reused on client-nonce
|
|
162
|
+
* replay, so summing by runId would count a prior attempt's billing and let a
|
|
163
|
+
* later reusing attempt settle free. */
|
|
164
|
+
billedMicrosForReservation(userId: string, reservationId: string): Promise<number>;
|
|
165
|
+
/** Durably record that an ACTIVE reservation must be charged the fallback hold if
|
|
166
|
+
* it expires, BEFORE attempting the actual fallback charge. Committed on its own so
|
|
167
|
+
* the intent survives a subsequent failed charge write — the expiry sweep then
|
|
168
|
+
* charges a marked reservation even with zero billed rows (no free started run on a
|
|
169
|
+
* brief finalization-time DB outage). Idempotent; a no-op on a non-active row. */
|
|
170
|
+
markReservationFallbackCharge(userId: string, reservationId: string): Promise<void>;
|
|
171
|
+
getBalance(userId: string, now?: Date): Promise<MeteringBalance>;
|
|
172
|
+
/** Most-recent credit ledger for the account activity view: grants/purchases
|
|
173
|
+
* (positive) merged with usage/refund/fallback debits (negative), newest first,
|
|
174
|
+
* scoped to the user and capped at `limit` (clamped 1..50). Descriptions are
|
|
175
|
+
* generic/sanitized; zero-amount usage rows (zero-token) are omitted as noise. */
|
|
176
|
+
listLedger(userId: string, limit: number): Promise<CreditLedgerEntry[]>;
|
|
177
|
+
/**
|
|
178
|
+
* Reserve credit for a run. Serialized per user (advisory transaction
|
|
179
|
+
* lock) so concurrent reservations cannot jointly overdraw. Idempotent per
|
|
180
|
+
* runId: re-reserving while a reservation is still active returns the
|
|
181
|
+
* existing reservation instead of double-holding. Throws
|
|
182
|
+
* InsufficientCreditError when the available balance is below
|
|
183
|
+
* minAvailableMicros (default: the reservation amount itself).
|
|
184
|
+
*/
|
|
185
|
+
reserve(input: ReserveInput, now?: Date): Promise<ReserveResult>;
|
|
186
|
+
/** Idempotent ledger insert; returns whether a new row was written. Serialized
|
|
187
|
+
* under the user's advisory lock so a positive debit is ordered with that user's
|
|
188
|
+
* reserve()/expiry: a debit that pushes the balance below the floor is visible to a
|
|
189
|
+
* concurrent reserve (which then waits and is refused), keeping the documented
|
|
190
|
+
* "overshoot → next run refused" boundary even for over-budget/misrouted runs. */
|
|
191
|
+
recordUsage(input: RecordUsageInput): Promise<RecordUsageResult>;
|
|
192
|
+
/**
|
|
193
|
+
* Finish a reservation. Settling also recovers reservations that expired
|
|
194
|
+
* before a delayed settlement retry, so charged usage never leaves a
|
|
195
|
+
* reservation dangling. Releasing only touches active rows. Idempotent:
|
|
196
|
+
* repeat calls are no-ops.
|
|
197
|
+
*
|
|
198
|
+
* A runId may have more than one row (an expired row plus a fresh active
|
|
199
|
+
* retry), so the runId fallback resolves to the single newest matching row
|
|
200
|
+
* — a settle never flips both the dead and the live reservation together.
|
|
201
|
+
*/
|
|
202
|
+
finishReservation(input: FinishReservationInput, status: ReservationFinalStatus): Promise<{
|
|
203
|
+
updated: boolean;
|
|
204
|
+
}>;
|
|
205
|
+
/** Expire stale active reservations without charging. Returns the count. */
|
|
206
|
+
expireStaleReservations(now?: Date): Promise<number>;
|
|
207
|
+
/**
|
|
208
|
+
* Expire one user's stale active reservations under the SINGLE charge-aware
|
|
209
|
+
* policy. CALLER MUST already hold pg_advisory_xact_lock(hashtext(userId)).
|
|
210
|
+
* A reservation that reached TTL without an explicit settle/release had a failed
|
|
211
|
+
* finalization: if it has POSITIVE billed usage (`billedTotal > 0`) OR carries the
|
|
212
|
+
* durable `charge_on_expire` marker, the run did chargeable work, so top it up to
|
|
213
|
+
* the hold (idempotent) rather than free it; a reservation with only zero-billed
|
|
214
|
+
* rows and no marker is a non-billable/pre-execution abandon and is freed (so a
|
|
215
|
+
* user who closed the tab isn't over-charged).
|
|
216
|
+
*/
|
|
217
|
+
private expireUserStaleReservations;
|
|
218
|
+
private computeBalance;
|
|
219
|
+
private sumGrants;
|
|
220
|
+
private sumUsage;
|
|
221
|
+
private sumActiveReservations;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export { type CreditLedgerEntry as C, type FinishReservationInput as F, type GrantOnceInput as G, InsufficientCreditError as I, type MeteringBalance as M, PostgresMeteringStore as P, type RecordUsageInput as R, type RecordUsageResult as a, type ReservationFinalStatus as b, type ReserveInput as c, type ReserveResult as d, type RunMigrationsOptions as e, runMigrations as r };
|
|
@@ -10,16 +10,28 @@ interface ChatFirstPublicShellOptions {
|
|
|
10
10
|
eyebrow?: string;
|
|
11
11
|
title?: string;
|
|
12
12
|
description?: string;
|
|
13
|
+
footer?: ReactNode;
|
|
13
14
|
};
|
|
14
15
|
suggestions?: ChatSuggestion[];
|
|
16
|
+
/**
|
|
17
|
+
* Hand-drawn "Your agent" / "Your remote computer" annotations point at the
|
|
18
|
+
* composer and the workspace surface to teach the empty public shell. Apps
|
|
19
|
+
* that open a center panel on load (e.g. a landing page) should disable them,
|
|
20
|
+
* otherwise the fixed-position arrows overlay the open panel. Defaults to on.
|
|
21
|
+
*/
|
|
22
|
+
showTeachingArrows?: boolean;
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
type ChatEntryMode = 'auth-first' | 'chat-first';
|
|
18
|
-
|
|
26
|
+
type RoutedWorkspaceAgentProps<TSession extends WorkspaceAgentSession = WorkspaceAgentSession> = Omit<WorkspaceAgentFrontProps<TSession>, 'workspaceId' | 'frontPluginHotReload' | 'hotReloadEnabled'>;
|
|
27
|
+
interface CoreWorkspaceAgentFrontProps<TSession extends WorkspaceAgentSession = WorkspaceAgentSession> extends RoutedWorkspaceAgentProps<TSession> {
|
|
19
28
|
/** Core consumes plugins statically for now; app-level hot reload is explicitly unsupported. */
|
|
20
29
|
hotReload?: false;
|
|
21
30
|
chatEntryMode?: ChatEntryMode;
|
|
22
31
|
chatFirstPublicShell?: ChatFirstPublicShellOptions;
|
|
32
|
+
/** Extra workspace props used only by the unauthenticated chat-first public shell. */
|
|
33
|
+
chatFirstPublicWorkspaceProps?: Partial<RoutedWorkspaceAgentProps<TSession>>;
|
|
34
|
+
publicPaths?: string[];
|
|
23
35
|
authPages?: CoreFrontAuthPagesOverride;
|
|
24
36
|
cspNonce?: string;
|
|
25
37
|
children?: ReactNode;
|
|
@@ -29,6 +41,207 @@ interface CoreWorkspaceAgentFrontProps<TSession extends WorkspaceAgentSession =
|
|
|
29
41
|
loadingFallback?: ReactNode;
|
|
30
42
|
bootPreloadPaths?: string[];
|
|
31
43
|
}
|
|
32
|
-
|
|
44
|
+
/** Default top-bar right content. Exported so apps can compose extra widgets
|
|
45
|
+
* (e.g. a credit balance badge) alongside the user menu. */
|
|
46
|
+
declare function DefaultTopBarRight(): react_jsx_runtime.JSX.Element;
|
|
47
|
+
declare function CoreWorkspaceAgentFront<TSession extends WorkspaceAgentSession = WorkspaceAgentSession>({ authPages, cspNonce, children, workspaceRoute, workspaceIdParam, workspaceHref, loadingFallback, bootPreloadPaths, topBarLeft, topBarRight, appTitle, bridgeEndpoint, hotReload, chatEntryMode, chatFirstPublicShell, chatFirstPublicWorkspaceProps, publicPaths, ...workspaceProps }: CoreWorkspaceAgentFrontProps<TSession>): react_jsx_runtime.JSX.Element;
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
interface CreditBalanceBadgeProps {
|
|
50
|
+
/** Base URL for the credits API (default: same origin). */
|
|
51
|
+
apiBaseUrl?: string;
|
|
52
|
+
/** Fallback enable for the add-credits action. The server's `checkoutEnabled`
|
|
53
|
+
* (from /api/credits/balance) takes precedence when present. */
|
|
54
|
+
buyEnabled?: boolean;
|
|
55
|
+
/** Poll interval for the balance, ms (default 30s). */
|
|
56
|
+
pollMs?: number;
|
|
57
|
+
locale?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Top-bar credit balance: a DISCRETE remaining-balance figure plus a small "+" button.
|
|
61
|
+
* Clicking "+" opens a popover of fixed top-up amounts (Anthropic-style); picking one starts
|
|
62
|
+
* the server-created checkout and opens it. Polls `/api/credits/balance`; hides itself when
|
|
63
|
+
* credits are disabled or the user is unauthenticated.
|
|
64
|
+
*/
|
|
65
|
+
declare function CreditBalanceBadge({ apiBaseUrl, buyEnabled, pollMs, locale, }: CreditBalanceBadgeProps): react_jsx_runtime.JSX.Element | null;
|
|
66
|
+
|
|
67
|
+
interface CreditsSettingsPanelProps {
|
|
68
|
+
/** Base URL for the credits API (default: same origin). */
|
|
69
|
+
apiBaseUrl?: string;
|
|
70
|
+
locale?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Account-settings "Billing & credits" panel: current balance (+ debt/low notices,
|
|
74
|
+
* staleness), a pack picker → checkout, and a lazy-loaded recent-activity list.
|
|
75
|
+
* Renders nothing when credits are disabled or the user is unauthenticated.
|
|
76
|
+
*/
|
|
77
|
+
declare function CreditsSettingsPanel({ apiBaseUrl, locale }: CreditsSettingsPanelProps): react_jsx_runtime.JSX.Element | null;
|
|
78
|
+
|
|
79
|
+
/** Front-end helpers for the credit balance badge + buy-credits flow. */
|
|
80
|
+
/** Display-ready credit pack (server-authored). The client never infers price from
|
|
81
|
+
* the id and never sees the provider's price/variant id. */
|
|
82
|
+
interface CreditPack {
|
|
83
|
+
id: string;
|
|
84
|
+
creditMicros: number;
|
|
85
|
+
/** Price in the currency's minor unit (e.g. cents). For a custom pack, the MINIMUM. */
|
|
86
|
+
priceMinor: number;
|
|
87
|
+
currency: string;
|
|
88
|
+
label: string;
|
|
89
|
+
isDefault: boolean;
|
|
90
|
+
/** Pay-what-you-want pack: the buyer enters the amount on the hosted checkout, so
|
|
91
|
+
* `creditMicros` is 0 here and `priceMinor` is the minimum. The picker shows an
|
|
92
|
+
* "enter amount" option instead of a fixed price. */
|
|
93
|
+
custom?: boolean;
|
|
94
|
+
}
|
|
95
|
+
interface CreditBalanceResponse {
|
|
96
|
+
enabled: boolean;
|
|
97
|
+
userId: string;
|
|
98
|
+
grantedMicros: number;
|
|
99
|
+
usedMicros: number;
|
|
100
|
+
remainingMicros: number;
|
|
101
|
+
activeReservedMicros: number;
|
|
102
|
+
availableMicros: number;
|
|
103
|
+
/** Amount owed when the ledger went negative (e.g. refund of spent credits). */
|
|
104
|
+
debtMicros: number;
|
|
105
|
+
/** Server truth: whether the Buy-credits checkout is wired (avoids client drift). */
|
|
106
|
+
checkoutEnabled?: boolean;
|
|
107
|
+
/** Display-ready packs (present only when checkout is wired). */
|
|
108
|
+
packs?: CreditPack[];
|
|
109
|
+
currency: 'credits';
|
|
110
|
+
}
|
|
111
|
+
type CreditLedgerKind = 'grant' | 'purchase' | 'usage' | 'refund' | 'fallback';
|
|
112
|
+
/** One credit-activity row (mirrors the server `CreditLedgerEntry`). `amountMicros`
|
|
113
|
+
* is signed: positive = added, negative = consumed/removed. */
|
|
114
|
+
interface CreditLedgerEntry {
|
|
115
|
+
id: string;
|
|
116
|
+
kind: CreditLedgerKind;
|
|
117
|
+
amountMicros: number;
|
|
118
|
+
createdAt: string;
|
|
119
|
+
description: string;
|
|
120
|
+
}
|
|
121
|
+
/** Format SIGNED credit micros as a currency string with an explicit +/− sign.
|
|
122
|
+
* `currency` is the configured display currency (1 credit-unit = 1 major unit); defaults
|
|
123
|
+
* to EUR for callers without a configured purchase currency. */
|
|
124
|
+
declare function formatSignedCreditMicros(micros: number, currency?: string, locale?: string): string;
|
|
125
|
+
/** Format a minor-unit price (e.g. cents) in its currency for pack labels/buttons. */
|
|
126
|
+
declare function formatMinorPrice(priceMinor: number, currency: string, locale?: string): string;
|
|
127
|
+
/** Format credit micros as a currency string. 1 credit-unit = 1 major unit of the
|
|
128
|
+
* configured display `currency` (µ/1e6). Defaults to EUR for callers without a
|
|
129
|
+
* configured purchase currency (e.g. a consumption-only deployment). */
|
|
130
|
+
declare function formatCreditMicros(micros: number, currency?: string, locale?: string): string;
|
|
131
|
+
/** True when the remaining balance is at or below the low-balance threshold. */
|
|
132
|
+
declare function isLowBalance(micros: number, thresholdMicros?: number): boolean;
|
|
133
|
+
/** Stable server error code for an out-of-credits rejection (mirrors the agent's
|
|
134
|
+
* ErrorCode enum). Kept here so the credits feature owns the credit↔code mapping
|
|
135
|
+
* and the agent stays billing-agnostic. */
|
|
136
|
+
declare const PAYMENT_REQUIRED_ERROR_CODE = "PAYMENT_REQUIRED";
|
|
137
|
+
/** True when a run-rejected notice was an out-of-credits rejection. Hosts use this
|
|
138
|
+
* in renderNoticeAction to decide whether to show the Buy-credits CTA. */
|
|
139
|
+
declare function isPaymentRequiredNotice(notice: {
|
|
140
|
+
errorCode?: string;
|
|
141
|
+
}): boolean;
|
|
142
|
+
|
|
143
|
+
/** Window event other code can dispatch to force an immediate balance refetch —
|
|
144
|
+
* e.g. the chat dispatching `window.dispatchEvent(new Event(CREDITS_REFRESH_EVENT))`
|
|
145
|
+
* when a run finishes, so the balance updates without waiting for the poll. */
|
|
146
|
+
declare const CREDITS_REFRESH_EVENT = "credits:refresh";
|
|
147
|
+
interface UseCreditBalanceOptions {
|
|
148
|
+
/** Base URL for the credits API (default: same origin). */
|
|
149
|
+
apiBaseUrl?: string;
|
|
150
|
+
/** Poll interval for the balance, ms (default 30s). */
|
|
151
|
+
pollMs?: number;
|
|
152
|
+
/** Credit pack id to purchase (server maps it to a Lemon Squeezy variant). */
|
|
153
|
+
pack?: string;
|
|
154
|
+
}
|
|
155
|
+
interface UseCreditBalanceResult {
|
|
156
|
+
/** Latest balance, or null before the first successful load. */
|
|
157
|
+
balance: CreditBalanceResponse | null;
|
|
158
|
+
/** True when credits are disabled or the user is unauthenticated (UI should hide). */
|
|
159
|
+
hidden: boolean;
|
|
160
|
+
/** Set when a balance fetch failed (network/server) before a successful load; null
|
|
161
|
+
* otherwise. Lets a consumer show an error state rather than an endless spinner. */
|
|
162
|
+
error: string | null;
|
|
163
|
+
/** Refetch the balance now. */
|
|
164
|
+
refresh: () => Promise<void>;
|
|
165
|
+
/** Refetch now, then a short backoff burst (~15s) — credit writes settle
|
|
166
|
+
* asynchronously after a run/purchase, so a single refetch can read a stale value.
|
|
167
|
+
* Concurrent bursts are deduped (a new call restarts the window). */
|
|
168
|
+
refreshWithRetry: () => void;
|
|
169
|
+
/** Start a Lemon Squeezy checkout (server creates it, sets the buyer id from the
|
|
170
|
+
* session) and open it in a new tab. Resolves to an error message on failure.
|
|
171
|
+
* Pass a pack id to buy a specific pack. */
|
|
172
|
+
buy: (pack?: string) => Promise<string | null>;
|
|
173
|
+
/** True while a checkout request is in flight. */
|
|
174
|
+
buying: boolean;
|
|
175
|
+
/** Epoch ms of the last successful balance read (null before first load). */
|
|
176
|
+
lastUpdatedAt: number | null;
|
|
177
|
+
/** True while a refresh (incl. a retry burst) is in flight. */
|
|
178
|
+
updating: boolean;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Shared credit-balance state for the top-bar badge and the settings panel:
|
|
182
|
+
* polls `/api/credits/balance`, refetches on window focus and on the
|
|
183
|
+
* `credits:refresh` event, and exposes a server-side checkout action. The buyer
|
|
184
|
+
* id is set SERVER-side (POST /api/credits/checkout) so the client never supplies it.
|
|
185
|
+
*/
|
|
186
|
+
declare function useCreditBalance({ apiBaseUrl, pollMs, pack, }?: UseCreditBalanceOptions): UseCreditBalanceResult;
|
|
187
|
+
|
|
188
|
+
interface UseCreditHistoryResult {
|
|
189
|
+
entries: CreditLedgerEntry[] | null;
|
|
190
|
+
loading: boolean;
|
|
191
|
+
error: boolean;
|
|
192
|
+
/** Fetch (or refetch) the activity. Call lazily, e.g. when the section expands. */
|
|
193
|
+
load: () => Promise<void>;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Lazy loader for the account credit-activity list (`GET /api/credits/history`).
|
|
197
|
+
* Does NOT fetch on mount — call `load()` when the section is opened. `entries` is
|
|
198
|
+
* null until the first load; `[]` means "no activity yet".
|
|
199
|
+
*/
|
|
200
|
+
declare function useCreditHistory(apiBaseUrl?: string, limit?: number): UseCreditHistoryResult;
|
|
201
|
+
|
|
202
|
+
type CheckoutReturnStatus = 'idle' | 'checking' | 'confirmed' | 'processing' | 'cancelled';
|
|
203
|
+
interface UseCheckoutReturnHandlerOptions {
|
|
204
|
+
apiBaseUrl?: string;
|
|
205
|
+
/** Query param name the LS redirect uses (default 'checkout'). */
|
|
206
|
+
param?: string;
|
|
207
|
+
}
|
|
208
|
+
interface UseCheckoutReturnHandlerResult {
|
|
209
|
+
status: CheckoutReturnStatus;
|
|
210
|
+
dismiss: () => void;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Handle the return from a Lemon Squeezy hosted checkout. The URL marker (`?checkout=
|
|
214
|
+
* return|success|cancelled`) is NOT treated as proof of payment — on a return we poll
|
|
215
|
+
* the AUTHENTICATED balance and only show success once it actually increases (credits
|
|
216
|
+
* settle asynchronously via the webhook). The marker is stripped immediately so a reload
|
|
217
|
+
* can't replay it, and a `credits:refresh` event + BroadcastChannel signal refresh any
|
|
218
|
+
* other open app tab. Returns the status for a small banner to render.
|
|
219
|
+
*/
|
|
220
|
+
declare function useCheckoutReturnHandler({ apiBaseUrl, param }?: UseCheckoutReturnHandlerOptions): UseCheckoutReturnHandlerResult;
|
|
221
|
+
|
|
222
|
+
interface CheckoutReturnBannerProps {
|
|
223
|
+
apiBaseUrl?: string;
|
|
224
|
+
/** Query param the LS redirect uses (default 'checkout'). */
|
|
225
|
+
param?: string;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Renders the post-checkout return state (see useCheckoutReturnHandler). Mount it once
|
|
229
|
+
* near the app root. It NEVER claims success from the URL — only after the server
|
|
230
|
+
* balance actually increases. ARIA-live so the status is announced. Self-hides when idle.
|
|
231
|
+
*/
|
|
232
|
+
declare function CheckoutReturnBanner({ apiBaseUrl, param }: CheckoutReturnBannerProps): react_jsx_runtime.JSX.Element | null;
|
|
233
|
+
|
|
234
|
+
interface BuyCreditsNoticeActionProps {
|
|
235
|
+
/** Base URL for the credits API (default: same origin). */
|
|
236
|
+
apiBaseUrl?: string;
|
|
237
|
+
label?: string;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Inline "Buy credits" action for a PAYMENT_REQUIRED run-rejected notice. Self-contained:
|
|
241
|
+
* starts a server-created checkout (the buyer id is set server-side) on click. Renders
|
|
242
|
+
* nothing once the server reports checkout is disabled, so it can't dangle a dead button.
|
|
243
|
+
* Mount it from a host's renderNoticeAction (see isPaymentRequiredNotice).
|
|
244
|
+
*/
|
|
245
|
+
declare function BuyCreditsNoticeAction({ apiBaseUrl, label }: BuyCreditsNoticeActionProps): react_jsx_runtime.JSX.Element | null;
|
|
246
|
+
|
|
247
|
+
export { BuyCreditsNoticeAction, type BuyCreditsNoticeActionProps, CREDITS_REFRESH_EVENT, type ChatFirstPublicShellOptions, CheckoutReturnBanner, type CheckoutReturnBannerProps, type CheckoutReturnStatus, CoreWorkspaceAgentFront, type CoreWorkspaceAgentFrontProps, CreditBalanceBadge, type CreditBalanceBadgeProps, type CreditBalanceResponse, type CreditLedgerEntry, type CreditLedgerKind, type CreditPack, CreditsSettingsPanel, type CreditsSettingsPanelProps, DefaultTopBarRight, PAYMENT_REQUIRED_ERROR_CODE, type UseCheckoutReturnHandlerResult, type UseCreditBalanceOptions, type UseCreditBalanceResult, type UseCreditHistoryResult, formatCreditMicros, formatMinorPrice, formatSignedCreditMicros, isLowBalance, isPaymentRequiredNotice, useCheckoutReturnHandler, useCreditBalance, useCreditHistory };
|