@rynfar/meridian 1.40.0 → 1.41.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/{cli-3azh7s3k.js → cli-51a9sav0.js} +811 -93
- package/dist/{cli-m9pfb7h9.js → cli-e289rj3k.js} +79 -46
- package/dist/cli.js +3 -3
- package/dist/{profilePage-77z05e0r.js → profilePage-k0faye28.js} +196 -4
- package/dist/proxy/adapters/droid.d.ts.map +1 -1
- package/dist/proxy/agentDefs.d.ts +2 -0
- package/dist/proxy/agentDefs.d.ts.map +1 -1
- package/dist/proxy/errors.d.ts +42 -0
- package/dist/proxy/errors.d.ts.map +1 -1
- package/dist/proxy/models.d.ts +26 -0
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/oauthUsage.d.ts +67 -0
- package/dist/proxy/oauthUsage.d.ts.map +1 -0
- package/dist/proxy/openai.d.ts +140 -14
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/tokenRefresh.d.ts +33 -1
- package/dist/proxy/tokenRefresh.d.ts.map +1 -1
- package/dist/proxy/tools.d.ts.map +1 -1
- package/dist/proxy/transforms/droid.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/telemetry/profilePage.d.ts.map +1 -1
- package/dist/telemetry/profileUsage.d.ts +57 -0
- package/dist/telemetry/profileUsage.d.ts.map +1 -0
- package/dist/tokenRefresh-psq94r54.js +19 -0
- package/package.json +3 -3
- package/dist/tokenRefresh-y7d1qvb3.js +0 -11
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/proxy/tokenRefresh.ts
|
|
2
2
|
import { execFile as execFileCb } from "child_process";
|
|
3
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
4
4
|
import { homedir, platform, userInfo } from "os";
|
|
5
|
+
import { join, dirname, resolve } from "path";
|
|
6
|
+
import { createHash } from "crypto";
|
|
5
7
|
import { promisify } from "util";
|
|
6
8
|
|
|
7
9
|
// src/logger.ts
|
|
@@ -72,6 +74,20 @@ var OAUTH_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
|
72
74
|
var OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
73
75
|
var KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
74
76
|
var CREDENTIALS_FILE = `${homedir()}/.claude/.credentials.json`;
|
|
77
|
+
var DEFAULT_CLAUDE_DIR = `${homedir()}/.claude`;
|
|
78
|
+
function configDirToKeychainService(claudeConfigDir) {
|
|
79
|
+
const abs = resolve(claudeConfigDir);
|
|
80
|
+
if (abs === resolve(DEFAULT_CLAUDE_DIR))
|
|
81
|
+
return KEYCHAIN_SERVICE;
|
|
82
|
+
const hash = createHash("sha256").update(abs).digest("hex").slice(0, 8);
|
|
83
|
+
return `${KEYCHAIN_SERVICE}-${hash}`;
|
|
84
|
+
}
|
|
85
|
+
function configDirToCredentialsFile(claudeConfigDir) {
|
|
86
|
+
return join(resolve(claudeConfigDir), ".credentials.json");
|
|
87
|
+
}
|
|
88
|
+
function serializeCredentials(credentials) {
|
|
89
|
+
return JSON.stringify(credentials);
|
|
90
|
+
}
|
|
75
91
|
function parseKeychainValue(raw) {
|
|
76
92
|
const trimmed = raw.trim();
|
|
77
93
|
try {
|
|
@@ -83,57 +99,74 @@ function parseKeychainValue(raw) {
|
|
|
83
99
|
} catch {}
|
|
84
100
|
return null;
|
|
85
101
|
}
|
|
86
|
-
var
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
var keychainWasHexByService = new Map;
|
|
103
|
+
function buildMacosStore(serviceName) {
|
|
104
|
+
return {
|
|
105
|
+
async read() {
|
|
106
|
+
try {
|
|
107
|
+
const { stdout } = await execFile("/usr/bin/security", ["find-generic-password", "-s", serviceName, "-a", userInfo().username, "-w"], { timeout: 5000 });
|
|
108
|
+
const parsed = parseKeychainValue(stdout);
|
|
109
|
+
if (!parsed)
|
|
110
|
+
throw new Error("Could not parse keychain value as JSON or hex-encoded JSON");
|
|
111
|
+
keychainWasHexByService.set(serviceName, parsed.wasHex);
|
|
112
|
+
return parsed.credentials;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
claudeLog("token_refresh.keychain_read_failed", { service: serviceName, error: String(err) });
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
async write(credentials) {
|
|
119
|
+
const json = serializeCredentials(credentials);
|
|
120
|
+
const wasHex = keychainWasHexByService.get(serviceName) ?? false;
|
|
121
|
+
const value = wasHex ? Buffer.from(json).toString("hex") : json;
|
|
122
|
+
try {
|
|
123
|
+
await execFile("/usr/bin/security", ["add-generic-password", "-U", "-s", serviceName, "-a", userInfo().username, "-w", value], { timeout: 5000 });
|
|
124
|
+
return true;
|
|
125
|
+
} catch (err) {
|
|
126
|
+
claudeLog("token_refresh.keychain_write_failed", { service: serviceName, error: String(err) });
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
110
129
|
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
var
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
var macosStore = buildMacosStore(KEYCHAIN_SERVICE);
|
|
133
|
+
function buildFileStore(filePath) {
|
|
134
|
+
return {
|
|
135
|
+
async read() {
|
|
136
|
+
try {
|
|
137
|
+
if (!existsSync(filePath))
|
|
138
|
+
return null;
|
|
139
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
140
|
+
} catch (err) {
|
|
141
|
+
claudeLog("token_refresh.file_read_failed", { path: filePath, error: String(err) });
|
|
117
142
|
return null;
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
async write(credentials) {
|
|
146
|
+
try {
|
|
147
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
148
|
+
writeFileSync(filePath, serializeCredentials(credentials), "utf-8");
|
|
149
|
+
return true;
|
|
150
|
+
} catch (err) {
|
|
151
|
+
claudeLog("token_refresh.file_write_failed", { path: filePath, error: String(err) });
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
122
154
|
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return false;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
var fileStore = buildFileStore(CREDENTIALS_FILE);
|
|
158
|
+
function createPlatformCredentialStore(opts) {
|
|
159
|
+
if (opts?.claudeConfigDir) {
|
|
160
|
+
if (platform() === "darwin") {
|
|
161
|
+
return buildMacosStore(configDirToKeychainService(opts.claudeConfigDir));
|
|
131
162
|
}
|
|
163
|
+
return buildFileStore(configDirToCredentialsFile(opts.claudeConfigDir));
|
|
132
164
|
}
|
|
133
|
-
};
|
|
134
|
-
function createPlatformCredentialStore() {
|
|
135
165
|
return platform() === "darwin" ? macosStore : fileStore;
|
|
136
166
|
}
|
|
167
|
+
function credentialsFilePathForProfile(claudeConfigDir) {
|
|
168
|
+
return claudeConfigDir ? configDirToCredentialsFile(claudeConfigDir) : CREDENTIALS_FILE;
|
|
169
|
+
}
|
|
137
170
|
var inflightRefresh = null;
|
|
138
171
|
async function refreshOAuthToken(store) {
|
|
139
172
|
if (inflightRefresh)
|
|
@@ -200,4 +233,4 @@ function resetInflightRefresh() {
|
|
|
200
233
|
inflightRefresh = null;
|
|
201
234
|
}
|
|
202
235
|
|
|
203
|
-
export { withClaudeLogContext, claudeLog, createPlatformCredentialStore, refreshOAuthToken, resetInflightRefresh };
|
|
236
|
+
export { withClaudeLogContext, claudeLog, configDirToKeychainService, configDirToCredentialsFile, serializeCredentials, createPlatformCredentialStore, credentialsFilePathForProfile, refreshOAuthToken, resetInflightRefresh };
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
startProxyServer
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-51a9sav0.js";
|
|
5
5
|
import"./cli-vdp9s10c.js";
|
|
6
6
|
import"./cli-sry5aqdj.js";
|
|
7
7
|
import"./cli-4rqtm83g.js";
|
|
8
8
|
import"./cli-340h1chz.js";
|
|
9
9
|
import"./cli-rtab0qa6.js";
|
|
10
|
-
import"./cli-
|
|
10
|
+
import"./cli-e289rj3k.js";
|
|
11
11
|
import {
|
|
12
12
|
__require
|
|
13
13
|
} from "./cli-p9swy5t3.js";
|
|
@@ -89,7 +89,7 @@ Restart OpenCode for the plugin to take effect.`);
|
|
|
89
89
|
process.exit(0);
|
|
90
90
|
}
|
|
91
91
|
if (args[0] === "refresh-token") {
|
|
92
|
-
const { refreshOAuthToken } = await import("./tokenRefresh-
|
|
92
|
+
const { refreshOAuthToken } = await import("./tokenRefresh-psq94r54.js");
|
|
93
93
|
const success = await refreshOAuthToken();
|
|
94
94
|
if (success) {
|
|
95
95
|
console.log("Token refreshed successfully");
|
|
@@ -9,6 +9,19 @@ import"./cli-p9swy5t3.js";
|
|
|
9
9
|
|
|
10
10
|
// src/telemetry/profilePage.ts
|
|
11
11
|
init_profileBar();
|
|
12
|
+
|
|
13
|
+
// src/telemetry/profileUsage.ts
|
|
14
|
+
var WINDOW_LABELS = {
|
|
15
|
+
five_hour: "5h",
|
|
16
|
+
seven_day: "7d",
|
|
17
|
+
seven_day_opus: "7d Opus",
|
|
18
|
+
seven_day_sonnet: "7d Sonnet",
|
|
19
|
+
seven_day_oauth_apps: "7d Apps",
|
|
20
|
+
seven_day_cowork: "7d Cowork",
|
|
21
|
+
seven_day_omelette: "7d Omelette"
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/telemetry/profilePage.ts
|
|
12
25
|
var profilePageHtml = `<!DOCTYPE html>
|
|
13
26
|
<html lang="en">
|
|
14
27
|
<head>
|
|
@@ -92,6 +105,48 @@ var profilePageHtml = `<!DOCTYPE html>
|
|
|
92
105
|
}
|
|
93
106
|
.copy-btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
94
107
|
.copy-btn.copied { color: var(--green); border-color: var(--green); }
|
|
108
|
+
|
|
109
|
+
/* OAuth usage panel — one block per profile, mirrors pylon's quota strip. */
|
|
110
|
+
.usage-section { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
|
|
111
|
+
.usage-section-title {
|
|
112
|
+
font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px;
|
|
113
|
+
margin-bottom: 10px; display: flex; align-items: center; gap: 8px;
|
|
114
|
+
}
|
|
115
|
+
.usage-as-of { font-size: 10px; color: var(--muted); text-transform: none; letter-spacing: 0; opacity: 0.7; }
|
|
116
|
+
.usage-grid {
|
|
117
|
+
display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
118
|
+
gap: 8px;
|
|
119
|
+
}
|
|
120
|
+
.usage-card {
|
|
121
|
+
background: var(--bg); border: 1px solid var(--border); border-radius: 6px;
|
|
122
|
+
padding: 8px 10px; min-width: 0;
|
|
123
|
+
}
|
|
124
|
+
.usage-row {
|
|
125
|
+
display: flex; justify-content: space-between; align-items: baseline;
|
|
126
|
+
font-size: 11px; gap: 8px; margin-bottom: 6px;
|
|
127
|
+
}
|
|
128
|
+
.usage-label { color: var(--muted); font-weight: 500; white-space: nowrap; }
|
|
129
|
+
.usage-pct { font-family: 'SF Mono', SFMono-Regular, Consolas, monospace; font-weight: 600; font-size: 12px; }
|
|
130
|
+
.usage-bar {
|
|
131
|
+
height: 4px; background: rgba(127,127,127,0.18); border-radius: 2px; overflow: hidden;
|
|
132
|
+
margin-bottom: 4px;
|
|
133
|
+
}
|
|
134
|
+
.usage-fill { height: 100%; transition: width 0.4s ease; background: var(--green); }
|
|
135
|
+
.usage-card.status-warn .usage-fill,
|
|
136
|
+
.usage-card.status-warn .usage-pct { color: var(--yellow); }
|
|
137
|
+
.usage-card.status-warn .usage-fill { background: var(--yellow); }
|
|
138
|
+
.usage-card.status-high .usage-fill,
|
|
139
|
+
.usage-card.status-high .usage-pct { color: var(--red); }
|
|
140
|
+
.usage-card.status-high .usage-fill { background: var(--red); }
|
|
141
|
+
.usage-reset { font-size: 10px; color: var(--muted); white-space: nowrap; }
|
|
142
|
+
.usage-extra {
|
|
143
|
+
margin-top: 8px; padding: 8px 10px; background: var(--bg); border: 1px solid var(--border);
|
|
144
|
+
border-radius: 6px; font-size: 11px;
|
|
145
|
+
}
|
|
146
|
+
.usage-extra-row { display: flex; justify-content: space-between; gap: 8px; }
|
|
147
|
+
.usage-empty {
|
|
148
|
+
font-size: 11px; color: var(--muted); padding: 6px 0; font-style: italic;
|
|
149
|
+
}
|
|
95
150
|
` + profileBarCss + `
|
|
96
151
|
</style>
|
|
97
152
|
</head>
|
|
@@ -145,11 +200,73 @@ var profilePageHtml = `<!DOCTYPE html>
|
|
|
145
200
|
</div>
|
|
146
201
|
|
|
147
202
|
<script>
|
|
203
|
+
// Inlined from src/telemetry/profileUsage.ts. The TS source is unit-tested
|
|
204
|
+
// (see profile-usage.test.ts) and the labels object is interpolated here so
|
|
205
|
+
// the browser script and TS module share their data.
|
|
206
|
+
var WINDOW_LABELS = ${JSON.stringify(WINDOW_LABELS)};
|
|
207
|
+
|
|
208
|
+
function labelForWindow(type) {
|
|
209
|
+
if (WINDOW_LABELS[type]) return WINDOW_LABELS[type];
|
|
210
|
+
return String(type || '').split('_').map(function (p) {
|
|
211
|
+
return p.length > 0 ? p[0].toUpperCase() + p.slice(1) : p;
|
|
212
|
+
}).join(' ');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function classifyUtilization(u) {
|
|
216
|
+
if (u == null || !isFinite(u)) return 'ok';
|
|
217
|
+
if (u >= 0.85) return 'high';
|
|
218
|
+
if (u >= 0.6) return 'warn';
|
|
219
|
+
return 'ok';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function formatResetCountdown(resetsAt) {
|
|
223
|
+
if (resetsAt == null || !isFinite(resetsAt)) return '';
|
|
224
|
+
var ms = resetsAt - Date.now();
|
|
225
|
+
if (ms <= 0) return 'resetting…';
|
|
226
|
+
var minutes = Math.floor(ms / 60000);
|
|
227
|
+
if (minutes < 60) return 'in ' + Math.max(1, minutes) + 'm';
|
|
228
|
+
var hours = Math.floor(minutes / 60);
|
|
229
|
+
var remMin = minutes % 60;
|
|
230
|
+
if (hours < 24) return remMin > 0 ? 'in ' + hours + 'h ' + remMin + 'm' : 'in ' + hours + 'h';
|
|
231
|
+
var days = Math.floor(hours / 24);
|
|
232
|
+
var remHr = hours % 24;
|
|
233
|
+
return remHr > 0 ? 'in ' + days + 'd ' + remHr + 'h' : 'in ' + days + 'd';
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function formatExtraUsage(eu) {
|
|
237
|
+
if (!eu || !eu.isEnabled) return null;
|
|
238
|
+
var monthlyLimit = isFinite(eu.monthlyLimit) ? eu.monthlyLimit : 0;
|
|
239
|
+
if (monthlyLimit <= 0) return null;
|
|
240
|
+
var used = isFinite(eu.usedCredits) ? eu.usedCredits : 0;
|
|
241
|
+
var utilization = (eu.utilization != null && isFinite(eu.utilization))
|
|
242
|
+
? Math.max(0, Math.min(1, eu.utilization))
|
|
243
|
+
: (monthlyLimit > 0 ? Math.max(0, Math.min(1, used / monthlyLimit)) : 0);
|
|
244
|
+
var currency = eu.currency || '';
|
|
245
|
+
return {
|
|
246
|
+
used: (currency + used.toFixed(2)).trim(),
|
|
247
|
+
limit: (currency + monthlyLimit.toFixed(2)).trim(),
|
|
248
|
+
utilizationPct: Math.round(utilization * 100),
|
|
249
|
+
status: classifyUtilization(utilization),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Cache the last seen quota response so the /profiles/list refresh can
|
|
254
|
+
// keep showing usage even if a single /v1/usage/quota/all call fails.
|
|
255
|
+
var lastQuota = null;
|
|
256
|
+
|
|
148
257
|
async function refresh() {
|
|
149
258
|
try {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
259
|
+
var [profilesRes, quotaRes] = await Promise.all([
|
|
260
|
+
fetch('/profiles/list'),
|
|
261
|
+
fetch('/v1/usage/quota/all').catch(function () { return null; }),
|
|
262
|
+
]);
|
|
263
|
+
var profiles = await profilesRes.json();
|
|
264
|
+
var quota = null;
|
|
265
|
+
if (quotaRes && quotaRes.ok) {
|
|
266
|
+
try { quota = await quotaRes.json(); } catch (_) { quota = null; }
|
|
267
|
+
}
|
|
268
|
+
if (quota) lastQuota = quota;
|
|
269
|
+
render(profiles, lastQuota);
|
|
153
270
|
} catch {
|
|
154
271
|
document.getElementById('content').innerHTML = '<div class="empty-state"><h2>Could not load profiles</h2><p>Is Meridian running?</p></div>';
|
|
155
272
|
}
|
|
@@ -157,9 +274,82 @@ async function refresh() {
|
|
|
157
274
|
|
|
158
275
|
function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
|
159
276
|
|
|
160
|
-
function
|
|
277
|
+
function renderUsageSection(profileQuota) {
|
|
278
|
+
// No quota data for this profile yet (cold start or fetch failed) — hide
|
|
279
|
+
// entirely so we don't render an empty box.
|
|
280
|
+
if (!profileQuota) return '';
|
|
281
|
+
// API-key profiles cannot use OAuth usage — silently omit.
|
|
282
|
+
if (profileQuota.error === 'not_oauth') return '';
|
|
283
|
+
|
|
284
|
+
var windows = (profileQuota.windows || []).filter(function (w) {
|
|
285
|
+
return typeof w.utilization === 'number';
|
|
286
|
+
});
|
|
287
|
+
var extra = formatExtraUsage(profileQuota.extraUsage);
|
|
288
|
+
|
|
289
|
+
if (windows.length === 0 && !extra) {
|
|
290
|
+
if (profileQuota.error === 'no_token') {
|
|
291
|
+
return '<div class="usage-section">'
|
|
292
|
+
+ '<div class="usage-section-title">Usage</div>'
|
|
293
|
+
+ '<div class="usage-empty">Run <code style="background:var(--bg);padding:1px 5px;border-radius:3px">claude login</code> to see usage.</div>'
|
|
294
|
+
+ '</div>';
|
|
295
|
+
}
|
|
296
|
+
return ''; // nothing fetched yet
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
var asOf = profileQuota.fetchedAt
|
|
300
|
+
? '<span class="usage-as-of">updated ' + timeAgo(profileQuota.fetchedAt) + '</span>'
|
|
301
|
+
: '';
|
|
302
|
+
|
|
303
|
+
var cards = windows.map(function (w) {
|
|
304
|
+
var pct = Math.max(0, Math.min(1, w.utilization));
|
|
305
|
+
var pctRound = Math.round(pct * 100);
|
|
306
|
+
var status = classifyUtilization(pct);
|
|
307
|
+
var label = labelForWindow(w.type);
|
|
308
|
+
var reset = formatResetCountdown(w.resetsAt);
|
|
309
|
+
var tip = label + ' — ' + pctRound + '%' + (reset ? ' (resets ' + reset + ')' : '');
|
|
310
|
+
return '<div class="usage-card status-' + esc(status) + '" title="' + esc(tip) + '">'
|
|
311
|
+
+ '<div class="usage-row">'
|
|
312
|
+
+ '<span class="usage-label">' + esc(label) + '</span>'
|
|
313
|
+
+ '<span class="usage-pct">' + pctRound + '%</span>'
|
|
314
|
+
+ '</div>'
|
|
315
|
+
+ '<div class="usage-bar"><div class="usage-fill" style="width:' + (pct * 100).toFixed(1) + '%"></div></div>'
|
|
316
|
+
+ (reset ? '<div class="usage-reset">' + esc(reset) + '</div>' : '')
|
|
317
|
+
+ '</div>';
|
|
318
|
+
}).join('');
|
|
319
|
+
|
|
320
|
+
var extraBlock = '';
|
|
321
|
+
if (extra) {
|
|
322
|
+
extraBlock = '<div class="usage-extra status-' + esc(extra.status) + '">'
|
|
323
|
+
+ '<div class="usage-extra-row">'
|
|
324
|
+
+ '<span class="usage-label">Extra usage</span>'
|
|
325
|
+
+ '<span class="usage-pct">' + extra.utilizationPct + '%</span>'
|
|
326
|
+
+ '</div>'
|
|
327
|
+
+ '<div class="usage-bar"><div class="usage-fill" style="width:' + extra.utilizationPct + '%"></div></div>'
|
|
328
|
+
+ '<div class="usage-extra-row" style="margin-top:4px">'
|
|
329
|
+
+ '<span class="usage-reset">' + esc(extra.used) + ' / ' + esc(extra.limit) + '</span>'
|
|
330
|
+
+ '</div>'
|
|
331
|
+
+ '</div>';
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return '<div class="usage-section">'
|
|
335
|
+
+ '<div class="usage-section-title">Usage' + asOf + '</div>'
|
|
336
|
+
+ (cards ? '<div class="usage-grid">' + cards + '</div>' : '')
|
|
337
|
+
+ extraBlock
|
|
338
|
+
+ '</div>';
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function render(data, quotaData) {
|
|
161
342
|
const profiles = data.profiles || [];
|
|
162
343
|
const active = data.activeProfile;
|
|
344
|
+
// Build quick lookup: profileId -> per-profile quota entry from
|
|
345
|
+
// /v1/usage/quota/all. Endpoint may be unavailable (older Meridian)
|
|
346
|
+
// or have errored — in that case quotaById is empty and the per-card
|
|
347
|
+
// renderer simply hides its usage section.
|
|
348
|
+
const quotaProfiles = (quotaData && Array.isArray(quotaData.profiles)) ? quotaData.profiles : [];
|
|
349
|
+
const quotaById = {};
|
|
350
|
+
for (var qi = 0; qi < quotaProfiles.length; qi++) {
|
|
351
|
+
quotaById[quotaProfiles[qi].id] = quotaProfiles[qi];
|
|
352
|
+
}
|
|
163
353
|
|
|
164
354
|
if (profiles.length === 0) {
|
|
165
355
|
document.getElementById('content').innerHTML = '<div class="empty-state">'
|
|
@@ -218,6 +408,8 @@ function render(data) {
|
|
|
218
408
|
html += '</button>';
|
|
219
409
|
html += '</div>';
|
|
220
410
|
|
|
411
|
+
html += renderUsageSection(quotaById[p.id]);
|
|
412
|
+
|
|
221
413
|
if (!isActive) {
|
|
222
414
|
html += '<button class="switch-btn" onclick="switchProfile("'+esc(p.id)+'")">Switch to ' + esc(p.id) + '</button>';
|
|
223
415
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"droid.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/droid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAwC9C,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"droid.d.ts","sourceRoot":"","sources":["../../../src/proxy/adapters/droid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAwC9C,eAAO,MAAM,YAAY,EAAE,YAmF1B,CAAA;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,CAAA"}
|
|
@@ -50,4 +50,6 @@ export declare function mapModelTier(model?: string): "sonnet" | "opus" | "opus[
|
|
|
50
50
|
* @param mcpToolNames - Optional list of MCP tool names to make available to agents
|
|
51
51
|
*/
|
|
52
52
|
export declare function buildAgentDefinitions(taskDescription: string, mcpToolNames?: string[]): Record<string, AgentDefinition>;
|
|
53
|
+
export declare function parseAgentNamesFromSchema(taskTool: unknown): string[];
|
|
54
|
+
export declare function buildAgentDefinitionsFromTool(taskTool: unknown, mcpToolNames?: string[]): Record<string, AgentDefinition>;
|
|
53
55
|
//# sourceMappingURL=agentDefs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentDefs.d.ts","sourceRoot":"","sources":["../../src/proxy/agentDefs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB,YAAY,CAAA;AAc5C,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IAC/C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcnF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAOjG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAsBjC"}
|
|
1
|
+
{"version":3,"file":"agentDefs.d.ts","sourceRoot":"","sources":["../../src/proxy/agentDefs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,mBAAmB,YAAY,CAAA;AAc5C,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IAC/C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAcnF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAOjG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAsBjC;AA4ED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAIrE;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,OAAO,EACjB,YAAY,CAAC,EAAE,MAAM,EAAE,GACtB,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAuBjC"}
|
package/dist/proxy/errors.d.ts
CHANGED
|
@@ -38,4 +38,46 @@ export declare function isRateLimitError(errMsg: string): boolean;
|
|
|
38
38
|
* sonnet[1m] or opus[1m]. The fix is to fall back to the base model.
|
|
39
39
|
*/
|
|
40
40
|
export declare function isExtraUsageRequiredError(errMsg: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Structured SDK-termination metadata extracted from raw error text.
|
|
43
|
+
* Used by diagnosticLog to surface why the SDK subprocess ended (max_turns,
|
|
44
|
+
* exit, abort) plus the captured stderr tail — info that classifyError
|
|
45
|
+
* collapses into a generic api_error.
|
|
46
|
+
*/
|
|
47
|
+
export interface SdkTermination {
|
|
48
|
+
reason: "max_turns" | "process_exit" | "aborted" | "unknown";
|
|
49
|
+
/** Turn count when reason=max_turns and parseable. */
|
|
50
|
+
turns?: number;
|
|
51
|
+
/** Exit code when reason=process_exit and parseable. */
|
|
52
|
+
exitCode?: number;
|
|
53
|
+
/** Captured "Subprocess stderr: …" tail (truncated). */
|
|
54
|
+
stderrTail?: string;
|
|
55
|
+
/** Truncated raw error message — set only when reason="unknown" so the log
|
|
56
|
+
* line stays self-contained for unrecognized SDK errors (e.g. so we can
|
|
57
|
+
* add a new pattern next time). */
|
|
58
|
+
rawTail?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse the raw error message thrown by the Claude Agent SDK into structured
|
|
62
|
+
* termination metadata. Pure function — no I/O.
|
|
63
|
+
*
|
|
64
|
+
* Returns reason="unknown" when the message doesn't match any recognized
|
|
65
|
+
* pattern; callers can still log it with whatever surrounding context they have.
|
|
66
|
+
*/
|
|
67
|
+
export declare function extractSdkTermination(errMsg: string): SdkTermination;
|
|
68
|
+
/**
|
|
69
|
+
* Render an SdkTermination plus request context as a single greppable log line.
|
|
70
|
+
* Matches the key=value style used by token-health diagnostic messages so all
|
|
71
|
+
* /telemetry/logs entries are uniform.
|
|
72
|
+
*
|
|
73
|
+
* Session IDs are truncated to 8 chars to keep lines short — full IDs are
|
|
74
|
+
* already on the parent telemetry record.
|
|
75
|
+
*/
|
|
76
|
+
export declare function formatSdkTermination(t: SdkTermination, ctx: {
|
|
77
|
+
model?: string;
|
|
78
|
+
requestSource?: string;
|
|
79
|
+
isResume?: boolean;
|
|
80
|
+
hasDeferredTools?: boolean;
|
|
81
|
+
sdkSessionId?: string;
|
|
82
|
+
}): string;
|
|
41
83
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/proxy/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CA+G7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAO3D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjE"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/proxy/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CA+G7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAO3D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,GAAG,SAAS,CAAA;IAC5D,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;wCAEoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAuBD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAuCpE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,CAAC,EAAE,cAAc,EACjB,GAAG,EAAE;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GACA,MAAM,CAYR"}
|
package/dist/proxy/models.d.ts
CHANGED
|
@@ -82,6 +82,31 @@ export declare function getClaudeAuthStatusAsync(profileId?: string, envOverride
|
|
|
82
82
|
* The promise is cleared in `finally` to allow retry on failure while
|
|
83
83
|
* cachedClaudePath prevents re-resolution on success.
|
|
84
84
|
*/
|
|
85
|
+
/**
|
|
86
|
+
* Resolver step contract — each tries one source, returns a path on success
|
|
87
|
+
* or null on miss. Failures (thrown errors) are caught by the caller and
|
|
88
|
+
* treated as misses so unresolved sources never block subsequent steps.
|
|
89
|
+
*/
|
|
90
|
+
type ResolverDeps = {
|
|
91
|
+
existsSync: (p: string) => boolean;
|
|
92
|
+
statSync: (p: string) => {
|
|
93
|
+
size: number;
|
|
94
|
+
};
|
|
95
|
+
exec: (cmd: string) => Promise<{
|
|
96
|
+
stdout: string;
|
|
97
|
+
}>;
|
|
98
|
+
resolvePackage: (specifier: string) => string;
|
|
99
|
+
envGet: (name: string) => string | undefined;
|
|
100
|
+
platform: NodeJS.Platform;
|
|
101
|
+
arch: string;
|
|
102
|
+
isBun: boolean;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Pure resolver — runs each step and returns the first hit, or null when
|
|
106
|
+
* all steps miss. Exported for unit tests; production callers use
|
|
107
|
+
* resolveClaudeExecutableAsync, which adds caching on top.
|
|
108
|
+
*/
|
|
109
|
+
export declare function resolveClaudeExecutable(deps?: ResolverDeps): Promise<string | null>;
|
|
85
110
|
export declare function resolveClaudeExecutableAsync(): Promise<string>;
|
|
86
111
|
/** Reset cached path — for testing only */
|
|
87
112
|
export declare function resetCachedClaudePath(): void;
|
|
@@ -96,4 +121,5 @@ export declare function expireAuthStatusCache(): void;
|
|
|
96
121
|
* This happens when the client disconnects mid-stream.
|
|
97
122
|
*/
|
|
98
123
|
export declare function isClosedControllerError(error: unknown): boolean;
|
|
124
|
+
export {};
|
|
99
125
|
//# sourceMappingURL=models.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/proxy/models.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/proxy/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoBH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,CAAA;AAEjF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,oBAAoB,CAAA;AACrD,eAAO,MAAM,sBAAsB,sBAAsB,CAAA;AACzD,eAAO,MAAM,qBAAqB,qBAAqB,CAAA;AAEvD;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMhE;AACD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AA0BD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CA8B7H;AAWD;;;;;;GAMG;AACH,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,IAAI,OAAO,CAG3D;AAED,0EAA0E;AAC1E,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CAIpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAE9D;AAaD;gFACgF;AAChF,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAOzH;AAWD;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAuD1I;AAOD;;;;;;;;;;GAUG;AACH;;;;GAIG;AACH,KAAK,YAAY,GAAG;IAClB,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;IAClC,QAAQ,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACzC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAC5C,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAA;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AA4HD;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,GAAE,YAA2B,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQvG;AAED,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,MAAM,CAAC,CAqBpE;AAED,2CAA2C;AAC3C,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED,kDAAkD;AAClD,wBAAgB,2BAA2B,IAAI,IAAI,CAOlD;AAED;;6DAE6D;AAC7D,wBAAgB,qBAAqB,IAAI,IAAI,CAO5C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG/D"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continuous OAuth usage fetching from Anthropic's private OAuth endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Anthropic exposes `GET https://api.anthropic.com/api/oauth/usage` for OAuth
|
|
5
|
+
* (Claude Max) subscribers. Unlike the SDK's `rate_limit_event` (which only
|
|
6
|
+
* populates `utilization` near `allowed_warning` / `rejected`), this endpoint
|
|
7
|
+
* always returns continuous percentage values for every active rate-limit
|
|
8
|
+
* window — exactly what claude.ai's own UI uses.
|
|
9
|
+
*
|
|
10
|
+
* Headers required:
|
|
11
|
+
* Authorization: Bearer <oauth-access-token>
|
|
12
|
+
* anthropic-beta: oauth-2025-04-20
|
|
13
|
+
*
|
|
14
|
+
* We reuse `tokenRefresh.ts`'s cross-platform credential store (macOS Keychain
|
|
15
|
+
* or `~/.claude/.credentials.json`) to read the access token, and trigger a
|
|
16
|
+
* background refresh on 401.
|
|
17
|
+
*
|
|
18
|
+
* Per-profile caching: each profile has its own 30s TTL cache so multi-account
|
|
19
|
+
* setups can be queried independently without cross-contamination. Concurrent
|
|
20
|
+
* callers for the same profile share a single in-flight request.
|
|
21
|
+
*/
|
|
22
|
+
import { type CredentialStore } from "./tokenRefresh";
|
|
23
|
+
export interface OAuthUsageWindow {
|
|
24
|
+
type: string;
|
|
25
|
+
utilization: number | null;
|
|
26
|
+
resetsAt: number | null;
|
|
27
|
+
}
|
|
28
|
+
export interface OAuthExtraUsageInfo {
|
|
29
|
+
isEnabled: boolean;
|
|
30
|
+
monthlyLimit: number;
|
|
31
|
+
usedCredits: number;
|
|
32
|
+
utilization: number | null;
|
|
33
|
+
currency: string;
|
|
34
|
+
}
|
|
35
|
+
export interface OAuthUsageSnapshot {
|
|
36
|
+
windows: OAuthUsageWindow[];
|
|
37
|
+
extraUsage: OAuthExtraUsageInfo | null;
|
|
38
|
+
fetchedAt: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Fetch latest OAuth usage for a specific profile (or the default OAuth
|
|
42
|
+
* account if none specified). Returns null if no OAuth token is available
|
|
43
|
+
* or the upstream call fails (after one refresh attempt).
|
|
44
|
+
*
|
|
45
|
+
* Per-profile in-process cache (30s TTL by default) prevents hammering
|
|
46
|
+
* Anthropic's endpoint when many clients poll concurrently. Concurrent
|
|
47
|
+
* callers for the same profile share a single in-flight request.
|
|
48
|
+
*
|
|
49
|
+
* @param ttlMs Override the cache TTL (default 30s).
|
|
50
|
+
* @param force Bypass the cache and fetch fresh.
|
|
51
|
+
* @param store Override the credential store (for testing).
|
|
52
|
+
* @param profileId Logical profile identifier used as the cache key.
|
|
53
|
+
* Pass null/undefined for the default OAuth account.
|
|
54
|
+
* @param claudeConfigDir When provided, reads credentials from this dir's
|
|
55
|
+
* keychain entry (macOS) or `.credentials.json`
|
|
56
|
+
* (Linux) instead of the platform default.
|
|
57
|
+
*/
|
|
58
|
+
export declare function fetchOAuthUsage(opts?: {
|
|
59
|
+
ttlMs?: number;
|
|
60
|
+
force?: boolean;
|
|
61
|
+
store?: CredentialStore;
|
|
62
|
+
profileId?: string | null;
|
|
63
|
+
claudeConfigDir?: string;
|
|
64
|
+
}): Promise<OAuthUsageSnapshot | null>;
|
|
65
|
+
/** Test-only / shutdown helper — clears all cached snapshots and pending fetches. */
|
|
66
|
+
export declare function resetOAuthUsageCache(): void;
|
|
67
|
+
//# sourceMappingURL=oauthUsage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthUsage.d.ts","sourceRoot":"","sources":["../../src/proxy/oauthUsage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAoD,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAgCvG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AA0ED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CAAC,IAAI,CAAC,EAAE;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAgDrC;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
|