fix: Remove unused vars and improve type safety. Improve task recovery

This commit is contained in:
gsxdsm
2026-02-17 13:18:40 -08:00
parent 8bb10632b1
commit de021f96bf
68 changed files with 1028 additions and 534 deletions

View File

@@ -1,7 +1,12 @@
import { createLogger } from '@automaker/utils';
import { createEventEmitter } from '../lib/events.js';
import type { SettingsService } from './settings-service.js';
const logger = createLogger('ZaiUsage');
/** Default timeout for fetch requests in milliseconds */
const FETCH_TIMEOUT_MS = 10_000;
/**
* z.ai quota limit entry from the API
*/
@@ -112,6 +117,21 @@ interface ZaiApiResponse {
message?: string;
}
/** Result from configure method */
interface ConfigureResult {
success: boolean;
message: string;
isAvailable: boolean;
}
/** Result from verifyApiKey method */
interface VerifyResult {
success: boolean;
authenticated: boolean;
message?: string;
error?: string;
}
/**
* z.ai Usage Service
*
@@ -162,16 +182,163 @@ export class ZaiUsageService {
return Boolean(token && token.length > 0);
}
/**
* Configure z.ai API token and host.
* Persists the token via settingsService and updates in-memory state.
*/
async configure(
options: { apiToken?: string; apiHost?: string },
settingsService: SettingsService
): Promise<ConfigureResult> {
const emitter = createEventEmitter();
if (options.apiToken !== undefined) {
// Set in-memory token
this.setApiToken(options.apiToken || '');
// Persist to credentials
try {
await settingsService.updateCredentials({
apiKeys: { zai: options.apiToken || '' },
} as Parameters<typeof settingsService.updateCredentials>[0]);
logger.info('[configure] Saved z.ai API key to credentials');
} catch (persistError) {
logger.error('[configure] Failed to persist z.ai API key:', persistError);
}
}
if (options.apiHost) {
this.setApiHost(options.apiHost);
}
const result: ConfigureResult = {
success: true,
message: 'z.ai configuration updated',
isAvailable: this.isAvailable(),
};
emitter.emit('notification:created', {
type: 'zai.configured',
success: result.success,
isAvailable: result.isAvailable,
});
return result;
}
/**
* Verify an API key without storing it.
* Makes a test request to the z.ai quota URL with the given key.
*/
async verifyApiKey(apiKey: string | undefined): Promise<VerifyResult> {
const emitter = createEventEmitter();
if (!apiKey || typeof apiKey !== 'string' || apiKey.trim().length === 0) {
return {
success: false,
authenticated: false,
error: 'Please provide an API key to test.',
};
}
const quotaUrl =
process.env.Z_AI_QUOTA_URL ||
`${process.env.Z_AI_API_HOST ? `https://${process.env.Z_AI_API_HOST}` : 'https://api.z.ai'}/api/monitor/usage/quota/limit`;
logger.info(`[verify] Testing API key against: ${quotaUrl}`);
try {
const response = await fetch(quotaUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${apiKey.trim()}`,
Accept: 'application/json',
},
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
});
let result: VerifyResult;
if (response.ok) {
result = {
success: true,
authenticated: true,
message: 'Connection successful! z.ai API responded.',
};
} else if (response.status === 401 || response.status === 403) {
result = {
success: false,
authenticated: false,
error: 'Invalid API key. Please check your key and try again.',
};
} else {
result = {
success: false,
authenticated: false,
error: `API request failed: ${response.status} ${response.statusText}`,
};
}
emitter.emit('notification:created', {
type: 'zai.verify.result',
success: result.success,
authenticated: result.authenticated,
});
return result;
} catch (error) {
// Handle abort/timeout errors specifically
if (error instanceof Error && error.name === 'AbortError') {
const result: VerifyResult = {
success: false,
authenticated: false,
error: 'Request timed out. The z.ai API did not respond in time.',
};
emitter.emit('notification:created', {
type: 'zai.verify.result',
success: false,
error: 'timeout',
});
return result;
}
const message = error instanceof Error ? error.message : 'Unknown error';
logger.error('Error verifying z.ai API key:', error);
emitter.emit('notification:created', {
type: 'zai.verify.result',
success: false,
error: message,
});
return {
success: false,
authenticated: false,
error: `Network error: ${message}`,
};
}
}
/**
* Fetch usage data from z.ai API
*/
async fetchUsageData(): Promise<ZaiUsageData> {
logger.info('[fetchUsageData] Starting...');
const emitter = createEventEmitter();
emitter.emit('notification:created', { type: 'zai.usage.start' });
const token = this.getApiToken();
if (!token) {
logger.error('[fetchUsageData] No API token configured');
throw new Error('z.ai API token not configured. Set Z_AI_API_KEY environment variable.');
const error = new Error(
'z.ai API token not configured. Set Z_AI_API_KEY environment variable.'
);
emitter.emit('notification:created', {
type: 'zai.usage.error',
error: error.message,
});
throw error;
}
const quotaUrl =
@@ -180,31 +347,68 @@ export class ZaiUsageService {
logger.info(`[fetchUsageData] Fetching from: ${quotaUrl}`);
try {
const response = await fetch(quotaUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
});
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
if (!response.ok) {
logger.error(`[fetchUsageData] HTTP ${response.status}: ${response.statusText}`);
throw new Error(`z.ai API request failed: ${response.status} ${response.statusText}`);
try {
const response = await fetch(quotaUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
logger.error(`[fetchUsageData] HTTP ${response.status}: ${response.statusText}`);
throw new Error(`z.ai API request failed: ${response.status} ${response.statusText}`);
}
const data = (await response.json()) as unknown as ZaiApiResponse;
logger.info('[fetchUsageData] Response received:', JSON.stringify(data, null, 2));
const result = this.parseApiResponse(data);
emitter.emit('notification:created', {
type: 'zai.usage.success',
data: result,
});
return result;
} finally {
clearTimeout(timeoutId);
}
} catch (error) {
// Handle abort/timeout errors
if (error instanceof Error && error.name === 'AbortError') {
const timeoutError = new Error(`z.ai API request timed out after ${FETCH_TIMEOUT_MS}ms`);
emitter.emit('notification:created', {
type: 'zai.usage.error',
error: timeoutError.message,
});
throw timeoutError;
}
const data = (await response.json()) as unknown as ZaiApiResponse;
logger.info('[fetchUsageData] Response received:', JSON.stringify(data, null, 2));
return this.parseApiResponse(data);
} catch (error) {
if (error instanceof Error && error.message.includes('z.ai API')) {
emitter.emit('notification:created', {
type: 'zai.usage.error',
error: error.message,
});
throw error;
}
logger.error('[fetchUsageData] Failed to fetch:', error);
throw new Error(
const fetchError = new Error(
`Failed to fetch z.ai usage data: ${error instanceof Error ? error.message : String(error)}`
);
emitter.emit('notification:created', {
type: 'zai.usage.error',
error: fetchError.message,
});
throw fetchError;
}
}