mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 14:52:09 +03:00
refactor(types): Wave 3c — OAuth services + server utils typed
- oauth.ts: typed OAuthService class with config field, all method params - antigravity.ts: typed AntigravityService class + all OAuth flow params - iflow.ts: typed IFlowService class + Basic Auth flow params - gemini.ts: typed GeminiCLIService class + project ID fetching - qwen.ts: typed QwenService class + device code flow params - cursor.ts: typed CursorService class + checksum/headers params - server.ts: typed startLocalServer return + waitForCallback - Fixed resolve() calls to resolve(undefined) for strict Promise TS errors: 490 → 419 (-71) Total reduction: 984 → 419 (-565, 57.4%) Build: ✅ Tests: 368/368 ✅
This commit is contained in:
@@ -10,6 +10,8 @@ import { spinner as createSpinner } from "../utils/ui";
|
||||
* Uses standard OAuth2 Authorization Code flow (similar to Gemini)
|
||||
*/
|
||||
export class AntigravityService {
|
||||
config: any;
|
||||
|
||||
constructor() {
|
||||
this.config = ANTIGRAVITY_CONFIG;
|
||||
}
|
||||
@@ -17,7 +19,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Build Antigravity authorization URL
|
||||
*/
|
||||
buildAuthUrl(redirectUri, state) {
|
||||
buildAuthUrl(redirectUri: string, state: string) {
|
||||
const params = new URLSearchParams({
|
||||
client_id: this.config.clientId,
|
||||
response_type: "code",
|
||||
@@ -34,7 +36,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Exchange authorization code for tokens
|
||||
*/
|
||||
async exchangeCode(code, redirectUri) {
|
||||
async exchangeCode(code: string, redirectUri: string) {
|
||||
const response = await fetch(this.config.tokenUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -61,7 +63,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Get user info from Google
|
||||
*/
|
||||
async getUserInfo(accessToken) {
|
||||
async getUserInfo(accessToken: string) {
|
||||
const response = await fetch(`${this.config.userInfoUrl}?alt=json`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
@@ -80,7 +82,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Get common headers for Antigravity API calls
|
||||
*/
|
||||
getApiHeaders(accessToken) {
|
||||
getApiHeaders(accessToken: string) {
|
||||
return {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
@@ -104,7 +106,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Fetch Project ID and Tier from loadCodeAssist API
|
||||
*/
|
||||
async loadCodeAssist(accessToken) {
|
||||
async loadCodeAssist(accessToken: string) {
|
||||
const response = await fetch(this.config.loadCodeAssistEndpoint, {
|
||||
method: "POST",
|
||||
headers: this.getApiHeaders(accessToken),
|
||||
@@ -141,7 +143,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Onboard user to enable Gemini Code Assist for the project
|
||||
*/
|
||||
async onboardUser(accessToken, projectId, tierId) {
|
||||
async onboardUser(accessToken: string, projectId: string, tierId: string) {
|
||||
const response = await fetch(this.config.onboardUserEndpoint, {
|
||||
method: "POST",
|
||||
headers: this.getApiHeaders(accessToken),
|
||||
@@ -163,7 +165,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Complete onboarding flow with retry
|
||||
*/
|
||||
async completeOnboarding(accessToken, projectId, tierId, maxRetries = 10) {
|
||||
async completeOnboarding(accessToken: string, projectId: string, tierId: string, maxRetries = 10) {
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
const result = await this.onboardUser(accessToken, projectId, tierId);
|
||||
|
||||
@@ -191,7 +193,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Fetch Project ID from loadCodeAssist API (legacy method for compatibility)
|
||||
*/
|
||||
async fetchProjectId(accessToken) {
|
||||
async fetchProjectId(accessToken: string) {
|
||||
const { projectId } = await this.loadCodeAssist(accessToken);
|
||||
if (!projectId) {
|
||||
throw new Error("No cloudaicompanionProject found in response");
|
||||
@@ -202,7 +204,7 @@ export class AntigravityService {
|
||||
/**
|
||||
* Save Antigravity tokens to server
|
||||
*/
|
||||
async saveTokens(tokens, userInfo, projectId) {
|
||||
async saveTokens(tokens: any, userInfo: any, projectId: string) {
|
||||
const { server, token, userId } = getServerCredentials();
|
||||
|
||||
const response = await fetch(`${server}/api/cli/providers/antigravity`, {
|
||||
@@ -240,7 +242,7 @@ export class AntigravityService {
|
||||
spinner.text = "Starting local server...";
|
||||
|
||||
// Start local server for callback
|
||||
let callbackParams = null;
|
||||
let callbackParams: any = null;
|
||||
const { port, close } = await startLocalServer((params) => {
|
||||
callbackParams = params;
|
||||
});
|
||||
@@ -272,7 +274,7 @@ export class AntigravityService {
|
||||
if (callbackParams) {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
resolve(undefined);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
@@ -323,7 +325,7 @@ export class AntigravityService {
|
||||
`Antigravity connected successfully! (${userInfo.email}, Project: ${finalProjectId})`
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
spinner.fail(`Failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import { CURSOR_CONFIG } from "../constants/oauth";
|
||||
*/
|
||||
|
||||
export class CursorService {
|
||||
config: any;
|
||||
|
||||
constructor() {
|
||||
this.config = CURSOR_CONFIG;
|
||||
}
|
||||
@@ -24,7 +26,7 @@ export class CursorService {
|
||||
* Algorithm: XOR timestamp bytes with rolling key (initial 165), then base64 encode
|
||||
* Format: {encoded_timestamp},{machineId}
|
||||
*/
|
||||
generateChecksum(machineId) {
|
||||
generateChecksum(machineId: string) {
|
||||
const timestamp = Math.floor(Date.now() / 1000).toString();
|
||||
let key = 165;
|
||||
const encoded = [];
|
||||
@@ -42,7 +44,7 @@ export class CursorService {
|
||||
/**
|
||||
* Build request headers for Cursor API
|
||||
*/
|
||||
buildHeaders(accessToken, machineId, ghostMode = false) {
|
||||
buildHeaders(accessToken: string, machineId: string, ghostMode = false) {
|
||||
const checksum = this.generateChecksum(machineId);
|
||||
|
||||
return {
|
||||
@@ -92,7 +94,7 @@ export class CursorService {
|
||||
* @param {string} accessToken - Access token from state.vscdb
|
||||
* @param {string} machineId - Machine ID from state.vscdb
|
||||
*/
|
||||
async validateImportToken(accessToken, machineId) {
|
||||
async validateImportToken(accessToken: string, machineId: string) {
|
||||
// Basic validation
|
||||
if (!accessToken || typeof accessToken !== "string") {
|
||||
throw new Error("Access token is required");
|
||||
@@ -128,7 +130,7 @@ export class CursorService {
|
||||
* Extract user info from token if possible
|
||||
* Cursor tokens may contain encoded user info
|
||||
*/
|
||||
extractUserInfo(accessToken) {
|
||||
extractUserInfo(accessToken: string) {
|
||||
try {
|
||||
// Try to decode as JWT
|
||||
const parts = accessToken.split(".");
|
||||
|
||||
@@ -10,6 +10,8 @@ import { spinner as createSpinner } from "../utils/ui";
|
||||
* Uses standard OAuth2 Authorization Code flow (no PKCE)
|
||||
*/
|
||||
export class GeminiCLIService {
|
||||
config: any;
|
||||
|
||||
constructor() {
|
||||
this.config = GEMINI_CONFIG;
|
||||
}
|
||||
@@ -17,7 +19,7 @@ export class GeminiCLIService {
|
||||
/**
|
||||
* Build Gemini CLI authorization URL
|
||||
*/
|
||||
buildAuthUrl(redirectUri, state) {
|
||||
buildAuthUrl(redirectUri: string, state: string) {
|
||||
const params = new URLSearchParams({
|
||||
client_id: this.config.clientId,
|
||||
response_type: "code",
|
||||
@@ -34,7 +36,7 @@ export class GeminiCLIService {
|
||||
/**
|
||||
* Exchange authorization code for tokens
|
||||
*/
|
||||
async exchangeCode(code, redirectUri) {
|
||||
async exchangeCode(code: string, redirectUri: string) {
|
||||
const response = await fetch(this.config.tokenUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -61,7 +63,7 @@ export class GeminiCLIService {
|
||||
/**
|
||||
* Fetch project ID from Google Cloud Code Assist
|
||||
*/
|
||||
async fetchProjectId(accessToken) {
|
||||
async fetchProjectId(accessToken: string) {
|
||||
const response = await fetch("https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -109,7 +111,7 @@ export class GeminiCLIService {
|
||||
/**
|
||||
* Get user info from Google
|
||||
*/
|
||||
async getUserInfo(accessToken) {
|
||||
async getUserInfo(accessToken: string) {
|
||||
const response = await fetch(`${this.config.userInfoUrl}?alt=json`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
@@ -128,7 +130,7 @@ export class GeminiCLIService {
|
||||
/**
|
||||
* Save Gemini CLI tokens to server
|
||||
*/
|
||||
async saveTokens(tokens, userInfo, projectId) {
|
||||
async saveTokens(tokens: any, userInfo: any, projectId: string) {
|
||||
const { server, token, userId } = getServerCredentials();
|
||||
|
||||
const response = await fetch(`${server}/api/cli/providers/gemini-cli`, {
|
||||
@@ -166,7 +168,7 @@ export class GeminiCLIService {
|
||||
spinner.text = "Starting local server...";
|
||||
|
||||
// Start local server for callback
|
||||
let callbackParams = null;
|
||||
let callbackParams: any = null;
|
||||
const { port, close } = await startLocalServer((params) => {
|
||||
callbackParams = params;
|
||||
});
|
||||
@@ -198,7 +200,7 @@ export class GeminiCLIService {
|
||||
if (callbackParams) {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
resolve(undefined);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
@@ -237,7 +239,7 @@ export class GeminiCLIService {
|
||||
`Gemini CLI connected successfully! (${userInfo.email}, Project: ${projectId})`
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
spinner.fail(`Failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import { spinner as createSpinner } from "../utils/ui";
|
||||
* Uses Authorization Code flow with Basic Auth
|
||||
*/
|
||||
export class IFlowService {
|
||||
config: any;
|
||||
|
||||
constructor() {
|
||||
this.config = IFLOW_CONFIG;
|
||||
}
|
||||
@@ -17,7 +19,7 @@ export class IFlowService {
|
||||
/**
|
||||
* Build iFlow authorization URL
|
||||
*/
|
||||
buildAuthUrl(redirectUri, state) {
|
||||
buildAuthUrl(redirectUri: string, state: string) {
|
||||
const params = new URLSearchParams({
|
||||
loginMethod: this.config.extraParams.loginMethod,
|
||||
type: this.config.extraParams.type,
|
||||
@@ -32,7 +34,7 @@ export class IFlowService {
|
||||
/**
|
||||
* Exchange authorization code for tokens
|
||||
*/
|
||||
async exchangeCode(code, redirectUri) {
|
||||
async exchangeCode(code: string, redirectUri: string) {
|
||||
// Create Basic Auth header
|
||||
const basicAuth = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString(
|
||||
"base64"
|
||||
@@ -65,7 +67,7 @@ export class IFlowService {
|
||||
/**
|
||||
* Get user info from iFlow
|
||||
*/
|
||||
async getUserInfo(accessToken) {
|
||||
async getUserInfo(accessToken: string) {
|
||||
const response = await fetch(
|
||||
`${this.config.userInfoUrl}?accessToken=${encodeURIComponent(accessToken)}`,
|
||||
{
|
||||
@@ -92,7 +94,7 @@ export class IFlowService {
|
||||
/**
|
||||
* Save iFlow tokens to server
|
||||
*/
|
||||
async saveTokens(tokens, userInfo) {
|
||||
async saveTokens(tokens: any, userInfo: any) {
|
||||
const { server, token, userId } = getServerCredentials();
|
||||
|
||||
const response = await fetch(`${server}/api/cli/providers/iflow`, {
|
||||
@@ -129,7 +131,7 @@ export class IFlowService {
|
||||
spinner.text = "Starting local server...";
|
||||
|
||||
// Start local server for callback
|
||||
let callbackParams = null;
|
||||
let callbackParams: any = null;
|
||||
const { port, close } = await startLocalServer((params) => {
|
||||
callbackParams = params;
|
||||
});
|
||||
@@ -161,7 +163,7 @@ export class IFlowService {
|
||||
if (callbackParams) {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
resolve(undefined);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
@@ -193,7 +195,7 @@ export class IFlowService {
|
||||
|
||||
spinner.succeed(`iFlow connected successfully! (${userInfo.email || userInfo.phone})`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
spinner.fail(`Failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,16 @@ import { OAUTH_TIMEOUT } from "../constants/oauth";
|
||||
* Generic OAuth Authorization Code Flow with PKCE
|
||||
*/
|
||||
export class OAuthService {
|
||||
constructor(config) {
|
||||
config: any;
|
||||
|
||||
constructor(config: any) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build authorization URL
|
||||
*/
|
||||
buildAuthUrl(redirectUri, state, codeChallenge, extraParams = {}) {
|
||||
buildAuthUrl(redirectUri: string, state: string, codeChallenge: string, extraParams: Record<string, string> = {}) {
|
||||
const params = new URLSearchParams({
|
||||
client_id: this.config.clientId,
|
||||
response_type: "code",
|
||||
@@ -32,11 +34,11 @@ export class OAuthService {
|
||||
/**
|
||||
* Start local server and wait for callback
|
||||
*/
|
||||
async startAuthFlow(authUrl, providerName) {
|
||||
async startAuthFlow(authUrl: string | null, providerName: string) {
|
||||
const spinner = createSpinner("Starting local server...").start();
|
||||
|
||||
// Start local server for callback
|
||||
let callbackParams = null;
|
||||
let callbackParams: any = null;
|
||||
const { port, close } = await startLocalServer((params) => {
|
||||
callbackParams = params;
|
||||
});
|
||||
@@ -60,7 +62,7 @@ export class OAuthService {
|
||||
if (callbackParams) {
|
||||
clearInterval(checkInterval);
|
||||
clearTimeout(timeout);
|
||||
resolve();
|
||||
resolve(undefined);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
@@ -85,9 +87,9 @@ export class OAuthService {
|
||||
* Exchange authorization code for tokens
|
||||
*/
|
||||
async exchangeCode(
|
||||
code,
|
||||
redirectUri,
|
||||
codeVerifier,
|
||||
code: string,
|
||||
redirectUri: string,
|
||||
codeVerifier: string,
|
||||
contentType = "application/x-www-form-urlencoded"
|
||||
) {
|
||||
const body =
|
||||
@@ -127,7 +129,7 @@ export class OAuthService {
|
||||
/**
|
||||
* Complete OAuth flow
|
||||
*/
|
||||
async authenticate(providerName, buildAuthUrlFn) {
|
||||
async authenticate(providerName: string, buildAuthUrlFn: (redirectUri: string, state: string, codeChallenge: string) => string) {
|
||||
// Generate PKCE
|
||||
const { codeVerifier, codeChallenge, state } = generatePKCE();
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import { spinner as createSpinner } from "../utils/ui";
|
||||
* Uses Device Code Flow with PKCE
|
||||
*/
|
||||
export class QwenService {
|
||||
config: any;
|
||||
|
||||
constructor() {
|
||||
this.config = QWEN_CONFIG;
|
||||
}
|
||||
@@ -16,7 +18,7 @@ export class QwenService {
|
||||
/**
|
||||
* Request device code
|
||||
*/
|
||||
async requestDeviceCode(codeChallenge) {
|
||||
async requestDeviceCode(codeChallenge: string) {
|
||||
const response = await fetch(this.config.deviceCodeUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
@@ -42,7 +44,7 @@ export class QwenService {
|
||||
/**
|
||||
* Poll for token
|
||||
*/
|
||||
async pollForToken(deviceCode, codeVerifier, interval = 5) {
|
||||
async pollForToken(deviceCode: string, codeVerifier: string, interval = 5) {
|
||||
const maxAttempts = 60; // 5 minutes
|
||||
const pollInterval = interval * 1000;
|
||||
|
||||
@@ -89,7 +91,7 @@ export class QwenService {
|
||||
/**
|
||||
* Save Qwen tokens to server
|
||||
*/
|
||||
async saveTokens(tokens) {
|
||||
async saveTokens(tokens: any) {
|
||||
const { server, token, userId } = getServerCredentials();
|
||||
|
||||
const response = await fetch(`${server}/api/cli/providers/qwen`, {
|
||||
@@ -161,7 +163,7 @@ export class QwenService {
|
||||
|
||||
spinner.succeed("Qwen connected successfully!");
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
spinner.fail(`Failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import { URL } from "url";
|
||||
* @param {number} fixedPort - Optional fixed port number (default: random)
|
||||
* @returns {Promise<{server: http.Server, port: number, close: Function}>}
|
||||
*/
|
||||
export function startLocalServer(onCallback, fixedPort = null) {
|
||||
export function startLocalServer(onCallback: (params: Record<string, string>) => void, fixedPort: number | null = null): Promise<{ server: any; port: number; close: () => void }> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = http.createServer((req, res) => {
|
||||
const url = new URL(req.url, `http://localhost`);
|
||||
const url = new URL(req.url || "/", `http://localhost`);
|
||||
|
||||
if (url.pathname === "/callback" || url.pathname === "/auth/callback") {
|
||||
const params = Object.fromEntries(url.searchParams);
|
||||
@@ -67,15 +67,15 @@ export function startLocalServer(onCallback, fixedPort = null) {
|
||||
// Listen on fixed port or find available port
|
||||
const portToUse = fixedPort || 0;
|
||||
server.listen(portToUse, "127.0.0.1", () => {
|
||||
const { port } = server.address();
|
||||
const addr = server.address() as { port: number };
|
||||
resolve({
|
||||
server,
|
||||
port,
|
||||
port: addr.port,
|
||||
close: () => server.close(),
|
||||
});
|
||||
});
|
||||
|
||||
server.on("error", (err) => {
|
||||
server.on("error", (err: any) => {
|
||||
if (err.code === "EADDRINUSE" && fixedPort) {
|
||||
reject(
|
||||
new Error(
|
||||
@@ -105,7 +105,7 @@ export function waitForCallback(timeoutMs = 300000) {
|
||||
}
|
||||
}, timeoutMs);
|
||||
|
||||
const onCallback = (params) => {
|
||||
const onCallback = (params: Record<string, string>) => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
clearTimeout(timeout);
|
||||
@@ -114,6 +114,6 @@ export function waitForCallback(timeoutMs = 300000) {
|
||||
};
|
||||
|
||||
// Return the callback function
|
||||
resolve.__onCallback = onCallback;
|
||||
(resolve as any).__onCallback = onCallback;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user