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.
Files changed (36) hide show
  1. package/README.md +180 -0
  2. package/config/constants.js +206 -0
  3. package/config/defaultThinkingSignature.js +7 -0
  4. package/config/ollamaModels.js +19 -0
  5. package/config/providerModels.js +161 -0
  6. package/handlers/chatCore.js +277 -0
  7. package/handlers/responsesHandler.js +69 -0
  8. package/index.js +69 -0
  9. package/package.json +44 -0
  10. package/services/accountFallback.js +148 -0
  11. package/services/combo.js +69 -0
  12. package/services/compact.js +64 -0
  13. package/services/model.js +109 -0
  14. package/services/provider.js +237 -0
  15. package/services/tokenRefresh.js +542 -0
  16. package/services/usage.js +398 -0
  17. package/translator/formats.js +12 -0
  18. package/translator/from-openai/claude.js +341 -0
  19. package/translator/from-openai/gemini.js +469 -0
  20. package/translator/from-openai/openai-responses.js +361 -0
  21. package/translator/helpers/claudeHelper.js +179 -0
  22. package/translator/helpers/geminiHelper.js +131 -0
  23. package/translator/helpers/openaiHelper.js +80 -0
  24. package/translator/helpers/responsesApiHelper.js +103 -0
  25. package/translator/helpers/toolCallHelper.js +111 -0
  26. package/translator/index.js +167 -0
  27. package/translator/to-openai/claude.js +238 -0
  28. package/translator/to-openai/gemini.js +151 -0
  29. package/translator/to-openai/openai-responses.js +140 -0
  30. package/translator/to-openai/openai.js +371 -0
  31. package/utils/bypassHandler.js +258 -0
  32. package/utils/error.js +133 -0
  33. package/utils/ollamaTransform.js +82 -0
  34. package/utils/requestLogger.js +217 -0
  35. package/utils/stream.js +274 -0
  36. 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
+