Use user info proxy (#1963)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> Switches budget fetch to `https://api.dyad.sh/v1/user/info` and
validates/consumes `usedCredits`, `totalCredits`, and `budgetResetDate`
directly via Zod.
> 
> - **IPC/Pro handlers (`src/ipc/handlers/pro_handlers.ts`)**:
> - **Endpoint**: Update user info URL to
`https://api.dyad.sh/v1/user/info`.
> - **Validation**: Add `zod` schema `UserInfoResponseSchema` to
validate API response.
> - **Data mapping**: Use `usedCredits`, `totalCredits`,
`budgetResetDate`, and `userId` from response directly; remove
conversion logic and old nested `user_info` parsing.
> - **Redaction**: Compute `redactedUserId` from `userId` and return
parsed `UserBudgetInfo`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
da1f192c2cabb2154bd10b69555c27d62fbb6368. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated description by cubic. -->
## Summary by cubic
Switched user budget fetch to the new user info proxy and added schema
validation. Uses API-provided credits directly and removes the old
conversion logic.

- **Refactors**
  - Use https://api.dyad.sh/v1/user/info instead of llm-gateway.
- Validate response with a Zod schema (usedCredits, totalCredits,
budgetResetDate, userId).
  - Map fields directly to UserBudgetInfo and remove CONVERSION_RATIO.
  - Keep redacted user ID format (****1234).

- **Dependencies**
- Removed unused html-dom-parser, html-react-parser, and react-property
from the lockfile.

<sup>Written for commit da1f192c2cabb2154bd10b69555c27d62fbb6368.
Summary will update automatically on new commits.</sup>

<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Will Chen
2025-12-15 14:25:55 -08:00
committed by GitHub
parent 9d33f3757d
commit 213def4a67
2 changed files with 25 additions and 48 deletions

View File

@@ -4,12 +4,19 @@ import { createLoggedHandler } from "./safe_handle";
import { readSettings } from "../../main/settings"; // Assuming settings are read this way
import { UserBudgetInfo, UserBudgetInfoSchema } from "../ipc_types";
import { IS_TEST_BUILD } from "../utils/test_utils";
import { z } from "zod";
export const UserInfoResponseSchema = z.object({
usedCredits: z.number(),
totalCredits: z.number(),
budgetResetDate: z.string(), // ISO date string from API
userId: z.string(),
});
export type UserInfoResponse = z.infer<typeof UserInfoResponseSchema>;
const logger = log.scope("pro_handlers");
const handle = createLoggedHandler(logger);
const CONVERSION_RATIO = (10 * 3) / 2;
export function registerProHandlers() {
// This method should try to avoid throwing errors because this is auxiliary
// information and isn't critical to using the app
@@ -36,7 +43,7 @@ export function registerProHandlers() {
return null;
}
const url = "https://llm-gateway.dyad.sh/user/info";
const url = "https://api.dyad.sh/v1/user/info";
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
@@ -57,21 +64,28 @@ export function registerProHandlers() {
return null;
}
const data = await response.json();
const userInfoData = data["user_info"];
const userId = userInfoData["user_id"];
const rawData = await response.json();
// Validate the API response structure
const data = UserInfoResponseSchema.parse(rawData);
// Turn user_abc1234 => "****1234"
// Preserve the last 4 characters so we can correlate bug reports
// with the user.
const redactedUserId =
userId.length > 8 ? "****" + userId.slice(-4) : "<redacted>";
data.userId.length > 8 ? "****" + data.userId.slice(-4) : "<redacted>";
logger.info("Successfully fetched user budget information.");
return UserBudgetInfoSchema.parse({
usedCredits: userInfoData["spend"] * CONVERSION_RATIO,
totalCredits: userInfoData["max_budget"] * CONVERSION_RATIO,
budgetResetDate: new Date(userInfoData["budget_reset_at"]),
// Transform to UserBudgetInfo format
const userBudgetInfo = UserBudgetInfoSchema.parse({
usedCredits: data.usedCredits,
totalCredits: data.totalCredits,
budgetResetDate: new Date(data.budgetResetDate),
redactedUserId: redactedUserId,
});
return userBudgetInfo;
} catch (error: any) {
logger.error(`Error fetching user budget: ${error.message}`, error);
return null;