anygate 0.5.2 → 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,18 +10,21 @@ import { join } from "path";
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "anygate",
13
- version: "0.5.2",
13
+ version: "0.5.4",
14
14
  publishConfig: {
15
15
  access: "public"
16
16
  },
17
17
  description: "Route any model into any coding agent \u2014 launch Claude Code, Codex, and more with multi-provider gateways",
18
- author: "ramanan-techlover",
18
+ author: "ramananbuilds",
19
19
  license: "MIT",
20
20
  repository: {
21
21
  type: "git",
22
- url: "git+https://github.com/ramanan-techlover/anygate.git"
22
+ url: "git+https://github.com/ramananbuilds/anygate.git"
23
+ },
24
+ homepage: "https://github.com/ramananbuilds/anygate#readme",
25
+ bugs: {
26
+ url: "https://github.com/ramananbuilds/anygate/issues"
23
27
  },
24
- homepage: "https://github.com/ramanan-techlover/anygate#readme",
25
28
  keywords: [
26
29
  "claude",
27
30
  "claude-code",
@@ -44,13 +47,15 @@ var package_default = {
44
47
  node: ">=18"
45
48
  },
46
49
  scripts: {
47
- build: "tsup && node scripts/copy-ui-assets.mjs",
50
+ build: "tsup && npm run ui:build && node scripts/copy-ui-assets.mjs",
48
51
  dev: "tsup --watch",
49
52
  test: "vitest run",
50
53
  "test:watch": "vitest",
51
54
  typecheck: "tsc --noEmit",
52
55
  "refresh:models-dev": "node scripts/refresh-models-dev-cache.mjs",
53
- 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"
54
59
  },
55
60
  dependencies: {
56
61
  "@ai-sdk/alibaba": "^1.0.26",
@@ -136,6 +141,7 @@ var CONFLICTING_ENV_VARS = [
136
141
  ];
137
142
  var OPENCODE_CACHE_PATH = join(homedir(), ".cache", "opencode", "models.json");
138
143
  var MAX_MODEL_CATALOG = 20;
144
+ var GATEWAY_PORT = 17645;
139
145
  var VERTEX_ANTHROPIC_NPM = "@ai-sdk/google-vertex/anthropic";
140
146
  function classifyModelFormat(modelId, providerNpm) {
141
147
  if (providerNpm === "@ai-sdk/anthropic") return "anthropic";
@@ -3899,7 +3905,7 @@ function printTraceLog(debugLogPath) {
3899
3905
 
3900
3906
  // src/gateway/anthropic-proxy.ts
3901
3907
  import { createServer } from "http";
3902
- 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";
3903
3909
 
3904
3910
  // src/core/http-utils.ts
3905
3911
  import * as zlib from "zlib";
@@ -5399,7 +5405,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5399
5405
  log7?.(() => `sdk stream error (${errorType}): ${errMsg}`);
5400
5406
  closeOpen();
5401
5407
  emit("error", { type: "error", error: { type: errorType, message: errMsg } });
5402
- return;
5408
+ return { inputTokens: 0, outputTokens: 0 };
5403
5409
  }
5404
5410
  default:
5405
5411
  break;
@@ -5409,6 +5415,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5409
5415
  ensureStart();
5410
5416
  emit("message_delta", { type: "message_delta", delta: { stop_reason: finishReason, stop_sequence: null }, usage });
5411
5417
  emit("message_stop", { type: "message_stop" });
5418
+ return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
5412
5419
  }
5413
5420
  async function streamAnthropicResponse(model, params, modelId, write, log7) {
5414
5421
  const result = streamText({ model, ...params, onError: () => {
@@ -5423,7 +5430,7 @@ async function streamAnthropicResponse(model, params, modelId, write, log7) {
5423
5430
  });
5424
5431
  Promise.resolve(result.usage).catch(() => {
5425
5432
  });
5426
- await writeAnthropicStream(result.fullStream, modelId, write, log7);
5433
+ return await writeAnthropicStream(result.fullStream, modelId, write, log7);
5427
5434
  }
5428
5435
  async function generateAnthropicResponse(model, params, modelId, options) {
5429
5436
  let text4;
@@ -5455,7 +5462,196 @@ async function generateAnthropicResponse(model, params, modelId, options) {
5455
5462
  }))
5456
5463
  ],
5457
5464
  stop_reason: finishReason === "tool-calls" ? "tool_use" : "end_turn",
5458
- 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
5459
5655
  };
5460
5656
  }
