anygate 0.5.3 → 0.5.4

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getTemplateById
4
- } from "./chunk-YYSUTRMV.js";
4
+ } from "./chunk-VKROC37K.js";
5
5
 
6
6
  // src/core/constants.ts
7
7
  import { homedir } from "os";
@@ -10,7 +10,7 @@ import { join } from "path";
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "anygate",
13
- version: "0.5.3",
13
+ version: "0.5.4",
14
14
  publishConfig: {
15
15
  access: "public"
16
16
  },
@@ -47,13 +47,15 @@ var package_default = {
47
47
  node: ">=18"
48
48
  },
49
49
  scripts: {
50
- build: "tsup && node scripts/copy-ui-assets.mjs",
50
+ build: "tsup && npm run ui:build && node scripts/copy-ui-assets.mjs",
51
51
  dev: "tsup --watch",
52
52
  test: "vitest run",
53
53
  "test:watch": "vitest",
54
54
  typecheck: "tsc --noEmit",
55
55
  "refresh:models-dev": "node scripts/refresh-models-dev-cache.mjs",
56
- prepublishOnly: `node -e "if (require('./package.json').version !== require('./package-lock.json').version) { console.error('Error: package.json and package-lock.json versions are out of sync! Run npm install to sync.'); process.exit(1); }" && npm run build`
56
+ prepublishOnly: `node -e "if (require('./package.json').version !== require('./package-lock.json').version) { console.error('Error: package.json and package-lock.json versions are out of sync! Run npm install to sync.'); process.exit(1); }" && npm run build`,
57
+ "ui:dev": "npm --prefix ui run dev",
58
+ "ui:build": "npm --prefix ui run build"
57
59
  },
58
60
  dependencies: {
59
61
  "@ai-sdk/alibaba": "^1.0.26",
@@ -3903,7 +3905,7 @@ function printTraceLog(debugLogPath) {
3903
3905
 
3904
3906
  // src/gateway/anthropic-proxy.ts
3905
3907
  import { createServer } from "http";
3906
- import { appendFileSync as appendFileSync2, openSync as openSync2, writeSync as writeSync2, closeSync as closeSync2 } from "fs";
3908
+ import { appendFileSync as appendFileSync3, openSync as openSync3, writeSync as writeSync3, closeSync as closeSync3 } from "fs";
3907
3909
 
3908
3910
  // src/core/http-utils.ts
3909
3911
  import * as zlib from "zlib";
@@ -5403,7 +5405,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5403
5405
  log7?.(() => `sdk stream error (${errorType}): ${errMsg}`);
5404
5406
  closeOpen();
5405
5407
  emit("error", { type: "error", error: { type: errorType, message: errMsg } });
5406
- return;
5408
+ return { inputTokens: 0, outputTokens: 0 };
5407
5409
  }
5408
5410
  default:
5409
5411
  break;
@@ -5413,6 +5415,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5413
5415
  ensureStart();
5414
5416
  emit("message_delta", { type: "message_delta", delta: { stop_reason: finishReason, stop_sequence: null }, usage });
5415
5417
  emit("message_stop", { type: "message_stop" });
5418
+ return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
5416
5419
  }
5417
5420
  async function streamAnthropicResponse(model, params, modelId, write, log7) {
5418
5421
  const result = streamText({ model, ...params, onError: () => {
@@ -5427,7 +5430,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log7) {
5427
5430
  });
5428
5431
  Promise.resolve(result.usage).catch(() => {
5429
5432
  });
5430
- await writeAnthropicStream(result.fullStream, modelId, write, log7);
5433
+ return await writeAnthropicStream(result.fullStream, modelId, write, log7);
5431
5434
  }
5432
5435
  async function generateAnthropicResponse(model, params, modelId, options) {
5433
5436
  let text4;
@@ -5459,7 +5462,196 @@ async function generateAnthropicResponse(model, params, modelId, options) {
5459
5462
  }))
5460
5463
  ],
5461
5464
  stop_reason: finishReason === "tool-calls" ? "tool_use" : "end_turn",
