@opengeni/api-router 0.3.0 → 0.4.1

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/app.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  validateToolRefs,
15
15
  withDefaultEnabledCapabilityMcpTools,
16
16
  workflowIdForSession
17
- } from "./chunk-SVBM6RM6.js";
17
+ } from "./chunk-2JL5OXRE.js";
18
18
  export {
19
19
  allowedCorsOrigin,
20
20
  createApp,
@@ -6,6 +6,7 @@ import {
6
6
  } from "@opengeni/config";
7
7
  import { ClientConfig } from "@opengeni/contracts";
8
8
  import { createDocumentServices, indexDocumentNow } from "@opengeni/documents";
9
+ import { dbSql } from "@opengeni/db";
9
10
  import { createObservability } from "@opengeni/observability";
10
11
  import { createObjectStorage } from "@opengeni/storage";
11
12
  import { WebStandardStreamableHTTPServerTransport as WebStandardStreamableHTTPServerTransport2 } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
@@ -929,6 +930,11 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, json) {
929
930
  description: "Spawn a new agent session (a worker) with an initial message and optional goal, resources (e.g. repositories from github_repositories_list), tools, and workspace environment attachment. Environment attachment happens at creation only \u2014 it cannot be added to a running session \u2014 and requires the environments:use permission. When targetSandboxId names a machine, workingDir sets the working directory (cwd) the spawned session runs under on that machine.",
930
931
  inputSchema: {
931
932
  initialMessage: z4.string().min(1),
933
+ // Per-session agent persona/system instructions for the spawned worker
934
+ // (a per-agent-type prompt). Delivered system-level, composed AFTER the
935
+ // workspace persona; never shown in the worker's timeline. Trimmed,
936
+ // non-empty, max 32768 chars (re-validated by the contracts schema).
937
+ instructions: z4.string().min(1).max(32768).optional(),
932
938
  goal: z4.unknown().optional(),
933
939
  resources: z4.array(z4.unknown()).optional(),
934
940
  tools: z4.array(z4.unknown()).optional(),
@@ -1362,7 +1368,7 @@ function isAuthExempt(c, settings) {
1362
1368
  if (installExactPaths.has(path) || isInstallRedirectPath(path)) {
1363
1369
  return true;
1364
1370
  }
1365
- if (settings.authAllowHealth && path === "/healthz") {
1371
+ if (settings.authAllowHealth && (path === "/healthz" || path === "/readyz")) {
1366
1372
  return true;
1367
1373
  }
1368
1374
  if (settings.authAllowMetrics && path === "/metrics") {
@@ -3389,6 +3395,7 @@ async function handleCheckoutSessionCompleted(deps, event) {
3389
3395
  stripeCreditAmountUsd: credit.amountUsd
3390
3396
  }
3391
3397
  });
3398
+ recordCreditMicrosMetric(deps, "topup", credit.amountMicros);
3392
3399
  }
3393
3400
  async function mirrorPaymentIntentCustomer(deps, event) {
3394
3401
  const intent = event.data.object;
@@ -3421,18 +3428,23 @@ async function applyRefundDebit(deps, stripe, refund) {
3421
3428
  if (!accountId) {
3422
3429
  return;
3423
3430
  }
3431
+ const idempotencyKey = `stripe:refund:${refund.id}`;
3432
+ if (await hasCreditLedgerEntry(deps.db, accountId, idempotencyKey)) {
3433
+ return;
3434
+ }
3424
3435
  await applyCreditLedgerEntry(deps.db, {
3425
3436
  accountId,
3426
3437
  type: "credit_refund",
3427
3438
  amountMicros: -centsToMicros(refund.amount),
3428
3439
  sourceType: "stripe_refund",
3429
3440
  sourceId: refund.id,
3430
- idempotencyKey: `stripe:refund:${refund.id}`,
3441
+ idempotencyKey,
3431
3442
  metadata: {
3432
3443
  stripeRefundId: refund.id,
3433
3444
  stripePaymentIntentId: paymentIntentId(refund.payment_intent)
3434
3445
  }
3435
3446
  });
3447
+ recordCreditMicrosMetric(deps, "refund", centsToMicros(refund.amount));
3436
3448
  }
3437
3449
  async function holdDisputedCredits(deps, stripe, event) {
3438
3450
  const dispute = event.data.object;
@@ -3487,6 +3499,17 @@ async function mirrorCustomer(deps, event, customer) {
3487
3499
  email: typeof customer.email === "string" ? customer.email : null
3488
3500
  });
3489
3501
  }
3502
+ function recordCreditMicrosMetric(deps, kind, amountMicros) {
3503
+ if (amountMicros <= 0) {
3504
+ return;
3505
+ }
3506
+ deps.observability?.incrementCounter({
3507
+ name: "opengeni_credit_micros_total",
3508
+ help: "Total credit micros recorded by kind.",
3509
+ labels: { kind },
3510
+ amount: amountMicros
3511
+ });
3512
+ }
3490
3513
  async function metadataForRefund(stripe, refund) {
3491
3514
  if (Object.keys(refund.metadata ?? {}).length > 0) {
3492
3515
  return refund.metadata;
@@ -6189,13 +6212,19 @@ function createApp(deps) {
6189
6212
  service: deps.settings.serviceName,
6190
6213
  environment: deps.settings.environment,
6191
6214
  deploymentRevision: deps.settings.deploymentRevision,
6215
+ ...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
6192
6216
  ok: true
6193
6217
  }));
6194
- app.get("/metrics", (c) => c.text(observability.prometheusMetrics(), 200, {
6218
+ app.get("/readyz", async (c) => {
6219
+ const result = await runReadinessChecks(readinessChecks(deps), 2e3);
6220
+ return c.json(result, result.ok ? 200 : 503);
6221
+ });
6222
+ app.get("/metrics", async (c) => c.text(await observability.prometheusMetrics(), 200, {
6195
6223
  "content-type": "text/plain; version=0.0.4; charset=utf-8"
6196
6224
  }));
6197
6225
  app.get("/v1/config/client", (c) => c.json(ClientConfig.parse({
6198
6226
  deploymentRevision: deps.settings.deploymentRevision,
6227
+ ...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
6199
6228
  defaultModel: deps.settings.openaiModel,
6200
6229
  allowedModels: configuredAllowedModels(deps.settings),
6201
6230
  // Provider-grouped model list for the picker. configuredModels() carries the
@@ -6278,8 +6307,56 @@ function httpStatusForError(error) {
6278
6307
  }
6279
6308
  return 500;
6280
6309
  }
6310
+ function readinessChecks(deps) {
6311
+ return {
6312
+ db: deps.readinessChecks?.db ?? (async () => {
6313
+ await deps.db.execute(dbSql`select 1`);
6314
+ }),
6315
+ nats: deps.readinessChecks?.nats ?? (() => {
6316
+ if (deps.bus.isConnected && !deps.bus.isConnected()) {
6317
+ throw new Error("NATS is not connected");
6318
+ }
6319
+ }),
6320
+ temporal: deps.readinessChecks?.temporal ?? deps.workflowClient.check ?? (() => {
6321
+ throw new Error("Temporal readiness check unavailable");
6322
+ })
6323
+ };
6324
+ }
6325
+ async function runReadinessChecks(checks, timeoutMs) {
6326
+ const entries = await Promise.all(
6327
+ Object.entries(checks).map(async ([name, check]) => {
6328
+ try {
6329
+ await withTimeout(Promise.resolve().then(check), timeoutMs);
6330
+ return [name, { ok: true }];
6331
+ } catch (error) {
6332
+ return [name, { ok: false, error: error instanceof Error ? error.message : String(error) }];
6333
+ }
6334
+ })
6335
+ );
6336
+ const result = Object.fromEntries(entries);
6337
+ return {
6338
+ ok: Object.values(result).every((check) => check.ok),
6339
+ checks: result
6340
+ };
6341
+ }
6342
+ async function withTimeout(promise, timeoutMs) {
6343
+ let timer;
6344
+ try {
6345
+ return await Promise.race([
6346
+ promise,
6347
+ new Promise((_, reject) => {
6348
+ timer = setTimeout(() => reject(new Error(`readiness check timed out after ${timeoutMs}ms`)), timeoutMs);
6349
+ })
6350
+ ]);
6351
+ } finally {
6352
+ if (timer) {
6353
+ clearTimeout(timer);
6354
+ }
6355
+ }
6356
+ }
6281
6357
  var routeLabelPatterns = [
6282
6358
  { pattern: /^\/healthz$/, label: "/healthz" },
6359
+ { pattern: /^\/readyz$/, label: "/readyz" },
6283
6360
  { pattern: /^\/v1\/workspaces\/[^/]+\/codex\/connect\/start$/, label: "/v1/workspaces/:workspaceId/codex/connect/start" },
6284
6361
  { pattern: /^\/v1\/workspaces\/[^/]+\/codex\/connect\/poll$/, label: "/v1/workspaces/:workspaceId/codex/connect/poll" },
6285
6362
  { pattern: /^\/v1\/workspaces\/[^/]+\/codex\/status$/, label: "/v1/workspaces/:workspaceId/codex/status" },
@@ -6379,4 +6456,4 @@ export {
6379
6456
  withDefaultEnabledCapabilityMcpTools,
6380
6457
  workflowIdForSession3 as workflowIdForSession
6381
6458
  };
6382
- //# sourceMappingURL=chunk-SVBM6RM6.js.map
6459
+ //# sourceMappingURL=chunk-2JL5OXRE.js.map