fix(credits): move credits fetch to extension side (#740)

* fix(credits): move credits fetch to extension side using install_id

Extension now reads `browseros.metrics_install_id` pref directly and fetches
credits from `llm.browseros.com` without going through the bundled server.
Unblocks the referral submit flow in prod without requiring a BrowserOS
binary release.

- Revert `/credits` route change that added `browserosId` to the response.
- Add `getOrCreateBrowserosId()` helper reading from BrowserOS prefs.
- Add `CREDITS_GATEWAY` to shared EXTERNAL_URLS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(credits): drop fallback UUID, read install_id directly

Extension only runs inside BrowserOS, so the prefs API is always available.
The chrome.storage fallback was dead code that would generate a ghost ID
diverging from the server's install_id anyway. Rename the helper to match
its simpler contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(credits): guard against empty install_id pref

Address Greptile P1 — throw instead of silently fetching `/credits/null`
when `browseros.metrics_install_id` is unset. Fails loudly so the broken
state is observable rather than masquerading as a credits outage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Felarof
2026-04-16 19:27:21 -07:00
committed by GitHub
parent 4f03afcac8
commit b5bbbe1aff
4 changed files with 25 additions and 5 deletions

View File

@@ -0,0 +1,15 @@
import { getBrowserOSAdapter } from '@/lib/browseros/adapter'
import { BROWSEROS_PREFS } from '@/lib/browseros/prefs'
// TODO(credits-identity): temporary shim — reuses the BrowserOS metrics
// install_id as the credits/referral identifier. Replace with a dedicated
// identity module once we have one.
export async function getBrowserosId(): Promise<string> {
const adapter = getBrowserOSAdapter()
const pref = await adapter.getPref(BROWSEROS_PREFS.INSTALL_ID)
const id = pref.value
if (typeof id !== 'string' || id.length === 0) {
throw new Error('browseros.metrics_install_id is not set')
}
return id
}

View File

@@ -1,5 +1,6 @@
import { EXTERNAL_URLS } from '@browseros/shared/constants/urls'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { getAgentServerUrl } from '@/lib/browseros/helpers'
import { getBrowserosId } from './browseros-id'
export interface CreditsInfo {
credits: number
@@ -11,11 +12,14 @@ export interface CreditsInfo {
const CREDITS_QUERY_KEY = ['credits']
async function fetchCredits(): Promise<CreditsInfo> {
const baseUrl = await getAgentServerUrl()
const response = await fetch(`${baseUrl}/credits`)
const browserosId = await getBrowserosId()
const response = await fetch(
`${EXTERNAL_URLS.CREDITS_GATEWAY}/credits/${browserosId}`,
)
if (!response.ok)
throw new Error(`Failed to fetch credits: ${response.status}`)
return response.json()
const data = (await response.json()) as CreditsInfo
return { ...data, browserosId }
}
export function useCredits() {

View File

@@ -25,7 +25,7 @@ export function createCreditsRoutes(deps: CreditsDeps) {
return new Hono().get('/', async (c) => {
try {
const credits = await fetchCredits(gatewayBaseUrl, browserosId)
return c.json({ ...credits, browserosId })
return c.json(credits)
} catch (error) {
logger.error('Failed to fetch credits', {
error: error instanceof Error ? error.message : String(error),

View File

@@ -20,4 +20,5 @@ export const EXTERNAL_URLS = {
QWEN_OAUTH_TOKEN: 'https://chat.qwen.ai/api/v1/oauth2/token',
QWEN_CODE_API: 'https://portal.qwen.ai/v1',
REFERRAL_SERVICE: 'https://browseros-referral.fly.dev',
CREDITS_GATEWAY: 'https://llm.browseros.com',
} as const