open-sse 1.0.0
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/README.md +180 -0
- package/config/constants.js +206 -0
- package/config/defaultThinkingSignature.js +7 -0
- package/config/ollamaModels.js +19 -0
- package/config/providerModels.js +161 -0
- package/handlers/chatCore.js +277 -0
- package/handlers/responsesHandler.js +69 -0
- package/index.js +69 -0
- package/package.json +44 -0
- package/services/accountFallback.js +148 -0
- package/services/combo.js +69 -0
- package/services/compact.js +64 -0
- package/services/model.js +109 -0
- package/services/provider.js +237 -0
- package/services/tokenRefresh.js +542 -0
- package/services/usage.js +398 -0
- package/translator/formats.js +12 -0
- package/translator/from-openai/claude.js +341 -0
- package/translator/from-openai/gemini.js +469 -0
- package/translator/from-openai/openai-responses.js +361 -0
- package/translator/helpers/claudeHelper.js +179 -0
- package/translator/helpers/geminiHelper.js +131 -0
- package/translator/helpers/openaiHelper.js +80 -0
- package/translator/helpers/responsesApiHelper.js +103 -0
- package/translator/helpers/toolCallHelper.js +111 -0
- package/translator/index.js +167 -0
- package/translator/to-openai/claude.js +238 -0
- package/translator/to-openai/gemini.js +151 -0
- package/translator/to-openai/openai-responses.js +140 -0
- package/translator/to-openai/openai.js +371 -0
- package/utils/bypassHandler.js +258 -0
- package/utils/error.js +133 -0
- package/utils/ollamaTransform.js +82 -0
- package/utils/requestLogger.js +217 -0
- package/utils/stream.js +274 -0
- package/utils/streamHandler.js +131 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage Fetcher - Get usage data from provider APIs
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// GitHub API config
|
|
6
|
+
const GITHUB_CONFIG = {
|
|
7
|
+
apiVersion: "2022-11-28",
|
|
8
|
+
userAgent: "GitHubCopilotChat/0.26.7",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Antigravity API config (from Quotio)
|
|
12
|
+
const ANTIGRAVITY_CONFIG = {
|
|
13
|
+
quotaApiUrl: "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels",
|
|
14
|
+
loadProjectApiUrl: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
|
|
15
|
+
tokenUrl: "https://oauth2.googleapis.com/token",
|
|
16
|
+
clientId: "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com",
|
|
17
|
+
clientSecret: "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",
|
|
18
|
+
userAgent: "antigravity/1.11.3 Darwin/arm64",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Codex (OpenAI) API config
|
|
22
|
+
const CODEX_CONFIG = {
|
|
23
|
+
usageUrl: "https://chatgpt.com/backend-api/wham/usage",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Claude API config
|
|
27
|
+
const CLAUDE_CONFIG = {
|
|
28
|
+
usageUrl: "https://api.anthropic.com/v1/organizations/{org_id}/usage",
|
|
29
|
+
settingsUrl: "https://api.anthropic.com/v1/settings",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get usage data for a provider connection
|
|
34
|
+
* @param {Object} connection - Provider connection with accessToken
|
|
35
|
+
* @returns {Object} Usage data with quotas
|
|
36
|
+
*/
|
|
37
|
+
export async function getUsageForProvider(connection) {
|
|
38
|
+
const { provider, accessToken, providerSpecificData } = connection;
|
|
39
|
+
|
|
40
|
+
switch (provider) {
|
|
41
|
+
case "github":
|
|
42
|
+
return await getGitHubUsage(accessToken, providerSpecificData);
|
|
43
|
+
case "gemini-cli":
|
|
44
|
+
return await getGeminiUsage(accessToken);
|
|
45
|
+
case "antigravity":
|
|
46
|
+
return await getAntigravityUsage(accessToken);
|
|
47
|
+
case "claude":
|
|
48
|
+
return await getClaudeUsage(accessToken);
|
|
49
|
+
case "codex":
|
|
50
|
+
return await getCodexUsage(accessToken);
|
|
51
|
+
case "qwen":
|
|
52
|
+
return await getQwenUsage(accessToken, providerSpecificData);
|
|
53
|
+
case "iflow":
|
|
54
|
+
return await getIflowUsage(accessToken);
|
|
55
|
+
default:
|
|
56
|
+
return { message: `Usage API not implemented for ${provider}` };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* GitHub Copilot Usage
|
|
62
|
+
*/
|
|
63
|
+
async function getGitHubUsage(accessToken, providerSpecificData) {
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch("https://api.github.com/copilot_internal/user", {
|
|
66
|
+
headers: {
|
|
67
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
68
|
+
"Accept": "application/json",
|
|
69
|
+
"X-GitHub-Api-Version": GITHUB_CONFIG.apiVersion,
|
|
70
|
+
"User-Agent": GITHUB_CONFIG.userAgent,
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
const error = await response.text();
|
|
76
|
+
throw new Error(`GitHub API error: ${error}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
|
|
81
|
+
// Handle different response formats (paid vs free)
|
|
82
|
+
if (data.quota_snapshots) {
|
|
83
|
+
// Paid plan format
|
|
84
|
+
const snapshots = data.quota_snapshots;
|
|
85
|
+
return {
|
|
86
|
+
plan: data.copilot_plan,
|
|
87
|
+
resetDate: data.quota_reset_date,
|
|
88
|
+
quotas: {
|
|
89
|
+
chat: formatGitHubQuotaSnapshot(snapshots.chat),
|
|
90
|
+
completions: formatGitHubQuotaSnapshot(snapshots.completions),
|
|
91
|
+
premium_interactions: formatGitHubQuotaSnapshot(snapshots.premium_interactions),
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
} else if (data.monthly_quotas || data.limited_user_quotas) {
|
|
95
|
+
// Free/limited plan format
|
|
96
|
+
const monthlyQuotas = data.monthly_quotas || {};
|
|
97
|
+
const usedQuotas = data.limited_user_quotas || {};
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
plan: data.copilot_plan || data.access_type_sku,
|
|
101
|
+
resetDate: data.limited_user_reset_date,
|
|
102
|
+
quotas: {
|
|
103
|
+
chat: {
|
|
104
|
+
used: usedQuotas.chat || 0,
|
|
105
|
+
total: monthlyQuotas.chat || 0,
|
|
106
|
+
unlimited: false,
|
|
107
|
+
},
|
|
108
|
+
completions: {
|
|
109
|
+
used: usedQuotas.completions || 0,
|
|
110
|
+
total: monthlyQuotas.completions || 0,
|
|
111
|
+
unlimited: false,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { message: "GitHub Copilot connected. Unable to parse quota data." };
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new Error(`Failed to fetch GitHub usage: ${error.message}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function formatGitHubQuotaSnapshot(quota) {
|
|
124
|
+
if (!quota) return { used: 0, total: 0, unlimited: true };
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
used: quota.entitlement - quota.remaining,
|
|
128
|
+
total: quota.entitlement,
|
|
129
|
+
remaining: quota.remaining,
|
|
130
|
+
unlimited: quota.unlimited || false,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Gemini CLI Usage (Google Cloud)
|
|
136
|
+
*/
|
|
137
|
+
async function getGeminiUsage(accessToken) {
|
|
138
|
+
try {
|
|
139
|
+
// Gemini CLI uses Google Cloud quotas
|
|
140
|
+
// Try to get quota info from Cloud Resource Manager
|
|
141
|
+
const response = await fetch(
|
|
142
|
+
"https://cloudresourcemanager.googleapis.com/v1/projects?filter=lifecycleState:ACTIVE",
|
|
143
|
+
{
|
|
144
|
+
headers: {
|
|
145
|
+
Authorization: `Bearer ${accessToken}`,
|
|
146
|
+
Accept: "application/json",
|
|
147
|
+
},
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
// Quota API may not be accessible, return generic message
|
|
153
|
+
return { message: "Gemini CLI uses Google Cloud quotas. Check Google Cloud Console for details." };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return { message: "Gemini CLI connected. Usage tracked via Google Cloud Console." };
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return { message: "Unable to fetch Gemini usage. Check Google Cloud Console." };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Antigravity Usage - Fetch quota from Google Cloud Code API
|
|
164
|
+
*/
|
|
165
|
+
async function getAntigravityUsage(accessToken, providerSpecificData) {
|
|
166
|
+
try {
|
|
167
|
+
// First get project ID from subscription info
|
|
168
|
+
const projectId = await getAntigravityProjectId(accessToken);
|
|
169
|
+
|
|
170
|
+
// Fetch quota data
|
|
171
|
+
const response = await fetch(ANTIGRAVITY_CONFIG.quotaApiUrl, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers: {
|
|
174
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
175
|
+
"User-Agent": ANTIGRAVITY_CONFIG.userAgent,
|
|
176
|
+
"Content-Type": "application/json",
|
|
177
|
+
},
|
|
178
|
+
body: JSON.stringify(projectId ? { project: projectId } : {}),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (response.status === 403) {
|
|
182
|
+
return { message: "Antigravity access forbidden. Check subscription." };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(`Antigravity API error: ${response.status}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
const quotas = {};
|
|
191
|
+
|
|
192
|
+
// Parse model quotas
|
|
193
|
+
if (data.models) {
|
|
194
|
+
for (const [name, info] of Object.entries(data.models)) {
|
|
195
|
+
// Only include gemini and claude models
|
|
196
|
+
if (!name.includes("gemini") && !name.includes("claude")) continue;
|
|
197
|
+
|
|
198
|
+
if (info.quotaInfo) {
|
|
199
|
+
const percentage = (info.quotaInfo.remainingFraction || 0) * 100;
|
|
200
|
+
quotas[name] = {
|
|
201
|
+
remaining: percentage,
|
|
202
|
+
resetTime: info.quotaInfo.resetTime || "",
|
|
203
|
+
unlimited: false,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Get subscription info for plan type
|
|
210
|
+
const subscriptionInfo = await getAntigravitySubscriptionInfo(accessToken);
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
plan: subscriptionInfo?.currentTier?.name || "Unknown",
|
|
214
|
+
quotas,
|
|
215
|
+
subscriptionInfo,
|
|
216
|
+
};
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return { message: `Antigravity error: ${error.message}` };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get Antigravity project ID from subscription info
|
|
224
|
+
*/
|
|
225
|
+
async function getAntigravityProjectId(accessToken) {
|
|
226
|
+
try {
|
|
227
|
+
const info = await getAntigravitySubscriptionInfo(accessToken);
|
|
228
|
+
return info?.cloudaicompanionProject || null;
|
|
229
|
+
} catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get Antigravity subscription info
|
|
236
|
+
*/
|
|
237
|
+
async function getAntigravitySubscriptionInfo(accessToken) {
|
|
238
|
+
try {
|
|
239
|
+
const response = await fetch(ANTIGRAVITY_CONFIG.loadProjectApiUrl, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: {
|
|
242
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
243
|
+
"User-Agent": ANTIGRAVITY_CONFIG.userAgent,
|
|
244
|
+
"Content-Type": "application/json",
|
|
245
|
+
},
|
|
246
|
+
body: JSON.stringify({ metadata: { ideType: "ANTIGRAVITY" } }),
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
if (!response.ok) return null;
|
|
250
|
+
|
|
251
|
+
return await response.json();
|
|
252
|
+
} catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Claude Usage - Try to fetch from Anthropic API
|
|
259
|
+
*/
|
|
260
|
+
async function getClaudeUsage(accessToken) {
|
|
261
|
+
try {
|
|
262
|
+
// Try to get organization/account settings first
|
|
263
|
+
const settingsResponse = await fetch("https://api.anthropic.com/v1/settings", {
|
|
264
|
+
method: "GET",
|
|
265
|
+
headers: {
|
|
266
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
267
|
+
"Content-Type": "application/json",
|
|
268
|
+
"anthropic-version": "2023-06-01",
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
if (settingsResponse.ok) {
|
|
273
|
+
const settings = await settingsResponse.json();
|
|
274
|
+
|
|
275
|
+
// Try usage endpoint if we have org info
|
|
276
|
+
if (settings.organization_id) {
|
|
277
|
+
const usageResponse = await fetch(
|
|
278
|
+
`https://api.anthropic.com/v1/organizations/${settings.organization_id}/usage`,
|
|
279
|
+
{
|
|
280
|
+
method: "GET",
|
|
281
|
+
headers: {
|
|
282
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
283
|
+
"Content-Type": "application/json",
|
|
284
|
+
"anthropic-version": "2023-06-01",
|
|
285
|
+
},
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
if (usageResponse.ok) {
|
|
290
|
+
const usage = await usageResponse.json();
|
|
291
|
+
return {
|
|
292
|
+
plan: settings.plan || "Unknown",
|
|
293
|
+
organization: settings.organization_name,
|
|
294
|
+
quotas: usage,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
plan: settings.plan || "Unknown",
|
|
301
|
+
organization: settings.organization_name,
|
|
302
|
+
message: "Claude connected. Usage details require admin access.",
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// If settings API fails, OAuth token may not have required scope
|
|
307
|
+
return { message: "Claude connected. Usage API requires admin permissions." };
|
|
308
|
+
} catch (error) {
|
|
309
|
+
return { message: `Claude connected. Unable to fetch usage: ${error.message}` };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Codex (OpenAI) Usage - Fetch from ChatGPT backend API
|
|
315
|
+
*/
|
|
316
|
+
async function getCodexUsage(accessToken) {
|
|
317
|
+
try {
|
|
318
|
+
const response = await fetch(CODEX_CONFIG.usageUrl, {
|
|
319
|
+
method: "GET",
|
|
320
|
+
headers: {
|
|
321
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
322
|
+
"Accept": "application/json",
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
throw new Error(`Codex API error: ${response.status}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const data = await response.json();
|
|
331
|
+
|
|
332
|
+
// Parse rate limit info
|
|
333
|
+
const rateLimit = data.rate_limit || {};
|
|
334
|
+
const primaryWindow = rateLimit.primary_window || {};
|
|
335
|
+
const secondaryWindow = rateLimit.secondary_window || {};
|
|
336
|
+
|
|
337
|
+
// Calculate reset dates
|
|
338
|
+
const sessionResetAt = primaryWindow.reset_at
|
|
339
|
+
? new Date(primaryWindow.reset_at * 1000).toISOString()
|
|
340
|
+
: null;
|
|
341
|
+
const weeklyResetAt = secondaryWindow.reset_at
|
|
342
|
+
? new Date(secondaryWindow.reset_at * 1000).toISOString()
|
|
343
|
+
: null;
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
plan: data.plan_type || "unknown",
|
|
347
|
+
limitReached: rateLimit.limit_reached || false,
|
|
348
|
+
quotas: {
|
|
349
|
+
session: {
|
|
350
|
+
used: primaryWindow.used_percent || 0,
|
|
351
|
+
total: 100,
|
|
352
|
+
remaining: 100 - (primaryWindow.used_percent || 0),
|
|
353
|
+
resetTime: sessionResetAt,
|
|
354
|
+
unlimited: false,
|
|
355
|
+
},
|
|
356
|
+
weekly: {
|
|
357
|
+
used: secondaryWindow.used_percent || 0,
|
|
358
|
+
total: 100,
|
|
359
|
+
remaining: 100 - (secondaryWindow.used_percent || 0),
|
|
360
|
+
resetTime: weeklyResetAt,
|
|
361
|
+
unlimited: false,
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
} catch (error) {
|
|
366
|
+
throw new Error(`Failed to fetch Codex usage: ${error.message}`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Qwen Usage
|
|
372
|
+
*/
|
|
373
|
+
async function getQwenUsage(accessToken, providerSpecificData) {
|
|
374
|
+
try {
|
|
375
|
+
const resourceUrl = providerSpecificData?.resourceUrl;
|
|
376
|
+
if (!resourceUrl) {
|
|
377
|
+
return { message: "Qwen connected. No resource URL available." };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Qwen may have usage endpoint at resource URL
|
|
381
|
+
return { message: "Qwen connected. Usage tracked per request." };
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return { message: "Unable to fetch Qwen usage." };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* iFlow Usage
|
|
389
|
+
*/
|
|
390
|
+
async function getIflowUsage(accessToken) {
|
|
391
|
+
try {
|
|
392
|
+
// iFlow may have usage endpoint
|
|
393
|
+
return { message: "iFlow connected. Usage tracked per request." };
|
|
394
|
+
} catch (error) {
|
|
395
|
+
return { message: "Unable to fetch iFlow usage." };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Format identifiers
|
|
2
|
+
export const FORMATS = {
|
|
3
|
+
OPENAI: "openai",
|
|
4
|
+
OPENAI_RESPONSES: "openai-responses",
|
|
5
|
+
OPENAI_RESPONSE: "openai-response",
|
|
6
|
+
CLAUDE: "claude",
|
|
7
|
+
GEMINI: "gemini",
|
|
8
|
+
GEMINI_CLI: "gemini-cli",
|
|
9
|
+
CODEX: "codex",
|
|
10
|
+
ANTIGRAVITY: "antigravity"
|
|
11
|
+
};
|
|
12
|
+
|