@scrylog/cli 0.2.0 → 0.3.1

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.
Files changed (171) hide show
  1. package/README.md +46 -7
  2. package/dist/assets/highlights-eq9cgrbb.scm +604 -0
  3. package/dist/assets/highlights-ghv9g403.scm +205 -0
  4. package/dist/assets/highlights-hk7bwhj4.scm +284 -0
  5. package/dist/assets/highlights-r812a2qc.scm +150 -0
  6. package/dist/assets/highlights-x6tmsnaa.scm +115 -0
  7. package/dist/assets/injections-73j83es3.scm +27 -0
  8. package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
  9. package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
  10. package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  11. package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
  12. package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  13. package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  14. package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
  15. package/dist/cli/assets/providers/SOURCES.md +21 -0
  16. package/dist/cli/assets/providers/claude-terminal.png +0 -0
  17. package/dist/cli/assets/providers/claude-terminal.svg +62 -0
  18. package/dist/cli/assets/providers/claude.png +0 -0
  19. package/dist/cli/assets/providers/copilot.png +0 -0
  20. package/dist/cli/assets/providers/copilot.svg +44 -0
  21. package/dist/cli/assets/providers/openai-terminal.png +0 -0
  22. package/dist/cli/assets/providers/openai-terminal.svg +62 -0
  23. package/dist/cli/assets/providers/openai.png +0 -0
  24. package/dist/cli/assets/providers/opencode-terminal.png +0 -0
  25. package/dist/cli/assets/providers/opencode-terminal.svg +62 -0
  26. package/dist/cli/assets/providers/opencode.png +0 -0
  27. package/dist/cli/assets/providers/shell.png +0 -0
  28. package/dist/cli/assets/providers/shell.svg +23 -0
  29. package/dist/cli/assets/providers/unknown.png +0 -0
  30. package/dist/cli/assets/providers/unknown.svg +23 -0
  31. package/dist/cli/index.js +499 -0
  32. package/dist/cli/index.js.map +61 -0
  33. package/dist/daemon/index.js +670 -0
  34. package/dist/daemon/index.js.map +1475 -0
  35. package/dist/hooks/claude.js +6 -0
  36. package/dist/hooks/claude.js.map +11 -0
  37. package/dist/hooks/codex.js +10 -0
  38. package/dist/hooks/codex.js.map +11 -0
  39. package/dist/mcp/index.js +100 -0
  40. package/dist/mcp/index.js.map +231 -0
  41. package/dist/oc/index.js +21 -0
  42. package/dist/oc/index.js.map +10 -0
  43. package/package.json +26 -20
  44. package/src/postinstall.cjs +11 -0
  45. package/src/api/docs/sessions.keys.ts +0 -6
  46. package/src/api/docs/sessions.ts +0 -81
  47. package/src/api/docs/system.keys.ts +0 -5
  48. package/src/api/docs/system.ts +0 -66
  49. package/src/api/docs/usage.keys.ts +0 -3
  50. package/src/api/docs/usage.ts +0 -24
  51. package/src/api/docs/webhooks.keys.ts +0 -5
  52. package/src/api/docs/webhooks.ts +0 -78
  53. package/src/api/index.ts +0 -18
  54. package/src/api/lib/auth.ts +0 -43
  55. package/src/api/lib/errors.ts +0 -28
  56. package/src/api/lib/route.ts +0 -18
  57. package/src/api/models/claude.ts +0 -4
  58. package/src/api/models/codex.ts +0 -4
  59. package/src/api/models/opencode.ts +0 -11
  60. package/src/api/routes/sessions.ts +0 -47
  61. package/src/api/routes/system.ts +0 -103
  62. package/src/api/routes/usage.ts +0 -15
  63. package/src/api/routes/webhooks/claude.ts +0 -141
  64. package/src/api/routes/webhooks/codex.ts +0 -175
  65. package/src/api/routes/webhooks/opencode.ts +0 -111
  66. package/src/api/routes.ts +0 -93
  67. package/src/api/schemas/common.ts +0 -21
  68. package/src/api/schemas/sessions.ts +0 -72
  69. package/src/api/schemas/usage.ts +0 -50
  70. package/src/api/schemas/webhooks/claude.ts +0 -32
  71. package/src/api/schemas/webhooks/codex.ts +0 -34
  72. package/src/api/schemas/webhooks/opencode.ts +0 -29
  73. package/src/api/services/claude-session.ts +0 -9
  74. package/src/api/services/codex-session.ts +0 -8
  75. package/src/api/services/opencode-session.ts +0 -73
  76. package/src/classifier.ts +0 -206
  77. package/src/cli/commands.ts +0 -632
  78. package/src/cli/prompt.ts +0 -44
  79. package/src/daemon/cli.ts +0 -27
  80. package/src/daemon/collectors/claude/README.md +0 -108
  81. package/src/daemon/collectors/claude/collector.ts +0 -423
  82. package/src/daemon/collectors/claude/discovery.ts +0 -148
  83. package/src/daemon/collectors/claude/hooks.ts +0 -181
  84. package/src/daemon/collectors/claude/index.ts +0 -8
  85. package/src/daemon/collectors/claude/jsonl.ts +0 -85
  86. package/src/daemon/collectors/claude/transcript.ts +0 -246
  87. package/src/daemon/collectors/codex/README.md +0 -147
  88. package/src/daemon/collectors/codex/collector.ts +0 -661
  89. package/src/daemon/collectors/codex/discovery.ts +0 -104
  90. package/src/daemon/collectors/codex/hooks.ts +0 -179
  91. package/src/daemon/collectors/codex/index.ts +0 -7
  92. package/src/daemon/collectors/codex/jsonl.ts +0 -108
  93. package/src/daemon/collectors/codex/transcript.ts +0 -299
  94. package/src/daemon/collectors/git.ts +0 -295
  95. package/src/daemon/collectors/opencode/README.md +0 -106
  96. package/src/daemon/collectors/opencode/collector.ts +0 -545
  97. package/src/daemon/collectors/opencode/discovery.ts +0 -257
  98. package/src/daemon/collectors/opencode/index.ts +0 -5
  99. package/src/daemon/collectors/opencode/session-builder.ts +0 -59
  100. package/src/daemon/collectors/opencode-db.ts +0 -282
  101. package/src/daemon/collectors/process.ts +0 -201
  102. package/src/daemon/collectors/tmux/README.md +0 -128
  103. package/src/daemon/collectors/tmux/collector.ts +0 -472
  104. package/src/daemon/collectors/tmux/enrichment.ts +0 -241
  105. package/src/daemon/collectors/tmux/index.ts +0 -4
  106. package/src/daemon/collectors/tmux/parse.ts +0 -128
  107. package/src/daemon/collectors/usage.ts +0 -193
  108. package/src/daemon/index.ts +0 -207
  109. package/src/daemon/scheduler.ts +0 -86
  110. package/src/daemon/store.ts +0 -588
  111. package/src/detect.ts +0 -143
  112. package/src/hooks/claude.ts +0 -596
  113. package/src/hooks/codex.ts +0 -517
  114. package/src/legacy/classifier.ts +0 -215
  115. package/src/legacy/detect.ts +0 -143
  116. package/src/mcp/client-server.ts +0 -21
  117. package/src/mcp/http-handler.ts +0 -126
  118. package/src/mcp/index.ts +0 -61
  119. package/src/mcp/resources/config-current.ts +0 -19
  120. package/src/mcp/resources/index.ts +0 -11
  121. package/src/mcp/resources/sessions-active.ts +0 -19
  122. package/src/mcp/resources/sessions-all.ts +0 -19
  123. package/src/mcp/schemas/sessions.ts +0 -3
  124. package/src/mcp/server.ts +0 -17
  125. package/src/mcp/services/api-adapter.ts +0 -48
  126. package/src/mcp/services/data-source.ts +0 -22
  127. package/src/mcp/services/store-adapter.ts +0 -40
  128. package/src/mcp/tools/get-active-sessions.ts +0 -16
  129. package/src/mcp/tools/get-session-children.ts +0 -24
  130. package/src/mcp/tools/get-session-messages.ts +0 -24
  131. package/src/mcp/tools/get-session.ts +0 -23
  132. package/src/mcp/tools/index.ts +0 -15
  133. package/src/mcp/tools/list-sessions.ts +0 -16
  134. package/src/neo-blessed.d.ts +0 -4
  135. package/src/oc/index.ts +0 -302
  136. package/src/service/manager.ts +0 -284
  137. package/src/shared/config.ts +0 -478
  138. package/src/shared/ipc.ts +0 -80
  139. package/src/shared/logger.ts +0 -248
  140. package/src/shared/paths.ts +0 -73
  141. package/src/shared/platform.ts +0 -110
  142. package/src/shared/session-view.ts +0 -241
  143. package/src/shared/types.ts +0 -299
  144. package/src/shared/utils.ts +0 -339
  145. package/src/tui/app.ts +0 -920
  146. package/src/tui/client.ts +0 -149
  147. package/src/tui/index.ts +0 -217
  148. package/src/types.ts +0 -113
  149. package/src/usage/README.md +0 -323
  150. package/src/usage/api.ts +0 -99
  151. package/src/usage/client.ts +0 -3
  152. package/src/usage/clients/anthropic-oauth-usage.ts +0 -178
  153. package/src/usage/clients/anthropic-web-usage.ts +0 -245
  154. package/src/usage/clients/claude-cli-probe.ts +0 -187
  155. package/src/usage/clients/codex-oauth-usage.ts +0 -113
  156. package/src/usage/clients/opencode-go-usage.ts +0 -441
  157. package/src/usage/index.ts +0 -20
  158. package/src/usage/mappers.ts +0 -273
  159. package/src/usage/planner.ts +0 -54
  160. package/src/usage/providers/claude.ts +0 -113
  161. package/src/usage/providers/codex.ts +0 -34
  162. package/src/usage/providers/opencode-go.ts +0 -37
  163. package/src/usage/resolver.ts +0 -4
  164. package/src/usage/resolvers/browser-cookies.ts +0 -470
  165. package/src/usage/resolvers/claude-auth.ts +0 -108
  166. package/src/usage/resolvers/claude-cli.ts +0 -24
  167. package/src/usage/resolvers/claude-desktop-auth.ts +0 -160
  168. package/src/usage/resolvers/claude-web.ts +0 -16
  169. package/src/usage/resolvers/codex-auth.ts +0 -117
  170. package/src/usage/resolvers/opencode-go-web.ts +0 -14
  171. package/src/usage/types.ts +0 -190
