@yansigit/opencodex 2.33.1 → 2.35.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 +3 -3
- package/gui/dist/assets/index-BjCaHxdz.js +112 -0
- package/gui/dist/assets/index-DLkXOXLC.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -3
- package/src/adapters/anthropic.ts +79 -2
- package/src/adapters/command-code.ts +40 -3
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/discovery.ts +60 -2
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +9 -9
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-shell.ts +103 -6
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +74 -39
- package/src/adapters/cursor/request-builder.ts +32 -10
- package/src/adapters/cursor/tool-definitions.ts +13 -10
- package/src/adapters/cursor/tool-result-normalize.ts +35 -3
- package/src/adapters/cursor/types.ts +16 -4
- package/src/adapters/cursor.ts +97 -70
- package/src/adapters/google-antigravity-replay.ts +105 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +41 -12
- package/src/adapters/google-http.ts +12 -11
- package/src/adapters/google.ts +156 -65
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +15 -0
- package/src/adapters/kiro-tools.ts +43 -15
- package/src/adapters/kiro.ts +54 -9
- package/src/adapters/openai-chat.ts +286 -242
- package/src/adapters/openai-responses.ts +334 -24
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +2 -2
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/bridge.ts +67 -26
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/outbound.ts +10 -8
- package/src/cli/account-api.ts +27 -7
- package/src/cli/account-extended.ts +10 -3
- package/src/cli/account.ts +29 -5
- package/src/cli/alias.ts +66 -0
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +13 -1
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/init.ts +1 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +13 -7
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/registry.ts +6 -1
- package/src/cli/telemetry-commands.ts +25 -0
- package/src/cli/v2.ts +34 -10
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +80 -6
- package/src/codex/auth-api.ts +48 -8
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/catalog/aggregation.ts +6 -0
- package/src/codex/catalog/model-metadata.ts +13 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +16 -0
- package/src/codex/catalog/provider-fetch.ts +20 -37
- package/src/codex/catalog/sync.ts +127 -2
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +22 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +18 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +14 -1
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +123 -18
- package/src/codex/routing.ts +9 -0
- package/src/codex/subagent-model-fallback.ts +198 -27
- package/src/codex/transition-state.ts +107 -8
- package/src/combos/types.ts +10 -0
- package/src/compatibility/openai-responses.ts +33 -1
- package/src/config/autonomous-remediation.ts +21 -0
- package/src/config/provider-validation.ts +14 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +191 -17
- package/src/generated/compatibility-version.json +229 -153
- package/src/images/loop.ts +5 -4
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +11 -2
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/oauth/aistudio-credentials.ts +65 -0
- package/src/oauth/aistudio-native-daemon.ts +54 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/index.ts +57 -5
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/login-cli.ts +19 -20
- package/src/oauth/types.ts +15 -0
- package/src/providers/codex-capacity.ts +5 -2
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +73 -14
- package/src/providers/registry.ts +160 -68
- package/src/providers/slug-codec.ts +52 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +34 -10
- package/src/responses/parser.ts +4 -0
- package/src/responses/schema.ts +5 -1
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +23 -0
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/effort-policy.ts +16 -0
- package/src/server/index.ts +152 -80
- package/src/server/lifecycle.ts +52 -1
- package/src/server/management/agent-settings-routes.ts +31 -15
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +27 -9
- package/src/server/management/context.ts +9 -0
- package/src/server/management/logs-usage-routes.ts +1 -1
- package/src/server/management/model-routes.ts +266 -0
- package/src/server/management/provider-routes.ts +137 -3
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management-api.ts +2 -0
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +21 -0
- package/src/server/request-log.ts +46 -20
- package/src/server/responses/collaboration.ts +42 -5
- package/src/server/responses/combo-stream-preflight.ts +10 -3
- package/src/server/responses/core.ts +571 -137
- package/src/server/responses/empty-completion-guard.ts +35 -0
- package/src/server/responses/fetch-helpers.ts +14 -6
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +35 -5
- package/src/server/responses-custom-tool-repair.ts +52 -7
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +15 -4
- package/src/telemetry/dispatcher.ts +44 -0
- package/src/telemetry/fingerprint.ts +24 -0
- package/src/telemetry/hook.ts +43 -0
- package/src/telemetry/ledger.ts +54 -0
- package/src/telemetry/types.ts +23 -0
- package/src/types/config.ts +66 -14
- package/src/types/provider.ts +77 -0
- package/src/types/request.ts +16 -10
- package/src/types/tools.ts +20 -25
- package/src/types.ts +1 -0
- package/src/usage/expected-prices.ts +43 -24
- package/src/usage/log.ts +17 -7
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-CIDo4y4k.js +0 -102
- package/gui/dist/assets/index-DrSQdTRd.css +0 -1
- package/src/server/aistudio-ws-hub.ts +0 -295
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-process WebSocket Relay Hub for Google AI Studio Web / Build sessions.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface WsRelayRequest {
|
|
6
|
-
url: string;
|
|
7
|
-
method: string;
|
|
8
|
-
headers?: Record<string, string>;
|
|
9
|
-
body?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface WsRelayStreamResult {
|
|
13
|
-
chunks: AsyncIterable<string>;
|
|
14
|
-
status?: number;
|
|
15
|
-
headers?: Record<string, string>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface WebSocketLike {
|
|
19
|
-
send(data: string): void;
|
|
20
|
-
close(code?: number, reason?: string): void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface PendingStream {
|
|
24
|
-
pushChunk(chunk: string): void;
|
|
25
|
-
finish(): void;
|
|
26
|
-
fail(err: Error): void;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AiStudioRelayHub {
|
|
30
|
-
hasActiveSessions(): boolean;
|
|
31
|
-
getActiveSessionCount(): number;
|
|
32
|
-
registerSession(id: string, ws: WebSocketLike): void;
|
|
33
|
-
unregisterSession(id: string): void;
|
|
34
|
-
reset(): void;
|
|
35
|
-
dispatchStream(req: WsRelayRequest, signal?: AbortSignal): Promise<WsRelayStreamResult>;
|
|
36
|
-
handleClientMessage(sessionId: string, raw: string): void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function createAiStudioRelayHub(): AiStudioRelayHub {
|
|
40
|
-
const sessions = new Map<string, WebSocketLike>();
|
|
41
|
-
const pendingRequests = new Map<string, PendingStream>();
|
|
42
|
-
const pendingBySession = new Map<string, Set<string>>();
|
|
43
|
-
let sessionRoundRobin = 0;
|
|
44
|
-
|
|
45
|
-
function getNextSession(): { id: string; ws: WebSocketLike } | undefined {
|
|
46
|
-
for (const [id, ws] of sessions.entries()) {
|
|
47
|
-
if ((ws as any).readyState !== undefined && (ws as any).readyState > 1) {
|
|
48
|
-
sessions.delete(id);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
const arr = Array.from(sessions.values());
|
|
52
|
-
if (arr.length === 0) return undefined;
|
|
53
|
-
const id = Array.from(sessions.keys())[sessionRoundRobin % arr.length]!;
|
|
54
|
-
const ws = sessions.get(id)!;
|
|
55
|
-
sessionRoundRobin = (sessionRoundRobin + 1) % arr.length;
|
|
56
|
-
return { id, ws };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
hasActiveSessions() {
|
|
61
|
-
return sessions.size > 0;
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
getActiveSessionCount() {
|
|
65
|
-
return sessions.size;
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
registerSession(id: string, ws: WebSocketLike) {
|
|
69
|
-
sessions.set(id, ws);
|
|
70
|
-
console.log(`[AIStudioHub] registered session: ${id}, total: ${sessions.size}`);
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
unregisterSession(id: string) {
|
|
74
|
-
sessions.delete(id);
|
|
75
|
-
console.log(`[AIStudioHub] unregistered session: ${id}, remaining: ${sessions.size}`);
|
|
76
|
-
const requestIds = pendingBySession.get(id);
|
|
77
|
-
if (!requestIds) return;
|
|
78
|
-
for (const requestId of requestIds) {
|
|
79
|
-
pendingRequests.get(requestId)?.fail(new Error("Google AI Studio browser session disconnected"));
|
|
80
|
-
}
|
|
81
|
-
pendingBySession.delete(id);
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
reset() {
|
|
85
|
-
for (const pending of pendingRequests.values()) {
|
|
86
|
-
pending.fail(new Error("AI Studio relay hub reset"));
|
|
87
|
-
}
|
|
88
|
-
sessions.clear();
|
|
89
|
-
pendingRequests.clear();
|
|
90
|
-
pendingBySession.clear();
|
|
91
|
-
sessionRoundRobin = 0;
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
async dispatchStream(req: WsRelayRequest, signal?: AbortSignal): Promise<WsRelayStreamResult> {
|
|
95
|
-
const session = getNextSession();
|
|
96
|
-
if (!session) {
|
|
97
|
-
throw new Error("No active Google AI Studio browser session connected. Open the AI Studio bridge in your browser to start.");
|
|
98
|
-
}
|
|
99
|
-
const { id: sessionId, ws } = session;
|
|
100
|
-
|
|
101
|
-
const reqId = `req_${crypto.randomUUID()}`;
|
|
102
|
-
const chunkQueue: string[] = [];
|
|
103
|
-
let resolveNextChunk: ((value: IteratorResult<string>) => void) | null = null;
|
|
104
|
-
let rejectNextChunk: ((err: Error) => void) | null = null;
|
|
105
|
-
let isFinished = false;
|
|
106
|
-
let finishError: Error | null = null;
|
|
107
|
-
|
|
108
|
-
const pending: PendingStream = {
|
|
109
|
-
pushChunk(chunk: string) {
|
|
110
|
-
if (isFinished) return;
|
|
111
|
-
if (resolveNextChunk) {
|
|
112
|
-
const r = resolveNextChunk;
|
|
113
|
-
resolveNextChunk = null;
|
|
114
|
-
rejectNextChunk = null;
|
|
115
|
-
r({ value: chunk, done: false });
|
|
116
|
-
} else {
|
|
117
|
-
chunkQueue.push(chunk);
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
finish() {
|
|
121
|
-
if (isFinished) return;
|
|
122
|
-
isFinished = true;
|
|
123
|
-
pendingRequests.delete(reqId);
|
|
124
|
-
pendingBySession.get(sessionId)?.delete(reqId);
|
|
125
|
-
if (resolveNextChunk) {
|
|
126
|
-
const r = resolveNextChunk;
|
|
127
|
-
resolveNextChunk = null;
|
|
128
|
-
rejectNextChunk = null;
|
|
129
|
-
r({ value: undefined as any, done: true });
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
fail(err: Error) {
|
|
133
|
-
if (isFinished) return;
|
|
134
|
-
isFinished = true;
|
|
135
|
-
finishError = err;
|
|
136
|
-
pendingRequests.delete(reqId);
|
|
137
|
-
pendingBySession.get(sessionId)?.delete(reqId);
|
|
138
|
-
if (rejectNextChunk) {
|
|
139
|
-
const r = rejectNextChunk;
|
|
140
|
-
resolveNextChunk = null;
|
|
141
|
-
rejectNextChunk = null;
|
|
142
|
-
r(err);
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
pendingRequests.set(reqId, pending);
|
|
148
|
-
const requestIds = pendingBySession.get(sessionId) ?? new Set<string>();
|
|
149
|
-
requestIds.add(reqId);
|
|
150
|
-
pendingBySession.set(sessionId, requestIds);
|
|
151
|
-
|
|
152
|
-
if (signal) {
|
|
153
|
-
signal.addEventListener("abort", () => {
|
|
154
|
-
try {
|
|
155
|
-
ws.send(JSON.stringify({ id: reqId, type: "abort" }));
|
|
156
|
-
} catch (err) {
|
|
157
|
-
void err;
|
|
158
|
-
}
|
|
159
|
-
pending.fail(new Error("Request aborted by client"));
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const msg = {
|
|
164
|
-
id: reqId,
|
|
165
|
-
type: "http_request",
|
|
166
|
-
payload: {
|
|
167
|
-
url: req.url,
|
|
168
|
-
method: req.method,
|
|
169
|
-
headers: req.headers ?? {},
|
|
170
|
-
body: req.body ?? "",
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
ws.send(JSON.stringify(msg));
|
|
175
|
-
|
|
176
|
-
const asyncIterable: AsyncIterable<string> = {
|
|
177
|
-
[Symbol.asyncIterator]() {
|
|
178
|
-
return {
|
|
179
|
-
async next(): Promise<IteratorResult<string>> {
|
|
180
|
-
if (chunkQueue.length > 0) {
|
|
181
|
-
return { value: chunkQueue.shift()!, done: false };
|
|
182
|
-
}
|
|
183
|
-
if (isFinished) {
|
|
184
|
-
if (finishError) throw finishError;
|
|
185
|
-
return { value: undefined as any, done: true };
|
|
186
|
-
}
|
|
187
|
-
return new Promise<IteratorResult<string>>((resolve, reject) => {
|
|
188
|
-
resolveNextChunk = resolve;
|
|
189
|
-
rejectNextChunk = reject;
|
|
190
|
-
});
|
|
191
|
-
},
|
|
192
|
-
};
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
return { chunks: asyncIterable };
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
handleClientMessage(sessionId: string, raw: string) {
|
|
200
|
-
let data: any;
|
|
201
|
-
try {
|
|
202
|
-
data = JSON.parse(raw);
|
|
203
|
-
} catch (err) {
|
|
204
|
-
console.log("[AIStudioHub] failed to parse JSON message:", err);
|
|
205
|
-
void err;
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const { id, type, payload } = data || {};
|
|
210
|
-
if (!id) {
|
|
211
|
-
console.log("[AIStudioHub] message missing id");
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const pending = pendingRequests.get(id);
|
|
216
|
-
if (!pending || !pendingBySession.get(sessionId)?.has(id)) {
|
|
217
|
-
console.log("[AIStudioHub] no pending request for id:", id, "session:", sessionId);
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (type === "stream_chunk") {
|
|
222
|
-
if (payload?.data) {
|
|
223
|
-
pending.pushChunk(payload.data);
|
|
224
|
-
}
|
|
225
|
-
} else if (type === "stream_end" || type === "http_response") {
|
|
226
|
-
if (payload?.body) {
|
|
227
|
-
pending.pushChunk(payload.body);
|
|
228
|
-
}
|
|
229
|
-
pending.finish();
|
|
230
|
-
} else if (type === "error") {
|
|
231
|
-
console.log(`[AIStudioHub] client error from ${sessionId}:`, payload?.error);
|
|
232
|
-
pending.fail(new Error(payload?.error || "Upstream AI Studio error"));
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export const globalAiStudioRelayHub = createAiStudioRelayHub();
|
|
239
|
-
|
|
240
|
-
export function getAiStudioUserScript(listenPort: number): string {
|
|
241
|
-
return `// ==UserScript==
|
|
242
|
-
// @name OpenCodex AI Studio Relay Bridge
|
|
243
|
-
// @namespace https://opencodex.dev/
|
|
244
|
-
// @version 1.0.0
|
|
245
|
-
// @description Relays requests from opencodex to Google AI Studio session
|
|
246
|
-
// @match https://aistudio.google.com/*
|
|
247
|
-
// @grant none
|
|
248
|
-
// @run-at document-idle
|
|
249
|
-
// ==/UserScript==
|
|
250
|
-
|
|
251
|
-
(function() {
|
|
252
|
-
'use strict';
|
|
253
|
-
const WS_URL = "ws://127.0.0.1:${listenPort}/v1/ws/aistudio";
|
|
254
|
-
let ws;
|
|
255
|
-
function connect() {
|
|
256
|
-
try { ws = new WebSocket(WS_URL); } catch { setTimeout(connect, 3000); return; }
|
|
257
|
-
ws.onmessage = async (e) => {
|
|
258
|
-
try {
|
|
259
|
-
const msg = JSON.parse(e.data);
|
|
260
|
-
if (msg.type === "http_request") {
|
|
261
|
-
const { id, payload } = msg;
|
|
262
|
-
try {
|
|
263
|
-
const res = await fetch(payload.url, { method: payload.method, headers: payload.headers, body: payload.body, credentials: "include" });
|
|
264
|
-
if (payload.url.includes("streamGenerateContent") && res.body) {
|
|
265
|
-
const reader = res.body.getReader();
|
|
266
|
-
const decoder = new TextDecoder();
|
|
267
|
-
while (true) {
|
|
268
|
-
const { done, value } = await reader.read();
|
|
269
|
-
if (done) break;
|
|
270
|
-
ws.send(JSON.stringify({ id, type: "stream_chunk", payload: { data: decoder.decode(value, { stream: true }) } }));
|
|
271
|
-
}
|
|
272
|
-
ws.send(JSON.stringify({ id, type: "stream_end", payload: {} }));
|
|
273
|
-
} else {
|
|
274
|
-
const text = await res.text();
|
|
275
|
-
ws.send(JSON.stringify({ id, type: "http_response", payload: { body: text, status: res.status } }));
|
|
276
|
-
}
|
|
277
|
-
} catch (err) {
|
|
278
|
-
ws.send(JSON.stringify({ id, type: "error", payload: { error: String(err) } }));
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
} catch (err) {
|
|
282
|
-
void err;
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
ws.onclose = () => setTimeout(connect, 3000);
|
|
286
|
-
}
|
|
287
|
-
connect();
|
|
288
|
-
})();
|
|
289
|
-
`;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
export function getAiStudioBridgeHtml(listenPort: number): string {
|
|
293
|
-
const extensionPath = typeof process !== "undefined" ? process.cwd() + "/integrations/aistudio-extension" : "integrations/aistudio-extension";
|
|
294
|
-
return `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Google AI Studio Bridge</title><style>body{font-family:system-ui,-apple-system,sans-serif;background:#121316;color:#e1e3e6;padding:2rem;max-width:680px;margin:auto;line-height:1.5}.card{background:#1e1f23;border:1px solid #2d2f34;border-radius:8px;padding:1.5rem;margin-bottom:1.5rem}.status{display:inline-block;padding:0.25rem 0.75rem;border-radius:4px;font-weight:500}.connected{background:#163820;color:#4ade80}.disconnected{background:#3b1717;color:#f87171}code{background:#2a2c31;padding:0.2rem 0.4rem;border-radius:4px}.btn{display:inline-block;padding:0.5rem 1rem;background:#3b82f6;color:#fff;text-decoration:none;border-radius:6px;font-weight:500;margin-top:0.5rem}pre{background:#16171a;padding:1rem;border-radius:6px;overflow-x:auto;font-size:0.85rem}</style></head><body><div class="card"><h2>Google AI Studio Bridge</h2><p>Status: <span id="status" class="status disconnected">Connecting...</span></p><p>This bridge connects <code>opencodex</code> with your active Google AI Studio / Google AI Pro session.</p></div><div class="card"><h3>🧩 Option 1: Unpacked Extension for Brave / Chrome (Zero Tab Overhead)</h3><p>Run entirely in the background without needing an active browser tab:</p><ol style="padding-left:1.2rem;margin:0.5rem 0"><li>Open <code>brave://extensions</code> (or <code>chrome://extensions</code>) and toggle <strong>Developer mode</strong> in the top-right.</li><li>Click <strong>Load unpacked</strong> and select this directory:<br><code id="ext-path">${extensionPath}</code></li></ol><button class="btn" style="cursor:pointer" onclick="navigator.clipboard.writeText(document.getElementById('ext-path').textContent);this.textContent='Copied!'">📋 Copy Extension Directory Path</button></div><div class="card"><h3>🌐 Option 2: Userscript (Tampermonkey) or Console</h3><p>Install via Tampermonkey/Violentmonkey or paste in Developer Tools Console:</p><p><a class="btn" href="/aistudio/bridge.user.js">⚡ Install Userscript</a> <a class="btn" style="background:#374151;margin-left:0.5rem" href="https://aistudio.google.com" target="_blank">Open aistudio.google.com ↗</a></p><pre style="margin-top:1rem"><code>const ws=new WebSocket('ws://127.0.0.1:${listenPort}/v1/ws/aistudio');ws.onmessage=async(e)=>{const{id,payload,type}=JSON.parse(e.data);if(type==='http_request'){try{const res=await fetch(payload.url,{method:payload.method,headers:payload.headers,body:payload.body,credentials:'include'});if(payload.url.includes('streamGenerateContent')&&res.body){const r=res.body.getReader(),d=new TextDecoder();while(true){const{done,value}=await r.read();if(done)break;ws.send(JSON.stringify({id,type:'stream_chunk',payload:{data:d.decode(value,{stream:true})}}))}ws.send(JSON.stringify({id,type:'stream_end',payload:{}}))}else{const body=await res.text();ws.send(JSON.stringify({id,type:'http_response',payload:{body,status:res.status}}))}}catch(err){ws.send(JSON.stringify({id,type:'error',payload:{error:String(err)}}))}}};console.log('opencodex AI Studio relay active!');</code></pre></div><script>let ws;const statusEl=document.getElementById("status");function connect(){const proto=location.protocol==="https:"?"wss:":"ws:";ws=new WebSocket(proto+"//"+location.host+"/v1/ws/aistudio");ws.onopen=()=>{statusEl.textContent="🟢 Connected (Relay Active)";statusEl.className="status connected"};ws.onclose=()=>{statusEl.textContent="🔴 Disconnected (Retrying...)";statusEl.className="status disconnected";setTimeout(connect,2000)}};connect()</script></body></html>`;
|
|
295
|
-
}
|