@wopr-network/platform-core 1.74.0 → 1.75.0

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.
Files changed (38) hide show
  1. package/dist/fleet/drizzle-bot-instance-repository.d.ts +3 -1
  2. package/dist/fleet/drizzle-bot-instance-repository.js +8 -2
  3. package/dist/fleet/instance.d.ts +8 -0
  4. package/dist/fleet/instance.js +20 -0
  5. package/dist/fleet/repository-types.d.ts +1 -1
  6. package/dist/gateway/proxy.js +10 -2
  7. package/dist/gateway/types.d.ts +3 -1
  8. package/dist/index.js +4 -0
  9. package/dist/monetization/credits/bot-billing.test.js +35 -1
  10. package/dist/monetization/credits/runtime-cron.d.ts +10 -2
  11. package/dist/monetization/credits/runtime-cron.js +19 -4
  12. package/dist/monetization/credits/runtime-cron.test.js +49 -34
  13. package/dist/monetization/credits/storage-tier-billing.test.js +9 -1
  14. package/dist/monetization/credits/storage-tier-cron.test.js +13 -7
  15. package/dist/server/__tests__/container.test.js +5 -1
  16. package/dist/server/container.d.ts +2 -0
  17. package/dist/server/container.js +6 -1
  18. package/dist/server/index.js +1 -1
  19. package/dist/server/mount-routes.d.ts +1 -1
  20. package/dist/server/mount-routes.js +35 -2
  21. package/dist/server/routes/__tests__/admin.test.js +3 -3
  22. package/package.json +1 -1
  23. package/src/fleet/drizzle-bot-instance-repository.ts +15 -2
  24. package/src/fleet/instance.ts +21 -0
  25. package/src/fleet/repository-types.ts +1 -1
  26. package/src/gateway/proxy.ts +9 -2
  27. package/src/gateway/types.ts +3 -1
  28. package/src/index.ts +4 -0
  29. package/src/monetization/credits/bot-billing.test.ts +35 -1
  30. package/src/monetization/credits/runtime-cron.test.ts +51 -38
  31. package/src/monetization/credits/runtime-cron.ts +21 -4
  32. package/src/monetization/credits/storage-tier-billing.test.ts +9 -1
  33. package/src/monetization/credits/storage-tier-cron.test.ts +13 -7
  34. package/src/server/__tests__/container.test.ts +5 -1
  35. package/src/server/container.ts +9 -1
  36. package/src/server/index.ts +1 -1
  37. package/src/server/mount-routes.ts +41 -3
  38. package/src/server/routes/__tests__/admin.test.ts +3 -3