@@ -1,273 +0,0 @@
1
- import type {
2
- UsageProviderEntry,
3
- UsageWindow,
4
- ProviderCostSnapshot,
5
- ClaudeUsageSnapshot,
6
- CodexUsageSnapshot,
7
- OpenCodeGoUsageSnapshot,
8
- RateWindowLegacy,
9
- ClaudeOAuthCredentials,
10
- CodexOAuthCredentials,
11
- } from "./types";
12
-
13
-
14
- function toIsoString(d: Date | null): string | null {
15
- return d && !Number.isNaN(d.getTime()) ? d.toISOString() : null;
16
- }
17
-
18
- function computeResetInSec(resetsAt: string | null | undefined): number | undefined {
19
- if (!resetsAt) return undefined;
20
- const d = new Date(resetsAt);
21
- if (Number.isNaN(d.getTime())) return undefined;
22
- return Math.max(0, Math.round(d.getTime() / 1000 - Date.now() / 1000));
23
- }
24
-
25
- function computeResetsAt(resetInSec: number | undefined): string | undefined {
26
- if (resetInSec === undefined || resetInSec < 0) return undefined;
27
- return new Date(Date.now() + resetInSec * 1000).toISOString();
28
- }
29
-
30
- function mapRateWindowLegacyToWindow(id: string, title: string, rw: RateWindowLegacy | null): UsageWindow | undefined {
31
- if (!rw) return undefined;
32
- const resetsAt = toIsoString(rw.resetsAt);
33
- return {
34
- id,
35
- title,
36
- usedPercent: rw.usedPercent,
37
- windowMinutes: rw.windowMinutes,
38
- resetsAt,
39
- resetInSec: computeResetInSec(resetsAt),
40
- };
41
- }
42
-
43
- function simpleHash(input: string): string {
44
- let hash = 0;
45
- for (let i = 0; i < input.length; i++) {
46
- const char = input.charCodeAt(i);
47
- hash = ((hash << 5) - hash + char) | 0;
48
- }
49
- return Math.abs(hash).toString(16).padStart(8, "0");
50
- }
51
-
52
- export function buildClaudeAccountId(
53
- snapshot: ClaudeUsageSnapshot | null,
54
- oauthCreds: ClaudeOAuthCredentials | null,
55
- ): string | null {
56
- // Best source: email from Web API
57
- if (snapshot?.accountEmail) {
58
- return snapshot.accountEmail;
59
- }
60
- // Second best: org UUID from Web API (not in snapshot currently, but we can add if needed)
61
- // For now, use Keychain metadata as fallback
62
- if (oauthCreds?.subscriptionType || oauthCreds?.rateLimitTier) {
63
- const parts = ["claude"];
64
- if (oauthCreds.subscriptionType) parts.push(oauthCreds.subscriptionType);
65
- if (oauthCreds.rateLimitTier) parts.push(oauthCreds.rateLimitTier);
66
- if (oauthCreds.scopes && oauthCreds.scopes.length > 0) {
67
- // Add a short hash of scopes to distinguish accounts with same tier
68
- const scopesHash = simpleHash(oauthCreds.scopes.join(","));
69
- parts.push(scopesHash);
70
- }
71
- return parts.join("-");
72
- }
73
- return null;
74
- }
75
-
76
- export function mapClaudeToEntry(
77
- snapshot: ClaudeUsageSnapshot | null,
78
- errors: string[] = [],
79
- available: boolean,
80
- oauthCreds: ClaudeOAuthCredentials | null,
81
- ): UsageProviderEntry {
82
- const base: UsageProviderEntry = {
83
- provider: "claude",
84
- accountId: buildClaudeAccountId(snapshot, oauthCreds),
85
- available,
86
- status: snapshot ? (errors.length > 0 ? "error" : "ok") : available ? "error" : "unavailable",
87
- };
88
-
89
- if (!snapshot) {
90
- if (errors.length > 0) {
91
- base.errors = errors;
92
- }
93
- return base;
94
- }
95
-
96
- const windows: UsageWindow[] = [];
97
-
98
- const primary = mapRateWindowLegacyToWindow("session", "Current Session", snapshot.primary);
99
- if (primary) windows.push(primary);
100
-
101
- const secondary = mapRateWindowLegacyToWindow("weekly", "Weekly", snapshot.secondary);
102
- if (secondary) windows.push(secondary);
103
-
104
- const opus = mapRateWindowLegacyToWindow("opus", "Opus", snapshot.opus);
105
- if (opus) windows.push(opus);
106
-
107
- for (const extra of snapshot.extraRateWindows) {
108
- const w = mapRateWindowLegacyToWindow(extra.id, extra.title, extra.window);
109
- if (w) windows.push(w);
110
- }
111
-
112
- let cost: ProviderCostSnapshot | null = null;
113
- if (snapshot.providerCost) {
114
- cost = {
115
- used: snapshot.providerCost.used,
116
- limit: snapshot.providerCost.limit,
117
- currency: snapshot.providerCost.currencyCode,
118
- period: snapshot.providerCost.period,
119
- resetsAt: toIsoString(snapshot.providerCost.resetsAt),
120
- updatedAt: toIsoString(snapshot.providerCost.updatedAt) ?? new Date().toISOString(),
121
- };
122
- }
123
-
124
- base.metadata = {
125
- dataSource: snapshot.source,
126
- accountEmail: snapshot.accountEmail,
127
- accountOrganization: snapshot.accountOrganization,
128
- subscriptionType: snapshot.subscriptionType,
129
- rateLimitTier: snapshot.rateLimitTier,
130
- };
131
-
132
- if (errors.length > 0) {
133
- base.errors = errors;
134
- }
135
-
136
- base.windows = windows;
137
- base.cost = cost;
138
- base.updatedAt = toIsoString(snapshot.updatedAt) ?? new Date().toISOString();
139
-
140
- return base;
141
- }
142
-
143
- export function buildCodexAccountId(
144
- snapshot: CodexUsageSnapshot | null,
145
- oauthCreds: CodexOAuthCredentials | null,
146
- ): string | null {
147
- if (oauthCreds?.accountEmail) {
148
- return oauthCreds.accountEmail;
149
- }
150
- if (oauthCreds?.accountId) {
151
- return oauthCreds.accountId;
152
- }
153
- if (snapshot?.accountEmail) {
154
- return snapshot.accountEmail;
155
- }
156
- if (snapshot?.planType) {
157
- return `codex-${snapshot.planType}`;
158
- }
159
- return null;
160
- }
161
-
162
- export function mapCodexToEntry(
163
- snapshot: CodexUsageSnapshot | null,
164
- errors: string[] = [],
165
- available: boolean,
166
- oauthCreds: CodexOAuthCredentials | null,
167
- ): UsageProviderEntry {
168
- const base: UsageProviderEntry = {
169
- provider: "codex",
170
- accountId: buildCodexAccountId(snapshot, oauthCreds),
171
- available,
172
- status: snapshot ? (errors.length > 0 ? "error" : "ok") : available ? "error" : "unavailable",
173
- };
174
-
175
- if (!snapshot) {
176
- if (errors.length > 0) {
177
- base.errors = errors;
178
- }
179
- return base;
180
- }
181
-
182
- const windows: UsageWindow[] = [];
183
-
184
- const primary = mapRateWindowLegacyToWindow("session", "Current Session", snapshot.primaryWindow);
185
- if (primary) windows.push(primary);
186
-
187
- const secondary = mapRateWindowLegacyToWindow("weekly", "Weekly", snapshot.secondaryWindow);
188
- if (secondary) windows.push(secondary);
189
-
190
- // Codex usage API provides rate windows (percentages) and credits (balance/remaining).
191
- // It does not expose separate "used" vs "limit" absolute amounts, so a
192
- // ProviderCostSnapshot cannot be accurately constructed. Only rate windows are
193
- // mapped; credit balance is surfaced via metadata instead of cost.
194
- const cost: ProviderCostSnapshot | null = null;
195
-
196
- base.metadata = {
197
- dataSource: snapshot.source,
198
- accountEmail: snapshot.accountEmail ?? oauthCreds?.accountEmail,
199
- subscriptionType: snapshot.planType,
200
- };
201
-
202
- if (errors.length > 0) {
203
- base.errors = errors;
204
- }
205
-
206
- base.windows = windows;
207
- base.cost = cost;
208
- base.updatedAt = toIsoString(snapshot.updatedAt) ?? new Date().toISOString();
209
-
210
- return base;
211
- }
212
-
213
- export function mapOpenCodeGoToEntry(
214
- snapshot: OpenCodeGoUsageSnapshot | null,
215
- errors: string[] = [],
216
- available: boolean,
217
- ): UsageProviderEntry {
218
- const base: UsageProviderEntry = {
219
- provider: "opencode-go",
220
- accountId: snapshot?.workspaceId ?? null,
221
- available,
222
- status: snapshot ? (errors.length > 0 ? "error" : "ok") : available ? "error" : "unavailable",
223
- };
224
-
225
- if (!snapshot) {
226
- if (errors.length > 0) {
227
- base.errors = errors;
228
- }
229
- return base;
230
- }
231
-
232
- const windows: UsageWindow[] = [
233
- {
234
- id: "session",
235
- title: "Rolling",
236
- usedPercent: snapshot.rollingUsagePercent,
237
- resetInSec: snapshot.rollingResetInSec,
238
- resetsAt: computeResetsAt(snapshot.rollingResetInSec),
239
- },
240
- {
241
- id: "weekly",
242
- title: "Weekly",
243
- usedPercent: snapshot.weeklyUsagePercent,
244
- resetInSec: snapshot.weeklyResetInSec,
245
- resetsAt: computeResetsAt(snapshot.weeklyResetInSec),
246
- },
247
- ];
248
-
249
- if (snapshot.hasMonthlyUsage) {
250
- windows.push({
251
- id: "monthly",
252
- title: "Monthly",
253
- usedPercent: snapshot.monthlyUsagePercent,
254
- resetInSec: snapshot.monthlyResetInSec,
255
- resetsAt: computeResetsAt(snapshot.monthlyResetInSec),
256
- });
257
- }
258
-
259
- base.metadata = {
260
- dataSource: snapshot.source,
261
- workspaceId: snapshot.workspaceId,
262
- workspaceName: snapshot.workspaceName,
263
- };
264
-
265
- if (errors.length > 0) {
266
- base.errors = errors;
267
- }
268
-
269
- base.windows = windows;
270
- base.updatedAt = toIsoString(snapshot.updatedAt) ?? new Date().toISOString();
271
-
272
- return base;
273
- }
@@ -1,54 +0,0 @@
1
- import type { TokenResolver } from "./resolver";
2
- import type { ClaudePlannerDecision, ClaudeDataSource, ClaudeOAuthCredentials, ClaudeWebSession } from "./types";
3
-
4
- export interface ClaudeSourcePlannerConfig {
5
- dataSource: ClaudeDataSource;
6
- webExtrasEnabled: boolean;
7
- }
8
-
9
- export class ClaudeSourcePlanner {
10
- constructor(
11
- private readonly config: ClaudeSourcePlannerConfig,
12
- private readonly oauthResolver: TokenResolver<ClaudeOAuthCredentials>,
13
- private readonly webResolver: TokenResolver<ClaudeWebSession>,
14
- private readonly cliResolver: TokenResolver<string>,
15
- ) {}
16
-
17
- async decide(): Promise<ClaudePlannerDecision> {
18
- const [hasOAuth, hasWeb, hasCli] = await Promise.all([
19
- this.oauthResolver.isAvailable(),
20
- this.webResolver.isAvailable(),
21
- this.cliResolver.isAvailable(),
22
- ]);
23
-
24
- const selected = this.config.dataSource;
25
- const webExtras = this.config.webExtrasEnabled;
26
-
27
- if (selected === "oauth") {
28
- return { primary: "oauth", fallback: [], webExtras };
29
- }
30
- if (selected === "web") {
31
- return { primary: "web", fallback: [], webExtras };
32
- }
33
- if (selected === "cli") {
34
- return { primary: "cli", fallback: [], webExtras };
35
- }
36
-
37
- // Auto mode
38
- const fallback: ClaudeDataSource[] = [];
39
- if (hasOAuth) {
40
- if (hasWeb) fallback.push("web");
41
- if (hasCli) fallback.push("cli");
42
- return { primary: "oauth", fallback, webExtras };
43
- }
44
- if (hasWeb) {
45
- if (hasCli) fallback.push("cli");
46
- return { primary: "web", fallback, webExtras };
47
- }
48
- if (hasCli) {
49
- return { primary: "cli", fallback: [], webExtras };
50
- }
51
-
52
- return { primary: "auto", fallback: [], webExtras };
53
- }
54
- }
@@ -1,113 +0,0 @@
1
- import type { UsageClient } from "../client";
2
- import type { ClaudeOAuthCredentials, ClaudeUsageSnapshot, ClaudeWebSession, UsageProviderEntry } from "../types";
3
- import { ClaudeSourcePlanner } from "../planner";
4
- import { AnthropicOAuthUsageClient, AuthenticationError, ScopeError } from "../clients/anthropic-oauth-usage";
5
- import { AnthropicWebUsageClient, InvalidCredentialsError } from "../clients/anthropic-web-usage";
6
- import { ClaudeCliProbeClient } from "../clients/claude-cli-probe";
7
- import type { TokenResolver } from "../resolver";
8
- import { mapClaudeToEntry } from "../mappers";
9
-
10
- export class ClaudeUsageProvider {
11
- private readonly oauthClient: UsageClient<ClaudeOAuthCredentials, ClaudeUsageSnapshot>;
12
- private readonly webClient: UsageClient<ClaudeWebSession, ClaudeUsageSnapshot>;
13
- private readonly cliClient: UsageClient<string, ClaudeUsageSnapshot>;
14
-
15
- constructor(
16
- private readonly planner: ClaudeSourcePlanner,
17
- private readonly oauthResolver: TokenResolver<ClaudeOAuthCredentials>,
18
- private readonly webResolver: TokenResolver<ClaudeWebSession>,
19
- private readonly cliResolver: TokenResolver<string>,
20
- ) {
21
- this.oauthClient = new AnthropicOAuthUsageClient();
22
- this.webClient = new AnthropicWebUsageClient();
23
- this.cliClient = new ClaudeCliProbeClient();
24
- }
25
-
26
- async fetch(options?: { signal?: AbortSignal }): Promise<{ entry: UsageProviderEntry; snapshot: ClaudeUsageSnapshot | null }> {
27
- const decision = await this.planner.decide();
28
- const errors: string[] = [];
29
-
30
- if (decision.primary === "auto") {
31
- // No credentials available on this machine
32
- return { entry: mapClaudeToEntry(null, [], false, null), snapshot: null };
33
- }
34
-
35
- // Resolve all credentials upfront for both fetch and accountId generation
36
- const [oauthCreds, webCreds, cliCreds] = await Promise.all([
37
- this.oauthResolver.resolve(),
38
- this.webResolver.resolve(),
39
- this.cliResolver.resolve(),
40
- ]);
41
-
42
- const sources: Array<{ type: "oauth" | "web" | "cli"; creds: unknown }> = [];
43
-
44
- // Primary
45
- if (decision.primary === "oauth" && oauthCreds) {
46
- sources.push({ type: "oauth", creds: oauthCreds });
47
- } else if (decision.primary === "web" && webCreds) {
48
- sources.push({ type: "web", creds: webCreds });
49
- } else if (decision.primary === "cli" && cliCreds) {
50
- sources.push({ type: "cli", creds: cliCreds });
51
- }
52
-
53
- // Fallbacks
54
- for (const fb of decision.fallback) {
55
- if (fb === "oauth" && oauthCreds) {
56
- sources.push({ type: "oauth", creds: oauthCreds });
57
- } else if (fb === "web" && webCreds) {
58
- sources.push({ type: "web", creds: webCreds });
59
- } else if (fb === "cli" && cliCreds) {
60
- sources.push({ type: "cli", creds: cliCreds });
61
- }
62
- }
63
-
64
- let snapshot: ClaudeUsageSnapshot | null = null;
65
-
66
- for (const source of sources) {
67
- try {
68
- if (source.type === "oauth") {
69
- snapshot = await this.oauthClient.fetch(source.creds as ClaudeOAuthCredentials, options);
70
- } else if (source.type === "web") {
71
- snapshot = await this.webClient.fetch(source.creds as ClaudeWebSession, options);
72
- } else if (source.type === "cli") {
73
- snapshot = await this.cliClient.fetch(source.creds as string, options);
74
- }
75
- break;
76
- } catch (err) {
77
- const message = err instanceof Error ? err.message : String(err);
78
- errors.push(`${source.type}: ${message}`);
79
- if (err instanceof InvalidCredentialsError || err instanceof AuthenticationError || err instanceof ScopeError) {
80
- continue;
81
- }
82
- // Continue to fallback for other errors too
83
- }
84
- }
85
-
86
- // Merge OAuth credential metadata into snapshot for accountId generation
87
- if (snapshot && oauthCreds) {
88
- if (snapshot.subscriptionType === null && oauthCreds.subscriptionType) {
89
- snapshot = { ...snapshot, subscriptionType: oauthCreds.subscriptionType };
90
- }
91
- if (snapshot.rateLimitTier === null && oauthCreds.rateLimitTier) {
92
- snapshot = { ...snapshot, rateLimitTier: oauthCreds.rateLimitTier };
93
- }
94
- }
95
-
96
- // Web extras merge
97
- if (snapshot && decision.webExtras && snapshot.source !== "web" && webCreds) {
98
- try {
99
- const webSnapshot = await this.webClient.fetch(webCreds, options);
100
- if (snapshot.extraRateWindows.length === 0 && webSnapshot.extraRateWindows.length > 0) {
101
- snapshot = { ...snapshot, extraRateWindows: webSnapshot.extraRateWindows };
102
- }
103
- if (!snapshot.providerCost && webSnapshot.providerCost) {
104
- snapshot = { ...snapshot, providerCost: webSnapshot.providerCost };
105
- }
106
- } catch {
107
- // Ignore web extras failures
108
- }
109
- }
110
-
111
- return { entry: mapClaudeToEntry(snapshot, errors, true, oauthCreds), snapshot };
112
- }
113
- }
@@ -1,34 +0,0 @@
1
- import type { UsageClient } from "../client";
2
- import type { CodexOAuthCredentials, CodexUsageSnapshot, UsageProviderEntry } from "../types";
3
- import type { TokenResolver } from "../resolver";
4
- import { CodexOAuthUsageClient } from "../clients/codex-oauth-usage";
5
- import { mapCodexToEntry } from "../mappers";
6
-
7
- export class CodexUsageProvider {
8
- private readonly oauthClient: UsageClient<CodexOAuthCredentials, CodexUsageSnapshot>;
9
-
10
- constructor(
11
- private readonly oauthResolver: TokenResolver<CodexOAuthCredentials>,
12
- ) {
13
- this.oauthClient = new CodexOAuthUsageClient();
14
- }
15
-
16
- async fetch(options?: { signal?: AbortSignal }): Promise<{ entry: UsageProviderEntry; snapshot: CodexUsageSnapshot | null }> {
17
- const errors: string[] = [];
18
- const oauthCreds = await this.oauthResolver.resolve();
19
-
20
- if (!oauthCreds) {
21
- return { entry: mapCodexToEntry(null, [], false, null), snapshot: null };
22
- }
23
-
24
- let snapshot: CodexUsageSnapshot | null = null;
25
- try {
26
- snapshot = await this.oauthClient.fetch(oauthCreds, options);
27
- } catch (err) {
28
- const message = err instanceof Error ? err.message : String(err);
29
- errors.push(`oauth: ${message}`);
30
- }
31
-
32
- return { entry: mapCodexToEntry(snapshot, errors, true, oauthCreds), snapshot };
33
- }
34
- }
@@ -1,37 +0,0 @@
1
- import type { TokenResolver } from "../resolver";
2
- import type { OpenCodeGoUsageSnapshot } from "../types";
3
- import type { UsageProviderEntry } from "../types";
4
- import { OpenCodeGoUsageClient } from "../clients/opencode-go-usage";
5
- import { mapOpenCodeGoToEntry } from "../mappers";
6
-
7
- export class OpenCodeGoUsageProvider {
8
- private readonly client = new OpenCodeGoUsageClient();
9
-
10
- constructor(
11
- private readonly resolver: TokenResolver<{ cookies: string }>,
12
- private readonly serverFnId: string,
13
- ) {}
14
-
15
- async fetch(options?: { signal?: AbortSignal }): Promise<{ entry: UsageProviderEntry; snapshot: OpenCodeGoUsageSnapshot | null }> {
16
- const errors: string[] = [];
17
- const available = await this.resolver.isAvailable();
18
- if (!available) {
19
- return { entry: mapOpenCodeGoToEntry(null, [], false), snapshot: null };
20
- }
21
-
22
- const creds = await this.resolver.resolve();
23
- if (!creds) {
24
- // Should not happen if isAvailable() is correct
25
- return { entry: mapOpenCodeGoToEntry(null, [], false), snapshot: null };
26
- }
27
-
28
- try {
29
- const snapshot = await this.client.fetch({ cookies: creds.cookies, serverFnId: this.serverFnId }, options);
30
- return { entry: mapOpenCodeGoToEntry(snapshot, errors, true), snapshot };
31
- } catch (err) {
32
- const message = err instanceof Error ? err.message : String(err);
33
- errors.push(`OpenCode Go: ${message}`);
34
- return { entry: mapOpenCodeGoToEntry(null, errors, true), snapshot: null };
35
- }
36
- }
37
- }
@@ -1,4 +0,0 @@
1
- export interface TokenResolver<T> {
2
- resolve(): Promise<T | null>;
3
- isAvailable(): Promise<boolean>;
4
- }