Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions Sources/CodexBarCore/Resources/Plugins/openrouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,26 +112,27 @@ defineProvider({
} else
try {
const now = ctx.date.now();
const today = now.toISOString().slice(0, 10);
const cutoffDate = new Date(now.getTime() - 29 * 24 * 60 * 60 * 1000);
const latestCompletedDate = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const latestCompleted = latestCompletedDate.toISOString().slice(0, 10);
const cutoffDate = new Date(latestCompletedDate.getTime() - 29 * 24 * 60 * 60 * 1000);
const cutoff = cutoffDate.toISOString().slice(0, 10);
// A management credential must never follow the user-configurable API base to a proxy.
const activityURL = "https://openrouter.ai/api/v1/activity";
const [historyResponse, todayResponse] = await Promise.all([
const [historyResponse, latestCompletedResponse] = await Promise.all([
ctx.http.get(activityURL, {
timeoutSeconds: optionalRequestTimeoutSeconds,
openRouterManagementAuth: true,
}),
ctx.http.get(`${activityURL}?date=${encodeURIComponent(today)}`, {
ctx.http.get(`${activityURL}?date=${encodeURIComponent(latestCompleted)}`, {
timeoutSeconds: optionalRequestTimeoutSeconds,
openRouterManagementAuth: true,
}),
]);
if (historyResponse.status !== 200 || todayResponse.status !== 200) {
const failed = historyResponse.status !== 200 ? historyResponse : todayResponse;
if (historyResponse.status !== 200 || latestCompletedResponse.status !== 200) {
const failed = historyResponse.status !== 200 ? historyResponse : latestCompletedResponse;
activityDegradation = activityDegradationReason(failed.status);
} else {
const payloads = [historyResponse, todayResponse].map((response) => JSON.parse(response.bodyText));
const payloads = [historyResponse, latestCompletedResponse].map((response) => JSON.parse(response.bodyText));
const rows = payloads.flatMap((payload) => {
if (!payload || !Array.isArray(payload.data)) throw new TypeError("activity.data must be an array");
return payload.data;
Expand All @@ -157,7 +158,9 @@ defineProvider({
if (!Number.isFinite(parsedDate.getTime()) || parsedDate.toISOString().slice(0, 10) !== date) {
throw new TypeError(`activity.data[${index}].date must be a real calendar date`);
}
if (date > today) throw new TypeError(`activity.data[${index}].date must not be in the future`);
if (date > latestCompleted) {
throw new TypeError(`activity.data[${index}].date must be a completed UTC day`);
}
if (date < cutoff) continue;
const rawModel = row.model_permaslug ?? row.model;
const model = typeof rawModel === "string" && rawModel.trim() ? rawModel.trim() : null;
Expand Down Expand Up @@ -246,7 +249,7 @@ defineProvider({
currency: "USD",
historyDays: 30,
historyLabel: "Last 30 days (UTC)",
windowEnd: today,
windowEnd: latestCompleted,
entries,
};
}
Expand Down
69 changes: 69 additions & 0 deletions Tests/CodexBarTests/OpenRouterUsageStatsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,75 @@ struct OpenRouterPluginGoldenTests {
#expect(ordinary.allSatisfy { $0.value(forHTTPHeaderField: "Authorization") == "Bearer standard-key" })
}

@Test
func `activity requests the latest completed UTC day`() async throws {
let requests = OpenRouterRequestRecorder()
let activityBody = #"""
{"data":[
{
"date":"2026-08-17",
"model":"openai/gpt-5.6",
"prompt_tokens":10,
"completion_tokens":5,
"reasoning_tokens":2,
"requests":1,
"usage":1
},
{
"date":"2026-07-19",
"model":"x-ai/grok-4",
"prompt_tokens":4,
"completion_tokens":1,
"reasoning_tokens":0,
"requests":1,
"usage":1
}
]}
"""#
let runtime = try ProviderPluginRuntime(
bundledPlugin: "openrouter",
transport: ProviderHTTPTransportHandler { request in
await requests.append(request)
let path = request.url?.path ?? ""
if path.hasSuffix("/activity") {
let date = request.url
.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: false) }?
.queryItems?
.first { $0.name == "date" }?
.value
if date == "2026-08-18" {
return try Self.response(
request,
body: #"{"error":{"message":"Date must be within the last 30 (completed) UTC days"}}"#,
statusCode: 400)
}
return try Self.response(request, body: activityBody)
}
if path.hasSuffix("/key") {
return try Self.response(request, body: #"{"data":{"limit":20,"usage":5}}"#)
}
return try Self.response(request, body: Self.defaultCreditsBody)
})
let now = Date(timeIntervalSince1970: 1_787_079_600) // 2026-08-18T12:00:00Z; stable injected clock.

let usage = try await runtime.fetchUsage(
secrets: [
OpenRouterSettingsReader.envKey: "fixture-key",
OpenRouterSettingsReader.managementAPIKeyEnvironmentKey: "fixture-management-key",
],
now: now)
let recorded = await requests.requests
let datedRequest = try #require(recorded.first { $0.url?.query != nil })
let date = datedRequest.url
.flatMap { URLComponents(url: $0, resolvingAgainstBaseURL: false) }?
.queryItems?
.first { $0.name == "date" }?
.value

#expect(date == "2026-08-17")
#expect(usage.costUsage?.last30DaysCostUSD == 2)
}

@Test
func `server remaining drives monthly quota golden`() async throws {
let usage = try await Self.fetch(keyBody: #"""
Expand Down