Add Google Gemini CLI and Antigravity OAuth providers
- Add google-gemini-cli provider: free Gemini 2.0/2.5 via Cloud Code Assist - Add google-antigravity provider: free Gemini 3, Claude, GPT-OSS via sandbox - Move OAuth infrastructure from coding-agent to ai package - Fix thinking signature handling for cross-model handoff - Fix OpenAI message ID length limit (max 64 chars) - Add GitHub Copilot overflow pattern detection - Add OAuth provider tests for context overflow and streaming
This commit is contained in:
@@ -1,54 +1,70 @@
|
||||
import { loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
|
||||
import { loginGitHubCopilot, refreshGitHubCopilotToken } from "./github-copilot.js";
|
||||
import { loginGoogleCloud, refreshGoogleCloudToken } from "./google-cloud.js";
|
||||
/**
|
||||
* OAuth management for coding-agent.
|
||||
* Re-exports from @mariozechner/pi-ai and adds convenience wrappers.
|
||||
*/
|
||||
|
||||
import {
|
||||
listOAuthProviders as listOAuthProvidersFromStorage,
|
||||
getOAuthApiKey,
|
||||
listOAuthProviders as listOAuthProvidersFromAi,
|
||||
loadOAuthCredentials,
|
||||
loginAnthropic,
|
||||
loginAntigravity,
|
||||
loginGeminiCli,
|
||||
loginGitHubCopilot,
|
||||
type OAuthCredentials,
|
||||
type OAuthProvider,
|
||||
refreshToken as refreshTokenFromAi,
|
||||
removeOAuthCredentials,
|
||||
saveOAuthCredentials,
|
||||
} from "./storage.js";
|
||||
} from "@mariozechner/pi-ai";
|
||||
|
||||
// Re-export for convenience
|
||||
export { listOAuthProvidersFromStorage as listOAuthProviders };
|
||||
// Re-export types and functions
|
||||
export type { OAuthCredentials, OAuthProvider };
|
||||
export { listOAuthProvidersFromAi as listOAuthProviders };
|
||||
export { getOAuthApiKey, loadOAuthCredentials, removeOAuthCredentials, saveOAuthCredentials };
|
||||
|
||||
export type SupportedOAuthProvider = "anthropic" | "github-copilot" | "google-cloud-code-assist";
|
||||
|
||||
export interface OAuthProviderInfo {
|
||||
id: SupportedOAuthProvider;
|
||||
name: string;
|
||||
available: boolean;
|
||||
}
|
||||
|
||||
export type OAuthPrompt = {
|
||||
message: string;
|
||||
placeholder?: string;
|
||||
allowEmpty?: boolean;
|
||||
};
|
||||
|
||||
export type OAuthAuthInfo = {
|
||||
// Types for OAuth flow
|
||||
export interface OAuthAuthInfo {
|
||||
url: string;
|
||||
instructions?: string;
|
||||
}
|
||||
|
||||
export interface OAuthPrompt {
|
||||
message: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export type OAuthProviderInfo = {
|
||||
id: OAuthProvider;
|
||||
name: string;
|
||||
description: string;
|
||||
available: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get list of OAuth providers
|
||||
*/
|
||||
export function getOAuthProviders(): OAuthProviderInfo[] {
|
||||
return [
|
||||
{
|
||||
id: "anthropic",
|
||||
name: "Anthropic (Claude Pro/Max)",
|
||||
description: "Use Claude with your Pro/Max subscription",
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
id: "github-copilot",
|
||||
name: "GitHub Copilot",
|
||||
description: "Use models via GitHub Copilot subscription",
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
id: "google-cloud-code-assist",
|
||||
name: "Google Cloud Code Assist (Gemini CLI)",
|
||||
id: "google-gemini-cli",
|
||||
name: "Google Gemini CLI",
|
||||
description: "Free Gemini 2.0/2.5 models via Google Cloud",
|
||||
available: true,
|
||||
},
|
||||
{
|
||||
id: "google-antigravity",
|
||||
name: "Antigravity",
|
||||
description: "Free Gemini 3, Claude, GPT-OSS via Google Cloud",
|
||||
available: true,
|
||||
},
|
||||
];
|
||||
@@ -58,7 +74,7 @@ export function getOAuthProviders(): OAuthProviderInfo[] {
|
||||
* Login with OAuth provider
|
||||
*/
|
||||
export async function login(
|
||||
provider: SupportedOAuthProvider,
|
||||
provider: OAuthProvider,
|
||||
onAuth: (info: OAuthAuthInfo) => void,
|
||||
onPrompt: (prompt: OAuthPrompt) => Promise<string>,
|
||||
onProgress?: (message: string) => void,
|
||||
@@ -79,8 +95,12 @@ export async function login(
|
||||
saveOAuthCredentials("github-copilot", creds);
|
||||
break;
|
||||
}
|
||||
case "google-cloud-code-assist": {
|
||||
await loginGoogleCloud(onAuth, onProgress);
|
||||
case "google-gemini-cli": {
|
||||
await loginGeminiCli((info) => onAuth({ url: info.url, instructions: info.instructions }), onProgress);
|
||||
break;
|
||||
}
|
||||
case "google-antigravity": {
|
||||
await loginAntigravity((info) => onAuth({ url: info.url, instructions: info.instructions }), onProgress);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -91,62 +111,21 @@ export async function login(
|
||||
/**
|
||||
* Logout from OAuth provider
|
||||
*/
|
||||
export async function logout(provider: SupportedOAuthProvider): Promise<void> {
|
||||
export async function logout(provider: OAuthProvider): Promise<void> {
|
||||
removeOAuthCredentials(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh OAuth token for provider
|
||||
* Refresh OAuth token for provider.
|
||||
* Delegates to the ai package implementation.
|
||||
*/
|
||||
export async function refreshToken(provider: SupportedOAuthProvider): Promise<string> {
|
||||
const credentials = loadOAuthCredentials(provider);
|
||||
if (!credentials) {
|
||||
throw new Error(`No OAuth credentials found for ${provider}`);
|
||||
}
|
||||
|
||||
let newCredentials: OAuthCredentials;
|
||||
|
||||
switch (provider) {
|
||||
case "anthropic":
|
||||
newCredentials = await refreshAnthropicToken(credentials.refresh);
|
||||
break;
|
||||
case "github-copilot":
|
||||
newCredentials = await refreshGitHubCopilotToken(credentials.refresh, credentials.enterpriseUrl);
|
||||
break;
|
||||
case "google-cloud-code-assist":
|
||||
newCredentials = await refreshGoogleCloudToken(credentials.refresh, credentials.projectId);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown OAuth provider: ${provider}`);
|
||||
}
|
||||
|
||||
// Save new credentials
|
||||
saveOAuthCredentials(provider, newCredentials);
|
||||
|
||||
return newCredentials.access;
|
||||
export async function refreshToken(provider: OAuthProvider): Promise<string> {
|
||||
return refreshTokenFromAi(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OAuth token for provider (auto-refreshes if expired)
|
||||
* Get OAuth token for provider (auto-refreshes if expired).
|
||||
*/
|
||||
export async function getOAuthToken(provider: SupportedOAuthProvider): Promise<string | null> {
|
||||
const credentials = loadOAuthCredentials(provider);
|
||||
if (!credentials) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if token is expired (with 5 min buffer already applied)
|
||||
if (Date.now() >= credentials.expires) {
|
||||
// Token expired - refresh it
|
||||
try {
|
||||
return await refreshToken(provider);
|
||||
} catch (error) {
|
||||
console.error(`Failed to refresh OAuth token for ${provider}:`, error);
|
||||
// Remove invalid credentials
|
||||
removeOAuthCredentials(provider);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return credentials.access;
|
||||
export async function getOAuthToken(provider: OAuthProvider): Promise<string | null> {
|
||||
return getOAuthApiKey(provider);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user