Harden provider security and build dependencies
This commit is contained in:
1 parent
67d2cf5649
commit
abe578b1a0
10 files changed
+783
-285
No files matched your search
@@ -9,7 +9,7 @@ import { generatePkce } from "./pkce";
|
||||
import type { OAuthCredentials, OAuthFlow, OAuthLoginCallbacks } from "./oauth-types";
|
||||
|
||||
const CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
||||
const CLIENT_SECRET = "***REMOVED***";
|
||||
const CLIENT_SECRET_ENV = "AGENTCOMPANY_GOOGLE_ANTIGRAVITY_CLIENT_SECRET";
|
||||
const CALLBACK_PORT = 51121;
|
||||
const CALLBACK_PATH = "/oauth-callback";
|
||||
const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
||||
@@ -22,9 +22,14 @@ const SCOPES = [
|
||||
];
|
||||
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
||||
const TOKEN_URL = "https://oauth2.googleapis.com/token";
|
||||
const DEFAULT_PROJECT_ID = "rising-fact-p41fc";
|
||||
const CODE_ASSIST_ENDPOINTS = ["https://cloudcode-pa.googleapis.com", "https://daily-cloudcode-pa.sandbox.googleapis.com"];
|
||||
|
||||
export function resolveAntigravityClientSecret(env: NodeJS.ProcessEnv = process.env): string {
|
||||
const secret = env[CLIENT_SECRET_ENV]?.trim();
|
||||
if (!secret) throw new Error(`${CLIENT_SECRET_ENV} is required for Google Antigravity OAuth.`);
|
||||
return secret;
|
||||
}
|
||||
|
||||
function buildAuthorizeUrl(challenge: string, state: string): string {
|
||||
const params = new URLSearchParams({
|
||||
client_id: CLIENT_ID,
|
||||
@@ -46,7 +51,7 @@ async function exchangeCode(code: string, verifier: string): Promise<{ access: s
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams({
|
||||
client_id: CLIENT_ID,
|
||||
client_secret: CLIENT_SECRET,
|
||||
client_secret: resolveAntigravityClientSecret(),
|
||||
code,
|
||||
grant_type: "authorization_code",
|
||||
redirect_uri: REDIRECT_URI,
|
||||
@@ -96,7 +101,7 @@ async function discoverProject(accessToken: string): Promise<string> {
|
||||
// Try the next endpoint.
|
||||
}
|
||||
}
|
||||
return DEFAULT_PROJECT_ID;
|
||||
throw new Error("Antigravity project discovery failed.");
|
||||
}
|
||||
|
||||
/** Refresh an expired Antigravity access token, retaining the project id. */
|
||||
@@ -104,7 +109,12 @@ export async function refreshAntigravity(refreshToken: string, projectId: string
|
||||
const response = await fetch(TOKEN_URL, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams({ client_id: CLIENT_ID, client_secret: CLIENT_SECRET, refresh_token: refreshToken, grant_type: "refresh_token" }),
|
||||
body: new URLSearchParams({
|
||||
client_id: CLIENT_ID,
|
||||
client_secret: resolveAntigravityClientSecret(),
|
||||
refresh_token: refreshToken,
|
||||
grant_type: "refresh_token",
|
||||
}),
|
||||
});
|
||||
const text = await response.text();
|
||||
if (!response.ok) throw new Error(`Antigravity token refresh failed: ${response.status} ${text}`);
|
||||
|
||||
@@ -66,13 +66,10 @@ function errorText(data: JsonRecord): string {
|
||||
|
||||
/**
|
||||
* Accumulate a Responses SSE stream (Codex/Copilot) into the final result. The
|
||||
* authoritative output arrives on the completion event's `response.output`; the
|
||||
* incremental events are accumulated only as a fallback.
|
||||
* result is accepted from the completion event's `response.output`.
|
||||
*/
|
||||
export async function accumulateResponsesStream(messages: AsyncIterable<ProviderEventStreamMessage>): Promise<AgentTurnResult> {
|
||||
const raw: string[] = [];
|
||||
const textParts: string[] = [];
|
||||
const calls = new Map<string, { id: string; name: string; args: string }>();
|
||||
for await (const message of messages) {
|
||||
raw.push(message.rawData);
|
||||
const type = eventType(message);
|
||||
@@ -84,23 +81,8 @@ export async function accumulateResponsesStream(messages: AsyncIterable<Provider
|
||||
const parsed = parseResponsesOutput(response);
|
||||
return { text: parsed.text, toolCalls: parsed.toolCalls, rawResponseJson: JSON.stringify(raw) };
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (type === "response.output_text.delta" && typeof message.data.delta === "string") {
|
||||
textParts.push(message.data.delta);
|
||||
} else if (type === "response.output_item.added" && isRecord(message.data.item) && message.data.item.type === "function_call") {
|
||||
const item = message.data.item;
|
||||
const key = typeof item.id === "string" ? item.id : typeof item.call_id === "string" ? item.call_id : "";
|
||||
const id = typeof item.call_id === "string" ? item.call_id : key;
|
||||
if (key) calls.set(key, { id, name: typeof item.name === "string" ? item.name : "", args: typeof item.arguments === "string" ? item.arguments : "" });
|
||||
} else if (type === "response.function_call_arguments.delta" && typeof message.data.item_id === "string" && typeof message.data.delta === "string") {
|
||||
const entry = calls.get(message.data.item_id);
|
||||
if (entry) entry.args += message.data.delta;
|
||||
} else if (type === "response.function_call_arguments.done" && typeof message.data.item_id === "string" && typeof message.data.arguments === "string") {
|
||||
const entry = calls.get(message.data.item_id);
|
||||
if (entry) entry.args = message.data.arguments;
|
||||
throw new Error("Responses completion event was missing response.output.");
|
||||
}
|
||||
}
|
||||
const toolCalls = [...calls.values()].filter((entry) => entry.name).map((entry) => ({ id: entry.id, name: entry.name, arguments: parseArguments(entry.args) }));
|
||||
return { text: textParts.join("").trim(), toolCalls, rawResponseJson: JSON.stringify(raw) };
|
||||
throw new Error("Responses stream ended before a completion event.");
|
||||
}
|
||||
Reference in new issue
Block a user