5462
- usage: { input_tokens: usage?.inputTokens ?? 0, output_tokens: usage?.outputTokens ?? 0 }
5465
+ usage: { input_tokens: usage?.inputTokens ?? 0, output_tokens: usage?.outputTokens ?? 0 },
5466
+ // Internal: surfaced to call sites so they can log analytics without re-parsing.
5467
+ _usage: { inputTokens: usage?.inputTokens ?? 0, outputTokens: usage?.outputTokens ?? 0 }
5468
+ };
5469
+ }
5470
+
5471
+ // src/core/analytics-log.ts
5472
+ import { appendFileSync as appendFileSync2, openSync as openSync2, writeSync as writeSync2, closeSync as closeSync2, readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
5473
+ import { join as join9 } from "path";
5474
+ var ANALYTICS_FILE = "analytics.jsonl";
5475
+ function analyticsPath() {
5476
+ return join9(getAppHome(), ANALYTICS_FILE);
5477
+ }
5478
+ function appendAtomic(path, line) {
5479
+ try {
5480
+ const fd = openSync2(path, "a", 384);
5481
+ try {
5482
+ writeSync2(fd, line + "\n");
5483
+ } finally {
5484
+ closeSync2(fd);
5485
+ }
5486
+ } catch {
5487
+ try {
5488
+ appendFileSync2(path, line + "\n");
5489
+ } catch {
5490
+ }
5491
+ }
5492
+ }
5493
+ function recordUsage(event) {
5494
+ if (!event?.ts || typeof event.modelId !== "string" || typeof event.app !== "string") return;
5495
+ const inputTokens = Math.max(0, Math.floor(event.inputTokens || 0));
5496
+ const outputTokens = Math.max(0, Math.floor(event.outputTokens || 0));
5497
+ if (inputTokens === 0 && outputTokens === 0) return;
5498
+ const clean = {
5499
+ ts: event.ts,
5500
+ modelId: event.modelId,
5501
+ app: event.app,
5502
+ inputTokens,
5503
+ outputTokens
5504
+ };
5505
+ if (event.npm) clean.npm = event.npm;
5506
+ if (event.providerId) clean.providerId = event.providerId;
5507
+ appendAtomic(analyticsPath(), JSON.stringify(clean));
5508
+ }
5509
+ function readAnalyticsLog() {
5510
+ const path = analyticsPath();
5511
+ if (!existsSync9(path)) return [];
5512
+ let raw;
5513
+ try {
5514
+ raw = readFileSync9(path, "utf8");
5515
+ } catch {
5516
+ return [];
5517
+ }
5518
+ const out = [];
5519
+ for (const line of raw.split("\n")) {
5520
+ const t = line.trim();
5521
+ if (!t) continue;
5522
+ try {
5523
+ const e = JSON.parse(t);
5524
+ if (e && typeof e.ts === "string" && typeof e.modelId === "string") out.push(e);
5525
+ } catch {
5526
+ }
5527
+ }
5528
+ return out;
5529
+ }
5530
+ var MODEL_PALETTE = [
5531
+ "oklch(75% 0.16 65)",
5532
+ // amber (accent)
5533
+ "oklch(70% 0.15 200)",
5534
+ // sky blue
5535
+ "oklch(68% 0.16 150)",
5536
+ // teal/green
5537
+ "oklch(72% 0.17 300)",
5538
+ // violet
5539
+ "oklch(70% 0.18 20)",
5540
+ // rose/red
5541
+ "oklch(74% 0.15 95)"
5542
+ // gold/yellow
5543
+ ];
5544
+ function rangeDays(range) {
5545
+ if (range === "7d") return 7;
5546
+ if (range === "30d") return 30;
5547
+ return 365;
5548
+ }
5549
+ function dayKey(iso) {
5550
+ return iso.slice(0, 10);
5551
+ }
5552
+ function aggregateAnalytics(range) {
5553
+ const all = readAnalyticsLog();
5554
+ const today = /* @__PURE__ */ new Date();
5555
+ today.setUTCHours(0, 0, 0, 0);
5556
+ const cutoff = new Date(today);
5557
+ cutoff.setUTCDate(today.getUTCDate() - (rangeDays(range) - 1));
5558
+ const cutoffIso = cutoff.toISOString();
5559
+ const endIso = new Date(today.getTime() + 864e5).toISOString();
5560
+ const events = all.filter((e) => e.ts >= cutoffIso && e.ts <= endIso);
5561
+ const eventsByDay = /* @__PURE__ */ new Map();
5562
+ const tokensByDay = /* @__PURE__ */ new Map();
5563
+ const hourCounts = new Array(24).fill(0);
5564
+ const activeDaySet = /* @__PURE__ */ new Set();
5565
+ const modelMap = /* @__PURE__ */ new Map();
5566
+ let totalTokens = 0;
5567
+ let messages = 0;
5568
+ for (const e of events) {
5569
+ const day = dayKey(e.ts);
5570
+ const tok = e.inputTokens + e.outputTokens;
5571
+ totalTokens += tok;
5572
+ messages += 1;
5573
+ eventsByDay.set(day, (eventsByDay.get(day) ?? 0) + 1);
5574
+ tokensByDay.set(day, (tokensByDay.get(day) ?? 0) + tok);
5575
+ activeDaySet.add(day);
5576
+ const hour = Number(e.ts.slice(11, 13));
5577
+ if (Number.isFinite(hour) && hour >= 0 && hour < 24) hourCounts[hour] += 1;
5578
+ const provider = e.providerId ?? e.npm?.replace(/^@/, "").replace(/\//g, "-") ?? "unknown";
5579
+ const key = `${provider}|${e.modelId}`;
5580
+ const m = modelMap.get(key) ?? { provider, model: e.modelId, app: e.app, inputTokens: 0, outputTokens: 0 };
5581
+ m.inputTokens += e.inputTokens;
5582
+ m.outputTokens += e.outputTokens;
5583
+ modelMap.set(key, m);
5584
+ }
5585
+ const busiestDay = Math.max(1, ...[...eventsByDay.values()]);
5586
+ const heatmap = [];
5587
+ for (let i = rangeDays(range) - 1; i >= 0; i--) {
5588
+ const d = new Date(today);
5589
+ d.setUTCDate(today.getUTCDate() - i);
5590
+ const date = d.toISOString().slice(0, 10);
5591
+ const count = eventsByDay.get(date) ?? 0;
5592
+ const intensity = Math.min(4, Math.round(count / busiestDay * 4)) || 0;
5593
+ heatmap.push({ date, count, intensity });
5594
+ }
5595
+ const dailyTokens = [];
5596
+ for (let i = rangeDays(range) - 1; i >= 0; i--) {
5597
+ const d = new Date(today);
5598
+ d.setUTCDate(today.getUTCDate() - i);
5599
+ const date = d.toISOString().slice(0, 10);
5600
+ dailyTokens.push({ date, tokens: tokensByDay.get(date) ?? 0 });
5601
+ }
5602
+ let currentStreak = 0;
5603
+ for (let i = 0; i < rangeDays(range); i++) {
5604
+ const d = new Date(today);
5605
+ d.setUTCDate(today.getUTCDate() - i);
5606
+ const date = d.toISOString().slice(0, 10);
5607
+ if (eventsByDay.has(date)) currentStreak++;
5608
+ else break;
5609
+ }
5610
+ let longestStreak = 0;
5611
+ let run3 = 0;
5612
+ for (const day of heatmap) {
5613
+ if (day.count > 0) {
5614
+ run3++;
5615
+ longestStreak = Math.max(longestStreak, run3);
5616
+ } else run3 = 0;
5617
+ }
5618
+ let peakHour = 0;
5619
+ let peakCount = -1;
5620
+ for (let h = 0; h < 24; h++) {
5621
+ if (hourCounts[h] > peakCount) {
5622
+ peakCount = hourCounts[h];
5623
+ peakHour = h;
5624
+ }
5625
+ }
5626
+ const models = [...modelMap.entries()].map(([, m], idx) => {
5627
+ const share = totalTokens > 0 ? (m.inputTokens + m.outputTokens) / totalTokens : 0;
5628
+ return {
5629
+ provider: m.provider,
5630
+ model: m.model,
5631
+ tier: "",
5632
+ // source tier isn't tracked in the log; UI shows it from catalog elsewhere
5633
+ app: m.app,
5634
+ inputTokens: m.inputTokens,
5635
+ outputTokens: m.outputTokens,
5636
+ share,
5637
+ color: MODEL_PALETTE[idx % MODEL_PALETTE.length]
5638
+ };
5639
+ });
5640
+ models.sort((a, b) => b.share - a.share);
5641
+ const favoriteModel = models.length > 0 ? `${models[0].provider}: ${models[0].model}` : "";
5642
+ return {
5643
+ range,
5644
+ sessions: activeDaySet.size,
5645
+ messages,
5646
+ totalTokens,
5647
+ activeDays: activeDaySet.size,
5648
+ currentStreakDays: currentStreak,
5649
+ longestStreakDays: longestStreak,
5650
+ peakHour,
5651
+ favoriteModel,
5652
+ heatmap,
5653
+ dailyTokens,
5654
+ models
5463
5655
  };
5464
5656
  }
5465
5657
 
@@ -5467,16 +5659,16 @@ async function generateAnthropicResponse(model, params, modelId, options) {
5467
5659
  function appendSecureLog(logPath, line) {
5468
5660
  const redacted = redactTraceLine(line);
5469
5661
  try {
5470
- const fd = openSync2(logPath, "a", 384);
5662
+ const fd = openSync3(logPath, "a", 384);
5471
5663
  try {
5472
- writeSync2(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5664
+ writeSync3(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5473
5665
  `);
5474
5666
  } finally {
5475
- closeSync2(fd);
5667
+ closeSync3(fd);
5476
5668
  }
5477
5669
  } catch {
5478
5670
  try {
5479
- appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5671
+ appendFileSync3(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5480
5672
  `);
5481
5673
  } catch {
5482
5674
  }
@@ -5665,7 +5857,16 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5665
5857
  "Cache-Control": "no-cache",
5666
5858
  "Connection": "keep-alive"
5667
5859
  });
5668
- await streamAnthropicResponse(model, params, originalModel, (c) => res.write(c), plog);
5860
+ const usage = await streamAnthropicResponse(model, params, originalModel, (c) => res.write(c), plog);
5861
+ recordUsage({
5862
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
5863
+ modelId: route.realModelId,
5864
+ npm: route.npm,
5865
+ providerId: route.providerId,
5866
+ app: route.app ?? "gateway",
5867
+ inputTokens: usage.inputTokens,
5868
+ outputTokens: usage.outputTokens
5869
+ });
5669
5870
  res.end();
5670
5871
  } else {
5671
5872
  const anthropicResponse = await generateAnthropicResponse(
@@ -5674,12 +5875,22 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5674
5875
  originalModel,
5675
5876
  { forceStream: openAiOAuth }
5676
5877
  );
5878
+ const u = anthropicResponse._usage;
5879
+ recordUsage({
5880
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
5881
+ modelId: route.realModelId,
5882
+ npm: route.npm,
5883
+ providerId: route.providerId,
5884
+ app: route.app ?? "gateway",
5885
+ inputTokens: u?.inputTokens ?? 0,
5886
+ outputTokens: u?.outputTokens ?? 0
5887
+ });
5677
5888
  sendJson(res, 200, anthropicResponse);
5678
5889
  }
5679
5890
  } catch (err) {
5680
5891
  const message = err instanceof Error ? err.message : String(err);
5681
5892
  const body = err && typeof err === "object" && "responseBody" in err ? err.responseBody : void 0;
5682
- plog(() => `sdk error: ${message}${body ? ` \u2014 body: ${body}` : ""}`);
5893
+ plog(() => `sdk error: ${message}${body ? ` \u0393\xC7\xF6 body: ${body}` : ""}`);
5683
5894
  if (!res.headersSent) {
5684
5895
  const status = upstreamHttpStatus(err);
5685
5896
  anthropicError(res, status === 500 ? 502 : status, message);
@@ -5697,7 +5908,7 @@ data: ${JSON.stringify({ type: "error", error: { type: errorType, message } })}
5697
5908
  if (route.modelFormat === "cloud-code") {
5698
5909
  const projectId = route.providerData?.projectId ?? "";
5699
5910
  if (!projectId) {
5700
- anthropicError(res, 500, "Antigravity provider missing projectId \u2014 re-authenticate with anygate providers auth antigravity");
5911
+ anthropicError(res, 500, "Antigravity provider missing projectId \u0393\xC7\xF6 re-authenticate with anygate providers auth antigravity");
5701
5912
  return;
5702
5913
  }
5703
5914
  const envelope = anthropicToCloudCode(anthropicBody, route.realModelId, projectId);
@@ -5708,7 +5919,7 @@ data: ${JSON.stringify({ type: "error", error: { type: errorType, message } })}
5708
5919
  const cloudMaxOutput = envelope.request.generationConfig?.maxOutputTokens;
5709
5920
  const baseUrl = upstreamUrl.replace(/\/+$/, "");
5710
5921
  const cloudCodeUrl = `${baseUrl}/v1internal:streamGenerateContent?alt=sse`;
5711
- plog(() => `cloud-code: model=${route.realModelId}, project=${projectId.slice(0, 8)}\u2026 msgs=${cloudContents.length} toolCalls=${cloudToolCalls} toolResults=${cloudToolResults} tools=${cloudTools} maxOutput=${cloudMaxOutput ?? "unset"} stream=${clientWantsStream}`);
5922
+ plog(() => `cloud-code: model=${route.realModelId}, project=${projectId.slice(0, 8)}\u0393\xC7\xAA msgs=${cloudContents.length} toolCalls=${cloudToolCalls} toolResults=${cloudToolResults} tools=${cloudTools} maxOutput=${cloudMaxOutput ?? "unset"} stream=${clientWantsStream}`);
5712
5923
  const fetchCloudCode = (token) => fetch(cloudCodeUrl, {
5713
5924
  method: "POST",
5714
5925
  headers: {
@@ -5789,7 +6000,8 @@ function startProxy(completionsUrl, modelId, debug = false, contextWindow, sdk,
5789
6000
  reasoning: sdk?.reasoning,
5790
6001
  interleavedReasoningField: sdk?.interleavedReasoningField,
5791
6002
  useResponsesLite: sdk?.useResponsesLite,
5792
- preferWebSockets: sdk?.preferWebSockets
6003
+ preferWebSockets: sdk?.preferWebSockets,
6004
+ app: sdk?.app
5793
6005
  }], clientModelId, debug);
5794
6006
  }
5795
6007
 
@@ -6959,17 +7171,24 @@ async function generateOpenAiResponse(model, params, responseModelId) {
6959
7171
  function: { name: tc.toolName, arguments: JSON.stringify(tc.args) }
6960
7172
  }));
6961
7173
  }
7174
+ const usage = {
7175
+ inputTokens: result.usage?.promptTokens ?? 0,
7176
+ outputTokens: result.usage?.completionTokens ?? 0
7177
+ };
6962
7178
  return {
6963
- id: `chatcmpl-${Date.now()}`,
6964
- object: "chat.completion",
6965
- created: Math.floor(Date.now() / 1e3),
6966
- model: responseModelId,
6967
- choices: [{ index: 0, message, finish_reason: result.finishReason || "stop" }],
6968
- usage: {
6969
- prompt_tokens: result.usage?.promptTokens ?? 0,
6970
- completion_tokens: result.usage?.completionTokens ?? 0,
6971
- total_tokens: result.usage?.totalTokens ?? 0
6972
- }
7179
+ response: {
7180
+ id: `chatcmpl-${Date.now()}`,
7181
+ object: "chat.completion",
7182
+ created: Math.floor(Date.now() / 1e3),
7183
+ model: responseModelId,
7184
+ choices: [{ index: 0, message, finish_reason: result.finishReason || "stop" }],
7185
+ usage: {
7186
+ prompt_tokens: usage.inputTokens,
7187
+ completion_tokens: usage.outputTokens,
7188
+ total_tokens: usage.inputTokens + usage.outputTokens
7189
+ }
7190
+ },
7191
+ usage
6973
7192
  };
6974
7193
  }
6975
7194
  async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
@@ -6980,6 +7199,7 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
6980
7199
  created: Math.floor(Date.now() / 1e3),
6981
7200
  model: responseModelId
6982
7201
  };
