@tokscale/cli 1.0.0

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 (211) hide show
  1. package/dist/auth.d.ts +17 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +162 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/claudecode.d.ts +39 -0
  6. package/dist/claudecode.d.ts.map +1 -0
  7. package/dist/claudecode.js +375 -0
  8. package/dist/claudecode.js.map +1 -0
  9. package/dist/cli.d.ts +9 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +761 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/credentials.d.ts +36 -0
  14. package/dist/credentials.d.ts.map +1 -0
  15. package/dist/credentials.js +109 -0
  16. package/dist/credentials.js.map +1 -0
  17. package/dist/cursor.d.ts +132 -0
  18. package/dist/cursor.d.ts.map +1 -0
  19. package/dist/cursor.js +432 -0
  20. package/dist/cursor.js.map +1 -0
  21. package/dist/gemini.d.ts +36 -0
  22. package/dist/gemini.d.ts.map +1 -0
  23. package/dist/gemini.js +125 -0
  24. package/dist/gemini.js.map +1 -0
  25. package/dist/graph-types.d.ts +152 -0
  26. package/dist/graph-types.d.ts.map +1 -0
  27. package/dist/graph-types.js +6 -0
  28. package/dist/graph-types.js.map +1 -0
  29. package/dist/graph.d.ts +29 -0
  30. package/dist/graph.d.ts.map +1 -0
  31. package/dist/graph.js +383 -0
  32. package/dist/graph.js.map +1 -0
  33. package/dist/native-runner.d.ts +12 -0
  34. package/dist/native-runner.d.ts.map +1 -0
  35. package/dist/native-runner.js +89 -0
  36. package/dist/native-runner.js.map +1 -0
  37. package/dist/native.d.ts +116 -0
  38. package/dist/native.d.ts.map +1 -0
  39. package/dist/native.js +359 -0
  40. package/dist/native.js.map +1 -0
  41. package/dist/opencode.d.ts +40 -0
  42. package/dist/opencode.d.ts.map +1 -0
  43. package/dist/opencode.js +69 -0
  44. package/dist/opencode.js.map +1 -0
  45. package/dist/pricing.d.ts +58 -0
  46. package/dist/pricing.d.ts.map +1 -0
  47. package/dist/pricing.js +232 -0
  48. package/dist/pricing.js.map +1 -0
  49. package/dist/sessions/claudecode.d.ts +8 -0
  50. package/dist/sessions/claudecode.d.ts.map +1 -0
  51. package/dist/sessions/claudecode.js +84 -0
  52. package/dist/sessions/claudecode.js.map +1 -0
  53. package/dist/sessions/codex.d.ts +8 -0
  54. package/dist/sessions/codex.d.ts.map +1 -0
  55. package/dist/sessions/codex.js +158 -0
  56. package/dist/sessions/codex.js.map +1 -0
  57. package/dist/sessions/gemini.d.ts +8 -0
  58. package/dist/sessions/gemini.d.ts.map +1 -0
  59. package/dist/sessions/gemini.js +66 -0
  60. package/dist/sessions/gemini.js.map +1 -0
  61. package/dist/sessions/index.d.ts +32 -0
  62. package/dist/sessions/index.d.ts.map +1 -0
  63. package/dist/sessions/index.js +96 -0
  64. package/dist/sessions/index.js.map +1 -0
  65. package/dist/sessions/opencode.d.ts +8 -0
  66. package/dist/sessions/opencode.d.ts.map +1 -0
  67. package/dist/sessions/opencode.js +54 -0
  68. package/dist/sessions/opencode.js.map +1 -0
  69. package/dist/sessions/reports.d.ts +58 -0
  70. package/dist/sessions/reports.d.ts.map +1 -0
  71. package/dist/sessions/reports.js +337 -0
  72. package/dist/sessions/reports.js.map +1 -0
  73. package/dist/sessions/types.d.ts +30 -0
  74. package/dist/sessions/types.d.ts.map +1 -0
  75. package/dist/sessions/types.js +29 -0
  76. package/dist/sessions/types.js.map +1 -0
  77. package/dist/spinner.d.ts +75 -0
  78. package/dist/spinner.d.ts.map +1 -0
  79. package/dist/spinner.js +203 -0
  80. package/dist/spinner.js.map +1 -0
  81. package/dist/submit.d.ts +21 -0
  82. package/dist/submit.d.ts.map +1 -0
  83. package/dist/submit.js +128 -0
  84. package/dist/submit.js.map +1 -0
  85. package/dist/table.d.ts +42 -0
  86. package/dist/table.d.ts.map +1 -0
  87. package/dist/table.js +181 -0
  88. package/dist/table.js.map +1 -0
  89. package/dist/test-selection.d.ts +2 -0
  90. package/dist/test-selection.d.ts.map +1 -0
  91. package/dist/test-selection.jsx +32 -0
  92. package/dist/test-selection.jsx.map +1 -0
  93. package/dist/tui/App.d.ts +4 -0
  94. package/dist/tui/App.d.ts.map +1 -0
  95. package/dist/tui/App.js +167 -0
  96. package/dist/tui/App.js.map +1 -0
  97. package/dist/tui/App.jsx +281 -0
  98. package/dist/tui/App.jsx.map +1 -0
  99. package/dist/tui/components/BarChart.d.ts +17 -0
  100. package/dist/tui/components/BarChart.d.ts.map +1 -0
  101. package/dist/tui/components/BarChart.js +63 -0
  102. package/dist/tui/components/BarChart.js.map +1 -0
  103. package/dist/tui/components/BarChart.jsx +163 -0
  104. package/dist/tui/components/BarChart.jsx.map +1 -0
  105. package/dist/tui/components/DailyView.d.ts +13 -0
  106. package/dist/tui/components/DailyView.d.ts.map +1 -0
  107. package/dist/tui/components/DailyView.js +32 -0
  108. package/dist/tui/components/DailyView.js.map +1 -0
  109. package/dist/tui/components/DailyView.jsx +84 -0
  110. package/dist/tui/components/DailyView.jsx.map +1 -0
  111. package/dist/tui/components/DateBreakdownPanel.d.ts +7 -0
  112. package/dist/tui/components/DateBreakdownPanel.d.ts.map +1 -0
  113. package/dist/tui/components/DateBreakdownPanel.jsx +61 -0
  114. package/dist/tui/components/DateBreakdownPanel.jsx.map +1 -0
  115. package/dist/tui/components/Footer.d.ts +26 -0
  116. package/dist/tui/components/Footer.d.ts.map +1 -0
  117. package/dist/tui/components/Footer.js +15 -0
  118. package/dist/tui/components/Footer.js.map +1 -0
  119. package/dist/tui/components/Footer.jsx +158 -0
  120. package/dist/tui/components/Footer.jsx.map +1 -0
  121. package/dist/tui/components/Header.d.ts +9 -0
  122. package/dist/tui/components/Header.d.ts.map +1 -0
  123. package/dist/tui/components/Header.js +12 -0
  124. package/dist/tui/components/Header.js.map +1 -0
  125. package/dist/tui/components/Header.jsx +38 -0
  126. package/dist/tui/components/Header.jsx.map +1 -0
  127. package/dist/tui/components/Legend.d.ts +7 -0
  128. package/dist/tui/components/Legend.d.ts.map +1 -0
  129. package/dist/tui/components/Legend.js +9 -0
  130. package/dist/tui/components/Legend.js.map +1 -0
  131. package/dist/tui/components/Legend.jsx +27 -0
  132. package/dist/tui/components/Legend.jsx.map +1 -0
  133. package/dist/tui/components/LoadingSpinner.d.ts +8 -0
  134. package/dist/tui/components/LoadingSpinner.d.ts.map +1 -0
  135. package/dist/tui/components/LoadingSpinner.jsx +62 -0
  136. package/dist/tui/components/LoadingSpinner.jsx.map +1 -0
  137. package/dist/tui/components/ModelListItem.d.ts +11 -0
  138. package/dist/tui/components/ModelListItem.d.ts.map +1 -0
  139. package/dist/tui/components/ModelListItem.js +18 -0
  140. package/dist/tui/components/ModelListItem.js.map +1 -0
  141. package/dist/tui/components/ModelListItem.jsx +17 -0
  142. package/dist/tui/components/ModelListItem.jsx.map +1 -0
  143. package/dist/tui/components/ModelRow.d.ts +13 -0
  144. package/dist/tui/components/ModelRow.d.ts.map +1 -0
  145. package/dist/tui/components/ModelRow.jsx +28 -0
  146. package/dist/tui/components/ModelRow.jsx.map +1 -0
  147. package/dist/tui/components/ModelView.d.ts +13 -0
  148. package/dist/tui/components/ModelView.d.ts.map +1 -0
  149. package/dist/tui/components/ModelView.js +34 -0
  150. package/dist/tui/components/ModelView.js.map +1 -0
  151. package/dist/tui/components/ModelView.jsx +111 -0
  152. package/dist/tui/components/ModelView.jsx.map +1 -0
  153. package/dist/tui/components/OverviewView.d.ts +14 -0
  154. package/dist/tui/components/OverviewView.d.ts.map +1 -0
  155. package/dist/tui/components/OverviewView.js +24 -0
  156. package/dist/tui/components/OverviewView.js.map +1 -0
  157. package/dist/tui/components/OverviewView.jsx +79 -0
  158. package/dist/tui/components/OverviewView.jsx.map +1 -0
  159. package/dist/tui/components/StatsView.d.ts +12 -0
  160. package/dist/tui/components/StatsView.d.ts.map +1 -0
  161. package/dist/tui/components/StatsView.js +43 -0
  162. package/dist/tui/components/StatsView.js.map +1 -0
  163. package/dist/tui/components/StatsView.jsx +180 -0
  164. package/dist/tui/components/StatsView.jsx.map +1 -0
  165. package/dist/tui/components/TokenBreakdown.d.ts +14 -0
  166. package/dist/tui/components/TokenBreakdown.d.ts.map +1 -0
  167. package/dist/tui/components/TokenBreakdown.jsx +27 -0
  168. package/dist/tui/components/TokenBreakdown.jsx.map +1 -0
  169. package/dist/tui/components/index.d.ts +16 -0
  170. package/dist/tui/components/index.d.ts.map +1 -0
  171. package/dist/tui/components/index.js +13 -0
  172. package/dist/tui/components/index.js.map +1 -0
  173. package/dist/tui/config/settings.d.ts +12 -0
  174. package/dist/tui/config/settings.d.ts.map +1 -0
  175. package/dist/tui/config/settings.js +105 -0
  176. package/dist/tui/config/settings.js.map +1 -0
  177. package/dist/tui/config/themes.d.ts +15 -0
  178. package/dist/tui/config/themes.d.ts.map +1 -0
  179. package/dist/tui/config/themes.js +82 -0
  180. package/dist/tui/config/themes.js.map +1 -0
  181. package/dist/tui/hooks/useData.d.ts +17 -0
  182. package/dist/tui/hooks/useData.d.ts.map +1 -0
  183. package/dist/tui/hooks/useData.js +430 -0
  184. package/dist/tui/hooks/useData.js.map +1 -0
  185. package/dist/tui/index.d.ts +4 -0
  186. package/dist/tui/index.d.ts.map +1 -0
  187. package/dist/tui/index.js +8 -0
  188. package/dist/tui/index.js.map +1 -0
  189. package/dist/tui/index.jsx +35 -0
  190. package/dist/tui/index.jsx.map +1 -0
  191. package/dist/tui/types/index.d.ts +133 -0
  192. package/dist/tui/types/index.d.ts.map +1 -0
  193. package/dist/tui/types/index.js +21 -0
  194. package/dist/tui/types/index.js.map +1 -0
  195. package/dist/tui/utils/cleanup.d.ts +22 -0
  196. package/dist/tui/utils/cleanup.d.ts.map +1 -0
  197. package/dist/tui/utils/cleanup.js +59 -0
  198. package/dist/tui/utils/cleanup.js.map +1 -0
  199. package/dist/tui/utils/colors.d.ts +18 -0
  200. package/dist/tui/utils/colors.d.ts.map +1 -0
  201. package/dist/tui/utils/colors.js +55 -0
  202. package/dist/tui/utils/colors.js.map +1 -0
  203. package/dist/tui/utils/format.d.ts +7 -0
  204. package/dist/tui/utils/format.d.ts.map +1 -0
  205. package/dist/tui/utils/format.js +45 -0
  206. package/dist/tui/utils/format.js.map +1 -0
  207. package/dist/tui/utils/responsive.d.ts +5 -0
  208. package/dist/tui/utils/responsive.d.ts.map +1 -0
  209. package/dist/tui/utils/responsive.js +5 -0
  210. package/dist/tui/utils/responsive.js.map +1 -0
  211. package/package.json +64 -0
