Feature: Add Gemini and Z.ai (GLM) usage tracking#773
Feature: Add Gemini and Z.ai (GLM) usage tracking#773gsxdsm wants to merge 4 commits intoAutoMaker-Org:v0.15.0rcfrom
Conversation
📝 WalkthroughWalkthroughThis PR adds comprehensive support for two new AI providers: z.ai and Gemini. It introduces backend services for usage tracking and API routes, alongside frontend UI components for authentication, configuration, and usage display, with corresponding type definitions and store updates. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI Component
participant Store as App Store
participant Hook as useZaiUsage Hook
participant Client as HTTP API Client
participant Server as Server (/api/zai)
participant Service as ZaiUsageService
participant ZaiAPI as z.ai API
UI->>Hook: useZaiUsage(enabled=true)
Hook->>Store: Read zaiUsage, check staleness
alt Data is stale or missing
Hook->>Client: http.zai.getUsage()
Client->>Server: GET /api/zai/usage
Server->>Service: fetchUsageData()
Service->>ZaiAPI: POST /quota (with token)
ZaiAPI-->>Service: Quota response
Service->>Service: parseApiResponse()
Service-->>Server: ZaiUsageData
Server-->>Client: ZaiUsageData
Client-->>Hook: ZaiUsageData
Hook->>Store: setZaiUsage(data)
end
Store-->>UI: zaiUsage state
UI->>UI: Render usage cards (tokens, MCP)
sequenceDiagram
participant UI as UI Component
participant Store as Setup Store
participant Hook as useProviderAuthInit
participant Client as HTTP API Client
participant Server as Server (/api/gemini)
participant Service as GeminiUsageService
participant CLI as Gemini CLI
Hook->>Client: http.setup.getGeminiStatus()
Client->>Server: GET /api/gemini/status
Server->>Service: Create GeminiProvider instance
Service->>Service: detectInstallation()
Service->>CLI: Find binary, read oauth2.js
CLI-->>Service: OAuth credentials, version
Service->>Service: Extract clientId/clientSecret
Service-->>Server: Status payload
Server-->>Client: { installed, authenticated, version, method }
Client-->>Hook: Status data
Hook->>Store: setGeminiAuthStatus(status)
Store-->>UI: geminiAuthStatus
UI->>UI: Enable/disable Gemini features
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @gsxdsm, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly expands the platform's monitoring capabilities by integrating usage tracking for Gemini and Z.ai models, offering users better visibility into their coding plan consumption. It also refines the Claude model experience by upgrading to Opus 4.6 with adaptive thinking and enhances authentication diagnostics. Furthermore, the update includes support for the latest GPT-5.3-Codex model and streamlines the Docker environment for automated testing with Playwright. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request adds usage tracking for Gemini and Z.ai models, including backend services, API routes, and UI components. The changes are extensive and well-structured. I've identified a few areas for improvement, mainly concerning error handling, performance in synchronous calls, and code duplication in the new backend services. Additionally, there's a minor regression in the UI that should be addressed. Overall, this is a solid feature addition.
| // Return error in a format the UI expects | ||
| res.status(200).json({ | ||
| authenticated: false, | ||
| authMethod: 'none', | ||
| usedPercent: 0, | ||
| remainingPercent: 100, | ||
| lastUpdated: new Date().toISOString(), | ||
| error: `Failed to fetch Gemini usage: ${message}`, | ||
| }); |
There was a problem hiding this comment.
The catch block for the /usage route currently returns a 200 status with authenticated: false for any unexpected error. This can be misleading, as an error (e.g., a file parsing error for credentials) doesn't necessarily mean the user is unauthenticated. For unexpected server-side errors, it's more appropriate to return a 500 status code. This provides a clearer signal to the client that something went wrong on the server, distinct from a normal "not authenticated" state which is handled gracefully within the service.
res.status(500).json({
error: `An unexpected error occurred while fetching Gemini usage: ${message}`,
});| private findGeminiBinaryPath(): string | null { | ||
| try { | ||
| // Try 'which' on Unix-like systems | ||
| const whichResult = execSync('which gemini 2>/dev/null', { encoding: 'utf8' }).trim(); |
There was a problem hiding this comment.
The use of execSync is synchronous and blocks the Node.js event loop, which can degrade server performance. While extractOAuthClientCredentials caches the result, the first call to this endpoint will block. This happens again on line 559. It is recommended to use the asynchronous exec from child_process and make the calling functions (findGeminiBinaryPath and extractOAuthClientCredentials) async to avoid blocking.
| // Test the key by making a request to z.ai API | ||
| 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}`); | ||
|
|
||
| const response = await fetch(quotaUrl, { | ||
| method: 'GET', | ||
| headers: { | ||
| Authorization: `Bearer ${apiKey.trim()}`, | ||
| Accept: 'application/json', | ||
| }, | ||
| }); |
There was a problem hiding this comment.
The logic for constructing the quotaUrl and performing the API key verification is implemented directly in this route handler. This duplicates logic from ZaiUsageService and couples the route tightly with the external API's implementation details. To improve separation of concerns and code reuse, this verification logic should be moved into a new method on the ZaiUsageService.
| : activeTab === 'codex' | ||
| ? { | ||
| icon: OpenAIIcon, | ||
| percentage: codexWindowUsage ?? 0, | ||
| isStale: isCodexStale, | ||
| title: `Usage (${codexWindowLabel})`, | ||
| }; | ||
| } |
There was a problem hiding this comment.
The title property for the codex case in indicatorInfo seems to have been accidentally removed. This will result in an empty title attribute for the usage indicator when the Codex tab is active. The previous implementation included a title, which should be restored for consistency and better user experience.
? {
icon: OpenAIIcon,
percentage: codexWindowUsage ?? 0,
isStale: isCodexStale,
title: `Usage (${codexWindowLabel})`,
}
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/server/src/services/settings-service.ts (1)
728-754:⚠️ Potential issue | 🟠 Major
getMaskedCredentials()does not include the newzaikey.The method returns masked credentials for
anthropic,openaibut omits the newly addedzaikey. This means the UI cannot display whether a z.ai API key is configured.🔧 Proposed fix
async getMaskedCredentials(): Promise<{ anthropic: { configured: boolean; masked: string }; google: { configured: boolean; masked: string }; openai: { configured: boolean; masked: string }; + zai: { configured: boolean; masked: string }; }> { const credentials = await this.getCredentials(); const maskKey = (key: string): string => { if (!key || key.length < 8) return ''; return `${key.substring(0, 4)}...${key.substring(key.length - 4)}`; }; return { anthropic: { configured: !!credentials.apiKeys.anthropic, masked: maskKey(credentials.apiKeys.anthropic), }, google: { configured: !!credentials.apiKeys.google, masked: maskKey(credentials.apiKeys.google), }, openai: { configured: !!credentials.apiKeys.openai, masked: maskKey(credentials.apiKeys.openai), }, + zai: { + configured: !!credentials.apiKeys.zai, + masked: maskKey(credentials.apiKeys.zai), + }, }; }apps/ui/src/components/usage-popover.tsx (1)
30-46:⚠️ Potential issue | 🟡 MinorGuard against past reset times in
formatResetTime.If the reset time is already in the past, the current logic yields negative “Resets in -Xm” text. Add a clamp or fallback message.
🧹 Suggested fix
const now = new Date(); const diff = date.getTime() - now.getTime(); + if (diff <= 0) { + return 'Resetting soon'; + }
🤖 Fix all issues with AI agents
In `@apps/server/src/routes/gemini/index.ts`:
- Around line 35-57: Remove the unsafe (status as any) casts: use the typed
InstallationStatus returned by GeminiProvider.detectInstallation() and compute
authMethod using the existing fields (status.authenticated / status.hasApiKey)
without casting; also remove or type-augment hasCredentialsFile—either add
hasCredentialsFile to the InstallationStatus interface (and populate it in
detectInstallation) or stop returning hasCredentialsFile and default it to false
in the JSON response; update the route in apps/server/src/routes/gemini/index.ts
(references: GeminiProvider, detectInstallation, authMethod, hasCredentialsFile,
InstallationStatus) accordingly.
In `@apps/server/src/routes/zai/index.ts`:
- Around line 63-155: The route handlers for router.post('/configure') and
router.post('/verify') contain business logic
(usageService.setApiToken/setApiHost, settingsService.updateCredentials, fetch
to quota URL and response handling) and must be moved into ZaiUsageService (or a
new service) as methods like configure({apiToken, apiHost}) and
verifyApiKey(apiKey); those service methods should perform the persistence (call
settingsService.updateCredentials), runtime updates (usageService calls),
external fetch, and emit events via the shared emitter from createEventEmitter()
(e.g., emit 'zai.configured' and 'zai.verify.result' with status and error
info). Update the route handlers to simply call ZaiUsageService.configure(...)
and ZaiUsageService.verifyApiKey(...), await results, and return minimal JSON
responses, handling exceptions only to map errors to HTTP responses. Ensure you
reference and replace direct calls to usageService and
settingsService.updateCredentials inside the service and add emitter.emit calls
for UI updates.
- Around line 120-147: The z.ai verification fetch call can hang indefinitely;
update the fetch invocation in the route handler that calls quotaUrl to pass a
signal created via AbortSignal.timeout(timeoutMs) (e.g., const controllerSignal
= AbortSignal.timeout(5000)) in the fetch options so the request auto-aborts
after a reasonable timeout, and enhance the catch block to handle aborts (detect
AbortError / error.name === 'AbortError' and return a clear timeout JSON
response). Ensure you reference the existing fetch call that sets headers
Authorization/Accept and the surrounding try/catch so you only add the signal
option and the abort-specific error branch.
In `@apps/server/src/services/gemini-usage-service.ts`:
- Around line 688-697: The refreshed token is always written to
this.credentialsPath (e.g., in the getValidAccessToken flow) even when
credentials were loaded from a different file (such as
~/.config/gemini/oauth_creds.json), causing divergence; modify the
credential-loading logic (where cachedCredentials is set) to record the actual
source path into a new property (e.g., this.loadedCredentialsPath) and then
change the write path in the save/refresh block to use that recorded
this.loadedCredentialsPath (falling back to this.credentialsPath if not set) so
refreshed tokens are persisted back to the same file they were read from.
- Around line 349-353: The possiblePaths array contains a duplicate because the
constructor already sets this.credentialsPath to the home
'.gemini/oauth_creds.json' path; remove the redundant literal path from
possiblePaths or dedupe the array before use (e.g., filter out duplicates by
keeping only the first occurrence) so possiblePaths only contains unique
entries; update references around possiblePaths and ensure the logic using
possiblePaths still checks the same fallback locations in
GeminiUsageService/constructor.
- Around line 343-384: The cachedCredentials in loadCredentials() is never
invalidated so stale/expired creds persist; modify loadCredentials (and/or
clearCache()) to implement automatic invalidation by adding a TTL (e.g., 5
minutes) or validating the credentials file mtime before returning
cachedCredentials: store a cachedAt timestamp alongside cachedCredentials when
setting it in loadCredentials(), and when loadCredentials is called check if
cachedAt + TTL has expired or if fs.statSync(credPath).mtimeMs is newer than
cachedAt — if expired or file changed, discard cachedCredentials and re-read
from disk (then update cachedAt); ensure clearCache() still exists to allow
explicit invalidation and reference credentialsPath/possiblePaths when comparing
mtimes.
- Around line 557-578: The fallback search in extractOAuthClientCredentials uses
execSync with an interpolated baseDir, creating a command-injection and
quoting/portability risk; change it to only run on POSIX (guard with
process.platform !== 'win32') and replace the string-shell execSync call with a
safe execFileSync/spawnSync invocation that passes 'find' and its arguments as
an array (e.g., args: [ `${baseDir}/..`, '-name', 'oauth2.js', '-path',
'*gemini*', '-path', '*code_assist*' ]) to avoid shell interpolation, keep the
same encoding/timeout options, then proceed to read and pass the result to
parseOAuthCredentialsFromSource and set this.cachedClientCredentials as before.
- Around line 168-202: The external fetch calls in fetchUsageData
(CODE_ASSIST_URL, QUOTA_API_URL, GOOGLE_TOKEN_URL) lack timeouts and can hang;
add an AbortSignal with a timeout for each request (e.g., use
AbortSignal.timeout(timeoutMs) or new AbortController().signal with setTimeout
to abort) and pass the signal option into fetch, catch the resulting
abort/timeout errors and handle/log them similarly to other fetch failures;
ensure each fetch (the code block around CODE_ASSIST_URL, the QUOTA_API_URL
POST, and the GOOGLE_TOKEN_URL call) is updated to create/use a signal and clean
up any controllers if you use AbortController.
In `@apps/server/src/services/zai-usage-service.ts`:
- Around line 165-176: The fetchUsageData method does not emit lifecycle events
for frontend streaming; modify fetchUsageData to call createEventEmitter()
(import from lib/events.ts) at start and emit a "zai.usage.start" event, then
emit "zai.usage.success" with the fetched ZaiUsageData on successful completion,
and emit "zai.usage.error" with error details if any exception occurs (rethrow
after emitting). Use the existing method name fetchUsageData and the same logger
for messages, ensure the emitter is created inside the method and closed/handled
per createEventEmitter's contract so events stream via WebSocket to the
frontend.
- Around line 182-190: The fetch call to quotaUrl currently has no timeout; wrap
it with an AbortController and a timeout like other services (e.g.,
verify-claude-auth.ts/event-hook-service.ts): create an AbortController, start a
setTimeout to call controller.abort() after the configured timeout, pass
controller.signal into the fetch options (where Authorization and Accept headers
are set), and clear the timeout after fetch completes; also handle the abort
error case so the calling function (the code around quotaUrl/token/response)
throws or returns a proper timeout error instead of hanging.
In `@apps/ui/src/components/views/board-view/mobile-usage-bar.tsx`:
- Around line 38-54: The formatResetTime function returns negative "Resets in
-Xm" for past timestamps; update formatResetTime(unixTimestamp, isMilliseconds)
to detect diff <= 0 and return a short message like "Resetting soon" (matching
server-side behavior) before computing minutes/hours/dates, keeping the existing
branches for <1h, <24h and fallback to date; touch the function name
formatResetTime and ensure the diff calculation (const diff = date.getTime() -
now.getTime()) is used to decide the early-return for past/resetting-soon cases.
- Around line 166-167: Add geminiUsage and geminiUsageLastUpdated to the
existing Zustand store alongside the other provider states (the same store that
exposes setClaudeUsage/setCodexUsage/setZaiUsage): add state fields geminiUsage:
GeminiUsage | null and geminiUsageLastUpdated: number | null, and a setter
setGeminiUsage((usage, lastUpdated?) => void) that updates both fields. In
mobile-usage-bar.tsx remove the local useState declarations for geminiUsage and
geminiUsageLastUpdated and replace them with the store selectors/hooks (e.g.,
const { geminiUsage, geminiUsageLastUpdated, setGeminiUsage } = useStore(...))
and update any places that call setGeminiUsage to use the store setter. Ensure
types match the existing GeminiUsage type and that other components (like
UsagePopover) can read the new store fields.
In
`@apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts`:
- Around line 239-259: The code persists the z.ai key to the local store via
setApiKeys before calling api.zai.configure, and uses a loose check
(result.success || result.isAvailable) so the local state can show "Saved" even
if server config failed; either move the setApiKeys call so it runs only after
api.zai.configure succeeds (and then update setZaiAuthStatus and call
queryClient.invalidateQueries), or implement a rollback in the catch where you
remove the stored zai key using the same setApiKeys call; also tighten the
success condition to require result.success (not just isAvailable) when deciding
success for configuring z.ai.
- Around line 246-252: The code in use-api-key-management.ts hardcodes
hasEnvApiKey: false when calling setZaiAuthStatus after saving a UI key, which
can incorrectly clear an existing env-configured key; update the call in the
block that calls setZaiAuthStatus to preserve the current env key flag by
reading the existing apiKeyStatus (e.g., use apiKeyStatus.hasEnvApiKey) and set
hasEnvApiKey to that value instead of false so you only update the fields that
truly change (authenticated, method, hasApiKey) while keeping hasEnvApiKey in
sync.
- Line 1: Remove the top-level "// `@ts-nocheck`" and add explicit TypeScript
typings for the hook and its internal entities: declare interfaces/types such as
ApiKey (id, name, key, createdAt, revoked?), ApiKeyState (array + validation
errors), AuthState (isAuthenticated, userId, token), and ServerConfig
(allowedOrigins, keyPolicy) used by the useApiKeyManagement hook; update
function signatures (useApiKeyManagement, loadApiKeys, saveApiKeys,
validateApiKey, revokeApiKey) and React state hooks to use these types, type any
localStorage/parsing and async calls with proper unions/Optionals, and fix
resulting type errors rather than silencing them so the file compiles with full
type safety.
In `@apps/ui/src/hooks/use-provider-auth-init.ts`:
- Around line 133-138: The code is assigning the wrong field to the CLI path:
update the call to setGeminiCliStatus so the path uses result.path (the actual
filesystem path) instead of result.status; locate the block where
setGeminiCliStatus is called (in use-provider-auth-init.ts) and replace the
path: result.status assignment with path: result.path while keeping installed:
result.installed ?? false and version: result.version unchanged.
- Around line 107-124: The z.ai auth response handler (api.zai.getStatus) can
leave authenticated/hasApiKey undefined; update the logic in the try block that
calls setZaiAuthStatus so it coerces result.available, result.hasApiKey, and
result.hasEnvApiKey to booleans (e.g., using !! or ?? false) and derive method
using those boolean values, and also add a fallback setZaiAuthStatus in the
non-success/exception path to set authenticated: false, method: 'none',
hasApiKey: false, hasEnvApiKey: false; reference setZaiAuthStatus,
api.zai.getStatus, and ZaiAuthMethod when making the changes.
🧹 Nitpick comments (6)
apps/ui/src/store/app-store.ts (1)
97-105: Consider hoisting Zai/Gemini usage types into@automaker/types.These usage shapes now exist in both server services and the UI store; centralizing them avoids drift and simplifies cross-layer contracts.
Based on learnings, “Core TypeScript definitions with no dependencies should be isolated in the automaker/types package”.Also applies to: 184-191
apps/ui/src/store/types/usage-types.ts (3)
65-66:limitType: 'TOKENS_LIMIT' | 'TIME_LIMIT' | string— the literal union members are redundant.Since
stringis a supertype of all string literals,'TOKENS_LIMIT' | 'TIME_LIMIT' | stringcollapses to juststringat the type level. If the intent is to provide autocomplete hints while still allowing arbitrary strings, use the(string & {})idiom instead.✏️ Suggested fix
export interface ZaiQuotaLimit { - limitType: 'TOKENS_LIMIT' | 'TIME_LIMIT' | string; + limitType: 'TOKENS_LIMIT' | 'TIME_LIMIT' | (string & {}); limit: number;
108-133:authMethodis typed asstringbut documented with known values — prefer a string literal union.The JSDoc on Line 111 lists
'cli_login' | 'api_key' | 'api_key_env' | 'none'but the actual type isstring. This weakens type safety for consumers (e.g., the ternary chains inmobile-usage-bar.tsxLines 407-411 comparing against literal strings). Using a union (with a(string & {})escape hatch if needed) would catch typos at compile time.✏️ Suggested fix
+export type GeminiAuthMethod = 'cli_login' | 'api_key' | 'api_key_env' | 'none'; + export interface GeminiUsage { /** Whether the user is authenticated (via CLI or API key) */ authenticated: boolean; - /** Authentication method: 'cli_login' | 'api_key' | 'api_key_env' | 'none' */ - authMethod: string; + /** Authentication method */ + authMethod: GeminiAuthMethod | (string & {});
62-84: Move API response types to@automaker/typesand standardize type definitions.
ZaiPlanType,ZaiQuotaLimit,ZaiUsage,GeminiQuotaBucket,GeminiTierQuota, andGeminiUsageare duplicated across frontend (apps/ui/src/store/types/usage-types.ts) and backend services (apps/server/src/services/). Since these are core type definitions with no app-specific dependencies, they should be defined once in@automaker/typesand imported by both frontend and backend.Additionally, align type safety for
GeminiUsage.authMethod: use a literal union type ('cli_login' | 'api_key' | 'none') instead ofstringto match the backend definition and prevent type mismatches. The JSDoc mentions'api_key_env', which is not present in the backend's type—clarify whether this is needed or remove it.apps/ui/src/components/views/board-view/mobile-usage-bar.tsx (2)
375-425: Gemini usage rendering handles multiple states clearly.The nested ternary covers: authenticated with tier quotas → authenticated without quotas (shows auth method + error/no-usage) → not authenticated → loading. The logic is sound, though the deeply nested ternaries could be extracted to a sub-component for readability in a follow-up.
24-54: ExtractformatNumberandformatResetTimeto a shared utility module.These helpers are duplicated identically in
usage-popover.tsxandmobile-usage-bar.tsx. Extract them to@/lib/format-utils.tsto eliminate duplication and improve maintainability across desktop (UsagePopover) and mobile (MobileUsageBar) usage-display components.Note:
codex-usage-popover.tsxhas a separate, simplerformatResetTimeimplementation for Codex-specific formatting that should remain separate.
| router.get('/status', async (_req: Request, res: Response) => { | ||
| try { | ||
| const provider = new GeminiProvider(); | ||
| const status = await provider.detectInstallation(); | ||
|
|
||
| const authMethod = | ||
| (status as any).authMethod || | ||
| (status.authenticated ? (status.hasApiKey ? 'api_key' : 'cli_login') : 'none'); | ||
|
|
||
| res.json({ | ||
| success: true, | ||
| installed: status.installed, | ||
| version: status.version || null, | ||
| path: status.path || null, | ||
| authenticated: status.authenticated || false, | ||
| authMethod, | ||
| hasCredentialsFile: (status as any).hasCredentialsFile || false, | ||
| }); | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : 'Unknown error'; | ||
| res.status(500).json({ success: false, error: message }); | ||
| } | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find GeminiProvider class and detectInstallation method
find . -type f -name "*.ts" -o -name "*.js" | xargs grep -l "class GeminiProvider" | head -5Repository: AutoMaker-Org/automaker
Length of output: 113
🏁 Script executed:
#!/bin/bash
# Search for detectInstallation method definition
rg "detectInstallation" --type ts --type tsx -B 5 -A 15Repository: AutoMaker-Org/automaker
Length of output: 94
🏁 Script executed:
#!/bin/bash
# Search for detectInstallation method with corrected flags
rg "detectInstallation" --type ts -B 5 -A 20Repository: AutoMaker-Org/automaker
Length of output: 50379
🏁 Script executed:
#!/bin/bash
# Read the GeminiProvider file
wc -l ./apps/server/src/providers/gemini-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 117
🏁 Script executed:
#!/bin/bash
# Read the detectInstallation method in gemini-provider.ts
sed -n '110,145p' ./apps/server/src/providers/gemini-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 1064
🏁 Script executed:
#!/bin/bash
# Also check InstallationStatus type definition
rg "interface InstallationStatus|type InstallationStatus" --type ts -A 15Repository: AutoMaker-Org/automaker
Length of output: 963
🏁 Script executed:
#!/bin/bash
# Read the full detectInstallation method in gemini-provider.ts to see exact return
rg "async detectInstallation" ./apps/server/src/providers/gemini-provider.ts -A 25Repository: AutoMaker-Org/automaker
Length of output: 731
🏁 Script executed:
#!/bin/bash
# Check if there's any place where status is modified before being used in the route
head -60 ./apps/server/src/routes/gemini/index.tsRepository: AutoMaker-Org/automaker
Length of output: 2061
Remove unnecessary as any casts; the accessed fields don't exist on InstallationStatus.
Lines 40-42 and 51 use (status as any) to access authMethod and hasCredentialsFile, which are not defined in the InstallationStatus interface. The code already has proper fallback logic that computes authMethod from existing fields; simply remove the unsafe cast and rely on the fallback computation. For hasCredentialsFile, either add it to the InstallationStatus type if needed, or remove it from the response and default to false.
🤖 Prompt for AI Agents
In `@apps/server/src/routes/gemini/index.ts` around lines 35 - 57, Remove the
unsafe (status as any) casts: use the typed InstallationStatus returned by
GeminiProvider.detectInstallation() and compute authMethod using the existing
fields (status.authenticated / status.hasApiKey) without casting; also remove or
type-augment hasCredentialsFile—either add hasCredentialsFile to the
InstallationStatus interface (and populate it in detectInstallation) or stop
returning hasCredentialsFile and default it to false in the JSON response;
update the route in apps/server/src/routes/gemini/index.ts (references:
GeminiProvider, detectInstallation, authMethod, hasCredentialsFile,
InstallationStatus) accordingly.
| // Configure API token (for settings page) | ||
| router.post('/configure', async (req: Request, res: Response) => { | ||
| try { | ||
| const { apiToken, apiHost } = req.body; | ||
|
|
||
| if (apiToken !== undefined) { | ||
| // Set in-memory token | ||
| usageService.setApiToken(apiToken || ''); | ||
|
|
||
| // Persist to credentials (deep merge happens in updateCredentials) | ||
| try { | ||
| await settingsService.updateCredentials({ | ||
| apiKeys: { zai: 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 (apiHost) { | ||
| usageService.setApiHost(apiHost); | ||
| } | ||
|
|
||
| res.json({ | ||
| success: true, | ||
| message: 'z.ai configuration updated', | ||
| isAvailable: usageService.isAvailable(), | ||
| }); | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : 'Unknown error'; | ||
| logger.error('Error configuring z.ai:', error); | ||
| res.status(500).json({ error: message }); | ||
| } | ||
| }); | ||
|
|
||
| // Verify API key without storing it (for testing in settings) | ||
| router.post('/verify', async (req: Request, res: Response) => { | ||
| try { | ||
| const { apiKey } = req.body; | ||
|
|
||
| if (!apiKey || typeof apiKey !== 'string' || apiKey.trim().length === 0) { | ||
| res.json({ | ||
| success: false, | ||
| authenticated: false, | ||
| error: 'Please provide an API key to test.', | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Test the key by making a request to z.ai API | ||
| 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}`); | ||
|
|
||
| const response = await fetch(quotaUrl, { | ||
| method: 'GET', | ||
| headers: { | ||
| Authorization: `Bearer ${apiKey.trim()}`, | ||
| Accept: 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| if (response.ok) { | ||
| res.json({ | ||
| success: true, | ||
| authenticated: true, | ||
| message: 'Connection successful! z.ai API responded.', | ||
| }); | ||
| } else if (response.status === 401 || response.status === 403) { | ||
| res.json({ | ||
| success: false, | ||
| authenticated: false, | ||
| error: 'Invalid API key. Please check your key and try again.', | ||
| }); | ||
| } else { | ||
| res.json({ | ||
| success: false, | ||
| authenticated: false, | ||
| error: `API request failed: ${response.status} ${response.statusText}`, | ||
| }); | ||
| } | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : 'Unknown error'; | ||
| logger.error('Error verifying z.ai API key:', error); | ||
| res.json({ | ||
| success: false, | ||
| authenticated: false, | ||
| error: `Network error: ${message}`, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Move z.ai config/verify logic into a service and emit events.
The route performs provider configuration and verification directly and doesn’t emit events for these operations. Please delegate this work to ZaiUsageService (or a dedicated service) and emit corresponding events via the shared emitter so the UI can react in real time. As per coding guidelines: 'Server business logic should be organized into services in the services/ directory, with Express route handlers in routes/ that delegate to services' and 'All server operations should emit events using createEventEmitter() from lib/events.ts that stream to the frontend via WebSocket'.
🤖 Prompt for AI Agents
In `@apps/server/src/routes/zai/index.ts` around lines 63 - 155, The route
handlers for router.post('/configure') and router.post('/verify') contain
business logic (usageService.setApiToken/setApiHost,
settingsService.updateCredentials, fetch to quota URL and response handling) and
must be moved into ZaiUsageService (or a new service) as methods like
configure({apiToken, apiHost}) and verifyApiKey(apiKey); those service methods
should perform the persistence (call settingsService.updateCredentials), runtime
updates (usageService calls), external fetch, and emit events via the shared
emitter from createEventEmitter() (e.g., emit 'zai.configured' and
'zai.verify.result' with status and error info). Update the route handlers to
simply call ZaiUsageService.configure(...) and
ZaiUsageService.verifyApiKey(...), await results, and return minimal JSON
responses, handling exceptions only to map errors to HTTP responses. Ensure you
reference and replace direct calls to usageService and
settingsService.updateCredentials inside the service and add emitter.emit calls
for UI updates.
| const response = await fetch(quotaUrl, { | ||
| method: 'GET', | ||
| headers: { | ||
| Authorization: `Bearer ${apiKey.trim()}`, | ||
| Accept: 'application/json', | ||
| }, | ||
| }); | ||
|
|
||
| if (response.ok) { | ||
| res.json({ | ||
| success: true, | ||
| authenticated: true, | ||
| message: 'Connection successful! z.ai API responded.', | ||
| }); | ||
| } else if (response.status === 401 || response.status === 403) { | ||
| res.json({ | ||
| success: false, | ||
| authenticated: false, | ||
| error: 'Invalid API key. Please check your key and try again.', | ||
| }); | ||
| } else { | ||
| res.json({ | ||
| success: false, | ||
| authenticated: false, | ||
| error: `API request failed: ${response.status} ${response.statusText}`, | ||
| }); | ||
| } | ||
| } catch (error) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find package.json files in the project root and apps/server to check Node engine requirements
fd package.json -t f -E node_modules -E dist -E build | head -5Repository: AutoMaker-Org/automaker
Length of output: 211
🏁 Script executed:
#!/bin/bash
# Get the actual content of the file in question
cat -n apps/server/src/routes/zai/index.ts | head -170 | tail -60Repository: AutoMaker-Org/automaker
Length of output: 2408
🏁 Script executed:
#!/bin/bash
# Check for service directory and existing service files in apps/server
find apps/server/src/services -type f -name "*zai*" 2>/dev/null || echo "No zai service found"
ls -la apps/server/src/services/ 2>/dev/null | head -20Repository: AutoMaker-Org/automaker
Length of output: 1493
🏁 Script executed:
#!/bin/bash
# Check Node engine requirement in apps/server package.json
cat apps/server/package.json | grep -A 5 '"engines"'Repository: AutoMaker-Org/automaker
Length of output: 179
🏁 Script executed:
#!/bin/bash
# Get fuller context of the zai route file to check for event emission
cat -n apps/server/src/routes/zai/index.ts | head -160Repository: AutoMaker-Org/automaker
Length of output: 6514
🏁 Script executed:
#!/bin/bash
# Check zai-usage-service to understand service structure
head -60 apps/server/src/services/zai-usage-service.tsRepository: AutoMaker-Org/automaker
Length of output: 1496
Add a timeout to the external z.ai verification request.
Without a timeout, the fetch request can hang indefinitely during upstream stalls, blocking the server thread. Your Node 22 runtime fully supports AbortSignal.timeout(), so add it directly:
Suggested fix
const response = await fetch(quotaUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${apiKey.trim()}`,
Accept: 'application/json',
},
+ signal: AbortSignal.timeout(5000),
});🤖 Prompt for AI Agents
In `@apps/server/src/routes/zai/index.ts` around lines 120 - 147, The z.ai
verification fetch call can hang indefinitely; update the fetch invocation in
the route handler that calls quotaUrl to pass a signal created via
AbortSignal.timeout(timeoutMs) (e.g., const controllerSignal =
AbortSignal.timeout(5000)) in the fetch options so the request auto-aborts after
a reasonable timeout, and enhance the catch block to handle aborts (detect
AbortError / error.name === 'AbortError' and return a clear timeout JSON
response). Ensure you reference the existing fetch call that sets headers
Authorization/Accept and the surrounding try/catch so you only add the signal
option and the abort-specific error branch.
| // First, get the project ID from loadCodeAssist endpoint | ||
| // This is required to get accurate quota data | ||
| let projectId: string | undefined; | ||
| try { | ||
| const codeAssistResponse = await fetch(CODE_ASSIST_URL, { | ||
| method: 'POST', | ||
| headers: { | ||
| Authorization: `Bearer ${accessToken}`, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify({}), | ||
| }); | ||
|
|
||
| if (codeAssistResponse.ok) { | ||
| const codeAssistData = (await codeAssistResponse.json()) as { | ||
| cloudaicompanionProject?: string; | ||
| currentTier?: { id?: string; name?: string }; | ||
| }; | ||
| projectId = codeAssistData.cloudaicompanionProject; | ||
| logger.debug('[fetchUsageData] Got project ID:', projectId); | ||
| } | ||
| } catch (e) { | ||
| logger.debug('[fetchUsageData] Failed to get project ID:', e); | ||
| } | ||
|
|
||
| // Fetch quota from Google Cloud API | ||
| // Pass project ID to get accurate quota (without it, returns default 100%) | ||
| const response = await fetch(QUOTA_API_URL, { | ||
| method: 'POST', | ||
| headers: { | ||
| Authorization: `Bearer ${accessToken}`, | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| body: JSON.stringify(projectId ? { project: projectId } : {}), | ||
| }); |
There was a problem hiding this comment.
No timeouts on external HTTP requests.
The fetch calls to CODE_ASSIST_URL, QUOTA_API_URL, and GOOGLE_TOKEN_URL have no timeout configured. If Google's servers are slow or unresponsive, these calls could hang indefinitely, blocking the route handler.
Consider using AbortSignal.timeout():
const response = await fetch(QUOTA_API_URL, {
method: 'POST',
headers: { ... },
body: JSON.stringify(...),
+ signal: AbortSignal.timeout(15000),
});🤖 Prompt for AI Agents
In `@apps/server/src/services/gemini-usage-service.ts` around lines 168 - 202, The
external fetch calls in fetchUsageData (CODE_ASSIST_URL, QUOTA_API_URL,
GOOGLE_TOKEN_URL) lack timeouts and can hang; add an AbortSignal with a timeout
for each request (e.g., use AbortSignal.timeout(timeoutMs) or new
AbortController().signal with setTimeout to abort) and pass the signal option
into fetch, catch the resulting abort/timeout errors and handle/log them
similarly to other fetch failures; ensure each fetch (the code block around
CODE_ASSIST_URL, the QUOTA_API_URL POST, and the GOOGLE_TOKEN_URL call) is
updated to create/use a signal and clean up any controllers if you use
AbortController.
| private async loadCredentials(): Promise<OAuthCredentials | null> { | ||
| if (this.cachedCredentials) { | ||
| return this.cachedCredentials; | ||
| } | ||
|
|
||
| // Check multiple possible paths | ||
| const possiblePaths = [ | ||
| this.credentialsPath, | ||
| path.join(os.homedir(), '.gemini', 'oauth_creds.json'), | ||
| path.join(os.homedir(), '.config', 'gemini', 'oauth_creds.json'), | ||
| ]; | ||
|
|
||
| for (const credPath of possiblePaths) { | ||
| try { | ||
| if (fs.existsSync(credPath)) { | ||
| const content = fs.readFileSync(credPath, 'utf8'); | ||
| const creds = JSON.parse(content); | ||
|
|
||
| // Handle different credential formats | ||
| if (creds.access_token || creds.refresh_token) { | ||
| this.cachedCredentials = creds; | ||
| logger.info('[loadCredentials] Loaded from:', credPath); | ||
| return creds; | ||
| } | ||
|
|
||
| // Some formats nest credentials under 'web' or 'installed' | ||
| if (creds.web?.client_id || creds.installed?.client_id) { | ||
| const clientCreds = creds.web || creds.installed; | ||
| this.cachedCredentials = { | ||
| client_id: clientCreds.client_id, | ||
| client_secret: clientCreds.client_secret, | ||
| }; | ||
| return this.cachedCredentials; | ||
| } | ||
| } | ||
| } catch (error) { | ||
| logger.debug('[loadCredentials] Failed to load from', credPath, error); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
Credentials are cached indefinitely with no invalidation strategy.
loadCredentials() caches the first successful read and never re-reads from disk. If a user runs gemini auth login to re-authenticate, the service will continue using stale/expired credentials until the process restarts. clearCache() exists but nothing calls it automatically.
Consider adding a TTL (e.g., 5 minutes) or checking the file's mtime before returning cached credentials.
🤖 Prompt for AI Agents
In `@apps/server/src/services/gemini-usage-service.ts` around lines 343 - 384, The
cachedCredentials in loadCredentials() is never invalidated so stale/expired
creds persist; modify loadCredentials (and/or clearCache()) to implement
automatic invalidation by adding a TTL (e.g., 5 minutes) or validating the
credentials file mtime before returning cachedCredentials: store a cachedAt
timestamp alongside cachedCredentials when setting it in loadCredentials(), and
when loadCredentials is called check if cachedAt + TTL has expired or if
fs.statSync(credPath).mtimeMs is newer than cachedAt — if expired or file
changed, discard cachedCredentials and re-read from disk (then update cachedAt);
ensure clearCache() still exists to allow explicit invalidation and reference
credentialsPath/possiblePaths when comparing mtimes.
| @@ -1,7 +1,11 @@ | |||
| // @ts-nocheck - API key management state with validation and persistence | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if `@ts-nocheck` was pre-existing or newly added in this PR
git log --oneline -10 -- "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null || echo "File may not have history yet"
echo "---"
echo "Checking if file exists and its first line:"
if [ -f "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" ]; then
head -1 "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts"
echo "---"
echo "Full diff for this file (latest change):"
git diff HEAD -- "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null | head -50 || echo "No diff available"
else
echo "File not found at expected path"
fiRepository: AutoMaker-Org/automaker
Length of output: 304
🏁 Script executed:
#!/bin/bash
# Check when `@ts-nocheck` was introduced to this file
echo "Searching for when `@ts-nocheck` was added to the file:"
git log -p -- "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null | grep -B5 "@ts-nocheck" | head -30
echo "---"
echo "Checking the commit that introduced `@ts-nocheck`:"
git log --all --full-history -S "@ts-nocheck" --oneline -- "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null | head -5
echo "---"
echo "Current HEAD and base comparison:"
git log --oneline -20 2>/dev/null | head -10Repository: AutoMaker-Org/automaker
Length of output: 537
🏁 Script executed:
#!/bin/bash
# Check the state of this file before the most recent commit
echo "Content of file in the commit BEFORE `@ts-nocheck` was added (bea26a6b~1):"
git show bea26a6b~1:"apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null | head -5
echo "---"
echo "Content of file at current HEAD (after `@ts-nocheck`):"
git show bea26a6b:"apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null | head -5
echo "---"
echo "Diff showing what changed in bea26a6b for this specific file:"
git show bea26a6b -- "apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts" 2>/dev/null | head -50Repository: AutoMaker-Org/automaker
Length of output: 2473
New file with @ts-nocheck should not be merged without type safety.
This entire file is newly created in this PR yet includes @ts-nocheck from line 1, disabling all TypeScript type checking for sensitive API key management, authentication state, and server-side configuration logic. This is not legacy code being refactored — it's new code intentionally written without type safety. Remove the directive and fix any resulting type errors before merging.
🤖 Prompt for AI Agents
In
`@apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts`
at line 1, Remove the top-level "// `@ts-nocheck`" and add explicit TypeScript
typings for the hook and its internal entities: declare interfaces/types such as
ApiKey (id, name, key, createdAt, revoked?), ApiKeyState (array + validation
errors), AuthState (isAuthenticated, userId, token), and ServerConfig
(allowedOrigins, keyPolicy) used by the useApiKeyManagement hook; update
function signatures (useApiKeyManagement, loadApiKeys, saveApiKeys,
validateApiKey, revokeApiKey) and React state hooks to use these types, type any
localStorage/parsing and async calls with proper unions/Optionals, and fix
resulting type errors rather than silencing them so the file compiles with full
type safety.
| // Configure z.ai service on the server with the new key | ||
| if (zaiKey && zaiKey.trim().length > 0) { | ||
| try { | ||
| const api = getHttpApiClient(); | ||
| const result = await api.zai.configure(zaiKey.trim()); | ||
|
|
||
| if (result.success || result.isAvailable) { | ||
| // Update z.ai auth status in the store | ||
| setZaiAuthStatus({ | ||
| authenticated: true, | ||
| method: 'api_key' as ZaiAuthMethod, | ||
| hasApiKey: true, | ||
| hasEnvApiKey: false, | ||
| }); | ||
| // Invalidate the z.ai usage query so it refetches with the new key | ||
| await queryClient.invalidateQueries({ queryKey: queryKeys.usage.zai() }); | ||
| logger.info('z.ai API key configured successfully'); | ||
| } | ||
| } catch (error) { | ||
| logger.error('Failed to configure z.ai API key:', error); | ||
| } |
There was a problem hiding this comment.
Partial failure: store is updated before the server call succeeds.
setApiKeys(...) on Line 232 persists the z.ai key to the local store before the server-side api.zai.configure(...) call on Line 243. If the configure call fails (caught on Line 257), the user sees "Saved" feedback, the store has the key, but the server isn't configured. On next app launch the key will be in the store but z.ai won't work until the user re-saves.
Consider either:
- Moving
setApiKeysafter successful server config, or - Rolling back the zai key in the
catchblock.
Also, the success check result.success || result.isAvailable (Line 245) is loose — isAvailable alone (without success) could represent a state where the service exists but the key wasn't actually accepted.
🤖 Prompt for AI Agents
In
`@apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts`
around lines 239 - 259, The code persists the z.ai key to the local store via
setApiKeys before calling api.zai.configure, and uses a loose check
(result.success || result.isAvailable) so the local state can show "Saved" even
if server config failed; either move the setApiKeys call so it runs only after
api.zai.configure succeeds (and then update setZaiAuthStatus and call
queryClient.invalidateQueries), or implement a rollback in the catch where you
remove the stored zai key using the same setApiKeys call; also tighten the
success condition to require result.success (not just isAvailable) when deciding
success for configuring z.ai.
| // Update z.ai auth status in the store | ||
| setZaiAuthStatus({ | ||
| authenticated: true, | ||
| method: 'api_key' as ZaiAuthMethod, | ||
| hasApiKey: true, | ||
| hasEnvApiKey: false, | ||
| }); |
There was a problem hiding this comment.
Hardcoding hasEnvApiKey: false may overwrite valid environment key state.
When the user saves a UI-provided key, you set hasEnvApiKey: false. If a z.ai key was also configured via environment variable, this would incorrectly clear that flag. Consider reading the current env key status (from apiKeyStatus) before overwriting.
✏️ Suggested fix
setZaiAuthStatus({
authenticated: true,
method: 'api_key' as ZaiAuthMethod,
hasApiKey: true,
- hasEnvApiKey: false,
+ hasEnvApiKey: apiKeyStatus?.hasZaiKey ?? false,
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Update z.ai auth status in the store | |
| setZaiAuthStatus({ | |
| authenticated: true, | |
| method: 'api_key' as ZaiAuthMethod, | |
| hasApiKey: true, | |
| hasEnvApiKey: false, | |
| }); | |
| // Update z.ai auth status in the store | |
| setZaiAuthStatus({ | |
| authenticated: true, | |
| method: 'api_key' as ZaiAuthMethod, | |
| hasApiKey: true, | |
| hasEnvApiKey: apiKeyStatus?.hasZaiKey ?? false, | |
| }); |
🤖 Prompt for AI Agents
In
`@apps/ui/src/components/views/settings-view/api-keys/hooks/use-api-key-management.ts`
around lines 246 - 252, The code in use-api-key-management.ts hardcodes
hasEnvApiKey: false when calling setZaiAuthStatus after saving a UI key, which
can incorrectly clear an existing env-configured key; update the call in the
block that calls setZaiAuthStatus to preserve the current env key flag by
reading the existing apiKeyStatus (e.g., use apiKeyStatus.hasEnvApiKey) and set
hasEnvApiKey to that value instead of false so you only update the fields that
truly change (authenticated, method, hasApiKey) while keeping hasEnvApiKey in
sync.
| // 3. z.ai Auth Status | ||
| try { | ||
| const result = await api.zai.getStatus(); | ||
| if (result.success || result.available !== undefined) { | ||
| let method: ZaiAuthMethod = 'none'; | ||
| if (result.hasEnvApiKey) { | ||
| method = 'api_key_env'; | ||
| } else if (result.hasApiKey || result.available) { | ||
| method = 'api_key'; | ||
| } | ||
|
|
||
| setZaiAuthStatus({ | ||
| authenticated: result.available, | ||
| method, | ||
| hasApiKey: result.hasApiKey ?? result.available, | ||
| hasEnvApiKey: result.hasEnvApiKey ?? false, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Coalesce z.ai auth fields to booleans to avoid undefined state.
If result.available (or hasApiKey) is missing, authenticated/hasApiKey can become undefined. Defaulting to false keeps the status shape consistent; consider a fallback status in the non-success path as well.
💡 Suggested adjustment
- setZaiAuthStatus({
- authenticated: result.available,
- method,
- hasApiKey: result.hasApiKey ?? result.available,
- hasEnvApiKey: result.hasEnvApiKey ?? false,
- });
+ const authenticated = result.available ?? false;
+ setZaiAuthStatus({
+ authenticated,
+ method,
+ hasApiKey: result.hasApiKey ?? authenticated,
+ hasEnvApiKey: result.hasEnvApiKey ?? false,
+ });🤖 Prompt for AI Agents
In `@apps/ui/src/hooks/use-provider-auth-init.ts` around lines 107 - 124, The z.ai
auth response handler (api.zai.getStatus) can leave authenticated/hasApiKey
undefined; update the logic in the try block that calls setZaiAuthStatus so it
coerces result.available, result.hasApiKey, and result.hasEnvApiKey to booleans
(e.g., using !! or ?? false) and derive method using those boolean values, and
also add a fallback setZaiAuthStatus in the non-success/exception path to set
authenticated: false, method: 'none', hasApiKey: false, hasEnvApiKey: false;
reference setZaiAuthStatus, api.zai.getStatus, and ZaiAuthMethod when making the
changes.
| // Set CLI status | ||
| setGeminiCliStatus({ | ||
| installed: result.installed ?? false, | ||
| version: result.version, | ||
| path: result.status, | ||
| }); |
There was a problem hiding this comment.
Use the Gemini CLI path field instead of status.
setGeminiCliStatus assigns path: result.status, but the CLI path is exposed as result.path. This will surface a status string where a filesystem path is expected.
🐛 Suggested fix
- path: result.status,
+ path: result.path ?? null,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Set CLI status | |
| setGeminiCliStatus({ | |
| installed: result.installed ?? false, | |
| version: result.version, | |
| path: result.status, | |
| }); | |
| // Set CLI status | |
| setGeminiCliStatus({ | |
| installed: result.installed ?? false, | |
| version: result.version, | |
| path: result.path ?? null, | |
| }); |
🤖 Prompt for AI Agents
In `@apps/ui/src/hooks/use-provider-auth-init.ts` around lines 133 - 138, The code
is assigning the wrong field to the CLI path: update the call to
setGeminiCliStatus so the path uses result.path (the actual filesystem path)
instead of result.status; locate the block where setGeminiCliStatus is called
(in use-provider-auth-init.ts) and replace the path: result.status assignment
with path: result.path while keeping installed: result.installed ?? false and
version: result.version unchanged.
Add usage tracking for Gemini and Z.ai coding plan models.
Summary by CodeRabbit
Release Notes