7202
+ let usage = { inputTokens: 0, outputTokens: 0 };
6983
7203
  const send = (delta, finish_reason = null) => onChunk(`data: ${JSON.stringify({ ...baseData, choices: [{ index: 0, delta, finish_reason }] })}
6984
7204
 
6985
7205
  `);
@@ -6998,11 +7218,15 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
6998
7218
  send({ tool_calls: [{ index: 0, function: { arguments: p8.delta ?? p8.text ?? p8.argsTextDelta ?? "" } }] });
6999
7219
  break;
7000
7220
  case "finish":
7221
+ if (p8.usage) {
7222
+ usage = { inputTokens: p8.usage.promptTokens ?? 0, outputTokens: p8.usage.completionTokens ?? 0 };
7223
+ }
7001
7224
  send({}, p8.finishReason || "stop");
7002
7225
  break;
7003
7226
  }
7004
7227
  }
7005
7228
  onChunk("data: [DONE]\n\n");
7229
+ return usage;
7006
7230
  }
7007
7231
 
7008
7232
  // src/registry/refresh-credentials.ts
@@ -7305,10 +7529,28 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
7305
7529
  "Cache-Control": "no-cache",
7306
7530
  "Connection": "keep-alive"
7307
7531
  });
7308
- await streamAnthropicResponse(languageModel, params, responseModelId, (chunk) => res.write(chunk));
7532
+ const usage = await streamAnthropicResponse(languageModel, params, responseModelId, (chunk) => res.write(chunk));
7533
+ recordUsage({
7534
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
7535
+ modelId: responseModelId,
7536
+ npm: model.npm,
7537
+ providerId: model.providerId,
7538
+ app: "gateway",
7539
+ inputTokens: usage.inputTokens,
7540
+ outputTokens: usage.outputTokens
7541
+ });
7309
7542
  res.end();