package/dist/cursor.js ADDED
@@ -0,0 +1,432 @@
1
+ /**
2
+ * Cursor IDE API Client
3
+ * Fetches usage data from Cursor's dashboard API via CSV export
4
+ *
5
+ * API Endpoint: https://cursor.com/api/dashboard/export-usage-events-csv?strategy=tokens
6
+ * Authentication: WorkosCursorSessionToken cookie
7
+ *
8
+ * CSV Format:
9
+ * Date,Model,Input (w/ Cache Write),Input (w/o Cache Write),Cache Read,Output Tokens,Total Tokens,Cost,Cost to you
10
+ */
11
+ import * as fs from "node:fs";
12
+ import * as path from "node:path";
13
+ import * as os from "node:os";
14
+ import { parse as parseCsv } from "csv-parse/sync";
15
+ // ============================================================================
16
+ // Credential Management
17
+ // ============================================================================
18
+ const OLD_CONFIG_DIR = path.join(os.homedir(), ".tokscale");
19
+ const CONFIG_DIR = path.join(os.homedir(), ".config", "tokscale");
20
+ const OLD_CURSOR_CREDENTIALS_FILE = path.join(OLD_CONFIG_DIR, "cursor-credentials.json");
21
+ const CURSOR_CREDENTIALS_FILE = path.join(CONFIG_DIR, "cursor-credentials.json");
22
+ function ensureConfigDir() {
23
+ if (!fs.existsSync(CONFIG_DIR)) {
24
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
25
+ }
26
+ }
27
+ /**
28
+ * Migrate Cursor credentials and cache from old path to new XDG path
29
+ */
30
+ function migrateCursorFromOldPath() {
31
+ try {
32
+ // Migrate cursor credentials
33
+ if (!fs.existsSync(CURSOR_CREDENTIALS_FILE) && fs.existsSync(OLD_CURSOR_CREDENTIALS_FILE)) {
34
+ ensureConfigDir();
35
+ fs.copyFileSync(OLD_CURSOR_CREDENTIALS_FILE, CURSOR_CREDENTIALS_FILE);
36
+ fs.chmodSync(CURSOR_CREDENTIALS_FILE, 0o600);
37
+ fs.unlinkSync(OLD_CURSOR_CREDENTIALS_FILE);
38
+ }
39
+ // Migrate cache directory (handled after CURSOR_CACHE_DIR is defined)
40
+ // Cache migration happens in migrateCursorCacheFromOldPath()
41
+ // Try to remove old config directory if empty
42
+ try {
43
+ fs.rmdirSync(OLD_CONFIG_DIR);
44
+ }
45
+ catch {
46
+ // Directory not empty - ignore
47
+ }
48
+ }
49
+ catch {
50
+ // Migration failed - continue with normal operation
51
+ }
52
+ }
53
+ export function saveCursorCredentials(credentials) {
54
+ ensureConfigDir();
55
+ fs.writeFileSync(CURSOR_CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), {
56
+ encoding: "utf-8",
57
+ mode: 0o600,
58
+ });
59
+ }
60
+ export function loadCursorCredentials() {
61
+ migrateCursorFromOldPath();
62
+ try {
63
+ if (!fs.existsSync(CURSOR_CREDENTIALS_FILE)) {
64
+ return null;
65
+ }
66
+ const data = fs.readFileSync(CURSOR_CREDENTIALS_FILE, "utf-8");
67
+ const parsed = JSON.parse(data);
68
+ if (!parsed.sessionToken) {
69
+ return null;
70
+ }
71
+ return parsed;
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
77
+ export function clearCursorCredentials() {
78
+ try {
79
+ if (fs.existsSync(CURSOR_CREDENTIALS_FILE)) {
80
+ fs.unlinkSync(CURSOR_CREDENTIALS_FILE);
81
+ return true;
82
+ }
83
+ return false;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ export function isCursorLoggedIn() {
90
+ return loadCursorCredentials() !== null;
91
+ }
92
+ // ============================================================================
93
+ // API Client
94
+ // ============================================================================
95
+ const CURSOR_API_BASE = "https://cursor.com";
96
+ const USAGE_CSV_ENDPOINT = `${CURSOR_API_BASE}/api/dashboard/export-usage-events-csv?strategy=tokens`;
97
+ const USAGE_SUMMARY_ENDPOINT = `${CURSOR_API_BASE}/api/usage-summary`;
98
+ /**
99
+ * Build HTTP headers for Cursor API requests
100
+ */
101
+ function buildCursorHeaders(sessionToken) {
102
+ return {
103
+ Accept: "*/*",
104
+ "Accept-Language": "en-US,en;q=0.9",
105
+ Cookie: `WorkosCursorSessionToken=${sessionToken}`,
106
+ Referer: "https://www.cursor.com/settings",
107
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
108
+ };
109
+ }
110
+ /**
111
+ * Validate Cursor session token by hitting the usage-summary endpoint
112
+ */
113
+ export async function validateCursorSession(sessionToken) {
114
+ try {
115
+ const response = await fetch(USAGE_SUMMARY_ENDPOINT, {
116
+ method: "GET",
117
+ headers: buildCursorHeaders(sessionToken),
118
+ });
119
+ if (response.status === 401 || response.status === 403) {
120
+ return { valid: false, error: "Session token expired or invalid" };
121
+ }
122
+ if (!response.ok) {
123
+ return { valid: false, error: `API returned status ${response.status}` };
124
+ }
125
+ const data = await response.json();
126
+ // Check for required fields that indicate valid auth
127
+ if (data.billingCycleStart && data.billingCycleEnd) {
128
+ return { valid: true, membershipType: data.membershipType };
129
+ }
130
+ return { valid: false, error: "Invalid response format" };
131
+ }
132
+ catch (error) {
133
+ return { valid: false, error: error.message };
134
+ }
135
+ }
136
+ /**
137
+ * Fetch usage CSV from Cursor API
138
+ */
139
+ export async function fetchCursorUsageCsv(sessionToken) {
140
+ const response = await fetch(USAGE_CSV_ENDPOINT, {
141
+ method: "GET",
142
+ headers: buildCursorHeaders(sessionToken),
143
+ });
144
+ if (response.status === 401 || response.status === 403) {
145
+ throw new Error("Cursor session expired. Please run 'tokscale cursor login' to re-authenticate.");
146
+ }
147
+ if (!response.ok) {
148
+ throw new Error(`Cursor API returned status ${response.status}`);
149
+ }
150
+ const text = await response.text();
151
+ // Validate it's actually CSV (handle both old and new formats)
152
+ // Old: "Date,Model,..."
153
+ // New: "Date,Kind,Model,..."
154
+ if (!text.startsWith("Date,")) {
155
+ throw new Error("Invalid response from Cursor API - expected CSV format");
156
+ }
157
+ return text;
158
+ }
159
+ // ============================================================================
160
+ // CSV Parsing
161
+ // ============================================================================
162
+ /**
163
+ * Parse cost string (e.g., "$0.50" or "0.50") to number
164
+ */
165
+ function parseCost(costStr) {
166
+ if (!costStr)
167
+ return 0;
168
+ const cleaned = costStr.replace(/[$,]/g, "").trim();
169
+ const value = parseFloat(cleaned);
170
+ return isNaN(value) ? 0 : value;
171
+ }
172
+ /**
173
+ * Infer provider from model name
174
+ */
175
+ function inferProvider(model) {
176
+ const lowerModel = model.toLowerCase();
177
+ if (lowerModel.includes("claude") || lowerModel.includes("sonnet") || lowerModel.includes("opus") || lowerModel.includes("haiku")) {
178
+ return "anthropic";
179
+ }
180
+ if (lowerModel.includes("gpt") || lowerModel.includes("o1") || lowerModel.includes("o3")) {
181
+ return "openai";
182
+ }
183
+ if (lowerModel.includes("gemini")) {
184
+ return "google";
185
+ }
186
+ if (lowerModel.includes("deepseek")) {
187
+ return "deepseek";
188
+ }
189
+ if (lowerModel.includes("llama") || lowerModel.includes("mixtral")) {
190
+ return "meta";
191
+ }
192
+ return "cursor"; // Default provider
193
+ }
194
+ /**
195
+ * Parse Cursor usage CSV into structured rows
196
+ */
197
+ export function parseCursorCsv(csvText) {
198
+ try {
199
+ const records = parseCsv(csvText, {
200
+ columns: true,
201
+ skip_empty_lines: true,
202
+ trim: true,
203
+ relax_column_count: true,
204
+ });
205
+ return records
206
+ .filter((record) => record["Date"] && record["Model"])
207
+ .map((record) => {
208
+ const dateStr = record["Date"] || "";
209
+ const date = new Date(dateStr);
210
+ const isValidDate = !isNaN(date.getTime());
211
+ const dateOnly = isValidDate
212
+ ? date.toISOString().slice(0, 10)
213
+ : dateStr.length >= 10
214
+ ? dateStr.slice(0, 10)
215
+ : dateStr;
216
+ return {
217
+ date: dateOnly,
218
+ timestamp: isValidDate ? date.getTime() : 0,
219
+ model: (record["Model"] || "").trim(),
220
+ inputWithCacheWrite: parseInt(record["Input (w/ Cache Write)"] || "0", 10),
221
+ inputWithoutCacheWrite: parseInt(record["Input (w/o Cache Write)"] || "0", 10),
222
+ cacheRead: parseInt(record["Cache Read"] || "0", 10),
223
+ outputTokens: parseInt(record["Output Tokens"] || "0", 10),
224
+ totalTokens: parseInt(record["Total Tokens"] || "0", 10),
225
+ apiCost: parseCost(record["Cost"] || record["API Cost"] || "0"),
226
+ costToYou: parseCost(record["Cost to you"] || "0"),
227
+ };
228
+ });
229
+ }
230
+ catch (error) {
231
+ throw new Error(`Failed to parse Cursor CSV: ${error.message}`);
232
+ }
233
+ }
234
+ // ============================================================================
235
+ // Data Aggregation (for table display)
236
+ // ============================================================================
237
+ /**
238
+ * Aggregate Cursor usage by model
239
+ */
240
+ export function aggregateCursorByModel(rows) {
241
+ const modelMap = new Map();
242
+ for (const row of rows) {
243
+ const key = row.model;
244
+ const existing = modelMap.get(key);
245
+ // Cache write = inputWithCacheWrite - inputWithoutCacheWrite (tokens written to cache)
246
+ const cacheWrite = Math.max(0, row.inputWithCacheWrite - row.inputWithoutCacheWrite);
247
+ // Input tokens (without cache) = inputWithoutCacheWrite
248
+ const input = row.inputWithoutCacheWrite;
249
+ if (existing) {
250
+ existing.messageCount += 1;
251
+ existing.input += input;
252
+ existing.output += row.outputTokens;
253
+ existing.cacheRead += row.cacheRead;
254
+ existing.cacheWrite += cacheWrite;
255
+ existing.cost += row.costToYou || row.apiCost;
256
+ }
257
+ else {
258
+ modelMap.set(key, {
259
+ source: "cursor",
260
+ model: row.model,
261
+ providerId: inferProvider(row.model),
262
+ messageCount: 1,
263
+ input,
264
+ output: row.outputTokens,
265
+ cacheRead: row.cacheRead,
266
+ cacheWrite,
267
+ reasoning: 0, // Cursor doesn't expose reasoning tokens
268
+ cost: row.costToYou || row.apiCost,
269
+ });
270
+ }
271
+ }
272
+ return Array.from(modelMap.values()).sort((a, b) => b.cost - a.cost);
273
+ }
274
+ // ============================================================================
275
+ // Data Conversion (for graph/native module)
276
+ // ============================================================================
277
+ /**
278
+ * Convert Cursor CSV rows to timestamped messages for graph generation
279
+ */
280
+ export function cursorRowsToMessages(rows) {
281
+ return rows.map((row) => {
282
+ const cacheWrite = Math.max(0, row.inputWithCacheWrite - row.inputWithoutCacheWrite);
283
+ const input = row.inputWithoutCacheWrite;
284
+ return {
285
+ source: "cursor",
286
+ model: row.model,
287
+ providerId: inferProvider(row.model),
288
+ timestamp: row.timestamp,
289
+ input,
290
+ output: row.outputTokens,
291
+ cacheRead: row.cacheRead,
292
+ cacheWrite,
293
+ reasoning: 0,
294
+ cost: row.costToYou || row.apiCost,
295
+ };
296
+ });
297
+ }
298
+ // ============================================================================
299
+ // High-Level API
300
+ // ============================================================================
301
+ /**
302
+ * Fetch and parse Cursor usage data
303
+ * Requires valid credentials to be stored
304
+ */
305
+ export async function readCursorUsage() {
306
+ const credentials = loadCursorCredentials();
307
+ if (!credentials) {
308
+ throw new Error("Cursor not authenticated. Run 'tokscale cursor login' first.");
309
+ }
310
+ const csvText = await fetchCursorUsageCsv(credentials.sessionToken);
311
+ const rows = parseCursorCsv(csvText);
312
+ const byModel = aggregateCursorByModel(rows);
313
+ const messages = cursorRowsToMessages(rows);
314
+ return { rows, byModel, messages };
315
+ }
316
+ /**
317
+ * Get Cursor credentials file path (for debugging)
318
+ */
319
+ export function getCursorCredentialsPath() {
320
+ return CURSOR_CREDENTIALS_FILE;
321
+ }
322
+ // ============================================================================
323
+ // Cache Management (for Rust integration)
324
+ // ============================================================================
325
+ const OLD_CURSOR_CACHE_DIR = path.join(os.homedir(), ".tokscale", "cursor-cache");
326
+ const CURSOR_CACHE_DIR = path.join(CONFIG_DIR, "cursor-cache");
327
+ const CURSOR_CACHE_FILE = path.join(CURSOR_CACHE_DIR, "usage.csv");
328
+ function ensureCacheDir() {
329
+ if (!fs.existsSync(CURSOR_CACHE_DIR)) {
330
+ fs.mkdirSync(CURSOR_CACHE_DIR, { recursive: true, mode: 0o700 });
331
+ }
332
+ }
333
+ /**
334
+ * Migrate cursor cache from old path to new XDG path
335
+ */
336
+ function migrateCursorCacheFromOldPath() {
337
+ try {
338
+ if (!fs.existsSync(CURSOR_CACHE_DIR) && fs.existsSync(OLD_CURSOR_CACHE_DIR)) {
339
+ ensureCacheDir();
340
+ fs.cpSync(OLD_CURSOR_CACHE_DIR, CURSOR_CACHE_DIR, { recursive: true });
341
+ fs.rmSync(OLD_CURSOR_CACHE_DIR, { recursive: true });
342
+ }
343
+ // Try to remove old config directory if empty
344
+ try {
345
+ fs.rmdirSync(OLD_CONFIG_DIR);
346
+ }
347
+ catch {
348
+ // Directory not empty - ignore
349
+ }
350
+ }
351
+ catch {
352
+ // Migration failed - continue with normal operation
353
+ }
354
+ }
355
+ /**
356
+ * Sync Cursor usage data from API to local cache
357
+ * This downloads the CSV and saves it for the Rust module to parse
358
+ */
359
+ export async function syncCursorCache() {
360
+ migrateCursorCacheFromOldPath();
361
+ const credentials = loadCursorCredentials();
362
+ if (!credentials) {
363
+ return { synced: false, rows: 0, error: "Not authenticated" };
364
+ }
365
+ try {
366
+ const csvText = await fetchCursorUsageCsv(credentials.sessionToken);
367
+ ensureCacheDir();
368
+ fs.writeFileSync(CURSOR_CACHE_FILE, csvText, { encoding: "utf-8", mode: 0o600 });
369
+ // Count rows for feedback
370
+ const rows = parseCursorCsv(csvText);
371
+ return { synced: true, rows: rows.length };
372
+ }
373
+ catch (error) {
374
+ return { synced: false, rows: 0, error: error.message };
375
+ }
376
+ }
377
+ /**
378
+ * Get the cache file path
379
+ */
380
+ export function getCursorCachePath() {
381
+ return CURSOR_CACHE_FILE;
382
+ }
383
+ /**
384
+ * Check if cache exists and when it was last updated
385
+ */
386
+ export function getCursorCacheStatus() {
387
+ const exists = fs.existsSync(CURSOR_CACHE_FILE);
388
+ let lastModified;
389
+ if (exists) {
390
+ try {
391
+ const stats = fs.statSync(CURSOR_CACHE_FILE);
392
+ lastModified = stats.mtime;
393
+ }
394
+ catch {
395
+ // Ignore stat errors
396
+ }
397
+ }
398
+ return { exists, lastModified, path: CURSOR_CACHE_FILE };
399
+ }
400
+ export function readCursorMessagesFromCache() {
401
+ if (!fs.existsSync(CURSOR_CACHE_FILE)) {
402
+ return [];
403
+ }
404
+ try {
405
+ const csvText = fs.readFileSync(CURSOR_CACHE_FILE, "utf-8");
406
+ const rows = parseCursorCsv(csvText);
407
+ return rows.map((row) => {
408
+ const cacheWrite = Math.max(0, row.inputWithCacheWrite - row.inputWithoutCacheWrite);
409
+ const input = row.inputWithoutCacheWrite;
410
+ return {
411
+ source: "cursor",
412
+ modelId: row.model,
413
+ providerId: inferProvider(row.model),
414
+ sessionId: `cursor-${row.date}-${row.model}`,
415
+ timestamp: row.timestamp,
416
+ date: row.date,
417
+ tokens: {
418
+ input,
419
+ output: row.outputTokens,
420
+ cacheRead: row.cacheRead,
421
+ cacheWrite,
422
+ reasoning: 0,
423
+ },
424
+ cost: row.costToYou || row.apiCost,
425
+ };
426
+ });
427
+ }
428
+ catch {
429
+ return [];
430
+ }
431
+ }
432
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Gemini CLI data reader
3
+ * Reads from ~/.gemini/tmp/{projectHash}/chats/session-*.json
4
+ */
5
+ export interface GeminiUsageData {
6
+ source: "gemini";
7
+ model: string;
8
+ messageCount: number;
9
+ input: number;
10
+ output: number;
11
+ cached: number;
12
+ thoughts: number;
13
+ tool: number;
14
+ }
15
+ /**
16
+ * Individual Gemini message with timestamp for graph generation
17
+ */
18
+ export interface GeminiMessageWithTimestamp {
19
+ source: "gemini";
20
+ model: string;
21
+ timestamp: number;
22
+ tokens: {
23
+ input: number;
24
+ output: number;
25
+ cached: number;
26
+ thoughts: number;
27
+ tool: number;
28
+ };
29
+ }
30
+ export declare function getGeminiBasePath(): string;
31
+ export declare function readGeminiSessions(): GeminiUsageData[];
32
+ /**
33
+ * Read Gemini messages with timestamp for graph generation
34
+ */
35
+ export declare function readGeminiMessagesWithTimestamp(): GeminiMessageWithTimestamp[];
36
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../src/gemini.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AA0BD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,kBAAkB,IAAI,eAAe,EAAE,CAgEtD;AAED;;GAEG;AACH,wBAAgB,+BAA+B,IAAI,0BAA0B,EAAE,CA2D9E"}
package/dist/gemini.js ADDED
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Gemini CLI data reader
3
+ * Reads from ~/.gemini/tmp/{projectHash}/chats/session-*.json
4
+ */
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ import * as os from "node:os";
8
+ export function getGeminiBasePath() {
9
+ return path.join(os.homedir(), ".gemini");
10
+ }
11
+ export function readGeminiSessions() {
12
+ const basePath = getGeminiBasePath();
13
+ const tmpPath = path.join(basePath, "tmp");
14
+ if (!fs.existsSync(tmpPath)) {
15
+ return [];
16
+ }
17
+ const modelUsage = new Map();
18
+ // Find all project directories
19
+ const projectDirs = fs
20
+ .readdirSync(tmpPath, { withFileTypes: true })
21
+ .filter((d) => d.isDirectory())
22
+ .map((d) => path.join(tmpPath, d.name));
23
+ for (const projectDir of projectDirs) {
24
+ const chatsDir = path.join(projectDir, "chats");
25
+ if (!fs.existsSync(chatsDir))
26
+ continue;
27
+ // Find all session JSON files
28
+ const sessionFiles = fs
29
+ .readdirSync(chatsDir)
30
+ .filter((f) => f.startsWith("session-") && f.endsWith(".json"));
31
+ for (const sessionFile of sessionFiles) {
32
+ try {
33
+ const content = fs.readFileSync(path.join(chatsDir, sessionFile), "utf-8");
34
+ const session = JSON.parse(content);
35
+ for (const msg of session.messages) {
36
+ // Only process gemini messages with token data
37
+ if (msg.type !== "gemini" || !msg.tokens || !msg.model)
38
+ continue;
39
+ const model = msg.model;
40
+ let usage = modelUsage.get(model);
41
+ if (!usage) {
42
+ usage = {
43
+ source: "gemini",
44
+ model,
45
+ messageCount: 0,
46
+ input: 0,
47
+ output: 0,
48
+ cached: 0,
49
+ thoughts: 0,
50
+ tool: 0,
51
+ };
52
+ modelUsage.set(model, usage);
53
+ }
54
+ usage.messageCount++;
55
+ usage.input += msg.tokens.input || 0;
56
+ usage.output += msg.tokens.output || 0;
57
+ usage.cached += msg.tokens.cached || 0;
58
+ usage.thoughts += msg.tokens.thoughts || 0;
59
+ usage.tool += msg.tokens.tool || 0;
60
+ }
61
+ }
62
+ catch {
63
+ // Skip malformed files
64
+ }
65
+ }
66
+ }
67
+ return Array.from(modelUsage.values());
68
+ }
69
+ /**
70
+ * Read Gemini messages with timestamp for graph generation
71
+ */
72
+ export function readGeminiMessagesWithTimestamp() {
73
+ const basePath = getGeminiBasePath();
74
+ const tmpPath = path.join(basePath, "tmp");
75
+ if (!fs.existsSync(tmpPath)) {
76
+ return [];
77
+ }
78
+ const messages = [];
79
+ // Find all project directories
80
+ const projectDirs = fs
81
+ .readdirSync(tmpPath, { withFileTypes: true })
82
+ .filter((d) => d.isDirectory())
83
+ .map((d) => path.join(tmpPath, d.name));
84
+ for (const projectDir of projectDirs) {
85
+ const chatsDir = path.join(projectDir, "chats");
86
+ if (!fs.existsSync(chatsDir))
87
+ continue;
88
+ // Find all session JSON files
89
+ const sessionFiles = fs
90
+ .readdirSync(chatsDir)
91
+ .filter((f) => f.startsWith("session-") && f.endsWith(".json"));
92
+ for (const sessionFile of sessionFiles) {
93
+ try {
94
+ const content = fs.readFileSync(path.join(chatsDir, sessionFile), "utf-8");
95
+ const session = JSON.parse(content);
96
+ for (const msg of session.messages) {
97
+ // Only process gemini messages with token data and timestamp
98
+ if (msg.type !== "gemini" || !msg.tokens || !msg.model || !msg.timestamp)
99
+ continue;
100
+ const timestamp = new Date(msg.timestamp).getTime();
101
+ // Skip invalid timestamps
102
+ if (isNaN(timestamp))
103
+ continue;
104
+ messages.push({
105
+ source: "gemini",
106
+ model: msg.model,
107
+ timestamp,
108
+ tokens: {
109
+ input: msg.tokens.input || 0,
110
+ output: msg.tokens.output || 0,
111
+ cached: msg.tokens.cached || 0,
112
+ thoughts: msg.tokens.thoughts || 0,
113
+ tool: msg.tokens.tool || 0,
114
+ },
115
+ });
116
+ }
117
+ }
118
+ catch {
119
+ // Skip malformed files
120
+ }
121
+ }
122
+ }
123
+ return messages;
124
+ }
125
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../src/gemini.ts"],"names":[],"mappings":"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"}