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:
diegosouzapw
2026-02-17 03:46:24 -03:00
parent 52b025ca5a
commit f1319448ac
7 changed files with 63 additions and 51 deletions

View File

@@ -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;
}

View File

@@ -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(".");

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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;
});
}