7310
7543
  } else {
7311
7544
  const anthropicResponse = await generateAnthropicResponse(languageModel, params, responseModelId);
7545
+ recordUsage({
7546
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
7547
+ modelId: responseModelId,
7548
+ npm: model.npm,
7549
+ providerId: model.providerId,
7550
+ app: "gateway",
7551
+ inputTokens: anthropicResponse._usage?.inputTokens ?? 0,
7552
+ outputTokens: anthropicResponse._usage?.outputTokens ?? 0
7553
+ });
7312
7554
  sendJson(res, 200, anthropicResponse);
7313
7555
  }
7314
7556
  } catch (err) {
@@ -7360,10 +7602,28 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
7360
7602
  "Cache-Control": "no-cache",
7361
7603
  "Connection": "keep-alive"
7362
7604
  });
7363
- await streamOpenAiResponse(languageModel, params, responseModelId, (chunk) => res.write(chunk));
7605
+ const usage = await streamOpenAiResponse(languageModel, params, responseModelId, (chunk) => res.write(chunk));
7606
+ recordUsage({
7607
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
7608
+ modelId: responseModelId,
7609
+ npm: model.npm ?? (model.modelFormat === "anthropic" ? "@ai-sdk/anthropic" : void 0),
7610
+ providerId: model.providerId,
7611
+ app: "gateway",
7612
+ inputTokens: usage.inputTokens,
7613
+ outputTokens: usage.outputTokens
7614
+ });
7364
7615
  res.end();
