superoc 0.1.23 → 0.1.25
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/index.d.ts +60 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +468 -194
- package/dist/index.js.map +1 -1
- package/dist/opencode-sync.d.ts.map +1 -1
- package/dist/opencode-sync.js +54 -6
- package/dist/opencode-sync.js.map +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.js +52 -2
package/dist/index.js
CHANGED
|
@@ -74,6 +74,203 @@ function findChainIndex(chain, model) {
|
|
|
74
74
|
return -1;
|
|
75
75
|
return chain.findIndex((entry) => entry.id === model.modelID);
|
|
76
76
|
}
|
|
77
|
+
function createSseUnwrapTransform() {
|
|
78
|
+
const decoder = new TextDecoder();
|
|
79
|
+
const encoder = new TextEncoder();
|
|
80
|
+
let buffer = "";
|
|
81
|
+
return new TransformStream({
|
|
82
|
+
transform(chunk, controller) {
|
|
83
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
84
|
+
const lines = buffer.split("\n");
|
|
85
|
+
buffer = lines.pop() || "";
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
if (line.startsWith("data:")) {
|
|
88
|
+
const jsonStr = line.slice(5).trim();
|
|
89
|
+
if (!jsonStr) {
|
|
90
|
+
controller.enqueue(encoder.encode(line + "\n"));
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const parsed = JSON.parse(jsonStr);
|
|
95
|
+
if (parsed.response !== undefined) {
|
|
96
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(parsed.response)}\n`));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch { }
|
|
101
|
+
}
|
|
102
|
+
controller.enqueue(encoder.encode(line + "\n"));
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
flush(controller) {
|
|
106
|
+
if (buffer.length > 0) {
|
|
107
|
+
if (buffer.startsWith("data:")) {
|
|
108
|
+
const jsonStr = buffer.slice(5).trim();
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(jsonStr);
|
|
111
|
+
if (parsed.response !== undefined) {
|
|
112
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(parsed.response)}\n`));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch { }
|
|
117
|
+
}
|
|
118
|
+
controller.enqueue(encoder.encode(buffer));
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function createAntigravityFetch(store, config, reloadFromDisk, safeSaveStore) {
|
|
124
|
+
return async (input, init, fallbackFetch = fetch) => {
|
|
125
|
+
const urlString = typeof input === "string"
|
|
126
|
+
? input
|
|
127
|
+
: input instanceof URL
|
|
128
|
+
? input.toString()
|
|
129
|
+
: input.url;
|
|
130
|
+
if (urlString.includes("generativelanguage.googleapis.com") || urlString.includes("antigravity")) {
|
|
131
|
+
const match = urlString.match(/\/models\/([^:]+):(\w+)/);
|
|
132
|
+
const rawModel = match ? match[1] : "";
|
|
133
|
+
const action = match ? match[2] : "streamGenerateContent";
|
|
134
|
+
const isStreaming = action === "streamGenerateContent" || urlString.includes("alt=sse");
|
|
135
|
+
const isAntigravityModel = rawModel.startsWith("antigravity-") ||
|
|
136
|
+
rawModel in BASE_ANTIGRAVITY_MODELS ||
|
|
137
|
+
/claude|gpt-oss|gemini-3|gemini-pro-agent/i.test(rawModel);
|
|
138
|
+
reloadFromDisk();
|
|
139
|
+
const activeAntigravityKeys = getActiveKeys(store, "antigravity");
|
|
140
|
+
if (isAntigravityModel || activeAntigravityKeys.length > 0) {
|
|
141
|
+
if (init?.signal?.aborted) {
|
|
142
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
143
|
+
}
|
|
144
|
+
let attempts = 0;
|
|
145
|
+
let lastResponse = null;
|
|
146
|
+
const maxAttempts = Math.max(1, activeAntigravityKeys.length);
|
|
147
|
+
while (attempts < maxAttempts) {
|
|
148
|
+
if (init?.signal?.aborted) {
|
|
149
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
150
|
+
}
|
|
151
|
+
attempts++;
|
|
152
|
+
const next = getNextKey(store, config, rawModel, "antigravity");
|
|
153
|
+
if (!next)
|
|
154
|
+
break;
|
|
155
|
+
const authRes = await getOrRefreshAntigravityAccessToken(next.key.key);
|
|
156
|
+
if (!authRes) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (init?.signal?.aborted) {
|
|
160
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
161
|
+
}
|
|
162
|
+
const effectiveModel = rawModel.replace(/^antigravity-/, "");
|
|
163
|
+
const candidateModels = [effectiveModel];
|
|
164
|
+
if (!effectiveModel.endsWith("-tiered") &&
|
|
165
|
+
(effectiveModel.includes("flash") || effectiveModel.includes("pro"))) {
|
|
166
|
+
candidateModels.push(`${effectiveModel}-tiered`);
|
|
167
|
+
}
|
|
168
|
+
else if (effectiveModel.endsWith("-tiered")) {
|
|
169
|
+
candidateModels.push(effectiveModel.replace(/-tiered$/, ""));
|
|
170
|
+
}
|
|
171
|
+
let bodyStr = init?.body;
|
|
172
|
+
let parsedBody = typeof bodyStr === "string" ? JSON.parse(bodyStr) : bodyStr;
|
|
173
|
+
const headers = new Headers(init?.headers ?? {});
|
|
174
|
+
headers.set("Authorization", `Bearer ${authRes.accessToken}`);
|
|
175
|
+
headers.set("User-Agent", `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Antigravity/1.18.3 Chrome/138.0.7204.235 Electron/37.3.1 Safari/537.36`);
|
|
176
|
+
headers.set("X-Goog-Api-Client", "google-cloud-sdk vscode_cloudshelleditor/0.1");
|
|
177
|
+
headers.set("Client-Metadata", `{"ideType":"ANTIGRAVITY","platform":"WINDOWS","pluginType":"GEMINI"}`);
|
|
178
|
+
headers.delete("x-goog-api-key");
|
|
179
|
+
headers.delete("x-api-key");
|
|
180
|
+
headers.delete("x-goog-user-project");
|
|
181
|
+
const endpoints = [
|
|
182
|
+
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
183
|
+
"https://cloudcode-pa.googleapis.com",
|
|
184
|
+
];
|
|
185
|
+
let gotRes = null;
|
|
186
|
+
endpointLoop: for (const ep of endpoints) {
|
|
187
|
+
if (init?.signal?.aborted) {
|
|
188
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
189
|
+
}
|
|
190
|
+
for (const candidate of candidateModels) {
|
|
191
|
+
if (init?.signal?.aborted) {
|
|
192
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
193
|
+
}
|
|
194
|
+
const transformedUrl = `${ep}/v1internal:${action}${isStreaming ? "?alt=sse" : ""}`;
|
|
195
|
+
const wrappedBody = JSON.stringify({
|
|
196
|
+
project: authRes.projectId || "rising-fact-p41fc",
|
|
197
|
+
model: candidate,
|
|
198
|
+
request: parsedBody,
|
|
199
|
+
requestType: "agent",
|
|
200
|
+
userAgent: "antigravity",
|
|
201
|
+
});
|
|
202
|
+
try {
|
|
203
|
+
const r = await fetch(transformedUrl, {
|
|
204
|
+
...init,
|
|
205
|
+
headers,
|
|
206
|
+
body: wrappedBody,
|
|
207
|
+
});
|
|
208
|
+
if (r.ok) {
|
|
209
|
+
gotRes = r;
|
|
210
|
+
break endpointLoop;
|
|
211
|
+
}
|
|
212
|
+
if (r.status === 429) {
|
|
213
|
+
gotRes = r;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (netErr) {
|
|
217
|
+
if (netErr?.name === "AbortError" || init?.signal?.aborted) {
|
|
218
|
+
throw netErr;
|
|
219
|
+
}
|
|
220
|
+
if (process.env.SUPEROC_DEBUG === "true") {
|
|
221
|
+
console.warn(`[superoc] Endpoint ${ep} socket/network error:`, netErr);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (gotRes) {
|
|
227
|
+
lastResponse = gotRes;
|
|
228
|
+
}
|
|
229
|
+
if (gotRes && gotRes.ok) {
|
|
230
|
+
if (isStreaming && gotRes.body) {
|
|
231
|
+
const transformedStream = gotRes.body.pipeThrough(createSseUnwrapTransform());
|
|
232
|
+
return new Response(transformedStream, {
|
|
233
|
+
status: gotRes.status,
|
|
234
|
+
statusText: gotRes.statusText,
|
|
235
|
+
headers: gotRes.headers,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return gotRes;
|
|
239
|
+
}
|
|
240
|
+
if (gotRes && gotRes.status === 429) {
|
|
241
|
+
recordRateLimit(store, next.key.id);
|
|
242
|
+
recordModelRateLimit(store, next.key.id, rawModel);
|
|
243
|
+
safeSaveStore();
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (gotRes)
|
|
247
|
+
return gotRes;
|
|
248
|
+
}
|
|
249
|
+
if (isAntigravityModel) {
|
|
250
|
+
if (lastResponse)
|
|
251
|
+
return lastResponse;
|
|
252
|
+
return new Response(JSON.stringify({
|
|
253
|
+
error: {
|
|
254
|
+
code: 429,
|
|
255
|
+
message: "All Antigravity accounts are currently rate limited or exhausted.",
|
|
256
|
+
status: "RESOURCE_EXHAUSTED",
|
|
257
|
+
},
|
|
258
|
+
}), { status: 429, headers: { "Content-Type": "application/json" } });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return (fallbackFetch || fetch)(input, init);
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function installGlobalFetchInterceptor(fetchHandler) {
|
|
266
|
+
if (!globalThis.__superoc_fetch_installed) {
|
|
267
|
+
globalThis.__superoc_fetch_installed = true;
|
|
268
|
+
const origFetch = globalThis.fetch;
|
|
269
|
+
globalThis.fetch = async function (input, init) {
|
|
270
|
+
return fetchHandler(input, init, origFetch);
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
77
274
|
export const SuperocPlugin = async (input, options) => {
|
|
78
275
|
const client = input.client;
|
|
79
276
|
const config = {
|
|
@@ -428,52 +625,6 @@ export const SuperocPlugin = async (input, options) => {
|
|
|
428
625
|
const reason = `Rate limited (429) — ${state.rateLimitCount}/${store.maxRateLimitFailures} consecutive`;
|
|
429
626
|
await triggerRetry(sessionID, state, reason);
|
|
430
627
|
};
|
|
431
|
-
function createSseUnwrapTransform() {
|
|
432
|
-
const decoder = new TextDecoder();
|
|
433
|
-
const encoder = new TextEncoder();
|
|
434
|
-
let buffer = "";
|
|
435
|
-
return new TransformStream({
|
|
436
|
-
transform(chunk, controller) {
|
|
437
|
-
buffer += decoder.decode(chunk, { stream: true });
|
|
438
|
-
const lines = buffer.split("\n");
|
|
439
|
-
buffer = lines.pop() || "";
|
|
440
|
-
for (const line of lines) {
|
|
441
|
-
if (line.startsWith("data:")) {
|
|
442
|
-
const jsonStr = line.slice(5).trim();
|
|
443
|
-
if (!jsonStr) {
|
|
444
|
-
controller.enqueue(encoder.encode(line + "\n"));
|
|
445
|
-
continue;
|
|
446
|
-
}
|
|
447
|
-
try {
|
|
448
|
-
const parsed = JSON.parse(jsonStr);
|
|
449
|
-
if (parsed.response !== undefined) {
|
|
450
|
-
controller.enqueue(encoder.encode(`data: ${JSON.stringify(parsed.response)}\n`));
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
catch { }
|
|
455
|
-
}
|
|
456
|
-
controller.enqueue(encoder.encode(line + "\n"));
|
|
457
|
-
}
|
|
458
|
-
},
|
|
459
|
-
flush(controller) {
|
|
460
|
-
if (buffer.length > 0) {
|
|
461
|
-
if (buffer.startsWith("data:")) {
|
|
462
|
-
const jsonStr = buffer.slice(5).trim();
|
|
463
|
-
try {
|
|
464
|
-
const parsed = JSON.parse(jsonStr);
|
|
465
|
-
if (parsed.response !== undefined) {
|
|
466
|
-
controller.enqueue(encoder.encode(`data: ${JSON.stringify(parsed.response)}\n`));
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
catch { }
|
|
471
|
-
}
|
|
472
|
-
controller.enqueue(encoder.encode(buffer));
|
|
473
|
-
}
|
|
474
|
-
},
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
628
|
if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
|
|
478
629
|
process.env.GOOGLE_GENERATIVE_AI_API_KEY = "antigravity-oauth";
|
|
479
630
|
}
|
|
@@ -481,153 +632,8 @@ export const SuperocPlugin = async (input, options) => {
|
|
|
481
632
|
process.env.GEMINI_API_KEY = "antigravity-oauth";
|
|
482
633
|
}
|
|
483
634
|
syncOpencodeAuth();
|
|
484
|
-
const antigravityFetch =
|
|
485
|
-
|
|
486
|
-
? input
|
|
487
|
-
: input instanceof URL
|
|
488
|
-
? input.toString()
|
|
489
|
-
: input.url;
|
|
490
|
-
if (urlString.includes("generativelanguage.googleapis.com") || urlString.includes("antigravity")) {
|
|
491
|
-
const match = urlString.match(/\/models\/([^:]+):(\w+)/);
|
|
492
|
-
const rawModel = match ? match[1] : "";
|
|
493
|
-
const action = match ? match[2] : "streamGenerateContent";
|
|
494
|
-
const isStreaming = action === "streamGenerateContent" || urlString.includes("alt=sse");
|
|
495
|
-
const isAntigravityModel = rawModel.startsWith("antigravity-") ||
|
|
496
|
-
rawModel in BASE_ANTIGRAVITY_MODELS ||
|
|
497
|
-
/claude|gpt-oss|gemini-3|gemini-pro-agent/i.test(rawModel);
|
|
498
|
-
reloadFromDisk();
|
|
499
|
-
const activeAntigravityKeys = getActiveKeys(store, "antigravity");
|
|
500
|
-
if (isAntigravityModel || activeAntigravityKeys.length > 0) {
|
|
501
|
-
if (init?.signal?.aborted) {
|
|
502
|
-
throw new DOMException("The operation was aborted.", "AbortError");
|
|
503
|
-
}
|
|
504
|
-
let attempts = 0;
|
|
505
|
-
let lastResponse = null;
|
|
506
|
-
const maxAttempts = Math.max(1, activeAntigravityKeys.length);
|
|
507
|
-
while (attempts < maxAttempts) {
|
|
508
|
-
if (init?.signal?.aborted) {
|
|
509
|
-
throw new DOMException("The operation was aborted.", "AbortError");
|
|
510
|
-
}
|
|
511
|
-
attempts++;
|
|
512
|
-
const next = getNextKey(store, config, rawModel, "antigravity");
|
|
513
|
-
if (!next)
|
|
514
|
-
break;
|
|
515
|
-
const authRes = await getOrRefreshAntigravityAccessToken(next.key.key);
|
|
516
|
-
if (!authRes) {
|
|
517
|
-
continue;
|
|
518
|
-
}
|
|
519
|
-
if (init?.signal?.aborted) {
|
|
520
|
-
throw new DOMException("The operation was aborted.", "AbortError");
|
|
521
|
-
}
|
|
522
|
-
const effectiveModel = rawModel.replace(/^antigravity-/, "");
|
|
523
|
-
const candidateModels = [effectiveModel];
|
|
524
|
-
if (!effectiveModel.endsWith("-tiered") &&
|
|
525
|
-
(effectiveModel.includes("flash") || effectiveModel.includes("pro"))) {
|
|
526
|
-
candidateModels.push(`${effectiveModel}-tiered`);
|
|
527
|
-
}
|
|
528
|
-
else if (effectiveModel.endsWith("-tiered")) {
|
|
529
|
-
candidateModels.push(effectiveModel.replace(/-tiered$/, ""));
|
|
530
|
-
}
|
|
531
|
-
let bodyStr = init?.body;
|
|
532
|
-
let parsedBody = typeof bodyStr === "string" ? JSON.parse(bodyStr) : bodyStr;
|
|
533
|
-
const headers = new Headers(init?.headers ?? {});
|
|
534
|
-
headers.set("Authorization", `Bearer ${authRes.accessToken}`);
|
|
535
|
-
headers.set("User-Agent", `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Antigravity/1.18.3 Chrome/138.0.7204.235 Electron/37.3.1 Safari/537.36`);
|
|
536
|
-
headers.set("X-Goog-Api-Client", "google-cloud-sdk vscode_cloudshelleditor/0.1");
|
|
537
|
-
headers.set("Client-Metadata", `{"ideType":"ANTIGRAVITY","platform":"WINDOWS","pluginType":"GEMINI"}`);
|
|
538
|
-
headers.delete("x-goog-api-key");
|
|
539
|
-
headers.delete("x-api-key");
|
|
540
|
-
headers.delete("x-goog-user-project");
|
|
541
|
-
const endpoints = [
|
|
542
|
-
"https://daily-cloudcode-pa.sandbox.googleapis.com",
|
|
543
|
-
"https://cloudcode-pa.googleapis.com",
|
|
544
|
-
];
|
|
545
|
-
let gotRes = null;
|
|
546
|
-
endpointLoop: for (const ep of endpoints) {
|
|
547
|
-
if (init?.signal?.aborted) {
|
|
548
|
-
throw new DOMException("The operation was aborted.", "AbortError");
|
|
549
|
-
}
|
|
550
|
-
for (const candidate of candidateModels) {
|
|
551
|
-
if (init?.signal?.aborted) {
|
|
552
|
-
throw new DOMException("The operation was aborted.", "AbortError");
|
|
553
|
-
}
|
|
554
|
-
const transformedUrl = `${ep}/v1internal:${action}${isStreaming ? "?alt=sse" : ""}`;
|
|
555
|
-
const wrappedBody = JSON.stringify({
|
|
556
|
-
project: authRes.projectId || "rising-fact-p41fc",
|
|
557
|
-
model: candidate,
|
|
558
|
-
request: parsedBody,
|
|
559
|
-
requestType: "agent",
|
|
560
|
-
userAgent: "antigravity",
|
|
561
|
-
});
|
|
562
|
-
try {
|
|
563
|
-
const r = await fetch(transformedUrl, {
|
|
564
|
-
...init,
|
|
565
|
-
headers,
|
|
566
|
-
body: wrappedBody,
|
|
567
|
-
});
|
|
568
|
-
if (r.ok) {
|
|
569
|
-
gotRes = r;
|
|
570
|
-
break endpointLoop;
|
|
571
|
-
}
|
|
572
|
-
if (r.status === 429) {
|
|
573
|
-
gotRes = r;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
catch (netErr) {
|
|
577
|
-
if (netErr?.name === "AbortError" || init?.signal?.aborted) {
|
|
578
|
-
throw netErr;
|
|
579
|
-
}
|
|
580
|
-
if (process.env.SUPEROC_DEBUG === "true") {
|
|
581
|
-
console.warn(`[superoc] Endpoint ${ep} socket/network error:`, netErr);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
if (gotRes) {
|
|
587
|
-
lastResponse = gotRes;
|
|
588
|
-
}
|
|
589
|
-
if (gotRes && gotRes.ok) {
|
|
590
|
-
if (isStreaming && gotRes.body) {
|
|
591
|
-
const transformedStream = gotRes.body.pipeThrough(createSseUnwrapTransform());
|
|
592
|
-
return new Response(transformedStream, {
|
|
593
|
-
status: gotRes.status,
|
|
594
|
-
statusText: gotRes.statusText,
|
|
595
|
-
headers: gotRes.headers,
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
return gotRes;
|
|
599
|
-
}
|
|
600
|
-
if (gotRes && gotRes.status === 429) {
|
|
601
|
-
recordRateLimit(store, next.key.id);
|
|
602
|
-
recordModelRateLimit(store, next.key.id, rawModel);
|
|
603
|
-
safeSaveStore();
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
if (gotRes)
|
|
607
|
-
return gotRes;
|
|
608
|
-
}
|
|
609
|
-
if (isAntigravityModel) {
|
|
610
|
-
if (lastResponse)
|
|
611
|
-
return lastResponse;
|
|
612
|
-
return new Response(JSON.stringify({
|
|
613
|
-
error: {
|
|
614
|
-
code: 429,
|
|
615
|
-
message: "All Antigravity accounts are currently rate limited or exhausted.",
|
|
616
|
-
status: "RESOURCE_EXHAUSTED",
|
|
617
|
-
},
|
|
618
|
-
}), { status: 429, headers: { "Content-Type": "application/json" } });
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
return (fallbackFetch || fetch)(input, init);
|
|
623
|
-
};
|
|
624
|
-
if (!globalThis.__superoc_fetch_installed) {
|
|
625
|
-
globalThis.__superoc_fetch_installed = true;
|
|
626
|
-
const origFetch = globalThis.fetch;
|
|
627
|
-
globalThis.fetch = async function (input, init) {
|
|
628
|
-
return antigravityFetch(input, init, origFetch);
|
|
629
|
-
};
|
|
630
|
-
}
|
|
635
|
+
const antigravityFetch = createAntigravityFetch(store, config, reloadFromDisk, safeSaveStore);
|
|
636
|
+
installGlobalFetchInterceptor(antigravityFetch);
|
|
631
637
|
const hooks = {
|
|
632
638
|
config: async (cfg) => {
|
|
633
639
|
if (!process.env.OPENCODE_ENABLE_EXA) {
|
|
@@ -911,6 +917,274 @@ export const SuperocPlugin = async (input, options) => {
|
|
|
911
917
|
};
|
|
912
918
|
return hooks;
|
|
913
919
|
};
|
|
920
|
+
export async function setupV2(context) {
|
|
921
|
+
const options = context.options ?? {};
|
|
922
|
+
const config = {
|
|
923
|
+
storePath: options.storePath,
|
|
924
|
+
rotationStrategy: isValidStrategy(options.rotationStrategy)
|
|
925
|
+
? options.rotationStrategy
|
|
926
|
+
: "round-robin",
|
|
927
|
+
};
|
|
928
|
+
const store = loadStore(config) ?? getDefaultStore();
|
|
929
|
+
if (!store.fallbackChains)
|
|
930
|
+
store.fallbackChains = { nvidia: [], google: [], antigravity: [] };
|
|
931
|
+
const sessions = new Map();
|
|
932
|
+
const reloadFromDisk = () => {
|
|
933
|
+
let fresh = null;
|
|
934
|
+
try {
|
|
935
|
+
fresh = loadStore(config);
|
|
936
|
+
}
|
|
937
|
+
catch (err) {
|
|
938
|
+
console.debug("[superoc] Failed to reload store from disk:", err);
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (fresh === null)
|
|
942
|
+
return;
|
|
943
|
+
try {
|
|
944
|
+
store.keys = fresh.keys;
|
|
945
|
+
store.currentIndex = fresh.currentIndex;
|
|
946
|
+
store.rotationStrategy = fresh.rotationStrategy;
|
|
947
|
+
store.updatedAt = fresh.updatedAt;
|
|
948
|
+
store.lastUsedKeyId = fresh.lastUsedKeyId;
|
|
949
|
+
store.fallbackChains = {
|
|
950
|
+
nvidia: Array.isArray(fresh.fallbackChains?.nvidia) ? fresh.fallbackChains.nvidia : [],
|
|
951
|
+
google: Array.isArray(fresh.fallbackChains?.google) ? fresh.fallbackChains.google : [],
|
|
952
|
+
antigravity: Array.isArray(fresh.fallbackChains?.antigravity) ? fresh.fallbackChains.antigravity : [],
|
|
953
|
+
};
|
|
954
|
+
store.maxRateLimitFailures =
|
|
955
|
+
typeof fresh.maxRateLimitFailures === "number" &&
|
|
956
|
+
Number.isFinite(fresh.maxRateLimitFailures) &&
|
|
957
|
+
fresh.maxRateLimitFailures >= 1
|
|
958
|
+
? fresh.maxRateLimitFailures
|
|
959
|
+
: getDefaultStore().maxRateLimitFailures;
|
|
960
|
+
}
|
|
961
|
+
catch (err) {
|
|
962
|
+
console.debug("[superoc] Failed to apply reloaded store:", err);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
const safeSaveStore = () => {
|
|
966
|
+
try {
|
|
967
|
+
saveStore(store, config);
|
|
968
|
+
}
|
|
969
|
+
catch (err) {
|
|
970
|
+
console.error("[superoc] Failed to save store:", err);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
for (const provider of PROVIDERS) {
|
|
974
|
+
const activeKeys = getActiveKeys(store, provider);
|
|
975
|
+
if (activeKeys.length === 0) {
|
|
976
|
+
const envKey = process.env[getEnvKeyName(provider)];
|
|
977
|
+
if (envKey) {
|
|
978
|
+
const existing = store.keys.find((k) => k.name === "env-default" && k.provider === provider);
|
|
979
|
+
if (!existing) {
|
|
980
|
+
addKey(store, "env-default", envKey, provider);
|
|
981
|
+
safeSaveStore();
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
const getState = (sessionID) => {
|
|
987
|
+
const existing = sessions.get(sessionID);
|
|
988
|
+
if (existing)
|
|
989
|
+
return existing;
|
|
990
|
+
const next = {
|
|
991
|
+
attemptIndex: 0,
|
|
992
|
+
inRetry: false,
|
|
993
|
+
aborting: false,
|
|
994
|
+
pendingRetryIndex: undefined,
|
|
995
|
+
lastUserMessageID: undefined,
|
|
996
|
+
activeChainKey: undefined,
|
|
997
|
+
activeChainModelId: undefined,
|
|
998
|
+
rateLimitCount: 0,
|
|
999
|
+
currentModelId: undefined,
|
|
1000
|
+
lastFailedModelId: undefined,
|
|
1001
|
+
lastErrorHandledAt: 0,
|
|
1002
|
+
createdAt: Date.now(),
|
|
1003
|
+
sessionProviderId: undefined,
|
|
1004
|
+
lastUsedKeyId: undefined,
|
|
1005
|
+
};
|
|
1006
|
+
sessions.set(sessionID, next);
|
|
1007
|
+
return next;
|
|
1008
|
+
};
|
|
1009
|
+
// Seed environment variables
|
|
1010
|
+
if (!process.env.OPENCODE_ENABLE_EXA) {
|
|
1011
|
+
process.env.OPENCODE_ENABLE_EXA = "1";
|
|
1012
|
+
}
|
|
1013
|
+
if (!process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
|
|
1014
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = "antigravity-oauth";
|
|
1015
|
+
}
|
|
1016
|
+
if (!process.env.GEMINI_API_KEY) {
|
|
1017
|
+
process.env.GEMINI_API_KEY = "antigravity-oauth";
|
|
1018
|
+
}
|
|
1019
|
+
syncOpencodeAuth();
|
|
1020
|
+
// Install global fetch interceptor
|
|
1021
|
+
const antigravityFetch = createAntigravityFetch(store, config, reloadFromDisk, safeSaveStore);
|
|
1022
|
+
installGlobalFetchInterceptor(antigravityFetch);
|
|
1023
|
+
// Shell hook in V2
|
|
1024
|
+
if (context.shell?.hook) {
|
|
1025
|
+
await context.shell.hook("create.before", async (input) => {
|
|
1026
|
+
if (input?.env) {
|
|
1027
|
+
input.env["OPENCODE_ENABLE_EXA"] = "1";
|
|
1028
|
+
reloadFromDisk();
|
|
1029
|
+
for (const provider of PROVIDERS) {
|
|
1030
|
+
const envKeyName = getEnvKeyName(provider);
|
|
1031
|
+
if (input.env[envKeyName] !== undefined || getActiveKeys(store, provider).length > 0) {
|
|
1032
|
+
const next = getNextKey(store, config, undefined, provider);
|
|
1033
|
+
if (next) {
|
|
1034
|
+
input.env[envKeyName] = next.key.key;
|
|
1035
|
+
safeSaveStore();
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
// Session model.request hook in V2: inject rotated auth headers
|
|
1043
|
+
if (context.session?.hook) {
|
|
1044
|
+
await context.session.hook("model.request", async (input) => {
|
|
1045
|
+
const provider = detectProviderForRequest({
|
|
1046
|
+
provider: { info: { id: input.model?.providerID } },
|
|
1047
|
+
model: { providerID: input.model?.providerID, api: input.model?.modelID },
|
|
1048
|
+
});
|
|
1049
|
+
if (!provider)
|
|
1050
|
+
return;
|
|
1051
|
+
reloadFromDisk();
|
|
1052
|
+
const prevKeyId = store.lastUsedKeyId;
|
|
1053
|
+
const modelId = input.model?.modelID;
|
|
1054
|
+
const next = getNextKey(store, config, modelId, provider);
|
|
1055
|
+
if (next) {
|
|
1056
|
+
if (provider === "antigravity") {
|
|
1057
|
+
const authRes = await getOrRefreshAntigravityAccessToken(next.key.key);
|
|
1058
|
+
if (authRes) {
|
|
1059
|
+
const headers = getAntigravityHeaders(authRes.accessToken, authRes.projectId);
|
|
1060
|
+
input.headers = Object.assign(input.headers || {}, headers);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
const headers = getProviderHeaders(provider, next.key.key);
|
|
1065
|
+
input.headers = Object.assign(input.headers || {}, headers);
|
|
1066
|
+
}
|
|
1067
|
+
if (prevKeyId && prevKeyId !== next.key.id) {
|
|
1068
|
+
resetRateLimit(store, prevKeyId);
|
|
1069
|
+
}
|
|
1070
|
+
safeSaveStore();
|
|
1071
|
+
}
|
|
1072
|
+
if (input.sessionID) {
|
|
1073
|
+
const state = getState(input.sessionID);
|
|
1074
|
+
state.currentModelId = modelId;
|
|
1075
|
+
state.sessionProviderId = provider;
|
|
1076
|
+
if (next)
|
|
1077
|
+
state.lastUsedKeyId = next.key.id;
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
// Session http.response hook in V2: monitor 429 rate limits
|
|
1081
|
+
await context.session.hook("http.response", async (input) => {
|
|
1082
|
+
if (input.response?.status === 429) {
|
|
1083
|
+
const state = input.sessionID ? sessions.get(input.sessionID) : undefined;
|
|
1084
|
+
const errorKeyId = state?.lastUsedKeyId ?? store.lastUsedKeyId;
|
|
1085
|
+
reloadFromDisk();
|
|
1086
|
+
if (errorKeyId) {
|
|
1087
|
+
recordRateLimit(store, errorKeyId);
|
|
1088
|
+
const modelForBlacklist = state?.currentModelId;
|
|
1089
|
+
if (modelForBlacklist) {
|
|
1090
|
+
recordModelRateLimit(store, errorKeyId, modelForBlacklist);
|
|
1091
|
+
}
|
|
1092
|
+
if (state)
|
|
1093
|
+
state.lastFailedModelId = modelForBlacklist;
|
|
1094
|
+
}
|
|
1095
|
+
safeSaveStore();
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
// Session retry hook in V2: fallback model switching
|
|
1099
|
+
await context.session.hook("retry", async (input) => {
|
|
1100
|
+
const sessionID = input.sessionID;
|
|
1101
|
+
if (!sessionID)
|
|
1102
|
+
return;
|
|
1103
|
+
const state = getState(sessionID);
|
|
1104
|
+
const provider = state.sessionProviderId ?? "nvidia";
|
|
1105
|
+
const chain = store.fallbackChains[provider] || [];
|
|
1106
|
+
if (chain.length < 2)
|
|
1107
|
+
return;
|
|
1108
|
+
let nextIndex = (state.attemptIndex + 1) % chain.length;
|
|
1109
|
+
const target = chain[nextIndex];
|
|
1110
|
+
if (target && context.session?.switchModel) {
|
|
1111
|
+
state.attemptIndex = nextIndex;
|
|
1112
|
+
state.currentModelId = target.id;
|
|
1113
|
+
try {
|
|
1114
|
+
await context.session.switchModel({
|
|
1115
|
+
sessionID,
|
|
1116
|
+
model: { providerID: state.sessionProviderId ?? provider, modelID: target.id },
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
catch (err) {
|
|
1120
|
+
console.debug("[superoc] switchModel failed:", err);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
// Provider & Model transform in V2
|
|
1126
|
+
if (context.provider?.transform) {
|
|
1127
|
+
await context.provider.transform((editor) => {
|
|
1128
|
+
const existing = editor.get?.("antigravity");
|
|
1129
|
+
if (!existing && editor.add) {
|
|
1130
|
+
editor.add({
|
|
1131
|
+
info: {
|
|
1132
|
+
id: "antigravity",
|
|
1133
|
+
name: "Antigravity",
|
|
1134
|
+
package: "aisdk:@ai-sdk/google",
|
|
1135
|
+
settings: {
|
|
1136
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta",
|
|
1137
|
+
},
|
|
1138
|
+
},
|
|
1139
|
+
models: [],
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
if (context.model?.transform) {
|
|
1145
|
+
await context.model.transform((editor) => {
|
|
1146
|
+
for (const [id, def] of Object.entries(BASE_ANTIGRAVITY_MODELS)) {
|
|
1147
|
+
if (!editor.get?.("antigravity", id) && editor.update) {
|
|
1148
|
+
editor.update("antigravity", id, (draft) => {
|
|
1149
|
+
Object.assign(draft, {
|
|
1150
|
+
name: def.name,
|
|
1151
|
+
limit: def.limit,
|
|
1152
|
+
capabilities: {
|
|
1153
|
+
tools: true,
|
|
1154
|
+
input: ["text", "image", "pdf"],
|
|
1155
|
+
output: ["text"],
|
|
1156
|
+
},
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
// Query live Antigravity models in background
|
|
1164
|
+
reloadFromDisk();
|
|
1165
|
+
const activeKeys = getActiveKeys(store, "antigravity");
|
|
1166
|
+
if (activeKeys.length > 0) {
|
|
1167
|
+
getOrRefreshAntigravityAccessToken(activeKeys[0].key)
|
|
1168
|
+
.then(async (auth) => {
|
|
1169
|
+
if (auth) {
|
|
1170
|
+
try {
|
|
1171
|
+
await fetchLiveAntigravityModels(auth.accessToken, auth.projectId);
|
|
1172
|
+
}
|
|
1173
|
+
catch { }
|
|
1174
|
+
}
|
|
1175
|
+
})
|
|
1176
|
+
.catch(() => { });
|
|
1177
|
+
}
|
|
1178
|
+
return () => {
|
|
1179
|
+
sessions.clear();
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
export const SuperocPluginV2 = {
|
|
1183
|
+
id: "superoc",
|
|
1184
|
+
setup: setupV2,
|
|
1185
|
+
server: SuperocPlugin,
|
|
1186
|
+
};
|
|
914
1187
|
export const NimSuperPlugin = SuperocPlugin;
|
|
915
|
-
export
|
|
1188
|
+
export { SuperocPlugin as server };
|
|
1189
|
+
export default SuperocPluginV2;
|
|
916
1190
|
//# sourceMappingURL=index.js.map
|