@scrylog/cli 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/highlights-eq9cgrbb.scm +604 -0
- package/dist/assets/highlights-ghv9g403.scm +205 -0
- package/dist/assets/highlights-hk7bwhj4.scm +284 -0
- package/dist/assets/highlights-r812a2qc.scm +150 -0
- package/dist/assets/highlights-x6tmsnaa.scm +115 -0
- package/dist/assets/injections-73j83es3.scm +27 -0
- package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
- package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
- package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/cli/index.js +499 -0
- package/dist/cli/index.js.map +61 -0
- package/dist/daemon/index.js +670 -0
- package/dist/daemon/index.js.map +1475 -0
- package/dist/hooks/claude.js +6 -0
- package/dist/hooks/claude.js.map +11 -0
- package/dist/hooks/codex.js +10 -0
- package/dist/hooks/codex.js.map +11 -0
- package/dist/mcp/index.js +100 -0
- package/dist/mcp/index.js.map +231 -0
- package/dist/oc/index.js +21 -0
- package/dist/oc/index.js.map +10 -0
- package/package.json +16 -13
- package/src/api/docs/sessions.keys.ts +0 -6
- package/src/api/docs/sessions.ts +0 -82
- package/src/api/docs/system.keys.ts +0 -5
- package/src/api/docs/system.ts +0 -69
- package/src/api/docs/usage.keys.ts +0 -3
- package/src/api/docs/usage.ts +0 -24
- package/src/api/docs/webhooks.keys.ts +0 -5
- package/src/api/docs/webhooks.ts +0 -78
- package/src/api/index.ts +0 -18
- package/src/api/lib/auth.ts +0 -43
- package/src/api/lib/errors.ts +0 -28
- package/src/api/lib/route.ts +0 -18
- package/src/api/models/claude.ts +0 -4
- package/src/api/models/codex.ts +0 -4
- package/src/api/models/opencode.ts +0 -11
- package/src/api/routes/sessions.ts +0 -51
- package/src/api/routes/system.ts +0 -106
- package/src/api/routes/usage.ts +0 -15
- package/src/api/routes/webhooks/claude.ts +0 -149
- package/src/api/routes/webhooks/codex.ts +0 -175
- package/src/api/routes/webhooks/opencode.ts +0 -112
- package/src/api/routes.ts +0 -93
- package/src/api/schemas/common.ts +0 -29
- package/src/api/schemas/sessions.ts +0 -86
- package/src/api/schemas/usage.ts +0 -51
- package/src/api/schemas/webhooks/claude.ts +0 -32
- package/src/api/schemas/webhooks/codex.ts +0 -34
- package/src/api/schemas/webhooks/opencode.ts +0 -29
- package/src/api/services/claude-session.ts +0 -9
- package/src/api/services/codex-session.ts +0 -8
- package/src/api/services/opencode-session.ts +0 -73
- package/src/classifier.ts +0 -206
- package/src/cli/commands.ts +0 -714
- package/src/cli/index.ts +0 -18
- package/src/cli/program.ts +0 -175
- package/src/cli/prompt.ts +0 -44
- package/src/daemon/cli.ts +0 -27
- package/src/daemon/collectors/claude/README.md +0 -108
- package/src/daemon/collectors/claude/collector.ts +0 -423
- package/src/daemon/collectors/claude/discovery.ts +0 -127
- package/src/daemon/collectors/claude/hooks.ts +0 -181
- package/src/daemon/collectors/claude/index.ts +0 -8
- package/src/daemon/collectors/claude/jsonl.ts +0 -85
- package/src/daemon/collectors/claude/transcript.ts +0 -246
- package/src/daemon/collectors/codex/README.md +0 -147
- package/src/daemon/collectors/codex/collector.ts +0 -661
- package/src/daemon/collectors/codex/discovery.ts +0 -104
- package/src/daemon/collectors/codex/hooks.ts +0 -179
- package/src/daemon/collectors/codex/index.ts +0 -7
- package/src/daemon/collectors/codex/jsonl.ts +0 -108
- package/src/daemon/collectors/codex/transcript.ts +0 -299
- package/src/daemon/collectors/git.ts +0 -353
- package/src/daemon/collectors/herdr/collector.ts +0 -128
- package/src/daemon/collectors/herdr/index.ts +0 -2
- package/src/daemon/collectors/herdr/normalization.ts +0 -111
- package/src/daemon/collectors/opencode/README.md +0 -106
- package/src/daemon/collectors/opencode/collector.ts +0 -545
- package/src/daemon/collectors/opencode/discovery.ts +0 -257
- package/src/daemon/collectors/opencode/index.ts +0 -5
- package/src/daemon/collectors/opencode/session-builder.ts +0 -59
- package/src/daemon/collectors/opencode-db.ts +0 -294
- package/src/daemon/collectors/process.ts +0 -201
- package/src/daemon/collectors/tmux/README.md +0 -128
- package/src/daemon/collectors/tmux/collector.ts +0 -492
- package/src/daemon/collectors/tmux/enrichment.ts +0 -242
- package/src/daemon/collectors/tmux/index.ts +0 -4
- package/src/daemon/collectors/tmux/parse.ts +0 -128
- package/src/daemon/collectors/usage.ts +0 -259
- package/src/daemon/index.ts +0 -209
- package/src/daemon/scheduler.ts +0 -86
- package/src/daemon/store.ts +0 -630
- package/src/detect.ts +0 -143
- package/src/hooks/claude.ts +0 -596
- package/src/hooks/codex.ts +0 -517
- package/src/legacy/classifier.ts +0 -215
- package/src/legacy/detect.ts +0 -143
- package/src/mcp/client-server.ts +0 -21
- package/src/mcp/http-handler.ts +0 -126
- package/src/mcp/index.ts +0 -61
- package/src/mcp/resources/config-current.ts +0 -19
- package/src/mcp/resources/index.ts +0 -11
- package/src/mcp/resources/sessions-active.ts +0 -19
- package/src/mcp/resources/sessions-all.ts +0 -19
- package/src/mcp/schemas/sessions.ts +0 -3
- package/src/mcp/server.ts +0 -17
- package/src/mcp/services/api-adapter.ts +0 -48
- package/src/mcp/services/data-source.ts +0 -22
- package/src/mcp/services/store-adapter.ts +0 -40
- package/src/mcp/tools/get-active-sessions.ts +0 -16
- package/src/mcp/tools/get-session-children.ts +0 -24
- package/src/mcp/tools/get-session-messages.ts +0 -24
- package/src/mcp/tools/get-session.ts +0 -23
- package/src/mcp/tools/index.ts +0 -15
- package/src/mcp/tools/list-sessions.ts +0 -16
- package/src/oc/index.ts +0 -302
- package/src/service/manager.ts +0 -284
- package/src/shared/config.ts +0 -511
- package/src/shared/ipc.ts +0 -80
- package/src/shared/logger.ts +0 -248
- package/src/shared/paths.ts +0 -73
- package/src/shared/platform.ts +0 -110
- package/src/shared/session-exclusions.ts +0 -5
- package/src/shared/session-view.ts +0 -302
- package/src/shared/terminal-capabilities.ts +0 -130
- package/src/shared/types.ts +0 -330
- package/src/shared/utils.ts +0 -339
- package/src/shared/version.ts +0 -44
- package/src/tui/App.tsx +0 -240
- package/src/tui/client.ts +0 -163
- package/src/tui/components/BindingsModal.tsx +0 -82
- package/src/tui/components/DashboardHeader.tsx +0 -256
- package/src/tui/components/KeyBar.tsx +0 -67
- package/src/tui/components/SessionDetailModal.tsx +0 -169
- package/src/tui/components/SessionsView.tsx +0 -184
- package/src/tui/components/TabBar.tsx +0 -40
- package/src/tui/components/UsageSummaryPane.tsx +0 -272
- package/src/tui/components/UsageView.tsx +0 -242
- package/src/tui/components/tool-icons.ts +0 -24
- package/src/tui/index.tsx +0 -106
- package/src/tui/keybindings.ts +0 -49
- package/src/tui/model.ts +0 -295
- package/src/tui/state.ts +0 -77
- package/src/tui/theme.ts +0 -110
- package/src/types.ts +0 -113
- package/src/usage/README.md +0 -323
- package/src/usage/api.ts +0 -99
- package/src/usage/client.ts +0 -3
- package/src/usage/clients/anthropic-oauth-usage.ts +0 -258
- package/src/usage/clients/anthropic-web-usage.ts +0 -329
- package/src/usage/clients/claude-cli-probe.ts +0 -513
- package/src/usage/clients/codex-app-server.ts +0 -354
- package/src/usage/clients/codex-oauth-usage.ts +0 -240
- package/src/usage/clients/opencode-go-api.ts +0 -100
- package/src/usage/clients/opencode-go-usage.ts +0 -577
- package/src/usage/errors.ts +0 -33
- package/src/usage/index.ts +0 -20
- package/src/usage/local/opencode-go-local.ts +0 -194
- package/src/usage/mappers.ts +0 -282
- package/src/usage/planner.ts +0 -118
- package/src/usage/providers/claude.ts +0 -233
- package/src/usage/providers/codex.ts +0 -115
- package/src/usage/providers/opencode-go.ts +0 -115
- package/src/usage/resolver.ts +0 -4
- package/src/usage/resolvers/browser-cookies.ts +0 -808
- package/src/usage/resolvers/claude-auth.ts +0 -208
- package/src/usage/resolvers/claude-cli.ts +0 -24
- package/src/usage/resolvers/claude-desktop-auth.ts +0 -170
- package/src/usage/resolvers/claude-web.ts +0 -82
- package/src/usage/resolvers/codex-auth.ts +0 -174
- package/src/usage/resolvers/keychain-coordinator.ts +0 -397
- package/src/usage/resolvers/opencode-go-web.ts +0 -55
- package/src/usage/types.ts +0 -225
- package/src/usage/web-session-cache.ts +0 -37
- /package/{src/tui → dist/cli}/assets/providers/SOURCES.md +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.svg +0 -0
|
@@ -1,577 +0,0 @@
|
|
|
1
|
-
import type { UsageClient } from "../client";
|
|
2
|
-
import type { OpenCodeGoUsageSnapshot } from "../types";
|
|
3
|
-
import { classifyUsageError, UsageError } from "../errors";
|
|
4
|
-
|
|
5
|
-
const AUTH_COOKIE_NAMES = new Set(["auth", "__Host-auth", "__Host-console_session"]);
|
|
6
|
-
|
|
7
|
-
export interface OpenCodeGoClientCredentials {
|
|
8
|
-
cookies: string;
|
|
9
|
-
serverFnId: string;
|
|
10
|
-
onCookieRotation?: (cookies: string) => void;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function randomUuid(): string {
|
|
14
|
-
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
15
|
-
const r = (Math.random() * 16) | 0;
|
|
16
|
-
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
17
|
-
return v.toString(16);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function isSignedOut(text: string): boolean {
|
|
22
|
-
const lower = text.toLowerCase();
|
|
23
|
-
return (
|
|
24
|
-
lower.includes("auth/authorize") ||
|
|
25
|
-
lower.includes("not associated with an account") ||
|
|
26
|
-
lower.includes('actor of type "public"') ||
|
|
27
|
-
/(?:href|action)=["'][^"']*\/(?:login|signin)(?:[?"'])/.test(lower)
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function parseCookieHeader(header: string): Map<string, string> {
|
|
32
|
-
const cookies = new Map<string, string>();
|
|
33
|
-
for (const part of header.split(";")) {
|
|
34
|
-
const separator = part.indexOf("=");
|
|
35
|
-
if (separator < 1) continue;
|
|
36
|
-
const name = part.slice(0, separator).trim();
|
|
37
|
-
if (AUTH_COOKIE_NAMES.has(name)) cookies.set(name, part.slice(separator + 1).trim());
|
|
38
|
-
}
|
|
39
|
-
return cookies;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function buildAuthCookieHeader(cookies: ReadonlyMap<string, string>): string {
|
|
43
|
-
return [...cookies].map(([name, value]) => `${name}=${value}`).join("; ");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function getSetCookieHeaders(headers: Headers): string[] {
|
|
47
|
-
const values = (headers as Headers & { getSetCookie?: () => string[] }).getSetCookie?.();
|
|
48
|
-
if (values?.length) return values;
|
|
49
|
-
const combined = headers.get("set-cookie");
|
|
50
|
-
return combined ? combined.split(/,\s*(?=(?:auth|__Host-auth)=)/) : [];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function applyAuthCookieRotation(current: string, headers: Headers): string {
|
|
54
|
-
const cookies = parseCookieHeader(current);
|
|
55
|
-
for (const setCookie of getSetCookieHeaders(headers)) {
|
|
56
|
-
const [pair, ...attributes] = setCookie.split(";");
|
|
57
|
-
const separator = pair!.indexOf("=");
|
|
58
|
-
if (separator < 1) continue;
|
|
59
|
-
const name = pair!.slice(0, separator).trim();
|
|
60
|
-
if (!AUTH_COOKIE_NAMES.has(name)) continue;
|
|
61
|
-
|
|
62
|
-
const value = pair!.slice(separator + 1).trim();
|
|
63
|
-
const expired = attributes.some((attribute) => {
|
|
64
|
-
const [rawName, rawValue = ""] = attribute.trim().split("=", 2);
|
|
65
|
-
const attributeName = rawName!.toLowerCase();
|
|
66
|
-
if (attributeName === "max-age") return Number(rawValue) <= 0;
|
|
67
|
-
if (attributeName !== "expires") return false;
|
|
68
|
-
const expiresAt = Date.parse(rawValue);
|
|
69
|
-
return !Number.isNaN(expiresAt) && expiresAt <= Date.now();
|
|
70
|
-
});
|
|
71
|
-
if (!value || expired) cookies.delete(name);
|
|
72
|
-
else cookies.set(name, value);
|
|
73
|
-
}
|
|
74
|
-
return buildAuthCookieHeader(cookies);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function retryAfterMs(response: Response): number | undefined {
|
|
78
|
-
const value = response.headers.get("retry-after");
|
|
79
|
-
if (!value) return undefined;
|
|
80
|
-
const seconds = Number(value);
|
|
81
|
-
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);
|
|
82
|
-
const date = Date.parse(value);
|
|
83
|
-
return Number.isNaN(date) ? undefined : Math.max(0, date - Date.now());
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function assertResponseStatus(response: Response, context: string): void {
|
|
87
|
-
if (response.ok) return;
|
|
88
|
-
if (response.status === 401) throw new UsageError("invalid-credentials", `${context} rejected the session`);
|
|
89
|
-
if (response.status === 403) throw new UsageError("forbidden", `${context} is forbidden`);
|
|
90
|
-
if (response.status === 429) {
|
|
91
|
-
throw new UsageError("rate-limited", `${context} is rate limited`, retryAfterMs(response));
|
|
92
|
-
}
|
|
93
|
-
if (response.status >= 500) throw new UsageError("transient", `${context} failed with HTTP ${response.status}`);
|
|
94
|
-
throw new UsageError("invalid-response", `${context} failed with HTTP ${response.status}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function isAuthenticationUrl(url: URL): boolean {
|
|
98
|
-
const path = url.pathname.toLowerCase().replace(/\/+$/, "");
|
|
99
|
-
return path === "/login" || path === "/signin" || path === "/console/login" || path === "/auth/authorize";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function fetchSameHost(url: string, init: RequestInit): Promise<Response> {
|
|
103
|
-
let currentUrl = new URL(url);
|
|
104
|
-
let currentInit = init;
|
|
105
|
-
for (let redirects = 0; redirects <= 5; redirects++) {
|
|
106
|
-
let response: Response;
|
|
107
|
-
try {
|
|
108
|
-
response = await fetch(currentUrl, { ...currentInit, redirect: "manual" });
|
|
109
|
-
} catch (error) {
|
|
110
|
-
throw classifyUsageError(error);
|
|
111
|
-
}
|
|
112
|
-
if (response.status < 300 || response.status >= 400) return response;
|
|
113
|
-
|
|
114
|
-
const location = response.headers.get("location");
|
|
115
|
-
if (!location) throw new UsageError("invalid-response", "OpenCode Go redirect is missing a location");
|
|
116
|
-
const nextUrl = new URL(location, currentUrl);
|
|
117
|
-
if (nextUrl.protocol !== "https:" || nextUrl.hostname !== "opencode.ai") {
|
|
118
|
-
throw new UsageError("invalid-response", "OpenCode Go refused a cross-origin redirect");
|
|
119
|
-
}
|
|
120
|
-
if (isAuthenticationUrl(nextUrl)) {
|
|
121
|
-
throw new UsageError("invalid-credentials", "OpenCode Go session redirected to sign in");
|
|
122
|
-
}
|
|
123
|
-
if (response.status === 303 || ((response.status === 301 || response.status === 302) && currentInit.method === "POST")) {
|
|
124
|
-
currentInit = { ...currentInit, method: "GET", body: undefined };
|
|
125
|
-
}
|
|
126
|
-
currentUrl = nextUrl;
|
|
127
|
-
}
|
|
128
|
-
throw new UsageError("invalid-response", "OpenCode Go exceeded the redirect limit");
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function extractWorkspaceIds(text: string): string[] {
|
|
132
|
-
const ids: string[] = [];
|
|
133
|
-
// Strategy 1: regex
|
|
134
|
-
const regex = /id\s*:\s*"(wrk_[^"]+)"/g;
|
|
135
|
-
let m: RegExpExecArray | null;
|
|
136
|
-
while ((m = regex.exec(text))) {
|
|
137
|
-
ids.push(m[1]!);
|
|
138
|
-
}
|
|
139
|
-
if (ids.length > 0) return ids;
|
|
140
|
-
|
|
141
|
-
// Strategy 2: JSON traversal
|
|
142
|
-
try {
|
|
143
|
-
const data = JSON.parse(text);
|
|
144
|
-
function walk(obj: unknown): void {
|
|
145
|
-
if (typeof obj === "string" && obj.startsWith("wrk_") && obj.length > 4) {
|
|
146
|
-
ids.push(obj);
|
|
147
|
-
} else if (Array.isArray(obj)) {
|
|
148
|
-
for (const item of obj) walk(item);
|
|
149
|
-
} else if (obj && typeof obj === "object") {
|
|
150
|
-
for (const v of Object.values(obj)) walk(v);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
walk(data);
|
|
154
|
-
} catch {
|
|
155
|
-
// ignore
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return ids;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function extractWorkspace(text: string): { id: string; name: string | null } | null {
|
|
162
|
-
// Try to extract id + name from the same object pattern
|
|
163
|
-
const regex = /id\s*:\s*"(wrk_[^"]+)"(?:\s*,\s*name\s*:\s*"([^"]*)")?/;
|
|
164
|
-
const m = regex.exec(text);
|
|
165
|
-
if (m) {
|
|
166
|
-
return { id: m[1]!, name: m[2] || null };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Fallback to ids only
|
|
170
|
-
const ids = extractWorkspaceIds(text);
|
|
171
|
-
if (ids.length > 0) {
|
|
172
|
-
return { id: ids[0]!, name: null };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
interface WindowCandidate {
|
|
179
|
-
percent: number;
|
|
180
|
-
resetInSec: number;
|
|
181
|
-
path: string;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function findCandidates(obj: unknown, path = "", depth = 0): WindowCandidate[] {
|
|
185
|
-
if (depth > 10 || !obj || typeof obj !== "object") return [];
|
|
186
|
-
const candidates: WindowCandidate[] = [];
|
|
187
|
-
|
|
188
|
-
if (Array.isArray(obj)) {
|
|
189
|
-
for (let i = 0; i < obj.length; i++) {
|
|
190
|
-
candidates.push(...findCandidates(obj[i], `${path}[${i}]`, depth + 1));
|
|
191
|
-
}
|
|
192
|
-
return candidates;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const record = obj as Record<string, unknown>;
|
|
196
|
-
const keys = Object.keys(record);
|
|
197
|
-
|
|
198
|
-
// Check if this object is a candidate
|
|
199
|
-
const percentKeys = [
|
|
200
|
-
"usagePercent",
|
|
201
|
-
"usedPercent",
|
|
202
|
-
"percentUsed",
|
|
203
|
-
"percent",
|
|
204
|
-
"usage_percent",
|
|
205
|
-
"used_percent",
|
|
206
|
-
"utilization",
|
|
207
|
-
"utilizationPercent",
|
|
208
|
-
"utilization_percent",
|
|
209
|
-
"usage",
|
|
210
|
-
];
|
|
211
|
-
const resetSecKeys = [
|
|
212
|
-
"resetInSec",
|
|
213
|
-
"resetInSeconds",
|
|
214
|
-
"resetSeconds",
|
|
215
|
-
"reset_sec",
|
|
216
|
-
"reset_in_sec",
|
|
217
|
-
"resetsInSec",
|
|
218
|
-
"resetsInSeconds",
|
|
219
|
-
"resetIn",
|
|
220
|
-
"resetSec",
|
|
221
|
-
];
|
|
222
|
-
const resetTsKeys = [
|
|
223
|
-
"resetAt",
|
|
224
|
-
"resetsAt",
|
|
225
|
-
"reset_at",
|
|
226
|
-
"resets_at",
|
|
227
|
-
"nextReset",
|
|
228
|
-
"next_reset",
|
|
229
|
-
"renewAt",
|
|
230
|
-
"renew_at",
|
|
231
|
-
];
|
|
232
|
-
const usedKeys = ["used", "usage", "consumed", "count", "usedTokens"];
|
|
233
|
-
const limitKeys = ["limit", "total", "quota", "max", "cap", "tokenLimit"];
|
|
234
|
-
|
|
235
|
-
let percent: number | null = null;
|
|
236
|
-
let resetInSec = 0;
|
|
237
|
-
|
|
238
|
-
for (const key of keys) {
|
|
239
|
-
const val = record[key];
|
|
240
|
-
if (percentKeys.includes(key) && (typeof val === "number" || typeof val === "string")) {
|
|
241
|
-
const n = typeof val === "string" ? Number.parseFloat(val) : val;
|
|
242
|
-
if (!Number.isNaN(n)) percent = n;
|
|
243
|
-
}
|
|
244
|
-
if (resetSecKeys.includes(key) && typeof val === "number") {
|
|
245
|
-
resetInSec = val;
|
|
246
|
-
}
|
|
247
|
-
if (resetTsKeys.includes(key) && (typeof val === "number" || typeof val === "string")) {
|
|
248
|
-
if (typeof val === "number") {
|
|
249
|
-
const ts = val > 1_000_000_000_000 ? val / 1000 : val;
|
|
250
|
-
resetInSec = Math.max(0, Math.round(ts - Date.now() / 1000));
|
|
251
|
-
} else {
|
|
252
|
-
const d = new Date(val);
|
|
253
|
-
if (!Number.isNaN(d.getTime())) {
|
|
254
|
-
resetInSec = Math.max(0, Math.round(d.getTime() / 1000 - Date.now() / 1000));
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (usedKeys.includes(key) && typeof val === "number" && percent === null) {
|
|
259
|
-
const limitVal = keys.find((k) => limitKeys.includes(k));
|
|
260
|
-
if (limitVal && typeof record[limitVal] === "number" && record[limitVal]! > 0) {
|
|
261
|
-
percent = (val / (record[limitVal] as number)) * 100;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (percent !== null) {
|
|
267
|
-
let p = percent <= 1 && percent >= 0 ? percent * 100 : percent;
|
|
268
|
-
p = Math.max(0, Math.min(100, p));
|
|
269
|
-
candidates.push({ percent: p, resetInSec, path });
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Recurse
|
|
273
|
-
for (const [key, val] of Object.entries(record)) {
|
|
274
|
-
candidates.push(...findCandidates(val, path ? `${path}.${key}` : key, depth + 1));
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return candidates;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function classifyCandidates(candidates: WindowCandidate[]): {
|
|
281
|
-
rolling: WindowCandidate | null;
|
|
282
|
-
weekly: WindowCandidate | null;
|
|
283
|
-
monthly: WindowCandidate | null;
|
|
284
|
-
} {
|
|
285
|
-
let rolling: WindowCandidate | null = null;
|
|
286
|
-
let weekly: WindowCandidate | null = null;
|
|
287
|
-
let monthly: WindowCandidate | null = null;
|
|
288
|
-
const used = new Set<number>();
|
|
289
|
-
|
|
290
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
291
|
-
if (used.has(i)) continue;
|
|
292
|
-
const c = candidates[i]!;
|
|
293
|
-
const lowerPath = c.path.toLowerCase();
|
|
294
|
-
if (
|
|
295
|
-
lowerPath.includes("rolling") ||
|
|
296
|
-
lowerPath.includes("hour") ||
|
|
297
|
-
lowerPath.includes("5h") ||
|
|
298
|
-
lowerPath.includes("5-hour")
|
|
299
|
-
) {
|
|
300
|
-
if (!rolling || c.resetInSec < rolling.resetInSec || (c.resetInSec === rolling.resetInSec && c.percent > rolling.percent)) {
|
|
301
|
-
if (rolling) used.add(candidates.indexOf(rolling));
|
|
302
|
-
rolling = c;
|
|
303
|
-
used.add(i);
|
|
304
|
-
}
|
|
305
|
-
} else if (lowerPath.includes("weekly") || lowerPath.includes("week")) {
|
|
306
|
-
if (!weekly || c.resetInSec > weekly.resetInSec || (c.resetInSec === weekly.resetInSec && c.percent > weekly.percent)) {
|
|
307
|
-
if (weekly) used.add(candidates.indexOf(weekly));
|
|
308
|
-
weekly = c;
|
|
309
|
-
used.add(i);
|
|
310
|
-
}
|
|
311
|
-
} else if (lowerPath.includes("monthly") || lowerPath.includes("month")) {
|
|
312
|
-
if (!monthly || c.resetInSec > monthly.resetInSec || (c.resetInSec === monthly.resetInSec && c.percent > monthly.percent)) {
|
|
313
|
-
if (monthly) used.add(candidates.indexOf(monthly));
|
|
314
|
-
monthly = c;
|
|
315
|
-
used.add(i);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// If still missing, assign remaining candidates by resetInSec heuristic
|
|
321
|
-
const remaining = candidates.filter((_, i) => !used.has(i));
|
|
322
|
-
remaining.sort((a, b) => a.resetInSec - b.resetInSec);
|
|
323
|
-
if (!rolling && remaining.length > 0) {
|
|
324
|
-
rolling = remaining.shift()!;
|
|
325
|
-
}
|
|
326
|
-
if (!weekly && remaining.length > 0) {
|
|
327
|
-
weekly = remaining.pop()!;
|
|
328
|
-
}
|
|
329
|
-
if (!monthly && remaining.length > 0) {
|
|
330
|
-
monthly = remaining.pop()!;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
return { rolling, weekly, monthly };
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function parseSubscriptionPage(text: string): {
|
|
337
|
-
rollingPercent: number;
|
|
338
|
-
weeklyPercent: number;
|
|
339
|
-
monthlyPercent: number;
|
|
340
|
-
rollingReset: number;
|
|
341
|
-
weeklyReset: number;
|
|
342
|
-
monthlyReset: number;
|
|
343
|
-
hasMonthly: boolean;
|
|
344
|
-
} {
|
|
345
|
-
if (isSignedOut(text)) {
|
|
346
|
-
throw new UsageError("invalid-credentials", "Session is signed out");
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Strategy 1: JSON direct lookup
|
|
350
|
-
let data: unknown;
|
|
351
|
-
try {
|
|
352
|
-
data = JSON.parse(text);
|
|
353
|
-
} catch {
|
|
354
|
-
data = null;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (data && typeof data === "object") {
|
|
358
|
-
const record = data as Record<string, unknown>;
|
|
359
|
-
const nestedKeys = ["data", "result", "usage", "billing", "payload"];
|
|
360
|
-
|
|
361
|
-
const rollingKeys = ["rollingUsage", "rolling", "rolling_usage", "rollingWindow", "rolling_window"];
|
|
362
|
-
const weeklyKeys = ["weeklyUsage", "weekly", "weekly_usage", "weeklyWindow", "weekly_window"];
|
|
363
|
-
const monthlyKeys = ["monthlyUsage", "monthly", "monthly_usage", "monthlyWindow", "monthly_window"];
|
|
364
|
-
|
|
365
|
-
function getVal(root: Record<string, unknown>, keys: string[]): unknown {
|
|
366
|
-
for (const k of keys) {
|
|
367
|
-
if (k in root) return root[k];
|
|
368
|
-
}
|
|
369
|
-
for (const nk of nestedKeys) {
|
|
370
|
-
const nested = root[nk];
|
|
371
|
-
if (nested && typeof nested === "object") {
|
|
372
|
-
for (const k of keys) {
|
|
373
|
-
if (k in (nested as Record<string, unknown>)) return (nested as Record<string, unknown>)[k];
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return undefined;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const rollingVal = getVal(record, rollingKeys);
|
|
381
|
-
const weeklyVal = getVal(record, weeklyKeys);
|
|
382
|
-
const monthlyVal = getVal(record, monthlyKeys);
|
|
383
|
-
|
|
384
|
-
if (rollingVal !== undefined && weeklyVal !== undefined) {
|
|
385
|
-
function extractPercent(v: unknown): number {
|
|
386
|
-
if (typeof v === "number") return v <= 1 && v >= 0 ? v * 100 : v;
|
|
387
|
-
if (typeof v === "object" && v !== null) {
|
|
388
|
-
const r = v as Record<string, unknown>;
|
|
389
|
-
for (const k of ["usagePercent", "usedPercent", "percentUsed", "percent", "utilization"]) {
|
|
390
|
-
if (typeof r[k] === "number") {
|
|
391
|
-
const n = r[k] as number;
|
|
392
|
-
return n <= 1 && n >= 0 ? n * 100 : n;
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
return 0;
|
|
397
|
-
}
|
|
398
|
-
function extractReset(v: unknown): number {
|
|
399
|
-
if (typeof v === "number") return v;
|
|
400
|
-
if (typeof v === "object" && v !== null) {
|
|
401
|
-
const r = v as Record<string, unknown>;
|
|
402
|
-
for (const k of ["resetInSec", "resetInSeconds", "resetSeconds"]) {
|
|
403
|
-
if (typeof r[k] === "number") return r[k] as number;
|
|
404
|
-
}
|
|
405
|
-
for (const k of ["resetAt", "resetsAt", "reset_at"]) {
|
|
406
|
-
if (typeof r[k] === "string") {
|
|
407
|
-
const d = new Date(r[k] as string);
|
|
408
|
-
if (!Number.isNaN(d.getTime())) return Math.max(0, Math.round(d.getTime() / 1000 - Date.now() / 1000));
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
return 0;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const rollingPercent = extractPercent(rollingVal);
|
|
416
|
-
const weeklyPercent = extractPercent(weeklyVal);
|
|
417
|
-
const monthlyPercent = monthlyVal !== undefined ? extractPercent(monthlyVal) : 0;
|
|
418
|
-
return {
|
|
419
|
-
rollingPercent: Math.max(0, Math.min(100, rollingPercent)),
|
|
420
|
-
weeklyPercent: Math.max(0, Math.min(100, weeklyPercent)),
|
|
421
|
-
monthlyPercent: Math.max(0, Math.min(100, monthlyPercent)),
|
|
422
|
-
rollingReset: extractReset(rollingVal),
|
|
423
|
-
weeklyReset: extractReset(weeklyVal),
|
|
424
|
-
monthlyReset: monthlyVal !== undefined ? extractReset(monthlyVal) : 0,
|
|
425
|
-
hasMonthly: monthlyVal !== undefined,
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Strategy 1b + 1c: nested search and candidate search
|
|
430
|
-
const candidates = findCandidates(data);
|
|
431
|
-
const classified = classifyCandidates(candidates);
|
|
432
|
-
if (classified.rolling && classified.weekly) {
|
|
433
|
-
return {
|
|
434
|
-
rollingPercent: classified.rolling.percent,
|
|
435
|
-
weeklyPercent: classified.weekly.percent,
|
|
436
|
-
monthlyPercent: classified.monthly?.percent ?? 0,
|
|
437
|
-
rollingReset: classified.rolling.resetInSec,
|
|
438
|
-
weeklyReset: classified.weekly.resetInSec,
|
|
439
|
-
monthlyReset: classified.monthly?.resetInSec ?? 0,
|
|
440
|
-
hasMonthly: !!classified.monthly,
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Strategy 2: Regex fallback
|
|
446
|
-
const rollingPercentMatch = text.match(/rollingUsage[^}]*?usagePercent\s*:\s*([0-9]+(?:\.[0-9]+)?)/);
|
|
447
|
-
const rollingResetMatch = text.match(/rollingUsage[^}]*?resetInSec\s*:\s*([0-9]+)/);
|
|
448
|
-
const weeklyPercentMatch = text.match(/weeklyUsage[^}]*?usagePercent\s*:\s*([0-9]+(?:\.[0-9]+)?)/);
|
|
449
|
-
const weeklyResetMatch = text.match(/weeklyUsage[^}]*?resetInSec\s*:\s*([0-9]+)/);
|
|
450
|
-
const monthlyPercentMatch = text.match(/monthlyUsage[^}]*?usagePercent\s*:\s*([0-9]+(?:\.[0-9]+)?)/);
|
|
451
|
-
const monthlyResetMatch = text.match(/monthlyUsage[^}]*?resetInSec\s*:\s*([0-9]+)/);
|
|
452
|
-
|
|
453
|
-
const rollingPercent = rollingPercentMatch ? Number.parseFloat(rollingPercentMatch[1]!) : null;
|
|
454
|
-
const weeklyPercent = weeklyPercentMatch ? Number.parseFloat(weeklyPercentMatch[1]!) : null;
|
|
455
|
-
|
|
456
|
-
if (rollingPercent === null || weeklyPercent === null) {
|
|
457
|
-
throw new UsageError("invalid-response", "Missing usage fields.");
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
return {
|
|
461
|
-
rollingPercent: Math.max(0, Math.min(100, rollingPercent)),
|
|
462
|
-
weeklyPercent: Math.max(0, Math.min(100, weeklyPercent)),
|
|
463
|
-
monthlyPercent: monthlyPercentMatch ? Math.max(0, Math.min(100, Number.parseFloat(monthlyPercentMatch[1]!))) : 0,
|
|
464
|
-
rollingReset: rollingResetMatch ? Number.parseInt(rollingResetMatch[1]!, 10) : 0,
|
|
465
|
-
weeklyReset: weeklyResetMatch ? Number.parseInt(weeklyResetMatch[1]!, 10) : 0,
|
|
466
|
-
monthlyReset: monthlyResetMatch ? Number.parseInt(monthlyResetMatch[1]!, 10) : 0,
|
|
467
|
-
hasMonthly: monthlyPercentMatch !== null,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
export class OpenCodeGoUsageClient implements UsageClient<OpenCodeGoClientCredentials, OpenCodeGoUsageSnapshot> {
|
|
472
|
-
async fetch(creds: OpenCodeGoClientCredentials, options?: { signal?: AbortSignal }): Promise<OpenCodeGoUsageSnapshot> {
|
|
473
|
-
let cookies = buildAuthCookieHeader(parseCookieHeader(creds.cookies));
|
|
474
|
-
const request = async (url: string, init: RequestInit = {}): Promise<Response> => {
|
|
475
|
-
const headers = new Headers(init.headers);
|
|
476
|
-
headers.set("Cookie", cookies);
|
|
477
|
-
const response = await fetchSameHost(url, { ...init, headers, signal: options?.signal });
|
|
478
|
-
const rotated = applyAuthCookieRotation(cookies, response.headers);
|
|
479
|
-
if (rotated !== cookies) {
|
|
480
|
-
cookies = rotated;
|
|
481
|
-
creds.onCookieRotation?.(cookies);
|
|
482
|
-
}
|
|
483
|
-
return response;
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
const workspace = await this.resolveWorkspace(creds.serverFnId, request);
|
|
487
|
-
const url = `https://opencode.ai/workspace/${workspace.id}/go`;
|
|
488
|
-
|
|
489
|
-
const res = await request(url, {
|
|
490
|
-
headers: {
|
|
491
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
|
492
|
-
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
493
|
-
},
|
|
494
|
-
});
|
|
495
|
-
assertResponseStatus(res, "OpenCode Go subscription request");
|
|
496
|
-
|
|
497
|
-
let text: string;
|
|
498
|
-
try {
|
|
499
|
-
text = await res.text();
|
|
500
|
-
} catch (error) {
|
|
501
|
-
throw classifyUsageError(error);
|
|
502
|
-
}
|
|
503
|
-
if (isSignedOut(text)) {
|
|
504
|
-
throw new UsageError("invalid-credentials", "OpenCode Go session is signed out");
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
const parsed = parseSubscriptionPage(text);
|
|
508
|
-
|
|
509
|
-
return {
|
|
510
|
-
source: "web",
|
|
511
|
-
hasMonthlyUsage: parsed.hasMonthly,
|
|
512
|
-
rollingUsagePercent: parsed.rollingPercent,
|
|
513
|
-
weeklyUsagePercent: parsed.weeklyPercent,
|
|
514
|
-
monthlyUsagePercent: parsed.monthlyPercent,
|
|
515
|
-
rollingResetInSec: parsed.rollingReset,
|
|
516
|
-
weeklyResetInSec: parsed.weeklyReset,
|
|
517
|
-
monthlyResetInSec: parsed.monthlyReset,
|
|
518
|
-
updatedAt: new Date(),
|
|
519
|
-
workspaceId: workspace.id,
|
|
520
|
-
workspaceName: workspace.name,
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
private async resolveWorkspace(
|
|
525
|
-
serverFnId: string,
|
|
526
|
-
request: (url: string, init?: RequestInit) => Promise<Response>,
|
|
527
|
-
): Promise<{ id: string; name: string | null }> {
|
|
528
|
-
const headers: Record<string, string> = {
|
|
529
|
-
"X-Server-Id": serverFnId,
|
|
530
|
-
"X-Server-Instance": `server-fn:${randomUuid()}`,
|
|
531
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
|
532
|
-
Origin: "https://opencode.ai",
|
|
533
|
-
Referer: "https://opencode.ai",
|
|
534
|
-
Accept: "text/javascript, application/json;q=0.9, */*;q=0.8",
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
// GET attempt
|
|
538
|
-
let res = await request(`https://opencode.ai/_server?id=${serverFnId}`, { headers });
|
|
539
|
-
assertResponseStatus(res, "OpenCode Go workspace request");
|
|
540
|
-
let text: string;
|
|
541
|
-
try {
|
|
542
|
-
text = await res.text();
|
|
543
|
-
} catch (error) {
|
|
544
|
-
throw classifyUsageError(error);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (isSignedOut(text)) {
|
|
548
|
-
throw new UsageError("invalid-credentials", "OpenCode Go session is signed out");
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
let workspace = extractWorkspace(text);
|
|
552
|
-
if (!workspace) {
|
|
553
|
-
// POST fallback
|
|
554
|
-
res = await request("https://opencode.ai/_server", {
|
|
555
|
-
method: "POST",
|
|
556
|
-
headers: { ...headers, "Content-Type": "application/json" },
|
|
557
|
-
body: "[]",
|
|
558
|
-
});
|
|
559
|
-
assertResponseStatus(res, "OpenCode Go workspace request");
|
|
560
|
-
try {
|
|
561
|
-
text = await res.text();
|
|
562
|
-
} catch (error) {
|
|
563
|
-
throw classifyUsageError(error);
|
|
564
|
-
}
|
|
565
|
-
if (isSignedOut(text)) {
|
|
566
|
-
throw new UsageError("invalid-credentials", "OpenCode Go session is signed out");
|
|
567
|
-
}
|
|
568
|
-
workspace = extractWorkspace(text);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (!workspace) {
|
|
572
|
-
throw new UsageError("invalid-response", "Missing workspace id.");
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
return workspace;
|
|
576
|
-
}
|
|
577
|
-
}
|
package/src/usage/errors.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export type UsageFailureKind =
|
|
2
|
-
| "invalid-credentials"
|
|
3
|
-
| "forbidden"
|
|
4
|
-
| "rate-limited"
|
|
5
|
-
| "transient"
|
|
6
|
-
| "invalid-response"
|
|
7
|
-
| "cancelled";
|
|
8
|
-
|
|
9
|
-
export class UsageError extends Error {
|
|
10
|
-
constructor(
|
|
11
|
-
public readonly kind: UsageFailureKind,
|
|
12
|
-
message: string,
|
|
13
|
-
public readonly retryAfterMs?: number,
|
|
14
|
-
) {
|
|
15
|
-
super(message);
|
|
16
|
-
this.name = "UsageError";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function classifyUsageError(error: unknown): UsageError {
|
|
21
|
-
if (error instanceof UsageError) return error;
|
|
22
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
23
|
-
return new UsageError("cancelled", "Usage request cancelled");
|
|
24
|
-
}
|
|
25
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
26
|
-
return new UsageError("cancelled", "Usage request cancelled");
|
|
27
|
-
}
|
|
28
|
-
return new UsageError("transient", error instanceof Error ? error.message : String(error));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function isRecoverableAuthenticationError(error: unknown): boolean {
|
|
32
|
-
return classifyUsageError(error).kind === "invalid-credentials";
|
|
33
|
-
}
|
package/src/usage/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export * from "./types";
|
|
2
|
-
export * from "./resolver";
|
|
3
|
-
export * from "./client";
|
|
4
|
-
export * from "./planner";
|
|
5
|
-
export { UsageApi } from "./api";
|
|
6
|
-
export { mapClaudeToEntry, mapOpenCodeGoToEntry, mapCodexToEntry, buildClaudeAccountId, buildCodexAccountId } from "./mappers";
|
|
7
|
-
export { ClaudeUsageProvider } from "./providers/claude";
|
|
8
|
-
export { CodexUsageProvider } from "./providers/codex";
|
|
9
|
-
export { OpenCodeGoUsageProvider } from "./providers/opencode-go";
|
|
10
|
-
export { ClaudeAuthResolver } from "./resolvers/claude-auth";
|
|
11
|
-
export { ClaudeDesktopAuthResolver } from "./resolvers/claude-desktop-auth";
|
|
12
|
-
export { ClaudeWebResolver } from "./resolvers/claude-web";
|
|
13
|
-
export { ClaudeCliResolver } from "./resolvers/claude-cli";
|
|
14
|
-
export { CodexAuthResolver } from "./resolvers/codex-auth";
|
|
15
|
-
export { OpenCodeGoWebResolver } from "./resolvers/opencode-go-web";
|
|
16
|
-
export { extractCookies, buildCookieHeader, decryptSafeStorageBlob, getChromiumSafeStorageKey } from "./resolvers/browser-cookies";
|
|
17
|
-
export { AnthropicOAuthUsageClient } from "./clients/anthropic-oauth-usage";
|
|
18
|
-
export { AnthropicWebUsageClient } from "./clients/anthropic-web-usage";
|
|
19
|
-
export { CodexOAuthUsageClient } from "./clients/codex-oauth-usage";
|
|
20
|
-
export { OpenCodeGoUsageClient } from "./clients/opencode-go-usage";
|