diff options
| author | Adam Malczewski <[email protected]> | 2026-06-04 21:21:20 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-04 21:21:20 +0900 |
| commit | 394f1ed37ce860da6fdc385769bf29f9737105cd (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /packages/core/src/credentials/opencode.ts | |
| parent | 81a9cdbadf8c9d940d4fe9a2a0de607dee1f5f1a (diff) | |
| download | dispatch-394f1ed37ce860da6fdc385769bf29f9737105cd.tar.gz dispatch-394f1ed37ce860da6fdc385769bf29f9737105cd.zip | |
chore: genesis — remove all files to rebuild from scratch (arch rewrite)
Diffstat (limited to 'packages/core/src/credentials/opencode.ts')
| -rw-r--r-- | packages/core/src/credentials/opencode.ts | 126 |
1 files changed, 0 insertions, 126 deletions
diff --git a/packages/core/src/credentials/opencode.ts b/packages/core/src/credentials/opencode.ts deleted file mode 100644 index d4d4851..0000000 --- a/packages/core/src/credentials/opencode.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { resolveApiKey } from "./api-keys.js"; - -// ─── OpenCode Usage Tracking ────────────────────────────────── -// OpenCode has no public usage API. We scrape usage from the -// SolidStart SSR-rendered workspace page using a session cookie. -// Requires OPENCODE_COOKIE env var. -// Workspace IDs: OPENCODE_WS1_ID for opencode-1, OPENCODE_WS2_ID for opencode-2. - -export interface OpencodeUsageBucket { - utilization?: number; // 0-1 fraction - resetsAt?: number; // Unix timestamp ms -} - -export interface OpencodeUsageReport { - fiveHour?: OpencodeUsageBucket; - weekly?: OpencodeUsageBucket; - monthly?: OpencodeUsageBucket; -} - -function getWorkspaceId(keyId: string): string | undefined { - // Check DB for workspace ID: stored as "opencode-ws1", "opencode-ws2", or "opencode-ws" - const match = keyId.match(/opencode-(\d+)$/i); - if (match) { - const num = match[1]; - const specific = resolveApiKey(`opencode-ws${num}`); - if (specific) return specific; - } - return resolveApiKey("opencode-ws") ?? undefined; -} - -function parseOcDouble(html: string, key: string): number { - const idx = html.indexOf(`${key}:`); - if (idx === -1) return 0; - let start = idx + key.length + 1; - while (start < html.length && html[start] === " ") start++; - let end = start; - while (end < html.length && html[end] !== "," && html[end] !== "}") { - end++; - } - const val = parseFloat(html.slice(start, end)); - return Number.isNaN(val) ? 0 : val; -} - -function parseOcInt(html: string, key: string): number { - const idx = html.indexOf(`${key}:`); - if (idx === -1) return 0; - let i = idx + key.length + 1; - while (i < html.length && html[i] === " ") i++; - return parseInt(html.slice(i), 10) || 0; -} - -function parseOcBucket( - html: string, - bucketName: string, -): { utilization: number; resetsAt: number } | null { - const search = `${bucketName}:`; - const pos = html.indexOf(search); - if (pos === -1) return null; - - // Find the opening brace after the bucket name - const brace = html.indexOf("{", pos); - if (brace === -1) return null; - - const resetSecs = parseOcInt(html.slice(brace), "resetInSec"); - const usagePct = parseOcDouble(html.slice(brace), "usagePercent"); - - const utilization = usagePct / 100; // convert 0-100% to 0-1 fraction - const resetsAt = Date.now() + resetSecs * 1000; - - return { utilization, resetsAt }; -} - -export async function fetchOpencodeUsage(keyId: string): Promise<OpencodeUsageReport | null> { - const cookie = resolveApiKey("opencode-cookie"); - const wsId = getWorkspaceId(keyId); - - if (!cookie || !wsId) { - return null; - } - - const url = `https://opencode.ai/workspace/${encodeURIComponent(wsId)}/go`; - - try { - const response = await fetch(url, { - headers: { - accept: "text/html", - cookie: `auth=${cookie}`, - }, - redirect: "follow", - }); - - if (!response.ok) return null; - - const html = await response.text(); - - // Auth redirect check - if (html.includes("/auth/authorize") || html.includes('window.location="/auth/authorize"')) { - return null; - } - - // Find the lite.subscription data block. - // HTML contains: lite.subscription.get[\"<wsId>\"] - // We need literal backslashes; use \x5c (hex for backslash). - const wsKey = `lite.subscription.get[\x5c"${wsId}\x5c"]`; - const wsPos = html.indexOf(wsKey); - if (wsPos === -1) return null; - - // Search for the resolved data starting from the ws key position - const minePos = html.indexOf("mine:", wsPos); - const slice = minePos !== -1 ? html.slice(minePos) : ""; - - const fiveHour = parseOcBucket(slice, "rollingUsage"); - const weekly = parseOcBucket(slice, "weeklyUsage"); - const monthly = parseOcBucket(slice, "monthlyUsage"); - - if (!fiveHour && !weekly && !monthly) return null; - - return { - fiveHour: fiveHour ?? undefined, - weekly: weekly ?? undefined, - monthly: monthly ?? undefined, - }; - } catch { - return null; - } -} |