5461
5657
 
@@ -5463,16 +5659,16 @@ async function generateAnthropicResponse(model, params, modelId, options) {
5463
5659
  function appendSecureLog(logPath, line) {
5464
5660
  const redacted = redactTraceLine(line);
5465
5661
  try {
5466
- const fd = openSync2(logPath, "a", 384);
5662
+ const fd = openSync3(logPath, "a", 384);
5467
5663
  try {
5468
- writeSync2(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5664
+ writeSync3(fd, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5469
5665
  `);
5470
5666
  } finally {
5471
- closeSync2(fd);
5667
+ closeSync3(fd);
5472
5668
  }
5473
5669
  } catch {
5474
5670
  try {
5475
- appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5671
+ appendFileSync3(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${redacted}
5476
5672
  `);
5477
5673
  } catch {
5478
5674
  }
@@ -5661,7 +5857,16 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5661
5857
  "Cache-Control": "no-cache",
5662
5858
  "Connection": "keep-alive"
5663
5859
  });
5664
- 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
+ });
5665
5870
  res.end();
5666
5871
  } else {
5667
5872
  const anthropicResponse = await generateAnthropicResponse(
@@ -5670,12 +5875,22 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5670
5875
  originalModel,
5671
5876
  { forceStream: openAiOAuth }
5672
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
+ });
5673
5888
  sendJson(res, 200, anthropicResponse);
5674
5889
  }
