@hachej/boring-core 0.1.70 → 0.1.72
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/{PostgresModelBudgetStore-CFbEEdQL.d.ts → PostgresModelBudgetStore-DWwh7ElQ.d.ts} +93 -11
- package/dist/app/front/index.d.ts +6 -2
- package/dist/app/front/index.js +13 -6
- package/dist/app/server/index.d.ts +3 -3
- package/dist/app/server/index.js +2 -2
- package/dist/{chunk-CIRY3ZLU.js → chunk-DSENO5O4.js} +3 -1
- package/dist/{chunk-EFM5IWTK.js → chunk-JBEKMYST.js} +486 -214
- package/dist/front/index.d.ts +1 -1
- package/dist/{authHook-BUSGTx_o.d.ts → loadConfig-C3iHuZNw.d.ts} +11 -11
- package/dist/server/db/index.d.ts +4 -4
- package/dist/server/db/index.js +5 -1
- package/dist/server/index.d.ts +56 -56
- package/dist/server/index.js +6 -2
- package/dist/shared/index.d.ts +2 -2
- package/dist/{types-DVgeIw1d.d.ts → types-DDnayJjI.d.ts} +1 -1
- package/dist/{connection-D9s7Td0I.d.ts → types-Dm11Zm56.d.ts} +8 -8
- package/drizzle/0017_user_budget_reservations.sql +15 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +7 -7
package/dist/{PostgresModelBudgetStore-CFbEEdQL.d.ts → PostgresModelBudgetStore-DWwh7ElQ.d.ts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as CoreConfig } from './types-
|
|
1
|
+
import { C as CoreConfig } from './types-DDnayJjI.js';
|
|
2
2
|
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
3
3
|
|
|
4
4
|
interface RunMigrationsOptions {
|
|
@@ -89,7 +89,7 @@ interface RecordUsageResult {
|
|
|
89
89
|
inserted: boolean;
|
|
90
90
|
}
|
|
91
91
|
type ReservationFinalStatus = 'settled' | 'released';
|
|
92
|
-
interface FinishReservationInput {
|
|
92
|
+
interface FinishReservationInput$1 {
|
|
93
93
|
/** Preferred key: the id returned by reserve(). */
|
|
94
94
|
reservationId?: string;
|
|
95
95
|
/** Fallback key; pair with userId to scope across tenants. */
|
|
@@ -203,7 +203,7 @@ declare class PostgresMeteringStore {
|
|
|
203
203
|
* retry), so the runId fallback resolves to the single newest matching row
|
|
204
204
|
* — a settle never flips both the dead and the live reservation together.
|
|
205
205
|
*/
|
|
206
|
-
finishReservation(input: FinishReservationInput, status: ReservationFinalStatus): Promise<{
|
|
206
|
+
finishReservation(input: FinishReservationInput$1, status: ReservationFinalStatus): Promise<{
|
|
207
207
|
updated: boolean;
|
|
208
208
|
}>;
|
|
209
209
|
/** Expire stale active reservations without charging. Returns the count. */
|
|
@@ -225,6 +225,8 @@ declare class PostgresMeteringStore {
|
|
|
225
225
|
private sumActiveReservations;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
type BudgetReservationStatus = 'active' | 'settled' | 'released' | 'expired';
|
|
229
|
+
type BudgetReservationScope = 'model' | 'user';
|
|
228
230
|
declare class ModelBudgetExceededError extends Error {
|
|
229
231
|
readonly usedMicros: number;
|
|
230
232
|
readonly heldMicros: number;
|
|
@@ -234,37 +236,117 @@ declare class ModelBudgetExceededError extends Error {
|
|
|
234
236
|
readonly code = "MODEL_BUDGET_EXCEEDED";
|
|
235
237
|
constructor(usedMicros: number, heldMicros: number, budgetMicros: number, requestedMicros: number);
|
|
236
238
|
}
|
|
237
|
-
|
|
239
|
+
declare class UserBudgetExceededError extends Error {
|
|
240
|
+
readonly usedMicros: number;
|
|
241
|
+
readonly heldMicros: number;
|
|
242
|
+
readonly budgetMicros: number;
|
|
243
|
+
readonly requestedMicros: number;
|
|
244
|
+
readonly statusCode = 402;
|
|
245
|
+
readonly code = "MODEL_BUDGET_EXCEEDED";
|
|
246
|
+
constructor(usedMicros: number, heldMicros: number, budgetMicros: number, requestedMicros: number);
|
|
247
|
+
}
|
|
248
|
+
interface ReserveBudgetBaseInput {
|
|
238
249
|
userId: string;
|
|
239
250
|
workspaceId?: string;
|
|
240
251
|
sessionId?: string;
|
|
241
252
|
runId: string;
|
|
242
|
-
provider: string;
|
|
243
|
-
model: string;
|
|
244
253
|
budgetMicros: number;
|
|
245
254
|
holdMicros: number;
|
|
246
255
|
ttlSeconds: number;
|
|
247
256
|
now?: Date;
|
|
248
257
|
}
|
|
249
|
-
|
|
258
|
+
type ReserveBudgetInput = (ReserveBudgetBaseInput & {
|
|
259
|
+
scope: 'user';
|
|
260
|
+
provider?: never;
|
|
261
|
+
model?: never;
|
|
262
|
+
}) | (ReserveBudgetBaseInput & {
|
|
263
|
+
scope: 'model';
|
|
264
|
+
provider: string;
|
|
265
|
+
model: string;
|
|
266
|
+
});
|
|
267
|
+
interface ReserveBudgetResult {
|
|
268
|
+
scope: BudgetReservationScope;
|
|
250
269
|
reservationId: string;
|
|
251
270
|
created: boolean;
|
|
252
271
|
period: string;
|
|
253
272
|
}
|
|
254
|
-
interface
|
|
273
|
+
interface BudgetReservationAdmissionInput {
|
|
274
|
+
user?: Extract<ReserveBudgetInput, {
|
|
275
|
+
scope: 'user';
|
|
276
|
+
}>;
|
|
277
|
+
model: Extract<ReserveBudgetInput, {
|
|
278
|
+
scope: 'model';
|
|
279
|
+
}>;
|
|
280
|
+
}
|
|
281
|
+
interface BudgetReservationAdmission {
|
|
282
|
+
user?: ReserveBudgetResult & {
|
|
283
|
+
scope: 'user';
|
|
284
|
+
};
|
|
285
|
+
model: ReserveBudgetResult & {
|
|
286
|
+
scope: 'model';
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
interface FinishReservationInput {
|
|
290
|
+
scope: BudgetReservationScope;
|
|
255
291
|
reservationId?: string;
|
|
256
292
|
runId?: string;
|
|
257
293
|
userId?: string;
|
|
258
294
|
}
|
|
259
|
-
declare class
|
|
295
|
+
declare class PostgresBudgetReservationStore {
|
|
260
296
|
private db;
|
|
297
|
+
private readonly options;
|
|
298
|
+
constructor(db: PostgresJsDatabase, options?: {
|
|
299
|
+
eligibleLegacySources?: readonly string[];
|
|
300
|
+
});
|
|
301
|
+
static monthPeriodUtc(now?: Date): string;
|
|
302
|
+
sweepExpired(now?: Date): Promise<number>;
|
|
303
|
+
reserve(input: ReserveBudgetInput): Promise<ReserveBudgetResult>;
|
|
304
|
+
reserveAdmission(input: BudgetReservationAdmissionInput): Promise<BudgetReservationAdmission>;
|
|
305
|
+
reserveMany(inputs: readonly ReserveBudgetInput[]): Promise<ReserveBudgetResult[]>;
|
|
306
|
+
private reserveInTransaction;
|
|
307
|
+
settle(input: FinishReservationInput): Promise<void>;
|
|
308
|
+
release(input: FinishReservationInput): Promise<void>;
|
|
309
|
+
metadataForAdmission(admission: BudgetReservationAdmission): Record<string, string>;
|
|
310
|
+
releaseCreated(admission: BudgetReservationAdmission): Promise<void>;
|
|
311
|
+
finishAdmission(admission: BudgetReservationAdmission, status: Exclude<BudgetReservationStatus, 'active' | 'expired'>): Promise<void>;
|
|
312
|
+
private handlesForAdmission;
|
|
313
|
+
finishRun(input: {
|
|
314
|
+
userId: string;
|
|
315
|
+
runId: string;
|
|
316
|
+
}, status: Exclude<BudgetReservationStatus, 'active' | 'expired'>): Promise<void>;
|
|
317
|
+
finishMany(inputs: readonly FinishReservationInput[], status: Exclude<BudgetReservationStatus, 'active' | 'expired'>): Promise<void>;
|
|
318
|
+
private scopeBudgetWhere;
|
|
319
|
+
private reservationTotal;
|
|
320
|
+
private spend;
|
|
321
|
+
private spendTotals;
|
|
322
|
+
private finish;
|
|
323
|
+
private finishInTransaction;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
interface ReserveModelBudgetInput {
|
|
327
|
+
userId: string;
|
|
328
|
+
workspaceId?: string;
|
|
329
|
+
sessionId?: string;
|
|
330
|
+
runId: string;
|
|
331
|
+
provider: string;
|
|
332
|
+
model: string;
|
|
333
|
+
budgetMicros: number;
|
|
334
|
+
holdMicros: number;
|
|
335
|
+
ttlSeconds: number;
|
|
336
|
+
now?: Date;
|
|
337
|
+
}
|
|
338
|
+
type ReserveModelBudgetResult = Omit<ReserveBudgetResult, 'scope'>;
|
|
339
|
+
interface FinishModelBudgetReservationInput extends Omit<FinishReservationInput, 'scope'> {
|
|
340
|
+
scope?: 'model';
|
|
341
|
+
}
|
|
342
|
+
declare class PostgresModelBudgetStore {
|
|
343
|
+
private readonly store;
|
|
261
344
|
constructor(db: PostgresJsDatabase);
|
|
262
345
|
static monthPeriodUtc(now?: Date): string;
|
|
263
346
|
sweepExpired(now?: Date): Promise<number>;
|
|
264
347
|
reserve(input: ReserveModelBudgetInput): Promise<ReserveModelBudgetResult>;
|
|
265
348
|
settle(input: FinishModelBudgetReservationInput): Promise<void>;
|
|
266
349
|
release(input: FinishModelBudgetReservationInput): Promise<void>;
|
|
267
|
-
private finish;
|
|
268
350
|
}
|
|
269
351
|
|
|
270
|
-
export { type CreditLedgerEntry as C, type FinishReservationInput as F, type GrantOnceInput as G, InsufficientCreditError as I, type MeteringBalance as M,
|
|
352
|
+
export { type BudgetReservationAdmission as B, type CreditLedgerEntry as C, type FinishReservationInput as F, type GrantOnceInput as G, InsufficientCreditError as I, type MeteringBalance as M, PostgresBudgetReservationStore as P, type RecordUsageInput as R, UserBudgetExceededError as U, type BudgetReservationAdmissionInput as a, type BudgetReservationScope as b, type FinishReservationInput$1 as c, ModelBudgetExceededError as d, PostgresMeteringStore as e, PostgresModelBudgetStore as f, type RecordUsageResult as g, type ReservationFinalStatus as h, type ReserveBudgetInput as i, type ReserveBudgetResult as j, type ReserveInput as k, type ReserveResult as l, type RunMigrationsOptions as m, runMigrations as r };
|
|
@@ -154,13 +154,17 @@ interface CreditLedgerEntry {
|
|
|
154
154
|
/** Format SIGNED credit micros as a currency string with an explicit +/− sign.
|
|
155
155
|
* `currency` is the configured display currency (1 credit-unit = 1 major unit); defaults
|
|
156
156
|
* to EUR for callers without a configured purchase currency. */
|
|
157
|
-
|
|
157
|
+
interface CreditFormatOptions {
|
|
158
|
+
/** Show sub-cent/sub-rappen/sub-credit usage instead of rounding tiny amounts to 0.00. */
|
|
159
|
+
highPrecision?: boolean;
|
|
160
|
+
}
|
|
161
|
+
declare function formatSignedCreditMicros(micros: number, currency?: string, locale?: string, options?: CreditFormatOptions): string;
|
|
158
162
|
/** Format a minor-unit price (e.g. cents) in its currency for pack labels/buttons. */
|
|
159
163
|
declare function formatMinorPrice(priceMinor: number, currency: string, locale?: string): string;
|
|
160
164
|
/** Format credit micros as a currency string. 1 credit-unit = 1 major unit of the
|
|
161
165
|
* configured display `currency` (µ/1e6). Defaults to EUR for callers without a
|
|
162
166
|
* configured purchase currency (e.g. a consumption-only deployment). */
|
|
163
|
-
declare function formatCreditMicros(micros: number, currency?: string, locale?: string): string;
|
|
167
|
+
declare function formatCreditMicros(micros: number, currency?: string, locale?: string, options?: CreditFormatOptions): string;
|
|
164
168
|
/** True when the remaining balance is at or below the low-balance threshold. */
|
|
165
169
|
declare function isLowBalance(micros: number, thresholdMicros?: number): boolean;
|
|
166
170
|
/** Stable server error code for an out-of-credits rejection (mirrors the agent's
|
package/dist/app/front/index.js
CHANGED
|
@@ -916,19 +916,26 @@ function creditNetMicros(balance) {
|
|
|
916
916
|
const debt = Number.isFinite(balance.debtMicros) ? balance.debtMicros : 0;
|
|
917
917
|
return remaining - debt;
|
|
918
918
|
}
|
|
919
|
-
function
|
|
919
|
+
function creditFractionDigits(major, options) {
|
|
920
|
+
if (!options?.highPrecision) return {};
|
|
921
|
+
return {
|
|
922
|
+
minimumFractionDigits: 2,
|
|
923
|
+
maximumFractionDigits: Math.abs(major) > 0 && Math.abs(major) < 0.01 ? 6 : 2
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
function formatSignedCreditMicros(micros, currency = "EUR", locale, options) {
|
|
920
927
|
const major = (Number.isFinite(micros) ? micros : 0) / 1e6;
|
|
921
928
|
const sign = major > 0 ? "+" : major < 0 ? "\u2212" : "";
|
|
922
|
-
const abs = new Intl.NumberFormat(locale, { style: "currency", currency }).format(Math.abs(major));
|
|
929
|
+
const abs = new Intl.NumberFormat(locale, { style: "currency", currency, ...creditFractionDigits(major, options) }).format(Math.abs(major));
|
|
923
930
|
return `${sign}${abs}`;
|
|
924
931
|
}
|
|
925
932
|
function formatMinorPrice(priceMinor, currency, locale) {
|
|
926
933
|
const major = (Number.isFinite(priceMinor) ? priceMinor : 0) / 100;
|
|
927
934
|
return new Intl.NumberFormat(locale, { style: "currency", currency, maximumFractionDigits: major % 1 === 0 ? 0 : 2 }).format(major);
|
|
928
935
|
}
|
|
929
|
-
function formatCreditMicros(micros, currency = "EUR", locale) {
|
|
936
|
+
function formatCreditMicros(micros, currency = "EUR", locale, options) {
|
|
930
937
|
const major = (Number.isFinite(micros) ? Math.max(0, micros) : 0) / 1e6;
|
|
931
|
-
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(major);
|
|
938
|
+
return new Intl.NumberFormat(locale, { style: "currency", currency, ...creditFractionDigits(major, options) }).format(major);
|
|
932
939
|
}
|
|
933
940
|
function isLowBalance(micros, thresholdMicros = 5e5) {
|
|
934
941
|
return Number.isFinite(micros) && micros <= thresholdMicros;
|
|
@@ -1253,7 +1260,7 @@ function CreditsSettingsPanel({ apiBaseUrl = "", locale }) {
|
|
|
1253
1260
|
inDebt ? `\u2212${formatCreditMicros(balance.debtMicros, currency, locale)}` : formatCreditMicros(balance.remainingMicros, currency, locale),
|
|
1254
1261
|
updating && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-[11px] font-normal text-muted-foreground", "aria-live": "polite", children: "Updating\u2026" })
|
|
1255
1262
|
] }) }),
|
|
1256
|
-
/* @__PURE__ */ jsx7(DetailLine, { label: "Used so far", children: /* @__PURE__ */ jsx7("p", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCreditMicros(balance.usedMicros, currency, locale) }) })
|
|
1263
|
+
/* @__PURE__ */ jsx7(DetailLine, { label: "Used so far", children: /* @__PURE__ */ jsx7("p", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCreditMicros(balance.usedMicros, currency, locale, { highPrecision: true }) }) })
|
|
1257
1264
|
] }),
|
|
1258
1265
|
showBuy && packs.length > 0 && /* @__PURE__ */ jsxs6("fieldset", { className: "space-y-2", children: [
|
|
1259
1266
|
/* @__PURE__ */ jsx7("legend", { className: "text-[12px] font-medium text-foreground", children: "Buy credits" }),
|
|
@@ -1322,7 +1329,7 @@ function CreditsSettingsPanel({ apiBaseUrl = "", locale }) {
|
|
|
1322
1329
|
{
|
|
1323
1330
|
style: { fontVariantNumeric: "tabular-nums" },
|
|
1324
1331
|
className: e.amountMicros >= 0 ? "text-foreground" : "text-muted-foreground",
|
|
1325
|
-
children: formatSignedCreditMicros(e.amountMicros, currency, locale)
|
|
1332
|
+
children: formatSignedCreditMicros(e.amountMicros, currency, locale, e.kind === "usage" ? { highPrecision: true } : void 0)
|
|
1326
1333
|
}
|
|
1327
1334
|
)
|
|
1328
1335
|
] }, e.id)) })
|
|
@@ -2,10 +2,10 @@ import { RuntimeProvisioningContribution, RegisterAgentRoutesOptions } from '@ha
|
|
|
2
2
|
import { DirPluginEntry, CreateWorkspaceAgentServerOptions } from '@hachej/boring-workspace/app/server';
|
|
3
3
|
import { WorkspaceServerPlugin, WorkspaceBridgeOperationDefinition, WorkspaceBridgeHandler, WorkspaceBridgeRuntimeEnvOptions, WorkspaceBridgeCallRequest, WorkspaceBridgeCallResponse } from '@hachej/boring-workspace/server';
|
|
4
4
|
import { FastifyInstance } from 'fastify';
|
|
5
|
-
import { C as CoreConfig } from '../../types-
|
|
5
|
+
import { C as CoreConfig } from '../../types-DDnayJjI.js';
|
|
6
6
|
import { T as TelemetrySink } from '../../telemetry-DR18MeI0.js';
|
|
7
|
-
import { B as BetterAuthInstance, L as LoadConfigOptions } from '../../
|
|
8
|
-
import { D as Database, U as UserStore, W as WorkspaceStore } from '../../
|
|
7
|
+
import { B as BetterAuthInstance, L as LoadConfigOptions } from '../../loadConfig-C3iHuZNw.js';
|
|
8
|
+
import { D as Database, U as UserStore, W as WorkspaceStore } from '../../types-Dm11Zm56.js';
|
|
9
9
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
10
10
|
import 'better-auth';
|
|
11
11
|
import 'drizzle-orm/postgres-js';
|
package/dist/app/server/index.js
CHANGED
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
registerRoutes,
|
|
10
10
|
registerSettingsRoutes,
|
|
11
11
|
registerWorkspaceRoutes
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-DSENO5O4.js";
|
|
13
13
|
import {
|
|
14
14
|
PostgresUserStore,
|
|
15
15
|
PostgresWorkspaceStore,
|
|
16
16
|
createDatabase,
|
|
17
17
|
telemetryEvents
|
|
18
|
-
} from "../../chunk-
|
|
18
|
+
} from "../../chunk-JBEKMYST.js";
|
|
19
19
|
import {
|
|
20
20
|
noopTelemetry,
|
|
21
21
|
safeCapture
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
creditGrants,
|
|
3
3
|
creditPurchases,
|
|
4
4
|
idempotencyKeys,
|
|
5
|
+
modelBudgetReservations,
|
|
5
6
|
schema_exports,
|
|
6
7
|
usageLedger,
|
|
7
8
|
usageReservations,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
workspaceRuntimes,
|
|
13
14
|
workspaceSettings,
|
|
14
15
|
workspaces
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-JBEKMYST.js";
|
|
16
17
|
import {
|
|
17
18
|
noopTelemetry,
|
|
18
19
|
safeCapture
|
|
@@ -928,6 +929,7 @@ async function deleteUserCompletely(userId, deps) {
|
|
|
928
929
|
await tx.delete(verification_tokens).where(eq(verification_tokens.identifier, userRow.email));
|
|
929
930
|
}
|
|
930
931
|
await tx.delete(usageReservations).where(eq(usageReservations.userId, userId));
|
|
932
|
+
await tx.delete(modelBudgetReservations).where(eq(modelBudgetReservations.userId, userId));
|
|
931
933
|
await tx.delete(usageLedger).where(eq(usageLedger.userId, userId));
|
|
932
934
|
await tx.delete(creditGrants).where(eq(creditGrants.userId, userId));
|
|
933
935
|
await tx.delete(creditPurchases).where(eq(creditPurchases.userId, userId));
|