@@ -51,6 +51,8 @@ export interface StripeServices {
51
51
 
52
52
  export interface GatewayServices {
53
53
  serviceKeyRepo: IServiceKeyRepository;
54
+ meter: import("../metering/emitter.js").MeterEmitter;
55
+ budgetChecker: import("../monetization/budget/budget-checker.js").IBudgetChecker;
54
56
  }
55
57
 
56
58
  export interface HotPoolServices {
@@ -239,8 +241,14 @@ export async function buildContainer(bootConfig: BootConfig): Promise<PlatformCo
239
241
  let gateway: GatewayServices | null = null;
240
242
  if (bootConfig.features.gateway) {
241
243
  const { DrizzleServiceKeyRepository } = await import("../gateway/service-key-repository.js");
244
+ const { MeterEmitter } = await import("../metering/emitter.js");
245
+ const { DrizzleMeterEventRepository } = await import("../metering/meter-event-repository.js");
246
+ const { DrizzleBudgetChecker } = await import("../monetization/budget/budget-checker.js");
247
+
242
248
  const serviceKeyRepo: IServiceKeyRepository = new DrizzleServiceKeyRepository(db as never);
243
- gateway = { serviceKeyRepo };
249
+ const meter = new MeterEmitter(new DrizzleMeterEventRepository(db as never), { flushIntervalMs: 5_000 });
250
+ const budgetChecker = new DrizzleBudgetChecker(db as never, { cacheTtlMs: 30_000 });
251
+ gateway = { serviceKeyRepo, meter, budgetChecker };
244
252
  }
245
253
 
246
254
  // 12. Build the container (hotPool bound after construction)
@@ -56,7 +56,7 @@ export async function bootPlatformServer(config: BootConfig): Promise<BootResult
56
56
  const container = await buildContainer(config);
57
57
  const app = new Hono();
58
58
 
59
- mountRoutes(
59
+ await mountRoutes(
60
60
  app,
61
61
  container,
62
62
  {
@@ -44,12 +44,12 @@ export interface MountConfig {
44
44
  * 6. Product-specific route plugins
45
45
  * 7. Tenant proxy middleware (catch-all — must be last)
46
46
  */
47
- export function mountRoutes(
47
+ export async function mountRoutes(
48
48
  app: Hono,
49
49
  container: PlatformContainer,
50
50
  config: MountConfig,
51
51
  plugins: RoutePlugin[] = [],
52
- ): void {
52
+ ): Promise<void> {
53
53
  // 1. CORS middleware
54
54
  const origins = deriveCorsOrigins(container.productConfig.product, container.productConfig.domains);
55
55
  app.use(
@@ -95,7 +95,45 @@ export function mountRoutes(
95
95
  );
96
96
  }
97
97
 
98
- // 6. Product-specific route plugins
98
+ // 6. Metered inference gateway (when gateway is enabled)
99
+ if (container.gateway) {
100
+ // Validate billing config exists in DB — fail hard, no silent defaults
101
+ const billingConfig = container.productConfig.billing;
102
+ const marginConfig = billingConfig?.marginConfig as { default?: number } | null;
103
+ if (!marginConfig?.default) {
104
+ throw new Error(
105
+ "Gateway enabled but product_billing_config.margin_config.default is not set. " +
106
+ "Seed the DB: INSERT INTO product_billing_config (product_id, margin_config) VALUES ('<id>', '{\"default\": 4.0}')",
107
+ );
108
+ }
109
+
110
+ // Live margin — reads from productConfig per-request (DB-cached with TTL)
111
+ const initialMargin = marginConfig.default;
112
+ const resolveMargin = (): number => {
113
+ const cfg = container.productConfig.billing?.marginConfig as { default?: number } | null;
114
+ return cfg?.default ?? initialMargin;
115
+ };
116
+
117
+ const gw = container.gateway;
118
+ const { mountGateway } = await import("../gateway/index.js");
119
+ mountGateway(app, {
120
+ meter: gw.meter,
121
+ budgetChecker: gw.budgetChecker,
122
+ creditLedger: container.creditLedger,
123
+ resolveMargin,
124
+ providers: {
125
+ openrouter: process.env.OPENROUTER_API_KEY
126
+ ? { apiKey: process.env.OPENROUTER_API_KEY, baseUrl: process.env.OPENROUTER_BASE_URL || undefined }
127
+ : undefined,
128
+ },
129
+ resolveServiceKey: async (key: string) => {
130
+ const tenant = await gw.serviceKeyRepo.resolve(key);
131
+ return tenant ?? null;
132
+ },
133
+ });
134
+ }
135
+
136
+ // 7. Product-specific route plugins
99
137
  for (const plugin of plugins) {
100
138
  app.route(plugin.path, plugin.handler(container));
101
139
  }
@@ -173,6 +173,7 @@ describe("createAdminRouter", () => {
173
173
  },
174
174
  serviceKeyRepo: {} as never,
175
175
  },
176
+ gateway: { serviceKeyRepo: {} as never, meter: {} as never, budgetChecker: {} as never },
176
177
  });
177
178
 
178
179
  const caller = makeCaller(container);
@@ -220,6 +221,7 @@ describe("createAdminRouter", () => {
220
221
  },
221
222
  serviceKeyRepo: {} as never,
222
223
  },
224
+ gateway: { serviceKeyRepo: {} as never, meter: {} as never, budgetChecker: {} as never },
223
225
  });
224
226
 
225
227
  const caller = makeCaller(container);
@@ -273,9 +275,7 @@ describe("createAdminRouter", () => {
273
275
 
274
276
  const container = createTestContainer({
275
277
  pool: mockPool as never,
276
- gateway: {
277
- serviceKeyRepo: {} as never,
278
- },
278
+ gateway: { serviceKeyRepo: {} as never, meter: {} as never, budgetChecker: {} as never },
279
279
  });
280
280
 
281
281
  const caller = makeCaller(container);