5675
5890
  } catch (err) {
5676
5891
  const message = err instanceof Error ? err.message : String(err);
5677
5892
  const body = err && typeof err === "object" && "responseBody" in err ? err.responseBody : void 0;
5678
- plog(() => `sdk error: ${message}${body ? ` \u2014 body: ${body}` : ""}`);
5893
+ plog(() => `sdk error: ${message}${body ? ` \u0393\xC7\xF6 body: ${body}` : ""}`);
5679
5894
  if (!res.headersSent) {
5680
5895
  const status = upstreamHttpStatus(err);
5681
5896
  anthropicError(res, status === 500 ? 502 : status, message);
@@ -5693,7 +5908,7 @@ data: ${JSON.stringify({ type: "error", error: { type: errorType, message } })}
5693
5908
  if (route.modelFormat === "cloud-code") {
5694
5909
  const projectId = route.providerData?.projectId ?? "";
5695
5910
  if (!projectId) {
5696
- 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");
5697
5912
  return;
5698
5913
  }
5699
5914
  const envelope = anthropicToCloudCode(anthropicBody, route.realModelId, projectId);
@@ -5704,7 +5919,7 @@ data: ${JSON.stringify({ type: "error", error: { type: errorType, message } })}
5704
5919
  const cloudMaxOutput = envelope.request.generationConfig?.maxOutputTokens;
5705
5920
  const baseUrl = upstreamUrl.replace(/\/+$/, "");
5706
5921
  const cloudCodeUrl = `${baseUrl}/v1internal:streamGenerateContent?alt=sse`;
5707
- 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}`);
5708
5923
  const fetchCloudCode = (token) => fetch(cloudCodeUrl, {
5709
5924
  method: "POST",
5710
5925
  headers: {
@@ -5785,7 +6000,8 @@ function startProxy(completionsUrl, modelId, debug = false, contextWindow, sdk,
5785
6000
  reasoning: sdk?.reasoning,
5786
6001
  interleavedReasoningField: sdk?.interleavedReasoningField,
5787
6002
  useResponsesLite: sdk?.useResponsesLite,
5788
- preferWebSockets: sdk?.preferWebSockets
6003
+ preferWebSockets: sdk?.preferWebSockets,
6004
+ app: sdk?.app
5789
6005
  }], clientModelId, debug);
5790
6006
  }
5791
6007
 
@@ -6955,17 +7171,24 @@ async function generateOpenAiResponse(model, params, responseModelId) {
6955
7171
  function: { name: tc.toolName, arguments: JSON.stringify(tc.args) }
6956
7172
  }));
6957
7173
  }
7174
+ const usage = {
7175
+ inputTokens: result.usage?.promptTokens ?? 0,
7176
+ outputTokens: result.usage?.completionTokens ?? 0
7177
+ };
6958
7178
  return {
6959
- id: `chatcmpl-${Date.now()}`,
6960
- object: "chat.completion",
6961
- created: Math.floor(Date.now() / 1e3),
6962
- model: responseModelId,
6963
- choices: [{ index: 0, message, finish_reason: result.finishReason || "stop" }],
6964
- usage: {
6965
- prompt_tokens: result.usage?.promptTokens ?? 0,
6966
- completion_tokens: result.usage?.completionTokens ?? 0,
6967
- total_tokens: result.usage?.totalTokens ?? 0
6968
- }
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
6969
7192
  };
6970
7193
  }
6971
7194
  async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
@@ -6976,6 +7199,7 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
6976
7199
  created: Math.floor(Date.now() / 1e3),
6977
7200
  model: responseModelId
6978
7201
  };
7202
+ let usage = { inputTokens: 0, outputTokens: 0 };
6979
7203
  const send = (delta, finish_reason = null) => onChunk(`data: ${JSON.stringify({ ...baseData, choices: [{ index: 0, delta, finish_reason }] })}
6980
7204
 
6981
7205
  `);
@@ -6994,11 +7218,15 @@ async function streamOpenAiResponse(model, params, responseModelId, onChunk) {
6994
7218
  send({ tool_calls: [{ index: 0, function: { arguments: p8.delta ?? p8.text ?? p8.argsTextDelta ?? "" } }] });
6995
7219
  break;
6996
7220
  case "finish":
7221
+ if (p8.usage) {
7222
+ usage = { inputTokens: p8.usage.promptTokens ?? 0, outputTokens: p8.usage.completionTokens ?? 0 };
7223
+ }
6997
7224
  send({}, p8.finishReason || "stop");
6998
7225
  break;
6999
7226
  }
7000
7227
  }
7001
7228
  onChunk("data: [DONE]\n\n");
7229
+ return usage;
7002
7230
  }
7003
7231
 
7004
7232
  // src/registry/refresh-credentials.ts
@@ -7301,10 +7529,28 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
7301
7529
  "Cache-Control": "no-cache",
7302
7530
  "Connection": "keep-alive"
7303
7531
  });
7304
- 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
+ });
7305
7542
  res.end();
7306
7543
  } else {
7307
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
+ });
7308
7554
  sendJson(res, 200, anthropicResponse);
7309
7555
  }
7310
7556
  } catch (err) {
@@ -7356,10 +7602,28 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
7356
7602
  "Cache-Control": "no-cache",
7357
7603
  "Connection": "keep-alive"
7358
7604
  });
7359
- 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
+ });
7360
7615
  res.end();
7361
7616
  } else {
7362
- 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
+ });
7363
7627
  sendJson(res, 200, response);
7364
7628
  }
7365
7629
  } catch (err) {
@@ -7554,9 +7818,9 @@ async function selectServerProviders(available, initial) {
7554
7818
  }
7555
7819
 
7556
7820
  // src/gateway/vertex.ts
7557
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
7821
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
7558
7822
  import { homedir as homedir6 } from "os";
7559
- import { join as join9 } from "path";
7823
+ import { join as join10 } from "path";
7560
7824
  var DEFAULT_VERTEX_MODELS = [
7561
7825
  { id: "claude-sonnet-4-6", display_name: "Claude Sonnet 4.6" },
7562
7826
  { id: "claude-opus-4-6", display_name: "Claude Opus 4.6" },
@@ -7580,18 +7844,18 @@ function resolveVertexLocation(env = process.env) {
7580
7844
  return location.trim() || "global";
7581
7845
  }
7582
7846
  function defaultAdcCredentialsPath(home = homedir6()) {
7583
- return join9(home, ".config", "gcloud", "application_default_credentials.json");
7847
+ return join10(home, ".config", "gcloud", "application_default_credentials.json");
7584
7848
  }
7585
7849
  function hasApplicationDefaultCredentials(home = homedir6(), adcPath = defaultAdcCredentialsPath(home), env = process.env) {
7586
7850
  const explicitPath = env["GOOGLE_APPLICATION_CREDENTIALS"]?.trim();
7587
- if (explicitPath && existsSync9(explicitPath)) return true;
7588
- return existsSync9(adcPath);
7851
+ if (explicitPath && existsSync10(explicitPath)) return true;
7852
+ return existsSync10(adcPath);
7589
7853
  }
7590
7854
  function loadVertexModelEntries(env = process.env) {
7591
7855
  const configPath = getVertexModelsPath(env);
7592
- if (!existsSync9(configPath)) return DEFAULT_VERTEX_MODELS;
7856
+ if (!existsSync10(configPath)) return DEFAULT_VERTEX_MODELS;
7593
7857
  try {
7594
- const parsed = JSON.parse(readFileSync9(configPath, "utf8"));
7858
+ const parsed = JSON.parse(readFileSync10(configPath, "utf8"));
7595
7859
  if (!Array.isArray(parsed) || parsed.length === 0) return DEFAULT_VERTEX_MODELS;
7596
7860
  const models = parsed.filter(
7597
7861
  (entry) => !!entry && typeof entry === "object" && typeof entry.id === "string" && entry.id.length > 0 && typeof entry.display_name === "string" && entry.display_name.length > 0
@@ -8104,12 +8368,12 @@ async function runServerCommand(options = {}) {
8104
8368
  import {
8105
8369
  chmodSync as chmodSync5,
8106
8370
  mkdirSync as mkdirSync6,
8107
- readFileSync as readFileSync10,
8371
+ readFileSync as readFileSync11,
8108
8372
  renameSync as renameSync2,
8109
8373
  unlinkSync as unlinkSync2,
8110
8374
  writeFileSync as writeFileSync5
8111
8375
  } from "fs";
8112
- import { join as join10 } from "path";
8376
+ import { join as join11 } from "path";
8113
8377
  var UPDATE_CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
8114
8378
  var UPDATE_CHECK_TIMEOUT_MS = 2e3;
8115
8379
  var UPDATE_COMMAND = "npm install -g anygate@latest";
@@ -8155,11 +8419,11 @@ function isNewerVersion(currentVersion, latestVersion) {
8155
8419
  return comparePrerelease(current.prerelease, latest.prerelease) > 0;
8156
8420
  }
8157
8421
  function cachePath() {
8158
- return join10(getAppHome(), "update-check.json");
8422
+ return join11(getAppHome(), "update-check.json");
8159
8423
  }
8160
8424
  function readFreshCache(now) {
8161
8425
  try {
8162
- const parsed = JSON.parse(readFileSync10(cachePath(), "utf8"));
8426
+ const parsed = JSON.parse(readFileSync11(cachePath(), "utf8"));
8163
8427
  if (typeof parsed.latestVersion !== "string" || !parseVersion(parsed.latestVersion)) return null;
8164
8428
  if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt)) return null;
8165
8429
  const age = now - parsed.checkedAt;
@@ -8240,18 +8504,18 @@ function favoriteProviderDisplayName(provider) {
8240
8504
 
8241
8505
  // src/providers/opencode-serve.ts
8242
8506
  import { execSync as execSync2, spawn as spawn2 } from "child_process";
8243
- import { existsSync as existsSync10 } from "fs";
8507
+ import { existsSync as existsSync11 } from "fs";
8244
8508
  import { homedir as homedir7 } from "os";
8245
- import { join as join11 } from "path";
8509
+ import { join as join12 } from "path";
8246
8510
  var isWindows2 = process.platform === "win32";
8247
8511
  var OPENCODE_FALLBACK_PATHS = isWindows2 ? [
8248
- join11(process.env["APPDATA"] ?? homedir7(), "npm", "opencode.cmd"),
8249
- join11(process.env["APPDATA"] ?? homedir7(), "npm", "opencode"),
8250
- 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")
8251
8515
  ] : [
8252
- join11(homedir7(), ".opencode", "bin", "opencode"),
8253
- join11(homedir7(), ".local", "bin", "opencode"),
8254
- join11(homedir7(), ".npm", "bin", "opencode"),
8516
+ join12(homedir7(), ".opencode", "bin", "opencode"),
8517
+ join12(homedir7(), ".local", "bin", "opencode"),
8518
+ join12(homedir7(), ".npm", "bin", "opencode"),
8255
8519
  "/usr/local/bin/opencode",
8256
8520
  "/opt/homebrew/bin/opencode"
8257
8521
  ];
@@ -8267,7 +8531,7 @@ function findOpencodeBinary() {
8267
8531
  } catch {
8268
8532
  }
8269
8533
  for (const path of OPENCODE_FALLBACK_PATHS) {
8270
- if (existsSync10(path)) return path;
8534
+ if (existsSync11(path)) return path;
8271
8535
  }
8272
8536
  return null;
8273
8537
  }
@@ -9925,9 +10189,9 @@ ${pc6.bold("Device code (works on SSH/VPS):")}
9925
10189
 
9926
10190
  // src/agents/codex/app-launch.ts
9927
10191
  import { execSync as execSync3, spawn as spawn4 } from "child_process";
9928
- import { existsSync as existsSync11, readdirSync, statSync as statSync3 } from "fs";
10192
+ import { existsSync as existsSync12, readdirSync, statSync as statSync3 } from "fs";
9929
10193
  import { homedir as homedir8 } from "os";
9930
- import { join as join12 } from "path";
10194
+ import { join as join13 } from "path";
9931
10195
  import * as p6 from "@clack/prompts";
9932
10196
  var CODEX_BUNDLE_ID = "com.openai.codex";
9933
10197
  var DARWIN_APP_NAMES = ["ChatGPT", "Codex"];
@@ -9946,33 +10210,33 @@ function runPowerShell(script) {
9946
10210
  function darwinAppCandidates() {
9947
10211
  return DARWIN_APP_NAMES.flatMap((name) => [
9948
10212
  `/Applications/${name}.app`,
9949
- join12(homedir8(), "Applications", `${name}.app`)
10213
+ join13(homedir8(), "Applications", `${name}.app`)
9950
10214
  ]);
9951
10215
  }
9952
10216
  function winLocalAppData() {
9953
- return process.env.LOCALAPPDATA ?? join12(homedir8(), "AppData", "Local");
10217
+ return process.env.LOCALAPPDATA ?? join13(homedir8(), "AppData", "Local");
9954
10218
  }
9955
10219
  function winCodexExeCandidates() {
9956
10220
  const local = winLocalAppData();
9957
10221
  const bases = WIN_APP_NAMES.flatMap((name) => [
9958
- join12(local, "Programs", name),
9959
- join12(local, "Programs", `OpenAI ${name}`),
9960
- join12(local, name),
9961
- join12(local, `OpenAI ${name}`),
9962
- 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)
9963
10227
  ]);
9964
- 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"));
9965
10229
  const out = [];
9966
10230
  for (const base of bases) {
9967
10231
  for (const name of WIN_APP_NAMES) {
9968
- out.push(join12(base, `${name}.exe`));
10232
+ out.push(join13(base, `${name}.exe`));
9969
10233
  }
9970
10234
  try {
9971
- if (existsSync11(base)) {
10235
+ if (existsSync12(base)) {
9972
10236
  for (const dir of readdirSync(base)) {
9973
10237
  if (dir.startsWith("app-")) {
9974
10238
  for (const name of WIN_APP_NAMES) {
9975
- out.push(join12(base, dir, `${name}.exe`));
10239
+ out.push(join13(base, dir, `${name}.exe`));
9976
10240
  }
9977
10241
  }
9978
10242
  }
@@ -9986,7 +10250,7 @@ function mdfindCodexApp() {
9986
10250
  try {
9987
10251
  const out = run(`mdfind "kMDItemCFBundleIdentifier == '${CODEX_BUNDLE_ID}'"`);
9988
10252
  const first = out.split("\n").map((l) => l.trim()).find(Boolean);
9989
- return first && existsSync11(first) ? first : null;
10253
+ return first && existsSync12(first) ? first : null;
9990
10254
  } catch {
9991
10255
  return null;
9992
10256
  }
@@ -9994,14 +10258,14 @@ function mdfindCodexApp() {
9994
10258
  function findCodexApp() {
9995
10259
  if (process.platform === "darwin") {
9996
10260
  for (const path of darwinAppCandidates()) {
9997
- if (existsSync11(path)) return path;
10261
+ if (existsSync12(path)) return path;
9998
10262
  }
9999
10263
  return mdfindCodexApp();
10000
10264
  }
10001
10265
  if (process.platform === "win32") {
10002
10266
  for (const path of winCodexExeCandidates()) {
10003
10267
  try {
10004
- if (existsSync11(path) && statSync3(path).isFile()) return path;
10268
+ if (existsSync12(path) && statSync3(path).isFile()) return path;
10005
10269
  } catch {
10006
10270
  }
10007
10271
  }
@@ -10147,9 +10411,9 @@ function codexAppInstallHint() {
10147
10411
 
10148
10412
  // src/agents/claude/desktop-launch.ts
10149
10413
  import { execSync as execSync4, spawn as spawn5 } from "child_process";
10150
- 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";
10151
10415
  import { homedir as homedir9 } from "os";
10152
- import { join as join13 } from "path";
10416
+ import { join as join14 } from "path";
10153
10417
  import * as p7 from "@clack/prompts";
10154
10418
  var CLAUDE_BUNDLE_ID = "com.anthropic.claudefordesktop";
10155
10419
  function claudeAppSupported() {
@@ -10166,26 +10430,26 @@ function runPowerShell2(script) {
10166
10430
  function darwinAppCandidates2() {
10167
10431
  return [
10168
10432
  "/Applications/Claude.app",
10169
- join13(homedir9(), "Applications", "Claude.app")
10433
+ join14(homedir9(), "Applications", "Claude.app")
10170
10434
  ];
10171
10435
  }
10172
10436
  function winLocalAppData2() {
10173
- return process.env.LOCALAPPDATA ?? join13(homedir9(), "AppData", "Local");
10437
+ return process.env.LOCALAPPDATA ?? join14(homedir9(), "AppData", "Local");
10174
10438
  }
10175
10439
  function winClaudeExeCandidates() {
10176
10440
  const local = winLocalAppData2();
10177
10441
  const bases = [
10178
- join13(local, "Programs", "Claude"),
10179
- join13(local, "Claude")
10442
+ join14(local, "Programs", "Claude"),
10443
+ join14(local, "Claude")
10180
10444
  ];
10181
10445
  const out = [];
10182
10446
  for (const base of bases) {
10183
- out.push(join13(base, "Claude.exe"));
10447
+ out.push(join14(base, "Claude.exe"));
10184
10448
  try {
10185
- if (existsSync12(base)) {
10449
+ if (existsSync13(base)) {
10186
10450
  for (const name of readdirSync2(base)) {
10187
10451
  if (name.startsWith("app-")) {
10188
- out.push(join13(base, name, "Claude.exe"));
10452
+ out.push(join14(base, name, "Claude.exe"));
10189
10453
  }
10190
10454
  }
10191
10455
  }
@@ -10198,7 +10462,7 @@ function mdfindClaudeApp() {
10198
10462
  try {
10199
10463
  const out = run2(`mdfind "kMDItemCFBundleIdentifier == '${CLAUDE_BUNDLE_ID}'"`);
10200
10464
  const first = out.split("\n").map((l) => l.trim()).find(Boolean);
10201
- return first && existsSync12(first) ? first : null;
10465
+ return first && existsSync13(first) ? first : null;
10202
10466
  } catch {
10203
10467
  return null;
10204
10468
  }
@@ -10206,14 +10470,14 @@ function mdfindClaudeApp() {
10206
10470
  function findClaudeApp() {
10207
10471
  if (process.platform === "darwin") {
10208
10472
  for (const path of darwinAppCandidates2()) {
10209
- if (existsSync12(path)) return path;
10473
+ if (existsSync13(path)) return path;
10210
10474
  }
10211
10475
  return mdfindClaudeApp();
10212
10476
  }
10213
10477
  if (process.platform === "win32") {
10214
10478
  for (const path of winClaudeExeCandidates()) {
10215
10479
  try {
10216
- if (existsSync12(path) && statSync4(path).isFile()) return path;
10480
+ if (existsSync13(path) && statSync4(path).isFile()) return path;
10217
10481
  } catch {
10218
10482
  }
10219
10483
  }
@@ -10347,7 +10611,9 @@ async function launchOrRestartClaudeApp(prompt = "Restart Claude Desktop to appl
10347
10611
 
10348
10612
  export {
10349
10613
  BACKENDS,
10614
+ CONFLICTING_ENV_VARS,
10350
10615
  MAX_MODEL_CATALOG,
10616
+ GATEWAY_PORT,
10351
10617
  VERTEX_ANTHROPIC_NPM,
10352
10618
  VERSION,
10353
10619
  requestOpenAiDeviceCode,
@@ -10483,6 +10749,7 @@ export {
10483
10749
  encodeToolUseId,
10484
10750
  serializeToolResultContent,
10485
10751
  translateRequest,
10752
+ aggregateAnalytics,
10486
10753
  aliasModelId,
10487
10754
  startProxyCatalog,
10488
10755
  startProxy,
@@ -10524,6 +10791,7 @@ export {
10524
10791
  loadServerModels,
10525
10792
  resolveServerUpstreamApiKey,
10526
10793
  runServerCommand,
10794
+ UPDATE_COMMAND,
10527
10795
  checkForUpdates,
10528
10796
  formatUpdateNotification,
10529
10797
  favoriteProviderDisplayName,
@@ -10549,4 +10817,4 @@ export {
10549
10817
  quitClaudeAppGracefully,
10550
10818
  launchOrRestartClaudeApp
10551
10819
  };
10552
- //# sourceMappingURL=chunk-6GVUN4JO.js.map
10820
+ //# sourceMappingURL=chunk-E2MV3GDX.js.map