mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
* Add a RCC credit balance display * Replace the provider docs with the balance when logged in * PR feedback --------- Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
147 lines
3.7 KiB
TypeScript
147 lines
3.7 KiB
TypeScript
import { z } from "zod"
|
|
|
|
import { type AuthService, type ShareVisibility, type ShareResponse, shareResponseSchema } from "@roo-code/types"
|
|
|
|
import { getRooCodeApiUrl } from "./config.js"
|
|
import { getUserAgent } from "./utils.js"
|
|
import { AuthenticationError, CloudAPIError, NetworkError, TaskNotFoundError } from "./errors.js"
|
|
|
|
interface CloudAPIRequestOptions extends Omit<RequestInit, "headers"> {
|
|
timeout?: number
|
|
headers?: Record<string, string>
|
|
}
|
|
|
|
export class CloudAPI {
|
|
private authService: AuthService
|
|
private log: (...args: unknown[]) => void
|
|
private baseUrl: string
|
|
|
|
constructor(authService: AuthService, log?: (...args: unknown[]) => void) {
|
|
this.authService = authService
|
|
this.log = log || console.log
|
|
this.baseUrl = getRooCodeApiUrl()
|
|
}
|
|
|
|
private async request<T>(
|
|
endpoint: string,
|
|
options: CloudAPIRequestOptions & {
|
|
parseResponse?: (data: unknown) => T
|
|
} = {},
|
|
): Promise<T> {
|
|
const { timeout = 30_000, parseResponse, headers = {}, ...fetchOptions } = options
|
|
|
|
const sessionToken = this.authService.getSessionToken()
|
|
|
|
if (!sessionToken) {
|
|
throw new AuthenticationError()
|
|
}
|
|
|
|
const url = `${this.baseUrl}${endpoint}`
|
|
|
|
const requestHeaders = {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${sessionToken}`,
|
|
"User-Agent": getUserAgent(),
|
|
...headers,
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
...fetchOptions,
|
|
headers: requestHeaders,
|
|
signal: AbortSignal.timeout(timeout),
|
|
})
|
|
|
|
if (!response.ok) {
|
|
await this.handleErrorResponse(response, endpoint)
|
|
}
|
|
|
|
const data = await response.json()
|
|
|
|
if (parseResponse) {
|
|
return parseResponse(data)
|
|
}
|
|
|
|
return data as T
|
|
} catch (error) {
|
|
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
throw new NetworkError(`Network error while calling ${endpoint}`)
|
|
}
|
|
|
|
if (error instanceof CloudAPIError) {
|
|
throw error
|
|
}
|
|
|
|
if (error instanceof Error && error.name === "AbortError") {
|
|
throw new CloudAPIError(`Request to ${endpoint} timed out`, undefined, undefined)
|
|
}
|
|
|
|
throw new CloudAPIError(
|
|
`Unexpected error while calling ${endpoint}: ${error instanceof Error ? error.message : String(error)}`,
|
|
)
|
|
}
|
|
}
|
|
|
|
private async handleErrorResponse(response: Response, endpoint: string): Promise<never> {
|
|
let responseBody: unknown
|
|
|
|
try {
|
|
responseBody = await response.json()
|
|
} catch {
|
|
responseBody = await response.text()
|
|
}
|
|
|
|
switch (response.status) {
|
|
case 401:
|
|
throw new AuthenticationError()
|
|
case 404:
|
|
if (endpoint.includes("/share")) {
|
|
throw new TaskNotFoundError()
|
|
}
|
|
throw new CloudAPIError(`Resource not found: ${endpoint}`, 404, responseBody)
|
|
default:
|
|
throw new CloudAPIError(
|
|
`HTTP ${response.status}: ${response.statusText}`,
|
|
response.status,
|
|
responseBody,
|
|
)
|
|
}
|
|
}
|
|
|
|
async shareTask(taskId: string, visibility: ShareVisibility = "organization"): Promise<ShareResponse> {
|
|
this.log(`[CloudAPI] Sharing task ${taskId} with visibility: ${visibility}`)
|
|
|
|
const response = await this.request("/api/extension/share", {
|
|
method: "POST",
|
|
body: JSON.stringify({ taskId, visibility }),
|
|
parseResponse: (data) => shareResponseSchema.parse(data),
|
|
})
|
|
|
|
this.log("[CloudAPI] Share response:", response)
|
|
return response
|
|
}
|
|
|
|
async bridgeConfig() {
|
|
return this.request("/api/extension/bridge/config", {
|
|
method: "GET",
|
|
parseResponse: (data) =>
|
|
z
|
|
.object({
|
|
userId: z.string(),
|
|
socketBridgeUrl: z.string(),
|
|
token: z.string(),
|
|
})
|
|
.parse(data),
|
|
})
|
|
}
|
|
|
|
async creditBalance(): Promise<number> {
|
|
return this.request("/api/extension/credit-balance", {
|
|
method: "GET",
|
|
parseResponse: (data) => {
|
|
const result = z.object({ balance: z.number() }).parse(data)
|
|
return result.balance
|
|
},
|
|
})
|
|
}
|
|
}
|