mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-18 22:33:08 +00:00
fix: Remove unused vars and improve type safety. Improve task recovery
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user