7365
7616
  } else {
7366
- const response = await generateOpenAiResponse(languageModel, params, responseModelId);
7617
+ const { response, usage } = await generateOpenAiResponse(languageModel, params, responseModelId);
7618
+ recordUsage({
7619
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
7620
+ modelId: responseModelId,
7621
+ npm: model.npm ?? (model.modelFormat === "anthropic" ? "@ai-sdk/anthropic" : void 0),
7622
+ providerId: model.providerId,
7623
+ app: "gateway",
7624
+ inputTokens: usage.inputTokens,
7625
+ outputTokens: usage.outputTokens
7626
+ });
7367
7627
  sendJson(res, 200, response);
7368
7628
  }
7369
7629
  } catch (err) {
@@ -7558,9 +7818,9 @@ async function selectServerProviders(available, initial) {
7558
7818
  }
7559
7819
 
7560
7820
  // src/gateway/vertex.ts
7561
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
7821
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
7562
7822
  import { homedir as homedir6 } from "os";
7563
- import { join as join9 } from "path";
7823
+ import { join as join10 } from "path";
7564
7824
  var DEFAULT_VERTEX_MODELS = [
7565
7825
  { id: "claude-sonnet-4-6", display_name: "Claude Sonnet 4.6" },
7566
7826
  { id: "claude-opus-4-6", display_name: "Claude Opus 4.6" },
@@ -7584,18 +7844,18 @@ function resolveVertexLocation(env = process.env) {
7584
7844
  return location.trim() || "global";
7585
7845
  }
7586
7846
  function defaultAdcCredentialsPath(home = homedir6()) {
7587
- return join9(home, ".config", "gcloud", "application_default_credentials.json");
7847
+ return join10(home, ".config", "gcloud", "application_default_credentials.json");
7588
7848
  }
7589
7849
  function hasApplicationDefaultCredentials(home = homedir6(), adcPath = defaultAdcCredentialsPath(home), env = process.env) {
7590
7850
  const explicitPath = env["GOOGLE_APPLICATION_CREDENTIALS"]?.trim();
7591
- if (explicitPath && existsSync9(explicitPath)) return true;
7592
- return existsSync9(adcPath);
7851
+ if (explicitPath && existsSync10(explicitPath)) return true;
7852
+ return existsSync10(adcPath);
7593
7853
  }
7594
7854
  function loadVertexModelEntries(env = process.env) {
7595
7855
  const configPath = getVertexModelsPath(env);
7596
- if (!existsSync9(configPath)) return DEFAULT_VERTEX_MODELS;
7856
+ if (!existsSync10(configPath)) return DEFAULT_VERTEX_MODELS;
7597
7857
  try {
7598
- const parsed = JSON.parse(readFileSync9(configPath, "utf8"));
7858
+ const parsed = JSON.parse(readFileSync10(configPath, "utf8"));
7599
7859
  if (!Array.isArray(parsed) || parsed.length === 0) return DEFAULT_VERTEX_MODELS;
7600
7860
  const models = parsed.filter(
7601
7861
  (entry) => !!entry && typeof entry === "object" && typeof entry.id === "string" && entry.id.length > 0 && typeof entry.display_name === "string" && entry.display_name.length > 0
@@ -8108,12 +8368,12 @@ async function runServerCommand(options = {}) {
8108
8368
  import {
8109
8369
  chmodSync as chmodSync5,
8110
8370
  mkdirSync as mkdirSync6,
8111
- readFileSync as readFileSync10,
8371
+ readFileSync as readFileSync11,
8112
8372
  renameSync as renameSync2,
8113
8373
  unlinkSync as unlinkSync2,
8114
8374
  writeFileSync as writeFileSync5
8115
8375
  } from "fs";
8116
- import { join as join10 } from "path";
8376
+ import { join as join11 } from "path";
8117
8377
  var UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
8118
8378
  var UPDATE_CHECK_TIMEOUT_MS = 2e3;
8119
8379
  var UPDATE_COMMAND = "npm install -g anygate@latest";
@@ -8159,11 +8419,11 @@ function isNewerVersion(currentVersion, latestVersion) {
8159
8419
  return comparePrerelease(current.prerelease, latest.prerelease) > 0;
8160
8420
  }
8161
8421
  function cachePath() {
8162
- return join10(getAppHome(), "update-check.json");
8422
+ return join11(getAppHome(), "update-check.json");
8163
8423
  }
8164
8424
  function readFreshCache(now) {
8165
8425
  try {
8166
- const parsed = JSON.parse(readFileSync10(cachePath(), "utf8"));
8426
+ const parsed = JSON.parse(readFileSync11(cachePath(), "utf8"));
8167
8427
  if (typeof parsed.latestVersion !== "string" || !parseVersion(parsed.latestVersion)) return null;
8168
8428
  if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt)) return null;
8169
8429
  const age = now - parsed.checkedAt;
@@ -8244,18 +8504,18 @@ function favoriteProviderDisplayName(provider) {
8244
8504
 
8245
8505
  // src/providers/opencode-serve.ts
8246
8506
  import { execSync as execSync2, spawn as spawn2 } from "child_process";
8247
- import { existsSync as existsSync10 } from "fs";
8507
+ import { existsSync as existsSync11 } from "fs";
8248
8508
  import { homedir as homedir7 } from "os";
8249
- import { join as join11 } from "path";
8509
+ import { join as join12 } from "path";
8250
8510
  var isWindows2 = process.platform === "win32";
8251
8511
  var OPENCODE_FALLBACK_PATHS = isWindows2 ? [
8252
- join11(process.env["APPDATA"] ?? homedir7(), "npm", "opencode.cmd"),
8253
- join11(process.env["APPDATA"] ?? homedir7(), "npm", "opencode"),
8254
- join11(homedir7(), "AppData", "Roaming", "npm", "opencode.cmd")
8512
+ join12(process.env["APPDATA"] ?? homedir7(), "npm", "opencode.cmd"),
8513
+ join12(process.env["APPDATA"] ?? homedir7(), "npm", "opencode"),
8514
+ join12(homedir7(), "AppData", "Roaming", "npm", "opencode.cmd")
8255
8515
  ] : [
8256
- join11(homedir7(), ".opencode", "bin", "opencode"),
8257
- join11(homedir7(), ".local", "bin", "opencode"),
8258
- join11(homedir7(), ".npm", "bin", "opencode"),
8516
+ join12(homedir7(), ".opencode", "bin", "opencode"),
8517
+ join12(homedir7(), ".local", "bin", "opencode"),
8518
+ join12(homedir7(), ".npm", "bin", "opencode"),
8259
8519
  "/usr/local/bin/opencode",
8260
8520
  "/opt/homebrew/bin/opencode"
8261
8521
  ];
@@ -8271,7 +8531,7 @@ function findOpencodeBinary() {
8271
8531
  } catch {
8272
8532
  }
8273
8533
  for (const path of OPENCODE_FALLBACK_PATHS) {
8274
- if (existsSync10(path)) return path;
8534
+ if (existsSync11(path)) return path;
8275
8535
  }
8276
8536
  return null;
8277
8537
  }
@@ -9929,9 +10189,9 @@ ${pc6.bold("Device code (works on SSH/VPS):")}
9929
10189
 
9930
10190
  // src/agents/codex/app-launch.ts
9931
10191
  import { execSync as execSync3, spawn as spawn4 } from "child_process";
9932
- import { existsSync as existsSync11, readdirSync, statSync as statSync3 } from "fs";
10192
+ import { existsSync as existsSync12, readdirSync, statSync as statSync3 } from "fs";
9933
10193
  import { homedir as homedir8 } from "os";
9934
- import { join as join12 } from "path";
10194
+ import { join as join13 } from "path";
9935
10195
  import * as p6 from "@clack/prompts";
9936
10196
  var CODEX_BUNDLE_ID = "com.openai.codex";
9937
10197
  var DARWIN_APP_NAMES = ["ChatGPT", "Codex"];
@@ -9950,33 +10210,33 @@ function runPowerShell(script) {
9950
10210
  function darwinAppCandidates() {
9951
10211
  return DARWIN_APP_NAMES.flatMap((name) => [
9952
10212
  `/Applications/${name}.app`,
9953
- join12(homedir8(), "Applications", `${name}.app`)
10213
+ join13(homedir8(), "Applications", `${name}.app`)
9954
10214
  ]);
9955
10215
  }
9956
10216
  function winLocalAppData() {
9957
- return process.env.LOCALAPPDATA ?? join12(homedir8(), "AppData", "Local");
10217
+ return process.env.LOCALAPPDATA ?? join13(homedir8(), "AppData", "Local");
9958
10218
  }
9959
10219
  function winCodexExeCandidates() {
9960
10220
  const local = winLocalAppData();
9961
10221
  const bases = WIN_APP_NAMES.flatMap((name) => [
9962
- join12(local, "Programs", name),
9963
- join12(local, "Programs", `OpenAI ${name}`),
9964
- join12(local, name),
9965
- join12(local, `OpenAI ${name}`),
9966
- join12(local, "OpenAI", name)
10222
+ join13(local, "Programs", name),
10223
+ join13(local, "Programs", `OpenAI ${name}`),
10224
+ join13(local, name),
10225
+ join13(local, `OpenAI ${name}`),
10226
+ join13(local, "OpenAI", name)
9967
10227
  ]);
9968
- bases.push(join12(local, "openai-codex-electron"), join12(local, "openai-chatgpt-electron"));
10228
+ bases.push(join13(local, "openai-codex-electron"), join13(local, "openai-chatgpt-electron"));
9969
10229
  const out = [];
9970
10230
  for (const base of bases) {
9971
10231
  for (const name of WIN_APP_NAMES) {
9972
- out.push(join12(base, `${name}.exe`));
10232
+ out.push(join13(base, `${name}.exe`));
9973
10233
  }
9974
10234
  try {
9975
- if (existsSync11(base)) {
10235
+ if (existsSync12(base)) {
9976
10236
  for (const dir of readdirSync(base)) {
9977
10237
  if (dir.startsWith("app-")) {
9978
10238
  for (const name of WIN_APP_NAMES) {
9979
- out.push(join12(base, dir, `${name}.exe`));
10239
+ out.push(join13(base, dir, `${name}.exe`));
9980
10240
  }
9981
10241
  }
9982
10242
  }
@@ -9990,7 +10250,7 @@ function mdfindCodexApp() {
9990
10250
  try {
9991
10251
  const out = run(`mdfind "kMDItemCFBundleIdentifier == '${CODEX_BUNDLE_ID}'"`);
9992
10252
  const first = out.split("\n").map((l) => l.trim()).find(Boolean);
9993
- return first && existsSync11(first) ? first : null;
10253
+ return first && existsSync12(first) ? first : null;
9994
10254
  } catch {
9995
10255
  return null;
9996
10256
  }
@@ -9998,14 +10258,14 @@ function mdfindCodexApp() {
9998
10258
  function findCodexApp() {
9999
10259
  if (process.platform === "darwin") {
10000
10260
  for (const path of darwinAppCandidates()) {
10001
- if (existsSync11(path)) return path;
10261
+ if (existsSync12(path)) return path;
10002
10262
  }
10003
10263
  return mdfindCodexApp();
10004
10264
  }
10005
10265
  if (process.platform === "win32") {
10006
10266
  for (const path of winCodexExeCandidates()) {
10007
10267
  try {
10008
- if (existsSync11(path) && statSync3(path).isFile()) return path;
10268
+ if (existsSync12(path) && statSync3(path).isFile()) return path;
10009
10269
  } catch {
10010
10270
  }
10011
10271
  }
@@ -10151,9 +10411,9 @@ function codexAppInstallHint() {
10151
10411
 
10152
10412
  // src/agents/claude/desktop-launch.ts
10153
10413
  import { execSync as execSync4, spawn as spawn5 } from "child_process";
10154
- import { existsSync as existsSync12, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
10414
+ import { existsSync as existsSync13, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
10155
10415
  import { homedir as homedir9 } from "os";
10156
- import { join as join13 } from "path";
10416
+ import { join as join14 } from "path";
10157
10417
  import * as p7 from "@clack/prompts";
10158
10418
  var CLAUDE_BUNDLE_ID = "com.anthropic.claudefordesktop";
10159
10419
  function claudeAppSupported() {
@@ -10170,26 +10430,26 @@ function runPowerShell2(script) {
10170
10430
  function darwinAppCandidates2() {
10171
10431
  return [
10172
10432
  "/Applications/Claude.app",
10173
- join13(homedir9(), "Applications", "Claude.app")
10433
+ join14(homedir9(), "Applications", "Claude.app")
10174
10434
  ];
10175
10435
  }
10176
10436
  function winLocalAppData2() {
10177
- return process.env.LOCALAPPDATA ?? join13(homedir9(), "AppData", "Local");
10437
+ return process.env.LOCALAPPDATA ?? join14(homedir9(), "AppData", "Local");
10178
10438
  }
10179
10439
  function winClaudeExeCandidates() {
10180
10440
  const local = winLocalAppData2();
10181
10441
  const bases = [
10182
- join13(local, "Programs", "Claude"),
10183
- join13(local, "Claude")
10442
+ join14(local, "Programs", "Claude"),
10443
+ join14(local, "Claude")
10184
10444
  ];
10185
10445
  const out = [];
10186
10446
  for (const base of bases) {
10187
- out.push(join13(base, "Claude.exe"));
10447
+ out.push(join14(base, "Claude.exe"));
10188
10448
  try {
10189
- if (existsSync12(base)) {
10449
+ if (existsSync13(base)) {
10190
10450
  for (const name of readdirSync2(base)) {
10191
10451
  if (name.startsWith("app-")) {
10192
- out.push(join13(base, name, "Claude.exe"));
10452
+ out.push(join14(base, name, "Claude.exe"));
10193
10453
  }
10194
10454
  }
10195
10455
  }
@@ -10202,7 +10462,7 @@ function mdfindClaudeApp() {
10202
10462
  try {
10203
10463
  const out = run2(`mdfind "kMDItemCFBundleIdentifier == '${CLAUDE_BUNDLE_ID}'"`);
10204
10464
  const first = out.split("\n").map((l) => l.trim()).find(Boolean);
10205
- return first && existsSync12(first) ? first : null;
10465
+ return first && existsSync13(first) ? first : null;
10206
10466
  } catch {
10207
10467
  return null;
10208
10468
  }
@@ -10210,14 +10470,14 @@ function mdfindClaudeApp() {
10210
10470
  function findClaudeApp() {
10211
10471
  if (process.platform === "darwin") {
10212
10472
  for (const path of darwinAppCandidates2()) {
10213
- if (existsSync12(path)) return path;
10473
+ if (existsSync13(path)) return path;
10214
10474
  }
10215
10475
  return mdfindClaudeApp();
10216
10476
  }
10217
10477
  if (process.platform === "win32") {
10218
10478
  for (const path of winClaudeExeCandidates()) {
10219
10479
  try {
10220
- if (existsSync12(path) && statSync4(path).isFile()) return path;
10480
+ if (existsSync13(path) && statSync4(path).isFile()) return path;
10221
10481
  } catch {
10222
10482
  }
10223
10483
  }
@@ -10489,6 +10749,7 @@ export {
10489
10749
  encodeToolUseId,
10490
10750
  serializeToolResultContent,
10491
10751
  translateRequest,
10752
+ aggregateAnalytics,
10492
10753
  aliasModelId,
10493
10754
  startProxyCatalog,
10494
10755
  startProxy,
@@ -10556,4 +10817,4 @@ export {
10556
10817
  quitClaudeAppGracefully,
10557
10818
  launchOrRestartClaudeApp
10558
10819
  };
10559
- //# sourceMappingURL=chunk-QPXRFBQI.js.map
10820
+ //# sourceMappingURL=chunk-E2MV3GDX.js.map