codex-token-tracker 0.1.0 → 0.2.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.
- package/CHANGELOG.md +58 -0
- package/README.md +40 -7
- package/dist/cli.js +709 -180
- package/dist/main.js +393 -81
- package/dist/preload.js +2 -0
- package/dist/renderer/renderer.js +131 -4
- package/dist/renderer/styles.css +30 -0
- package/package.json +17 -14
package/dist/cli.js
CHANGED
|
@@ -67,6 +67,11 @@ function normalizeModelName(model) {
|
|
|
67
67
|
m = m.replace(/-preview$/, "");
|
|
68
68
|
return m;
|
|
69
69
|
}
|
|
70
|
+
function isOpenAIModel(model) {
|
|
71
|
+
const m = normalizeModelName(model);
|
|
72
|
+
if (m === "unknown") return true;
|
|
73
|
+
return /^(gpt[-.]|chatgpt|chat-latest|o[1-9](?:[-.]|$)|codex|text-|davinci|babbage|ada|curie)/.test(m);
|
|
74
|
+
}
|
|
70
75
|
function tierOf(name) {
|
|
71
76
|
for (const t2 of TIERS) if (name.split("-").includes(t2)) return t2;
|
|
72
77
|
return null;
|
|
@@ -106,31 +111,65 @@ function resolvePrice(model, overrides) {
|
|
|
106
111
|
}
|
|
107
112
|
function computeCost(u, p) {
|
|
108
113
|
const input = u.input ?? 0;
|
|
114
|
+
const rate = p.long && input > p.long.threshold ? p.long : p;
|
|
109
115
|
const cached = Math.min(u.cached ?? 0, input);
|
|
110
116
|
const cacheWrite = Math.min(u.cacheWrite ?? 0, Math.max(0, input - cached));
|
|
111
117
|
const fresh = Math.max(0, input - cached - cacheWrite);
|
|
112
118
|
const output = u.output ?? 0;
|
|
113
|
-
const cost = fresh *
|
|
119
|
+
const cost = fresh * rate.input + cached * rate.cachedInput + cacheWrite * (p.cacheWrite ?? rate.input) + output * rate.output;
|
|
114
120
|
return cost / 1e6;
|
|
115
121
|
}
|
|
116
|
-
var DEFAULT_PRICING, FALLBACK_PRICE_KEY, TIERS;
|
|
122
|
+
var LONG_CONTEXT_THRESHOLD, long, DEFAULT_PRICING, FALLBACK_PRICE_KEY, TIERS;
|
|
117
123
|
var init_pricing = __esm({
|
|
118
124
|
"../shared/src/pricing.ts"() {
|
|
119
125
|
"use strict";
|
|
126
|
+
LONG_CONTEXT_THRESHOLD = 272e3;
|
|
127
|
+
long = (threshold, input, cachedInput, output) => ({
|
|
128
|
+
threshold,
|
|
129
|
+
input,
|
|
130
|
+
cachedInput,
|
|
131
|
+
output
|
|
132
|
+
});
|
|
120
133
|
DEFAULT_PRICING = {
|
|
121
|
-
// GPT-5.
|
|
134
|
+
// GPT-5.6 family
|
|
135
|
+
"gpt-5.6-sol": { input: 4, cachedInput: 0.4, output: 20, long: long(LONG_CONTEXT_THRESHOLD, 8, 0.8, 30) },
|
|
136
|
+
"gpt-5.6-sol-codex": { input: 4, cachedInput: 0.4, output: 20, long: long(LONG_CONTEXT_THRESHOLD, 8, 0.8, 30) },
|
|
137
|
+
"gpt-5.6-terra": { input: 2, cachedInput: 0.2, output: 12, long: long(LONG_CONTEXT_THRESHOLD, 4, 0.4, 18) },
|
|
138
|
+
"gpt-5.6-terra-codex": { input: 2, cachedInput: 0.2, output: 12, long: long(LONG_CONTEXT_THRESHOLD, 4, 0.4, 18) },
|
|
139
|
+
"gpt-5.6-luna": { input: 0.2, cachedInput: 0.02, output: 1.2, long: long(LONG_CONTEXT_THRESHOLD, 0.4, 0.04, 1.8) },
|
|
140
|
+
"gpt-5.6-cyber": { input: 12.5, cachedInput: 1.25, output: 75 },
|
|
141
|
+
// GPT-5.5 family
|
|
142
|
+
"gpt-5.5": { input: 5, cachedInput: 0.5, output: 30, long: long(LONG_CONTEXT_THRESHOLD, 10, 1, 45) },
|
|
143
|
+
"gpt-5.5-codex": { input: 5, cachedInput: 0.5, output: 30, long: long(LONG_CONTEXT_THRESHOLD, 10, 1, 45) },
|
|
144
|
+
"gpt-5.5-pro": { input: 30, cachedInput: 30, output: 180, long: long(LONG_CONTEXT_THRESHOLD, 60, 60, 270) },
|
|
145
|
+
"gpt-5.5-cyber": { input: 12.5, cachedInput: 1.25, output: 75 },
|
|
146
|
+
// GPT-5.4 family
|
|
147
|
+
"gpt-5.4": { input: 2.5, cachedInput: 0.25, output: 15, long: long(LONG_CONTEXT_THRESHOLD, 5, 0.5, 22.5) },
|
|
148
|
+
"gpt-5.4-codex": { input: 2.5, cachedInput: 0.25, output: 15, long: long(LONG_CONTEXT_THRESHOLD, 5, 0.5, 22.5) },
|
|
149
|
+
"gpt-5.4-mini": { input: 0.75, cachedInput: 0.075, output: 4.5 },
|
|
150
|
+
"gpt-5.4-nano": { input: 0.2, cachedInput: 0.02, output: 1.25 },
|
|
151
|
+
"gpt-5.4-pro": { input: 30, cachedInput: 30, output: 180, long: long(LONG_CONTEXT_THRESHOLD, 60, 60, 270) },
|
|
152
|
+
// GPT-5.3 family (Codex-only release)
|
|
153
|
+
"gpt-5.3": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
154
|
+
"gpt-5.3-codex": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
155
|
+
// GPT-5.2 family
|
|
156
|
+
"gpt-5.2": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
157
|
+
"gpt-5.2-codex": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
158
|
+
"gpt-5.2-pro": { input: 21, cachedInput: 21, output: 168 },
|
|
159
|
+
// GPT-5.1 family
|
|
160
|
+
"gpt-5.1": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
161
|
+
"gpt-5.1-codex": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
162
|
+
"gpt-5.1-codex-max": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
163
|
+
"gpt-5.1-codex-mini": { input: 0.25, cachedInput: 0.025, output: 2 },
|
|
164
|
+
// GPT-5 family
|
|
122
165
|
"gpt-5": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
123
166
|
"gpt-5-codex": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
124
167
|
"gpt-5-mini": { input: 0.25, cachedInput: 0.025, output: 2 },
|
|
125
168
|
"gpt-5-nano": { input: 0.05, cachedInput: 5e-3, output: 0.4 },
|
|
126
169
|
"gpt-5-pro": { input: 15, cachedInput: 15, output: 120 },
|
|
127
|
-
"gpt-5
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
"gpt-5.1-codex-mini": { input: 0.25, cachedInput: 0.025, output: 2 },
|
|
131
|
-
"gpt-5.2": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
132
|
-
"gpt-5.2-codex": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
133
|
-
"gpt-5.2-pro": { input: 21, cachedInput: 21, output: 168 },
|
|
170
|
+
"gpt-5-search-api": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
171
|
+
// ChatGPT-tuned endpoint
|
|
172
|
+
"chat-latest": { input: 5, cachedInput: 0.5, output: 30 },
|
|
134
173
|
// GPT-4.1 / 4o family
|
|
135
174
|
"gpt-4.1": { input: 2, cachedInput: 0.5, output: 8 },
|
|
136
175
|
"gpt-4.1-mini": { input: 0.4, cachedInput: 0.1, output: 1.6 },
|
|
@@ -146,7 +185,7 @@ var init_pricing = __esm({
|
|
|
146
185
|
"o4-mini": { input: 1.1, cachedInput: 0.275, output: 4.4 },
|
|
147
186
|
"codex-mini-latest": { input: 1.5, cachedInput: 0.375, output: 6 }
|
|
148
187
|
};
|
|
149
|
-
FALLBACK_PRICE_KEY = "gpt-5.
|
|
188
|
+
FALLBACK_PRICE_KEY = "gpt-5.3-codex";
|
|
150
189
|
TIERS = ["mini", "nano", "pro"];
|
|
151
190
|
}
|
|
152
191
|
});
|
|
@@ -339,14 +378,14 @@ function createSessionParser(fallbackSessionId) {
|
|
|
339
378
|
if (ts2 > lastActivityAt) lastActivityAt = ts2;
|
|
340
379
|
}
|
|
341
380
|
function onTokenCount(ts2, payload) {
|
|
342
|
-
const
|
|
381
|
+
const info2 = payload.info;
|
|
343
382
|
let cumulative = null;
|
|
344
383
|
let last = null;
|
|
345
|
-
if (
|
|
346
|
-
cumulative = toUsage(
|
|
347
|
-
last = toUsage(
|
|
348
|
-
if (typeof
|
|
349
|
-
} else if (
|
|
384
|
+
if (info2 && typeof info2 === "object") {
|
|
385
|
+
cumulative = toUsage(info2.total_token_usage);
|
|
386
|
+
last = toUsage(info2.last_token_usage);
|
|
387
|
+
if (typeof info2.model_context_window === "number") contextWindow = info2.model_context_window;
|
|
388
|
+
} else if (info2 === void 0) {
|
|
350
389
|
cumulative = toUsage(payload);
|
|
351
390
|
}
|
|
352
391
|
const rl = payload.rate_limits;
|
|
@@ -1101,11 +1140,11 @@ var init_src = __esm({
|
|
|
1101
1140
|
|
|
1102
1141
|
// src/core/config.ts
|
|
1103
1142
|
function configDir() {
|
|
1104
|
-
return process.env.CODEX_TRACKER_HOME ||
|
|
1143
|
+
return process.env.CODEX_TRACKER_HOME || import_node_path2.default.join(import_node_os2.default.homedir(), ".codex-tracker");
|
|
1105
1144
|
}
|
|
1106
1145
|
function readJson(file, fallback) {
|
|
1107
1146
|
try {
|
|
1108
|
-
const raw =
|
|
1147
|
+
const raw = import_node_fs2.default.readFileSync(file, "utf8");
|
|
1109
1148
|
const parsed = JSON.parse(raw);
|
|
1110
1149
|
return parsed && typeof parsed === "object" ? parsed : fallback;
|
|
1111
1150
|
} catch {
|
|
@@ -1113,13 +1152,13 @@ function readJson(file, fallback) {
|
|
|
1113
1152
|
}
|
|
1114
1153
|
}
|
|
1115
1154
|
function writeJsonAtomic(file, value) {
|
|
1116
|
-
|
|
1155
|
+
import_node_fs2.default.mkdirSync(import_node_path2.default.dirname(file), { recursive: true });
|
|
1117
1156
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
1118
|
-
|
|
1119
|
-
|
|
1157
|
+
import_node_fs2.default.writeFileSync(tmp, JSON.stringify(value, null, 2), { mode: 384 });
|
|
1158
|
+
import_node_fs2.default.renameSync(tmp, file);
|
|
1120
1159
|
}
|
|
1121
1160
|
function configPath() {
|
|
1122
|
-
return
|
|
1161
|
+
return import_node_path2.default.join(configDir(), "config.json");
|
|
1123
1162
|
}
|
|
1124
1163
|
function parseBool(raw) {
|
|
1125
1164
|
return ["1", "true", "yes", "on"].includes(raw.trim().toLowerCase());
|
|
@@ -1154,6 +1193,7 @@ function loadConfig() {
|
|
|
1154
1193
|
cfg.sources = normalizeSources(stored.sources);
|
|
1155
1194
|
cfg.trackAllProviders = stored.trackAllProviders === true;
|
|
1156
1195
|
cfg.liveRateLimits = stored.liveRateLimits !== false;
|
|
1196
|
+
cfg.checkUpdates = stored.checkUpdates !== false;
|
|
1157
1197
|
if (!(cfg.usageRefreshSec >= 15)) cfg.usageRefreshSec = DEFAULT_CONFIG.usageRefreshSec;
|
|
1158
1198
|
if (!["auto", "en", "zh"].includes(cfg.language)) cfg.language = "auto";
|
|
1159
1199
|
if (!(cfg.uploadIntervalSec >= 10)) cfg.uploadIntervalSec = DEFAULT_CONFIG.uploadIntervalSec;
|
|
@@ -1183,6 +1223,7 @@ function coerceConfigValue(key, raw) {
|
|
|
1183
1223
|
case "launchAtLogin":
|
|
1184
1224
|
case "trackAllProviders":
|
|
1185
1225
|
case "liveRateLimits":
|
|
1226
|
+
case "checkUpdates":
|
|
1186
1227
|
return parseBool(raw);
|
|
1187
1228
|
case "extraSessionDirs": {
|
|
1188
1229
|
const trimmed = raw.trim();
|
|
@@ -1224,7 +1265,7 @@ function coerceConfigValue(key, raw) {
|
|
|
1224
1265
|
}
|
|
1225
1266
|
}
|
|
1226
1267
|
function statePath() {
|
|
1227
|
-
return
|
|
1268
|
+
return import_node_path2.default.join(configDir(), "state.json");
|
|
1228
1269
|
}
|
|
1229
1270
|
function loadState() {
|
|
1230
1271
|
const s = readJson(statePath(), {});
|
|
@@ -1241,7 +1282,7 @@ function clearState() {
|
|
|
1241
1282
|
saveState({ pushedBuckets: {}, pushedSessions: {}, lastUploadAt: null });
|
|
1242
1283
|
}
|
|
1243
1284
|
function pricingPath() {
|
|
1244
|
-
return
|
|
1285
|
+
return import_node_path2.default.join(configDir(), "pricing.json");
|
|
1245
1286
|
}
|
|
1246
1287
|
function loadPricingOverrides() {
|
|
1247
1288
|
const raw = readJson(pricingPath(), {});
|
|
@@ -1259,13 +1300,13 @@ function loadPricingOverrides() {
|
|
|
1259
1300
|
}
|
|
1260
1301
|
return Object.keys(out).length ? out : void 0;
|
|
1261
1302
|
}
|
|
1262
|
-
var
|
|
1303
|
+
var import_node_fs2, import_node_os2, import_node_path2, DEFAULT_DASHBOARD_URL, SOURCE_FORMATS, DEFAULT_SOURCES, SOURCE_IDS, DEFAULT_CONFIG, EDITABLE_KEYS, AGENT_NAME_RE;
|
|
1263
1304
|
var init_config = __esm({
|
|
1264
1305
|
"src/core/config.ts"() {
|
|
1265
1306
|
"use strict";
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1307
|
+
import_node_fs2 = __toESM(require("node:fs"));
|
|
1308
|
+
import_node_os2 = __toESM(require("node:os"));
|
|
1309
|
+
import_node_path2 = __toESM(require("node:path"));
|
|
1269
1310
|
DEFAULT_DASHBOARD_URL = "https://codex.chenli.dev";
|
|
1270
1311
|
SOURCE_FORMATS = ["codex", "pi", "generic", "opencode", "cline"];
|
|
1271
1312
|
DEFAULT_SOURCES = { codex: true, pi: true, hermes: true, opencode: true, cline: true, roo: true, kilo: true };
|
|
@@ -1285,7 +1326,8 @@ var init_config = __esm({
|
|
|
1285
1326
|
sources: { ...DEFAULT_SOURCES },
|
|
1286
1327
|
trackAllProviders: false,
|
|
1287
1328
|
liveRateLimits: true,
|
|
1288
|
-
usageRefreshSec: 60
|
|
1329
|
+
usageRefreshSec: 60,
|
|
1330
|
+
checkUpdates: true
|
|
1289
1331
|
};
|
|
1290
1332
|
EDITABLE_KEYS = [
|
|
1291
1333
|
"dashboardUrl",
|
|
@@ -1298,7 +1340,8 @@ var init_config = __esm({
|
|
|
1298
1340
|
"sources",
|
|
1299
1341
|
"trackAllProviders",
|
|
1300
1342
|
"liveRateLimits",
|
|
1301
|
-
"usageRefreshSec"
|
|
1343
|
+
"usageRefreshSec",
|
|
1344
|
+
"checkUpdates"
|
|
1302
1345
|
];
|
|
1303
1346
|
AGENT_NAME_RE = /^[a-z0-9][a-z0-9_.-]{0,31}$/;
|
|
1304
1347
|
}
|
|
@@ -1380,8 +1423,20 @@ var init_en = __esm({
|
|
|
1380
1423
|
never: "never",
|
|
1381
1424
|
local: "Local",
|
|
1382
1425
|
remote: "Other devices",
|
|
1426
|
+
// updates
|
|
1427
|
+
update: "Update",
|
|
1428
|
+
updateAvailable: "Update to v{version}",
|
|
1429
|
+
updateNewVersion: "Version {version} is available (you have v{current})",
|
|
1430
|
+
updateUpToDate: "Up to date",
|
|
1431
|
+
updateChecking: "Checking\u2026",
|
|
1432
|
+
updateInstalling: "Installing\u2026",
|
|
1433
|
+
updateInstalled: "Updated to v{version} \u2014 restart to apply",
|
|
1434
|
+
updateFailed: "Update failed \u2014 run this yourself:",
|
|
1435
|
+
updateCheckFailed: "Update check failed: {message}",
|
|
1436
|
+
checkForUpdates: "Check for updates",
|
|
1437
|
+
releaseNotes: "Release notes",
|
|
1383
1438
|
// CLI
|
|
1384
|
-
cliUsage: `Usage: codex-tracker [command] [options]
|
|
1439
|
+
cliUsage: `Usage: codex-token-tracker [command] [options] (alias: codex-tracker)
|
|
1385
1440
|
|
|
1386
1441
|
Commands:
|
|
1387
1442
|
(none) Start the menu bar app (falls back to agent mode without a display)
|
|
@@ -1392,10 +1447,12 @@ Commands:
|
|
|
1392
1447
|
logout Disconnect this device
|
|
1393
1448
|
status Print today's usage, live session and rate limits
|
|
1394
1449
|
paths Show detected session directories (Codex, pi, OpenCode, Cline/Roo/Kilo, Hermes, custom)
|
|
1450
|
+
update Install the newest published version (--check only reports)
|
|
1395
1451
|
config get|set Read or change settings (config set uploadIntervalSec 30, config set sources.pi false)
|
|
1396
1452
|
lang <en|zh|auto> Set the display language
|
|
1397
1453
|
|
|
1398
1454
|
Options:
|
|
1455
|
+
--check update: report the newest version without installing it
|
|
1399
1456
|
--dashboard <url> Dashboard URL (self-hosted)
|
|
1400
1457
|
--background Start the menu bar app detached and return
|
|
1401
1458
|
--version, -v Print version
|
|
@@ -1404,6 +1461,8 @@ Options:
|
|
|
1404
1461
|
cliUnknownCommand: "Unknown command: {command}",
|
|
1405
1462
|
cliNoDisplay: "No display detected \u2014 starting agent mode (run `codex-tracker menubar` on a desktop).",
|
|
1406
1463
|
cliNoElectron: "Electron is not installed (optional dependency) \u2014 starting agent mode.",
|
|
1464
|
+
cliDownloadingElectron: "Electron runtime not found \u2014 downloading it now (about 100 MB, one time)\u2026",
|
|
1465
|
+
cliElectronDownloadFailed: "Electron download failed. Retry with `npm install -g codex-token-tracker --allow-scripts=electron` or `npm rebuild -g electron`; the headless `codex-tracker agent` works without it.",
|
|
1407
1466
|
cliStartingMenubar: "Starting menu bar app\u2026",
|
|
1408
1467
|
cliLoginStart: "Connecting this device to {dashboard}",
|
|
1409
1468
|
cliLoginCode: "Your code: {code}",
|
|
@@ -1435,7 +1494,13 @@ Options:
|
|
|
1435
1494
|
cliConfigSet: "Set {key} = {value}",
|
|
1436
1495
|
cliConfigUnknownKey: "Unknown config key: {key}. Keys: {keys}",
|
|
1437
1496
|
cliLangSet: "Language set to {lang}",
|
|
1438
|
-
cliConfigDir: "Config: {dir}"
|
|
1497
|
+
cliConfigDir: "Config: {dir}",
|
|
1498
|
+
cliUpdateLatest: "codex-token-tracker {version} is the latest version.",
|
|
1499
|
+
cliUpdateAvailable: "Update available: {current} \u2192 {latest}",
|
|
1500
|
+
cliUpdateRunning: "Running: {command}",
|
|
1501
|
+
cliUpdateDone: "Updated to {version}. Restart the menu bar app (or rerun the CLI) to use it.",
|
|
1502
|
+
cliUpdateFailed: "Update failed (exit {code}). Run it yourself: {command}",
|
|
1503
|
+
cliUpdateCheckFailed: "Could not reach the npm registry: {message}"
|
|
1439
1504
|
};
|
|
1440
1505
|
}
|
|
1441
1506
|
});
|
|
@@ -1515,7 +1580,18 @@ var init_zh = __esm({
|
|
|
1515
1580
|
never: "\u4ECE\u672A",
|
|
1516
1581
|
local: "\u672C\u673A",
|
|
1517
1582
|
remote: "\u5176\u4ED6\u8BBE\u5907",
|
|
1518
|
-
|
|
1583
|
+
update: "\u66F4\u65B0",
|
|
1584
|
+
updateAvailable: "\u66F4\u65B0\u5230 v{version}",
|
|
1585
|
+
updateNewVersion: "\u6709\u65B0\u7248\u672C {version}\uFF08\u5F53\u524D v{current}\uFF09",
|
|
1586
|
+
updateUpToDate: "\u5DF2\u662F\u6700\u65B0\u7248\u672C",
|
|
1587
|
+
updateChecking: "\u68C0\u67E5\u4E2D\u2026",
|
|
1588
|
+
updateInstalling: "\u5B89\u88C5\u4E2D\u2026",
|
|
1589
|
+
updateInstalled: "\u5DF2\u66F4\u65B0\u5230 v{version} \u2014 \u91CD\u542F\u540E\u751F\u6548",
|
|
1590
|
+
updateFailed: "\u66F4\u65B0\u5931\u8D25 \u2014 \u8BF7\u624B\u52A8\u6267\u884C\uFF1A",
|
|
1591
|
+
updateCheckFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25\uFF1A{message}",
|
|
1592
|
+
checkForUpdates: "\u68C0\u67E5\u66F4\u65B0",
|
|
1593
|
+
releaseNotes: "\u66F4\u65B0\u65E5\u5FD7",
|
|
1594
|
+
cliUsage: `\u7528\u6CD5\uFF1Acodex-token-tracker [\u547D\u4EE4] [\u9009\u9879] \uFF08\u522B\u540D\uFF1Acodex-tracker\uFF09
|
|
1519
1595
|
|
|
1520
1596
|
\u547D\u4EE4\uFF1A
|
|
1521
1597
|
(\u65E0) \u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\uFF08\u65E0\u663E\u793A\u73AF\u5883\u65F6\u81EA\u52A8\u5207\u6362\u4E3A agent \u6A21\u5F0F\uFF09
|
|
@@ -1526,10 +1602,12 @@ var init_zh = __esm({
|
|
|
1526
1602
|
logout \u65AD\u5F00\u672C\u8BBE\u5907
|
|
1527
1603
|
status \u6253\u5370\u4ECA\u65E5\u7528\u91CF\u3001\u5B9E\u65F6\u4F1A\u8BDD\u4E0E\u9650\u989D
|
|
1528
1604
|
paths \u663E\u793A\u68C0\u6D4B\u5230\u7684\u4F1A\u8BDD\u76EE\u5F55\uFF08Codex\u3001pi\u3001OpenCode\u3001Cline/Roo/Kilo\u3001Hermes\u3001\u81EA\u5B9A\u4E49\uFF09
|
|
1605
|
+
update \u5B89\u88C5\u6700\u65B0\u53D1\u5E03\u7248\u672C\uFF08--check \u53EA\u68C0\u67E5\u4E0D\u5B89\u88C5\uFF09
|
|
1529
1606
|
config get|set \u8BFB\u53D6\u6216\u4FEE\u6539\u8BBE\u7F6E\uFF08config set uploadIntervalSec 30\uFF0Cconfig set sources.pi false\uFF09
|
|
1530
1607
|
lang <en|zh|auto> \u8BBE\u7F6E\u663E\u793A\u8BED\u8A00
|
|
1531
1608
|
|
|
1532
1609
|
\u9009\u9879\uFF1A
|
|
1610
|
+
--check update\uFF1A\u53EA\u62A5\u544A\u6700\u65B0\u7248\u672C\uFF0C\u4E0D\u6267\u884C\u5B89\u88C5
|
|
1533
1611
|
--dashboard <url> \u4EEA\u8868\u76D8\u5730\u5740\uFF08\u81EA\u6258\u7BA1\uFF09
|
|
1534
1612
|
--background \u4EE5\u540E\u53F0\u65B9\u5F0F\u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\u5E76\u7ACB\u5373\u8FD4\u56DE
|
|
1535
1613
|
--version, -v \u663E\u793A\u7248\u672C
|
|
@@ -1538,6 +1616,8 @@ var init_zh = __esm({
|
|
|
1538
1616
|
cliUnknownCommand: "\u672A\u77E5\u547D\u4EE4\uFF1A{command}",
|
|
1539
1617
|
cliNoDisplay: "\u672A\u68C0\u6D4B\u5230\u663E\u793A\u73AF\u5883 \u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\uFF08\u684C\u9762\u73AF\u5883\u8BF7\u8FD0\u884C `codex-tracker menubar`\uFF09\u3002",
|
|
1540
1618
|
cliNoElectron: "\u672A\u5B89\u88C5 Electron\uFF08\u53EF\u9009\u4F9D\u8D56\uFF09\u2014 \u4EE5 agent \u6A21\u5F0F\u542F\u52A8\u3002",
|
|
1619
|
+
cliDownloadingElectron: "\u672A\u627E\u5230 Electron \u8FD0\u884C\u65F6\uFF0C\u6B63\u5728\u4E0B\u8F7D\uFF08\u7EA6 100 MB\uFF0C\u4EC5\u9700\u4E00\u6B21\uFF09\u2026",
|
|
1620
|
+
cliElectronDownloadFailed: "Electron \u4E0B\u8F7D\u5931\u8D25\u3002\u53EF\u91CD\u8BD5 `npm install -g codex-token-tracker --allow-scripts=electron` \u6216 `npm rebuild -g electron`\uFF1B\u65E0\u754C\u9762\u7684 `codex-tracker agent` \u65E0\u9700 Electron\u3002",
|
|
1541
1621
|
cliStartingMenubar: "\u6B63\u5728\u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\u2026",
|
|
1542
1622
|
cliLoginStart: "\u6B63\u5728\u5C06\u672C\u8BBE\u5907\u8FDE\u63A5\u5230 {dashboard}",
|
|
1543
1623
|
cliLoginCode: "\u4F60\u7684\u4EE3\u7801\uFF1A{code}",
|
|
@@ -1569,7 +1649,13 @@ var init_zh = __esm({
|
|
|
1569
1649
|
cliConfigSet: "\u5DF2\u8BBE\u7F6E {key} = {value}",
|
|
1570
1650
|
cliConfigUnknownKey: "\u672A\u77E5\u914D\u7F6E\u9879\uFF1A{key}\u3002\u53EF\u7528\uFF1A{keys}",
|
|
1571
1651
|
cliLangSet: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A {lang}",
|
|
1572
|
-
cliConfigDir: "\u914D\u7F6E\u76EE\u5F55\uFF1A{dir}"
|
|
1652
|
+
cliConfigDir: "\u914D\u7F6E\u76EE\u5F55\uFF1A{dir}",
|
|
1653
|
+
cliUpdateLatest: "codex-token-tracker {version} \u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
|
|
1654
|
+
cliUpdateAvailable: "\u53D1\u73B0\u65B0\u7248\u672C\uFF1A{current} \u2192 {latest}",
|
|
1655
|
+
cliUpdateRunning: "\u6B63\u5728\u6267\u884C\uFF1A{command}",
|
|
1656
|
+
cliUpdateDone: "\u5DF2\u66F4\u65B0\u5230 {version}\u3002\u8BF7\u91CD\u542F\u83DC\u5355\u680F\u5E94\u7528\uFF08\u6216\u91CD\u65B0\u8FD0\u884C CLI\uFF09\u4EE5\u751F\u6548\u3002",
|
|
1657
|
+
cliUpdateFailed: "\u66F4\u65B0\u5931\u8D25\uFF08\u9000\u51FA\u7801 {code}\uFF09\u3002\u8BF7\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
1658
|
+
cliUpdateCheckFailed: "\u65E0\u6CD5\u8BBF\u95EE npm registry\uFF1A{message}"
|
|
1573
1659
|
};
|
|
1574
1660
|
}
|
|
1575
1661
|
});
|
|
@@ -1646,7 +1732,7 @@ function isWSL() {
|
|
|
1646
1732
|
if (process.platform !== "linux") return wslCache = false;
|
|
1647
1733
|
if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) return wslCache = true;
|
|
1648
1734
|
try {
|
|
1649
|
-
const v2 =
|
|
1735
|
+
const v2 = import_node_fs3.default.readFileSync("/proc/version", "utf8").toLowerCase();
|
|
1650
1736
|
return wslCache = v2.includes("microsoft") || v2.includes("wsl");
|
|
1651
1737
|
} catch {
|
|
1652
1738
|
return wslCache = false;
|
|
@@ -1662,7 +1748,7 @@ function platformLabel(kind = platformKind()) {
|
|
|
1662
1748
|
}
|
|
1663
1749
|
function hostname() {
|
|
1664
1750
|
try {
|
|
1665
|
-
return
|
|
1751
|
+
return import_node_os3.default.hostname().replace(/\.local$/, "");
|
|
1666
1752
|
} catch {
|
|
1667
1753
|
return "unknown-host";
|
|
1668
1754
|
}
|
|
@@ -1680,7 +1766,7 @@ function hasDisplay() {
|
|
|
1680
1766
|
function run(cmd, args) {
|
|
1681
1767
|
return new Promise((resolve) => {
|
|
1682
1768
|
try {
|
|
1683
|
-
const child = (0,
|
|
1769
|
+
const child = (0, import_node_child_process2.spawn)(cmd, args, { stdio: "ignore", detached: true, windowsHide: true });
|
|
1684
1770
|
child.on("error", () => resolve(false));
|
|
1685
1771
|
child.on("spawn", () => {
|
|
1686
1772
|
child.unref();
|
|
@@ -1713,13 +1799,13 @@ function systemLocale() {
|
|
|
1713
1799
|
return "en";
|
|
1714
1800
|
}
|
|
1715
1801
|
}
|
|
1716
|
-
var
|
|
1802
|
+
var import_node_fs3, import_node_os3, import_node_child_process2, wslCache;
|
|
1717
1803
|
var init_platform = __esm({
|
|
1718
1804
|
"src/core/platform.ts"() {
|
|
1719
1805
|
"use strict";
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1806
|
+
import_node_fs3 = __toESM(require("node:fs"));
|
|
1807
|
+
import_node_os3 = __toESM(require("node:os"));
|
|
1808
|
+
import_node_child_process2 = require("node:child_process");
|
|
1723
1809
|
init_i18n();
|
|
1724
1810
|
wslCache = null;
|
|
1725
1811
|
}
|
|
@@ -3190,12 +3276,12 @@ function createApi(pathParts = []) {
|
|
|
3190
3276
|
`API path is expected to be of the form \`api.moduleName.functionName\`. Found: \`${found}\``
|
|
3191
3277
|
);
|
|
3192
3278
|
}
|
|
3193
|
-
const
|
|
3279
|
+
const path13 = pathParts.slice(0, -1).join("/");
|
|
3194
3280
|
const exportName = pathParts[pathParts.length - 1];
|
|
3195
3281
|
if (exportName === "default") {
|
|
3196
|
-
return
|
|
3282
|
+
return path13;
|
|
3197
3283
|
} else {
|
|
3198
|
-
return
|
|
3284
|
+
return path13 + ":" + exportName;
|
|
3199
3285
|
}
|
|
3200
3286
|
} else if (prop === Symbol.toStringTag) {
|
|
3201
3287
|
return "FunctionReference";
|
|
@@ -3490,16 +3576,16 @@ var init_simple_client_node = __esm({
|
|
|
3490
3576
|
});
|
|
3491
3577
|
require_node_gyp_build = __commonJS({
|
|
3492
3578
|
"../node_modules/.pnpm/node-gyp-build@4.8.4/node_modules/node-gyp-build/node-gyp-build.js"(exports2, module2) {
|
|
3493
|
-
var
|
|
3494
|
-
var
|
|
3495
|
-
var
|
|
3579
|
+
var fs11 = __require("fs");
|
|
3580
|
+
var path13 = __require("path");
|
|
3581
|
+
var os8 = __require("os");
|
|
3496
3582
|
var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
3497
3583
|
var vars2 = process.config && process.config.variables || {};
|
|
3498
3584
|
var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
|
|
3499
3585
|
var abi = process.versions.modules;
|
|
3500
3586
|
var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
|
|
3501
|
-
var arch = process.env.npm_config_arch ||
|
|
3502
|
-
var platform = process.env.npm_config_platform ||
|
|
3587
|
+
var arch = process.env.npm_config_arch || os8.arch();
|
|
3588
|
+
var platform = process.env.npm_config_platform || os8.platform();
|
|
3503
3589
|
var libc = process.env.LIBC || (isAlpine(platform) ? "musl" : "glibc");
|
|
3504
3590
|
var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars2.arm_version) || "";
|
|
3505
3591
|
var uv = (process.versions.uv || "").split(".")[0];
|
|
@@ -3508,21 +3594,21 @@ var init_simple_client_node = __esm({
|
|
|
3508
3594
|
return runtimeRequire(load.resolve(dir));
|
|
3509
3595
|
}
|
|
3510
3596
|
load.resolve = load.path = function(dir) {
|
|
3511
|
-
dir =
|
|
3597
|
+
dir = path13.resolve(dir || ".");
|
|
3512
3598
|
try {
|
|
3513
|
-
var name = runtimeRequire(
|
|
3599
|
+
var name = runtimeRequire(path13.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
|
|
3514
3600
|
if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
|
|
3515
3601
|
} catch (err) {
|
|
3516
3602
|
}
|
|
3517
3603
|
if (!prebuildsOnly) {
|
|
3518
|
-
var release = getFirst(
|
|
3604
|
+
var release = getFirst(path13.join(dir, "build/Release"), matchBuild);
|
|
3519
3605
|
if (release) return release;
|
|
3520
|
-
var debug = getFirst(
|
|
3606
|
+
var debug = getFirst(path13.join(dir, "build/Debug"), matchBuild);
|
|
3521
3607
|
if (debug) return debug;
|
|
3522
3608
|
}
|
|
3523
3609
|
var prebuild = resolve(dir);
|
|
3524
3610
|
if (prebuild) return prebuild;
|
|
3525
|
-
var nearby = resolve(
|
|
3611
|
+
var nearby = resolve(path13.dirname(process.execPath));
|
|
3526
3612
|
if (nearby) return nearby;
|
|
3527
3613
|
var target = [
|
|
3528
3614
|
"platform=" + platform,
|
|
@@ -3539,26 +3625,26 @@ var init_simple_client_node = __esm({
|
|
|
3539
3625
|
].filter(Boolean).join(" ");
|
|
3540
3626
|
throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
|
|
3541
3627
|
function resolve(dir2) {
|
|
3542
|
-
var tuples = readdirSync(
|
|
3628
|
+
var tuples = readdirSync(path13.join(dir2, "prebuilds")).map(parseTuple);
|
|
3543
3629
|
var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
|
|
3544
3630
|
if (!tuple) return;
|
|
3545
|
-
var prebuilds =
|
|
3631
|
+
var prebuilds = path13.join(dir2, "prebuilds", tuple.name);
|
|
3546
3632
|
var parsed = readdirSync(prebuilds).map(parseTags);
|
|
3547
3633
|
var candidates = parsed.filter(matchTags(runtime, abi));
|
|
3548
3634
|
var winner = candidates.sort(compareTags(runtime))[0];
|
|
3549
|
-
if (winner) return
|
|
3635
|
+
if (winner) return path13.join(prebuilds, winner.file);
|
|
3550
3636
|
}
|
|
3551
3637
|
};
|
|
3552
3638
|
function readdirSync(dir) {
|
|
3553
3639
|
try {
|
|
3554
|
-
return
|
|
3640
|
+
return fs11.readdirSync(dir);
|
|
3555
3641
|
} catch (err) {
|
|
3556
3642
|
return [];
|
|
3557
3643
|
}
|
|
3558
3644
|
}
|
|
3559
3645
|
function getFirst(dir, filter) {
|
|
3560
3646
|
var files = readdirSync(dir).filter(filter);
|
|
3561
|
-
return files[0] &&
|
|
3647
|
+
return files[0] && path13.join(dir, files[0]);
|
|
3562
3648
|
}
|
|
3563
3649
|
function matchBuild(name) {
|
|
3564
3650
|
return /\.node$/.test(name);
|
|
@@ -3645,7 +3731,7 @@ var init_simple_client_node = __esm({
|
|
|
3645
3731
|
return typeof window !== "undefined" && window.process && window.process.type === "renderer";
|
|
3646
3732
|
}
|
|
3647
3733
|
function isAlpine(platform2) {
|
|
3648
|
-
return platform2 === "linux" &&
|
|
3734
|
+
return platform2 === "linux" && fs11.existsSync("/etc/alpine-release");
|
|
3649
3735
|
}
|
|
3650
3736
|
load.parseTags = parseTags;
|
|
3651
3737
|
load.matchTags = matchTags;
|
|
@@ -7191,13 +7277,13 @@ var init_simple_client_node = __esm({
|
|
|
7191
7277
|
}
|
|
7192
7278
|
}
|
|
7193
7279
|
if (this.options.verifyClient) {
|
|
7194
|
-
const
|
|
7280
|
+
const info2 = {
|
|
7195
7281
|
origin: req.headers[`${version2 === 8 ? "sec-websocket-origin" : "origin"}`],
|
|
7196
7282
|
secure: !!(req.socket.authorized || req.socket.encrypted),
|
|
7197
7283
|
req
|
|
7198
7284
|
};
|
|
7199
7285
|
if (this.options.verifyClient.length === 2) {
|
|
7200
|
-
this.options.verifyClient(
|
|
7286
|
+
this.options.verifyClient(info2, (verified, code2, message, headers) => {
|
|
7201
7287
|
if (!verified) {
|
|
7202
7288
|
return abortHandshake(socket, code2 || 401, message, headers);
|
|
7203
7289
|
}
|
|
@@ -7213,7 +7299,7 @@ var init_simple_client_node = __esm({
|
|
|
7213
7299
|
});
|
|
7214
7300
|
return;
|
|
7215
7301
|
}
|
|
7216
|
-
if (!this.options.verifyClient(
|
|
7302
|
+
if (!this.options.verifyClient(info2)) return abortHandshake(socket, 401);
|
|
7217
7303
|
}
|
|
7218
7304
|
this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
|
|
7219
7305
|
}
|
|
@@ -8719,8 +8805,158 @@ var init_auth = __esm({
|
|
|
8719
8805
|
});
|
|
8720
8806
|
|
|
8721
8807
|
// src/cli.ts
|
|
8722
|
-
var
|
|
8723
|
-
var
|
|
8808
|
+
var import_node_path12 = __toESM(require("node:path"));
|
|
8809
|
+
var import_node_fs10 = __toESM(require("node:fs"));
|
|
8810
|
+
var import_node_child_process5 = require("node:child_process");
|
|
8811
|
+
|
|
8812
|
+
// src/core/electron-install.ts
|
|
8813
|
+
var import_node_fs = __toESM(require("node:fs"));
|
|
8814
|
+
var import_node_os = __toESM(require("node:os"));
|
|
8815
|
+
var import_node_path = __toESM(require("node:path"));
|
|
8816
|
+
var import_node_child_process = require("node:child_process");
|
|
8817
|
+
function archName() {
|
|
8818
|
+
switch (process.arch) {
|
|
8819
|
+
case "x64":
|
|
8820
|
+
return "x64";
|
|
8821
|
+
case "arm64":
|
|
8822
|
+
return "arm64";
|
|
8823
|
+
case "ia32":
|
|
8824
|
+
return "ia32";
|
|
8825
|
+
case "arm":
|
|
8826
|
+
return "armv7l";
|
|
8827
|
+
default:
|
|
8828
|
+
return process.arch;
|
|
8829
|
+
}
|
|
8830
|
+
}
|
|
8831
|
+
function platformExecutablePath() {
|
|
8832
|
+
switch (process.platform) {
|
|
8833
|
+
case "darwin":
|
|
8834
|
+
return "Electron.app/Contents/MacOS/Electron";
|
|
8835
|
+
case "win32":
|
|
8836
|
+
return "electron.exe";
|
|
8837
|
+
default:
|
|
8838
|
+
return "electron";
|
|
8839
|
+
}
|
|
8840
|
+
}
|
|
8841
|
+
function electronCacheRoots() {
|
|
8842
|
+
const roots = [];
|
|
8843
|
+
if (process.env.electron_config_cache) roots.push(process.env.electron_config_cache);
|
|
8844
|
+
const home = import_node_os.default.homedir();
|
|
8845
|
+
if (process.platform === "darwin") roots.push(import_node_path.default.join(home, "Library", "Caches", "electron"));
|
|
8846
|
+
else if (process.platform === "win32") {
|
|
8847
|
+
if (process.env.LOCALAPPDATA) roots.push(import_node_path.default.join(process.env.LOCALAPPDATA, "electron", "Cache"));
|
|
8848
|
+
} else roots.push(import_node_path.default.join(process.env.XDG_CACHE_HOME ?? import_node_path.default.join(home, ".cache"), "electron"));
|
|
8849
|
+
return roots.filter((r) => import_node_fs.default.existsSync(r));
|
|
8850
|
+
}
|
|
8851
|
+
function findInDirs(dirs, filename, depth = 3) {
|
|
8852
|
+
for (const dir of dirs) {
|
|
8853
|
+
const found = walk(dir, filename, depth);
|
|
8854
|
+
if (found) return found;
|
|
8855
|
+
}
|
|
8856
|
+
return null;
|
|
8857
|
+
}
|
|
8858
|
+
function walk(dir, filename, depth) {
|
|
8859
|
+
let entries;
|
|
8860
|
+
try {
|
|
8861
|
+
entries = import_node_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
8862
|
+
} catch {
|
|
8863
|
+
return null;
|
|
8864
|
+
}
|
|
8865
|
+
for (const e of entries) {
|
|
8866
|
+
const p = import_node_path.default.join(dir, e.name);
|
|
8867
|
+
if (e.isFile() && e.name === filename && import_node_fs.default.statSync(p).size > 1e6) return p;
|
|
8868
|
+
}
|
|
8869
|
+
if (depth <= 0) return null;
|
|
8870
|
+
for (const e of entries) {
|
|
8871
|
+
if (e.isDirectory()) {
|
|
8872
|
+
const r = walk(import_node_path.default.join(dir, e.name), filename, depth - 1);
|
|
8873
|
+
if (r) return r;
|
|
8874
|
+
}
|
|
8875
|
+
}
|
|
8876
|
+
return null;
|
|
8877
|
+
}
|
|
8878
|
+
function downloadUrl(version2, filename) {
|
|
8879
|
+
const mirror = process.env.ELECTRON_MIRROR || "https://github.com/electron/electron/releases/download/v";
|
|
8880
|
+
const dir = process.env.ELECTRON_CUSTOM_DIR ? process.env.ELECTRON_CUSTOM_DIR.replace("{{ version }}", version2) : version2;
|
|
8881
|
+
const base = mirror.endsWith("/") || mirror.endsWith("v") ? mirror : mirror + "/";
|
|
8882
|
+
return `${base}${dir}/${filename}`;
|
|
8883
|
+
}
|
|
8884
|
+
async function download(url, dest, log2) {
|
|
8885
|
+
const res = await fetch(url, { redirect: "follow" });
|
|
8886
|
+
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status} for ${url}`);
|
|
8887
|
+
const total = Number(res.headers.get("content-length") || 0);
|
|
8888
|
+
import_node_fs.default.mkdirSync(import_node_path.default.dirname(dest), { recursive: true });
|
|
8889
|
+
const tmp = dest + ".part";
|
|
8890
|
+
const out = import_node_fs.default.createWriteStream(tmp);
|
|
8891
|
+
let received = 0;
|
|
8892
|
+
let lastPct = -10;
|
|
8893
|
+
const reader = res.body.getReader();
|
|
8894
|
+
try {
|
|
8895
|
+
for (; ; ) {
|
|
8896
|
+
const { done, value } = await reader.read();
|
|
8897
|
+
if (done) break;
|
|
8898
|
+
received += value.byteLength;
|
|
8899
|
+
if (!out.write(value)) await new Promise((r) => out.once("drain", () => r()));
|
|
8900
|
+
if (total) {
|
|
8901
|
+
const pct = Math.floor(received / total * 100);
|
|
8902
|
+
if (pct >= lastPct + 10) {
|
|
8903
|
+
lastPct = pct;
|
|
8904
|
+
log2.info(` ${pct}% (${(received / 1048576).toFixed(0)} / ${(total / 1048576).toFixed(0)} MB)`);
|
|
8905
|
+
}
|
|
8906
|
+
}
|
|
8907
|
+
}
|
|
8908
|
+
} finally {
|
|
8909
|
+
await new Promise((resolve) => out.end(() => resolve()));
|
|
8910
|
+
}
|
|
8911
|
+
import_node_fs.default.renameSync(tmp, dest);
|
|
8912
|
+
}
|
|
8913
|
+
function extractZip(zip, dest) {
|
|
8914
|
+
import_node_fs.default.rmSync(dest, { recursive: true, force: true });
|
|
8915
|
+
import_node_fs.default.mkdirSync(dest, { recursive: true });
|
|
8916
|
+
const attempts = process.platform === "darwin" ? [["ditto", ["-x", "-k", zip, dest]], ["unzip", ["-q", "-o", zip, "-d", dest]]] : process.platform === "win32" ? [
|
|
8917
|
+
["tar", ["-xf", zip, "-C", dest]],
|
|
8918
|
+
["powershell", ["-NoProfile", "-Command", `Expand-Archive -LiteralPath '${zip}' -DestinationPath '${dest}' -Force`]]
|
|
8919
|
+
] : [["unzip", ["-q", "-o", zip, "-d", dest]], ["bsdtar", ["-xf", zip, "-C", dest]], ["tar", ["-xf", zip, "-C", dest]]];
|
|
8920
|
+
for (const [cmd, args] of attempts) {
|
|
8921
|
+
const r = (0, import_node_child_process.spawnSync)(cmd, args, { stdio: "ignore", windowsHide: true });
|
|
8922
|
+
if (r.status === 0 && import_node_fs.default.existsSync(import_node_path.default.join(dest, platformExecutablePath()))) return true;
|
|
8923
|
+
}
|
|
8924
|
+
return false;
|
|
8925
|
+
}
|
|
8926
|
+
async function installElectronBinary(pkgDir, log2) {
|
|
8927
|
+
let version2;
|
|
8928
|
+
try {
|
|
8929
|
+
version2 = JSON.parse(import_node_fs.default.readFileSync(import_node_path.default.join(pkgDir, "package.json"), "utf8")).version;
|
|
8930
|
+
} catch {
|
|
8931
|
+
return null;
|
|
8932
|
+
}
|
|
8933
|
+
const platform = process.platform === "darwin" && process.env.npm_config_platform === "mas" ? "mas" : process.platform;
|
|
8934
|
+
const filename = `electron-v${version2}-${platform}-${archName()}.zip`;
|
|
8935
|
+
const dist = import_node_path.default.join(pkgDir, "dist");
|
|
8936
|
+
const exe = import_node_path.default.join(dist, platformExecutablePath());
|
|
8937
|
+
let zip = findInDirs(electronCacheRoots(), filename);
|
|
8938
|
+
if (zip) log2.info(` using cached ${import_node_path.default.basename(zip)}`);
|
|
8939
|
+
else {
|
|
8940
|
+
const cacheRoot = electronCacheRoots()[0] ?? import_node_path.default.join(import_node_os.default.tmpdir(), "codex-tracker-electron");
|
|
8941
|
+
zip = import_node_path.default.join(cacheRoot, "codex-token-tracker", filename);
|
|
8942
|
+
const url = downloadUrl(version2, filename);
|
|
8943
|
+
log2.info(` ${url}`);
|
|
8944
|
+
try {
|
|
8945
|
+
await download(url, zip, log2);
|
|
8946
|
+
} catch (err) {
|
|
8947
|
+
log2.error(` download failed: ${err.message}`);
|
|
8948
|
+
return null;
|
|
8949
|
+
}
|
|
8950
|
+
}
|
|
8951
|
+
if (!extractZip(zip, dist)) {
|
|
8952
|
+
log2.error(" extraction failed (no ditto/unzip/tar available?)");
|
|
8953
|
+
return null;
|
|
8954
|
+
}
|
|
8955
|
+
import_node_fs.default.writeFileSync(import_node_path.default.join(pkgDir, "path.txt"), platformExecutablePath());
|
|
8956
|
+
return import_node_fs.default.existsSync(exe) ? exe : null;
|
|
8957
|
+
}
|
|
8958
|
+
|
|
8959
|
+
// src/cli.ts
|
|
8724
8960
|
init_src();
|
|
8725
8961
|
init_config();
|
|
8726
8962
|
|
|
@@ -8730,42 +8966,42 @@ init_src();
|
|
|
8730
8966
|
init_config();
|
|
8731
8967
|
|
|
8732
8968
|
// src/core/store.ts
|
|
8733
|
-
var
|
|
8969
|
+
var import_node_fs7 = __toESM(require("node:fs"));
|
|
8734
8970
|
|
|
8735
8971
|
// src/core/sources/index.ts
|
|
8736
|
-
var
|
|
8737
|
-
var
|
|
8738
|
-
var
|
|
8739
|
-
var
|
|
8972
|
+
var import_node_fs6 = __toESM(require("node:fs"));
|
|
8973
|
+
var import_node_os7 = __toESM(require("node:os"));
|
|
8974
|
+
var import_node_path9 = __toESM(require("node:path"));
|
|
8975
|
+
var import_node_child_process3 = require("node:child_process");
|
|
8740
8976
|
init_platform();
|
|
8741
8977
|
init_config();
|
|
8742
8978
|
|
|
8743
8979
|
// src/core/sources/codex.ts
|
|
8744
|
-
var
|
|
8745
|
-
var
|
|
8980
|
+
var import_node_os4 = __toESM(require("node:os"));
|
|
8981
|
+
var import_node_path4 = __toESM(require("node:path"));
|
|
8746
8982
|
init_src();
|
|
8747
8983
|
|
|
8748
8984
|
// src/core/sources/util.ts
|
|
8749
|
-
var
|
|
8750
|
-
var
|
|
8985
|
+
var import_node_fs4 = __toESM(require("node:fs"));
|
|
8986
|
+
var import_node_path3 = __toESM(require("node:path"));
|
|
8751
8987
|
var TWO_DAYS = 2 * 864e5;
|
|
8752
8988
|
function isDir(p) {
|
|
8753
8989
|
try {
|
|
8754
|
-
return
|
|
8990
|
+
return import_node_fs4.default.statSync(p).isDirectory();
|
|
8755
8991
|
} catch {
|
|
8756
8992
|
return false;
|
|
8757
8993
|
}
|
|
8758
8994
|
}
|
|
8759
8995
|
function isFile(p) {
|
|
8760
8996
|
try {
|
|
8761
|
-
return
|
|
8997
|
+
return import_node_fs4.default.statSync(p).isFile();
|
|
8762
8998
|
} catch {
|
|
8763
8999
|
return false;
|
|
8764
9000
|
}
|
|
8765
9001
|
}
|
|
8766
9002
|
function listDirs(p) {
|
|
8767
9003
|
try {
|
|
8768
|
-
return
|
|
9004
|
+
return import_node_fs4.default.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
8769
9005
|
} catch {
|
|
8770
9006
|
return [];
|
|
8771
9007
|
}
|
|
@@ -8775,8 +9011,8 @@ function recentSubdirs(root, maxAgeMs = TWO_DAYS) {
|
|
|
8775
9011
|
const now = Date.now();
|
|
8776
9012
|
for (const name of listDirs(root)) {
|
|
8777
9013
|
try {
|
|
8778
|
-
const st =
|
|
8779
|
-
if (now - st.mtimeMs < maxAgeMs) out.push(
|
|
9014
|
+
const st = import_node_fs4.default.statSync(import_node_path3.default.join(root, name));
|
|
9015
|
+
if (now - st.mtimeMs < maxAgeMs) out.push(import_node_path3.default.join(root, name));
|
|
8780
9016
|
} catch {
|
|
8781
9017
|
}
|
|
8782
9018
|
}
|
|
@@ -8784,7 +9020,7 @@ function recentSubdirs(root, maxAgeMs = TWO_DAYS) {
|
|
|
8784
9020
|
}
|
|
8785
9021
|
function readJsonFile(p) {
|
|
8786
9022
|
try {
|
|
8787
|
-
return JSON.parse(
|
|
9023
|
+
return JSON.parse(import_node_fs4.default.readFileSync(p, "utf8"));
|
|
8788
9024
|
} catch {
|
|
8789
9025
|
return null;
|
|
8790
9026
|
}
|
|
@@ -8793,7 +9029,7 @@ function makeRoot(dir, source, agent, format, kind, origin, exts, maxDepth = 6,
|
|
|
8793
9029
|
return { dir, source, agent, format, kind, origin, exts, maxDepth, text };
|
|
8794
9030
|
}
|
|
8795
9031
|
function basenameNoExt(p) {
|
|
8796
|
-
return
|
|
9032
|
+
return import_node_path3.default.basename(p).replace(/\.[^.]+$/, "");
|
|
8797
9033
|
}
|
|
8798
9034
|
function projectNameOf(cwd) {
|
|
8799
9035
|
if (!cwd) return null;
|
|
@@ -8830,11 +9066,11 @@ function mergeSessions(parts) {
|
|
|
8830
9066
|
|
|
8831
9067
|
// src/core/sources/codex.ts
|
|
8832
9068
|
function codexHome() {
|
|
8833
|
-
return process.env.CODEX_HOME ||
|
|
9069
|
+
return process.env.CODEX_HOME || import_node_path4.default.join(import_node_os4.default.homedir(), ".codex");
|
|
8834
9070
|
}
|
|
8835
9071
|
function underHome(h, envVar, ...rel) {
|
|
8836
|
-
if (envVar && h.home ===
|
|
8837
|
-
return
|
|
9072
|
+
if (envVar && h.home === import_node_os4.default.homedir()) return envVar;
|
|
9073
|
+
return import_node_path4.default.join(h.home, ...rel);
|
|
8838
9074
|
}
|
|
8839
9075
|
function dateDirs(root) {
|
|
8840
9076
|
const dirs = [];
|
|
@@ -8842,10 +9078,10 @@ function dateDirs(root) {
|
|
|
8842
9078
|
for (const offset of [0, -1]) {
|
|
8843
9079
|
const d = /* @__PURE__ */ new Date();
|
|
8844
9080
|
d.setDate(d.getDate() + offset);
|
|
8845
|
-
dirs.push(
|
|
9081
|
+
dirs.push(import_node_path4.default.join(root, String(d.getFullYear()), pad(d.getMonth() + 1), pad(d.getDate())));
|
|
8846
9082
|
}
|
|
8847
9083
|
const u = /* @__PURE__ */ new Date();
|
|
8848
|
-
dirs.push(
|
|
9084
|
+
dirs.push(import_node_path4.default.join(root, String(u.getUTCFullYear()), pad(u.getUTCMonth() + 1), pad(u.getUTCDate())));
|
|
8849
9085
|
return [...new Set(dirs)];
|
|
8850
9086
|
}
|
|
8851
9087
|
var codexSource = {
|
|
@@ -8856,8 +9092,8 @@ var codexSource = {
|
|
|
8856
9092
|
const roots = [];
|
|
8857
9093
|
for (const h of ctx.homes) {
|
|
8858
9094
|
const home = underHome(h, process.env.CODEX_HOME, ".codex");
|
|
8859
|
-
const sessions =
|
|
8860
|
-
const archived =
|
|
9095
|
+
const sessions = import_node_path4.default.join(home, "sessions");
|
|
9096
|
+
const archived = import_node_path4.default.join(home, "archived_sessions");
|
|
8861
9097
|
if (isDir(sessions)) roots.push(makeRoot(sessions, "codex", "codex", "codex", "sessions", h.origin, [".jsonl"]));
|
|
8862
9098
|
if (isDir(archived)) roots.push(makeRoot(archived, "codex", "codex", "codex", "archived", h.origin, [".jsonl"], 1));
|
|
8863
9099
|
}
|
|
@@ -8878,12 +9114,12 @@ var codexSource = {
|
|
|
8878
9114
|
};
|
|
8879
9115
|
|
|
8880
9116
|
// src/core/sources/pi.ts
|
|
8881
|
-
var
|
|
8882
|
-
var
|
|
9117
|
+
var import_node_os5 = __toESM(require("node:os"));
|
|
9118
|
+
var import_node_path5 = __toESM(require("node:path"));
|
|
8883
9119
|
init_src();
|
|
8884
9120
|
function underHome2(h, envVar, ...rel) {
|
|
8885
|
-
if (envVar && h.home ===
|
|
8886
|
-
return
|
|
9121
|
+
if (envVar && h.home === import_node_os5.default.homedir()) return envVar;
|
|
9122
|
+
return import_node_path5.default.join(h.home, ...rel);
|
|
8887
9123
|
}
|
|
8888
9124
|
var piSource = {
|
|
8889
9125
|
id: "pi",
|
|
@@ -8892,7 +9128,7 @@ var piSource = {
|
|
|
8892
9128
|
discover(ctx) {
|
|
8893
9129
|
const roots = [];
|
|
8894
9130
|
for (const h of ctx.homes) {
|
|
8895
|
-
const dir =
|
|
9131
|
+
const dir = import_node_path5.default.join(underHome2(h, process.env.PI_CODING_AGENT_DIR, ".pi", "agent"), "sessions");
|
|
8896
9132
|
if (isDir(dir)) roots.push(makeRoot(dir, "pi", "pi", "pi", "flat", h.origin, [".jsonl"], 3));
|
|
8897
9133
|
}
|
|
8898
9134
|
return roots;
|
|
@@ -8925,12 +9161,12 @@ var genericSource = {
|
|
|
8925
9161
|
};
|
|
8926
9162
|
|
|
8927
9163
|
// src/core/sources/hermes.ts
|
|
8928
|
-
var
|
|
8929
|
-
var
|
|
9164
|
+
var import_node_os6 = __toESM(require("node:os"));
|
|
9165
|
+
var import_node_path6 = __toESM(require("node:path"));
|
|
8930
9166
|
init_src();
|
|
8931
9167
|
function underHome3(h, envVar, ...rel) {
|
|
8932
|
-
if (envVar && h.home ===
|
|
8933
|
-
return
|
|
9168
|
+
if (envVar && h.home === import_node_os6.default.homedir()) return envVar;
|
|
9169
|
+
return import_node_path6.default.join(h.home, ...rel);
|
|
8934
9170
|
}
|
|
8935
9171
|
var INPUT_COLS = ["input_tokens", "prompt_tokens", "tokens_in", "input"];
|
|
8936
9172
|
var OUTPUT_COLS = ["output_tokens", "completion_tokens", "tokens_out", "output"];
|
|
@@ -9056,9 +9292,9 @@ var hermesSource = {
|
|
|
9056
9292
|
const roots = [];
|
|
9057
9293
|
for (const h of ctx.homes) {
|
|
9058
9294
|
const home = underHome3(h, process.env.HERMES_HOME, ".hermes");
|
|
9059
|
-
const sessions =
|
|
9295
|
+
const sessions = import_node_path6.default.join(home, "sessions");
|
|
9060
9296
|
if (isDir(sessions)) roots.push(makeRoot(sessions, "hermes", "hermes", "generic", "flat", h.origin, [".jsonl", ".json"], 4));
|
|
9061
|
-
if (isFile(
|
|
9297
|
+
if (isFile(import_node_path6.default.join(home, "state.db"))) roots.push(makeRoot(home, "hermes", "hermes", "generic", "flat", h.origin, ["state.db"], 0, false));
|
|
9062
9298
|
}
|
|
9063
9299
|
return roots;
|
|
9064
9300
|
},
|
|
@@ -9086,17 +9322,17 @@ var hermesSource = {
|
|
|
9086
9322
|
};
|
|
9087
9323
|
|
|
9088
9324
|
// src/core/sources/opencode.ts
|
|
9089
|
-
var
|
|
9090
|
-
var
|
|
9325
|
+
var import_node_fs5 = __toESM(require("node:fs"));
|
|
9326
|
+
var import_node_path7 = __toESM(require("node:path"));
|
|
9091
9327
|
init_src();
|
|
9092
9328
|
function opencodeDataDirs(h, env) {
|
|
9093
9329
|
if (h.layout === "win32") {
|
|
9094
|
-
const local = h.origin === "local" && env.LOCALAPPDATA ? env.LOCALAPPDATA :
|
|
9095
|
-
const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA :
|
|
9096
|
-
return [
|
|
9330
|
+
const local = h.origin === "local" && env.LOCALAPPDATA ? env.LOCALAPPDATA : import_node_path7.default.join(h.home, "AppData", "Local");
|
|
9331
|
+
const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA : import_node_path7.default.join(h.home, "AppData", "Roaming");
|
|
9332
|
+
return [import_node_path7.default.join(local, "opencode"), import_node_path7.default.join(roaming, "opencode")];
|
|
9097
9333
|
}
|
|
9098
|
-
const xdg = h.origin === "local" && env.XDG_DATA_HOME ? env.XDG_DATA_HOME :
|
|
9099
|
-
return [
|
|
9334
|
+
const xdg = h.origin === "local" && env.XDG_DATA_HOME ? env.XDG_DATA_HOME : import_node_path7.default.join(h.home, ".local", "share");
|
|
9335
|
+
return [import_node_path7.default.join(xdg, "opencode")];
|
|
9100
9336
|
}
|
|
9101
9337
|
var sessionCache = /* @__PURE__ */ new Map();
|
|
9102
9338
|
var authCache = /* @__PURE__ */ new Map();
|
|
@@ -9104,27 +9340,27 @@ function findSessionInfo(storage, sessionID) {
|
|
|
9104
9340
|
const key = `${storage}|${sessionID}`;
|
|
9105
9341
|
const cached = sessionCache.get(key);
|
|
9106
9342
|
if (cached && (cached.info || Date.now() - cached.at < 6e4)) return cached.info;
|
|
9107
|
-
let
|
|
9108
|
-
const sessionDir =
|
|
9109
|
-
const direct =
|
|
9110
|
-
if (
|
|
9111
|
-
if (!
|
|
9343
|
+
let info2 = null;
|
|
9344
|
+
const sessionDir = import_node_path7.default.join(storage, "session");
|
|
9345
|
+
const direct = import_node_path7.default.join(sessionDir, "info", `${sessionID}.json`);
|
|
9346
|
+
if (import_node_fs5.default.existsSync(direct)) info2 = readJsonFile(direct);
|
|
9347
|
+
if (!info2) {
|
|
9112
9348
|
for (const project of listDirs(sessionDir)) {
|
|
9113
|
-
const p =
|
|
9114
|
-
if (
|
|
9115
|
-
|
|
9349
|
+
const p = import_node_path7.default.join(sessionDir, project, `${sessionID}.json`);
|
|
9350
|
+
if (import_node_fs5.default.existsSync(p)) {
|
|
9351
|
+
info2 = readJsonFile(p);
|
|
9116
9352
|
break;
|
|
9117
9353
|
}
|
|
9118
9354
|
}
|
|
9119
9355
|
}
|
|
9120
|
-
sessionCache.set(key, { at: Date.now(), info });
|
|
9121
|
-
return
|
|
9356
|
+
sessionCache.set(key, { at: Date.now(), info: info2 });
|
|
9357
|
+
return info2;
|
|
9122
9358
|
}
|
|
9123
9359
|
function openaiIsOAuth(dataDir) {
|
|
9124
|
-
const file =
|
|
9360
|
+
const file = import_node_path7.default.join(dataDir, "auth.json");
|
|
9125
9361
|
let mtime = 0;
|
|
9126
9362
|
try {
|
|
9127
|
-
mtime =
|
|
9363
|
+
mtime = import_node_fs5.default.statSync(file).mtimeMs;
|
|
9128
9364
|
} catch {
|
|
9129
9365
|
return false;
|
|
9130
9366
|
}
|
|
@@ -9136,9 +9372,9 @@ function openaiIsOAuth(dataDir) {
|
|
|
9136
9372
|
return oauth;
|
|
9137
9373
|
}
|
|
9138
9374
|
function storageOf(file) {
|
|
9139
|
-
const parts = file.split(
|
|
9375
|
+
const parts = file.split(import_node_path7.default.sep);
|
|
9140
9376
|
const idx = parts.lastIndexOf("storage");
|
|
9141
|
-
return idx >= 0 ? parts.slice(0, idx + 1).join(
|
|
9377
|
+
return idx >= 0 ? parts.slice(0, idx + 1).join(import_node_path7.default.sep) : null;
|
|
9142
9378
|
}
|
|
9143
9379
|
var opencodeSource = {
|
|
9144
9380
|
id: "opencode",
|
|
@@ -9149,7 +9385,7 @@ var opencodeSource = {
|
|
|
9149
9385
|
const roots = [];
|
|
9150
9386
|
for (const h of ctx.homes) {
|
|
9151
9387
|
for (const data of opencodeDataDirs(h, ctx.env)) {
|
|
9152
|
-
const storage =
|
|
9388
|
+
const storage = import_node_path7.default.join(data, "storage");
|
|
9153
9389
|
if (isDir(storage)) roots.push(makeRoot(storage, "opencode", "opencode", "opencode", "flat", h.origin, [".json"], 5));
|
|
9154
9390
|
}
|
|
9155
9391
|
}
|
|
@@ -9157,7 +9393,7 @@ var opencodeSource = {
|
|
|
9157
9393
|
},
|
|
9158
9394
|
hotDirs(root) {
|
|
9159
9395
|
const out = [root.dir];
|
|
9160
|
-
for (const base of [
|
|
9396
|
+
for (const base of [import_node_path7.default.join(root.dir, "message"), import_node_path7.default.join(root.dir, "session", "message")]) out.push(...recentSubdirs(base));
|
|
9161
9397
|
return out;
|
|
9162
9398
|
},
|
|
9163
9399
|
watchRecursively: () => true,
|
|
@@ -9171,7 +9407,7 @@ var opencodeSource = {
|
|
|
9171
9407
|
if (!m || m.role !== "assistant" || !m.tokens || typeof m.sessionID !== "string") return null;
|
|
9172
9408
|
const provider = typeof m.providerID === "string" ? m.providerID : null;
|
|
9173
9409
|
const storage = storageOf(file.path);
|
|
9174
|
-
const dataDir = storage ?
|
|
9410
|
+
const dataDir = storage ? import_node_path7.default.dirname(storage) : null;
|
|
9175
9411
|
const codexAuth = isCodexAuthProvider(provider) || provider === "openai" && !!dataDir && openaiIsOAuth(dataDir);
|
|
9176
9412
|
if (!opts.includeAllProviders && !codexAuth) return null;
|
|
9177
9413
|
const t2 = m.tokens;
|
|
@@ -9188,16 +9424,16 @@ var opencodeSource = {
|
|
|
9188
9424
|
provider,
|
|
9189
9425
|
usage: { input, cached, cacheWrite, output, reasoning: t2.reasoning ?? 0, total: input + output, requests: 1 }
|
|
9190
9426
|
};
|
|
9191
|
-
const
|
|
9192
|
-
const cwd =
|
|
9427
|
+
const info2 = storage ? findSessionInfo(storage, m.sessionID) : null;
|
|
9428
|
+
const cwd = info2?.directory ?? null;
|
|
9193
9429
|
return {
|
|
9194
9430
|
sessionId: m.sessionID,
|
|
9195
9431
|
agent: file.root.agent,
|
|
9196
9432
|
provider,
|
|
9197
|
-
startedAt:
|
|
9433
|
+
startedAt: info2?.time?.created ?? ts2,
|
|
9198
9434
|
lastActivityAt: ts2,
|
|
9199
9435
|
cwd,
|
|
9200
|
-
projectName: projectNameOf(cwd) ??
|
|
9436
|
+
projectName: projectNameOf(cwd) ?? info2?.title ?? null,
|
|
9201
9437
|
originator: "opencode",
|
|
9202
9438
|
source: "opencode",
|
|
9203
9439
|
cliVersion: null,
|
|
@@ -9214,22 +9450,22 @@ var opencodeSource = {
|
|
|
9214
9450
|
};
|
|
9215
9451
|
|
|
9216
9452
|
// src/core/sources/cline.ts
|
|
9217
|
-
var
|
|
9453
|
+
var import_node_path8 = __toESM(require("node:path"));
|
|
9218
9454
|
init_src();
|
|
9219
9455
|
var APPS = ["Code", "Code - Insiders", "Cursor", "Windsurf", "VSCodium", "Trae"];
|
|
9220
9456
|
function globalStorageDirs(h, env) {
|
|
9221
9457
|
const out = [];
|
|
9222
|
-
if (h.layout === "darwin") for (const app of APPS) out.push(
|
|
9458
|
+
if (h.layout === "darwin") for (const app of APPS) out.push(import_node_path8.default.join(h.home, "Library", "Application Support", app, "User", "globalStorage"));
|
|
9223
9459
|
else if (h.layout === "win32") {
|
|
9224
|
-
const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA :
|
|
9225
|
-
for (const app of APPS) out.push(
|
|
9460
|
+
const roaming = h.origin === "local" && env.APPDATA ? env.APPDATA : import_node_path8.default.join(h.home, "AppData", "Roaming");
|
|
9461
|
+
for (const app of APPS) out.push(import_node_path8.default.join(roaming, app, "User", "globalStorage"));
|
|
9226
9462
|
} else {
|
|
9227
|
-
const cfg = h.origin === "local" && env.XDG_CONFIG_HOME ? env.XDG_CONFIG_HOME :
|
|
9228
|
-
for (const app of APPS) out.push(
|
|
9463
|
+
const cfg = h.origin === "local" && env.XDG_CONFIG_HOME ? env.XDG_CONFIG_HOME : import_node_path8.default.join(h.home, ".config");
|
|
9464
|
+
for (const app of APPS) out.push(import_node_path8.default.join(cfg, app, "User", "globalStorage"));
|
|
9229
9465
|
}
|
|
9230
9466
|
if (h.layout !== "win32") {
|
|
9231
9467
|
for (const server of [".vscode-server", ".vscode-server-insiders", ".cursor-server", ".windsurf-server"]) {
|
|
9232
|
-
out.push(
|
|
9468
|
+
out.push(import_node_path8.default.join(h.home, server, "data", "User", "globalStorage"));
|
|
9233
9469
|
}
|
|
9234
9470
|
}
|
|
9235
9471
|
return out;
|
|
@@ -9252,7 +9488,7 @@ function clineFamily(id, label, extensionId) {
|
|
|
9252
9488
|
const roots = [];
|
|
9253
9489
|
for (const h of ctx.homes) {
|
|
9254
9490
|
for (const gs of globalStorageDirs(h, ctx.env)) {
|
|
9255
|
-
const tasks =
|
|
9491
|
+
const tasks = import_node_path8.default.join(gs, extensionId, "tasks");
|
|
9256
9492
|
if (isDir(tasks)) roots.push(makeRoot(tasks, id, id, "cline", "flat", h.origin, ["ui_messages.json"], 2));
|
|
9257
9493
|
}
|
|
9258
9494
|
}
|
|
@@ -9268,8 +9504,8 @@ function clineFamily(id, label, extensionId) {
|
|
|
9268
9504
|
return null;
|
|
9269
9505
|
}
|
|
9270
9506
|
if (!Array.isArray(messages)) return null;
|
|
9271
|
-
const taskDir =
|
|
9272
|
-
const meta = readJsonFile(
|
|
9507
|
+
const taskDir = import_node_path8.default.dirname(file.path);
|
|
9508
|
+
const meta = readJsonFile(import_node_path8.default.join(taskDir, "task_metadata.json")) ?? {};
|
|
9273
9509
|
const usageList = Array.isArray(meta.model_usage) ? meta.model_usage : [];
|
|
9274
9510
|
const agent = file.root.agent;
|
|
9275
9511
|
const events = [];
|
|
@@ -9321,7 +9557,7 @@ function clineFamily(id, label, extensionId) {
|
|
|
9321
9557
|
}
|
|
9322
9558
|
const cwd = meta.cwd ?? meta.cwd_on_task_initialization ?? meta.workspace ?? null;
|
|
9323
9559
|
return {
|
|
9324
|
-
sessionId:
|
|
9560
|
+
sessionId: import_node_path8.default.basename(taskDir),
|
|
9325
9561
|
agent,
|
|
9326
9562
|
provider,
|
|
9327
9563
|
startedAt: startedAt ?? lastActivityAt,
|
|
@@ -9364,7 +9600,7 @@ function sourceFor(root) {
|
|
|
9364
9600
|
function wslDistros() {
|
|
9365
9601
|
if (process.platform !== "win32") return [];
|
|
9366
9602
|
try {
|
|
9367
|
-
const raw = (0,
|
|
9603
|
+
const raw = (0, import_node_child_process3.execFileSync)("wsl.exe", ["-l", "-q"], { timeout: 5e3, windowsHide: true });
|
|
9368
9604
|
return raw.toString("utf16le").split(String.fromCharCode(0)).join("").split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
|
|
9369
9605
|
} catch {
|
|
9370
9606
|
return [];
|
|
@@ -9374,22 +9610,22 @@ var SKIP_WINDOWS_USERS = /* @__PURE__ */ new Set(["public", "default", "default
|
|
|
9374
9610
|
function userHomes() {
|
|
9375
9611
|
const kind = platformKind();
|
|
9376
9612
|
const localLayout = kind === "darwin" ? "darwin" : kind === "win32" ? "win32" : "linux";
|
|
9377
|
-
const out = [{ home:
|
|
9613
|
+
const out = [{ home: import_node_os7.default.homedir(), origin: "local", layout: localLayout }];
|
|
9378
9614
|
if (kind === "win32") {
|
|
9379
9615
|
for (const distro of wslDistros()) {
|
|
9380
9616
|
for (const prefix of [`\\\\wsl$\\${distro}`, `\\\\wsl.localhost\\${distro}`]) {
|
|
9381
|
-
const home =
|
|
9617
|
+
const home = import_node_path9.default.join(prefix, "home");
|
|
9382
9618
|
const users = listDirs(home);
|
|
9383
9619
|
if (!users.length) continue;
|
|
9384
|
-
for (const u of users) out.push({ home:
|
|
9385
|
-
out.push({ home:
|
|
9620
|
+
for (const u of users) out.push({ home: import_node_path9.default.join(home, u), origin: "wsl", layout: "linux" });
|
|
9621
|
+
out.push({ home: import_node_path9.default.join(prefix, "root"), origin: "wsl", layout: "linux" });
|
|
9386
9622
|
break;
|
|
9387
9623
|
}
|
|
9388
9624
|
}
|
|
9389
9625
|
} else if (kind === "wsl") {
|
|
9390
9626
|
for (const u of listDirs("/mnt/c/Users")) {
|
|
9391
9627
|
if (SKIP_WINDOWS_USERS.has(u.toLowerCase())) continue;
|
|
9392
|
-
out.push({ home:
|
|
9628
|
+
out.push({ home: import_node_path9.default.join("/mnt/c/Users", u), origin: "windows", layout: "win32" });
|
|
9393
9629
|
}
|
|
9394
9630
|
}
|
|
9395
9631
|
return out;
|
|
@@ -9400,7 +9636,7 @@ function discoverSessionRoots(opts = {}) {
|
|
|
9400
9636
|
const roots = [];
|
|
9401
9637
|
const seen = /* @__PURE__ */ new Set();
|
|
9402
9638
|
const add = (r) => {
|
|
9403
|
-
const key = `${
|
|
9639
|
+
const key = `${import_node_path9.default.resolve(r.dir)}|${r.exts.join(",")}`;
|
|
9404
9640
|
if (seen.has(key)) return;
|
|
9405
9641
|
seen.add(key);
|
|
9406
9642
|
roots.push(r);
|
|
@@ -9415,8 +9651,8 @@ function discoverSessionRoots(opts = {}) {
|
|
|
9415
9651
|
for (const entry of opts.extraSessionDirs ?? []) {
|
|
9416
9652
|
const e = normalizeExtraDir(entry);
|
|
9417
9653
|
if (!e) continue;
|
|
9418
|
-
const dir = e.path.replace(/^~(?=$|\/|\\)/,
|
|
9419
|
-
if (!
|
|
9654
|
+
const dir = e.path.replace(/^~(?=$|\/|\\)/, import_node_os7.default.homedir());
|
|
9655
|
+
if (!import_node_fs6.default.existsSync(dir)) continue;
|
|
9420
9656
|
const def = byFormat[e.format ?? "generic"] ?? genericSource;
|
|
9421
9657
|
add(def.extraRoot(dir, e.agent));
|
|
9422
9658
|
}
|
|
@@ -9424,28 +9660,28 @@ function discoverSessionRoots(opts = {}) {
|
|
|
9424
9660
|
}
|
|
9425
9661
|
function walkFiles(root) {
|
|
9426
9662
|
const out = [];
|
|
9427
|
-
const
|
|
9663
|
+
const walk2 = (dir, depth) => {
|
|
9428
9664
|
let entries;
|
|
9429
9665
|
try {
|
|
9430
|
-
entries =
|
|
9666
|
+
entries = import_node_fs6.default.readdirSync(dir, { withFileTypes: true });
|
|
9431
9667
|
} catch {
|
|
9432
9668
|
return;
|
|
9433
9669
|
}
|
|
9434
9670
|
for (const e of entries) {
|
|
9435
|
-
const p =
|
|
9671
|
+
const p = import_node_path9.default.join(dir, e.name);
|
|
9436
9672
|
if (e.isDirectory()) {
|
|
9437
|
-
if (depth < root.maxDepth && !e.name.startsWith("."))
|
|
9673
|
+
if (depth < root.maxDepth && !e.name.startsWith(".")) walk2(p, depth + 1);
|
|
9438
9674
|
} else if (e.isFile() && root.exts.some((x) => e.name.endsWith(x))) {
|
|
9439
9675
|
out.push(p);
|
|
9440
9676
|
}
|
|
9441
9677
|
}
|
|
9442
9678
|
};
|
|
9443
|
-
|
|
9679
|
+
walk2(root.dir, 0);
|
|
9444
9680
|
return out;
|
|
9445
9681
|
}
|
|
9446
9682
|
function listFiles(dir, exts) {
|
|
9447
9683
|
try {
|
|
9448
|
-
return
|
|
9684
|
+
return import_node_fs6.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && exts.some((x) => e.name.endsWith(x))).map((e) => import_node_path9.default.join(dir, e.name));
|
|
9449
9685
|
} catch {
|
|
9450
9686
|
return [];
|
|
9451
9687
|
}
|
|
@@ -9509,7 +9745,7 @@ var SessionStore = class {
|
|
|
9509
9745
|
async syncFile(p, root) {
|
|
9510
9746
|
let st;
|
|
9511
9747
|
try {
|
|
9512
|
-
st = await
|
|
9748
|
+
st = await import_node_fs7.default.promises.stat(p);
|
|
9513
9749
|
} catch {
|
|
9514
9750
|
if (this.files.delete(p)) return true;
|
|
9515
9751
|
return false;
|
|
@@ -9519,7 +9755,7 @@ var SessionStore = class {
|
|
|
9519
9755
|
let session = null;
|
|
9520
9756
|
if (st.size <= MAX_FILE_BYTES) {
|
|
9521
9757
|
try {
|
|
9522
|
-
const text = root.text ? await
|
|
9758
|
+
const text = root.text ? await import_node_fs7.default.promises.readFile(p, "utf8") : "";
|
|
9523
9759
|
session = sourceFor(root).parse({ path: p, text, root }, { includeAllProviders: this.getOptions().trackAllProviders });
|
|
9524
9760
|
} catch {
|
|
9525
9761
|
session = null;
|
|
@@ -9604,8 +9840,8 @@ var SessionStore = class {
|
|
|
9604
9840
|
for (const [dir, recursive] of wanted) {
|
|
9605
9841
|
if (this.watchers.has(dir)) continue;
|
|
9606
9842
|
try {
|
|
9607
|
-
if (!
|
|
9608
|
-
const w =
|
|
9843
|
+
if (!import_node_fs7.default.existsSync(dir)) continue;
|
|
9844
|
+
const w = import_node_fs7.default.watch(dir, { persistent: false, recursive }, () => this.scheduleChange());
|
|
9609
9845
|
w.on("error", () => {
|
|
9610
9846
|
w.close();
|
|
9611
9847
|
this.watchers.delete(dir);
|
|
@@ -9630,7 +9866,20 @@ var SessionStore = class {
|
|
|
9630
9866
|
// src/core/stats.ts
|
|
9631
9867
|
init_src();
|
|
9632
9868
|
var LIVE_WINDOW_MS = 5 * 60 * 1e3;
|
|
9869
|
+
var RATE_WINDOW_MS = 6e4;
|
|
9870
|
+
var BURST_WINDOW_MS = 1e4;
|
|
9633
9871
|
var DAY3 = 864e5;
|
|
9872
|
+
function outputRate(events, now, windowMs, sessionStart) {
|
|
9873
|
+
const from = now - windowMs;
|
|
9874
|
+
let output = 0;
|
|
9875
|
+
for (const e of events) {
|
|
9876
|
+
if (e.ts <= from) continue;
|
|
9877
|
+
output += e.usage.output;
|
|
9878
|
+
}
|
|
9879
|
+
if (output <= 0) return 0;
|
|
9880
|
+
const elapsedSec = Math.max(1, (now - Math.max(from, sessionStart)) / 1e3);
|
|
9881
|
+
return output / elapsedSec;
|
|
9882
|
+
}
|
|
9634
9883
|
var PriceCache = class {
|
|
9635
9884
|
constructor(overrides) {
|
|
9636
9885
|
this.overrides = overrides;
|
|
@@ -9707,7 +9956,9 @@ function agentStats(sessions, since, prices) {
|
|
|
9707
9956
|
function computeStats(input) {
|
|
9708
9957
|
const now = input.now ?? Date.now();
|
|
9709
9958
|
const prices = new PriceCache(input.pricing);
|
|
9710
|
-
const sessions = input.sessions
|
|
9959
|
+
const sessions = input.sessions.map(
|
|
9960
|
+
(s) => s.events.every((e) => isOpenAIModel(e.model)) ? s : { ...s, events: s.events.filter((e) => isOpenAIModel(e.model)) }
|
|
9961
|
+
);
|
|
9711
9962
|
const allEvents = [];
|
|
9712
9963
|
const sessionCosts = /* @__PURE__ */ new Map();
|
|
9713
9964
|
let lastActivityAt = null;
|
|
@@ -9733,14 +9984,8 @@ function computeStats(input) {
|
|
|
9733
9984
|
let live = null;
|
|
9734
9985
|
const liveSession = sessions.filter((s) => s.events.length && now - s.lastActivityAt <= LIVE_WINDOW_MS).sort((a, b) => b.lastActivityAt - a.lastActivityAt)[0];
|
|
9735
9986
|
if (liveSession) {
|
|
9736
|
-
let t60 = 0;
|
|
9737
|
-
let t10 = 0;
|
|
9738
|
-
for (const e of liveSession.events) {
|
|
9739
|
-
const age = now - e.ts;
|
|
9740
|
-
if (age <= 6e4) t60 += e.usage.total;
|
|
9741
|
-
if (age <= 1e4) t10 += e.usage.total;
|
|
9742
|
-
}
|
|
9743
9987
|
const last = liveSession.events[liveSession.events.length - 1];
|
|
9988
|
+
const start = liveSession.startedAt || liveSession.events[0].ts;
|
|
9744
9989
|
live = {
|
|
9745
9990
|
sessionId: liveSession.sessionId,
|
|
9746
9991
|
agent: liveSession.agent,
|
|
@@ -9748,8 +9993,8 @@ function computeStats(input) {
|
|
|
9748
9993
|
model: liveSession.model,
|
|
9749
9994
|
startedAt: liveSession.startedAt,
|
|
9750
9995
|
lastEventAt: last.ts,
|
|
9751
|
-
tokensPerSecond:
|
|
9752
|
-
tokensPerSecond10s:
|
|
9996
|
+
tokensPerSecond: outputRate(liveSession.events, now, RATE_WINDOW_MS, start),
|
|
9997
|
+
tokensPerSecond10s: outputRate(liveSession.events, now, BURST_WINDOW_MS, start),
|
|
9753
9998
|
contextUsed: last.usage.input + last.usage.output,
|
|
9754
9999
|
contextWindow: liveSession.contextWindow,
|
|
9755
10000
|
sessionUsage: { ...liveSession.cumulative },
|
|
@@ -9776,6 +10021,7 @@ function computeStats(input) {
|
|
|
9776
10021
|
}
|
|
9777
10022
|
return {
|
|
9778
10023
|
buckets,
|
|
10024
|
+
sessions: sessions.filter((s) => s.events.length),
|
|
9779
10025
|
today,
|
|
9780
10026
|
week,
|
|
9781
10027
|
month,
|
|
@@ -9798,14 +10044,14 @@ init_auth();
|
|
|
9798
10044
|
init_platform();
|
|
9799
10045
|
|
|
9800
10046
|
// src/core/usage-api.ts
|
|
9801
|
-
var
|
|
9802
|
-
var
|
|
10047
|
+
var import_node_fs8 = __toESM(require("node:fs"));
|
|
10048
|
+
var import_node_path10 = __toESM(require("node:path"));
|
|
9803
10049
|
init_src();
|
|
9804
10050
|
function readCodexAuth() {
|
|
9805
|
-
const file =
|
|
10051
|
+
const file = import_node_path10.default.join(codexHome(), "auth.json");
|
|
9806
10052
|
let raw;
|
|
9807
10053
|
try {
|
|
9808
|
-
raw =
|
|
10054
|
+
raw = import_node_fs8.default.readFileSync(file, "utf8");
|
|
9809
10055
|
} catch {
|
|
9810
10056
|
return { error: `Codex login not found (${file})` };
|
|
9811
10057
|
}
|
|
@@ -9856,8 +10102,173 @@ async function fetchLiveRateLimits(appVersion, timeoutMs = 1e4) {
|
|
|
9856
10102
|
// src/core/engine.ts
|
|
9857
10103
|
init_i18n();
|
|
9858
10104
|
|
|
10105
|
+
// src/core/update.ts
|
|
10106
|
+
var import_node_fs9 = __toESM(require("node:fs"));
|
|
10107
|
+
var import_node_path11 = __toESM(require("node:path"));
|
|
10108
|
+
var import_node_child_process4 = require("node:child_process");
|
|
10109
|
+
init_config();
|
|
10110
|
+
|
|
9859
10111
|
// src/version.ts
|
|
9860
|
-
var APP_VERSION = true ? "0.
|
|
10112
|
+
var APP_VERSION = true ? "0.2.0" : "0.0.0-dev";
|
|
10113
|
+
|
|
10114
|
+
// src/core/update.ts
|
|
10115
|
+
var NPM_PACKAGE = "codex-token-tracker";
|
|
10116
|
+
var CHECK_TTL_MS = 6 * 60 * 60 * 1e3;
|
|
10117
|
+
var REQUEST_TIMEOUT_MS = 8e3;
|
|
10118
|
+
var INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
10119
|
+
var LOG_TAIL_CHARS = 4e3;
|
|
10120
|
+
function parseVersion(v2) {
|
|
10121
|
+
const s = String(v2 ?? "").trim().replace(/^v/, "");
|
|
10122
|
+
const i2 = s.indexOf("-");
|
|
10123
|
+
const core = i2 === -1 ? s : s.slice(0, i2);
|
|
10124
|
+
const pre = i2 === -1 ? "" : s.slice(i2 + 1);
|
|
10125
|
+
const n = core.split(".").map((x) => Number.parseInt(x, 10) || 0);
|
|
10126
|
+
return { nums: [n[0] ?? 0, n[1] ?? 0, n[2] ?? 0], pre };
|
|
10127
|
+
}
|
|
10128
|
+
function compareVersions(a, b) {
|
|
10129
|
+
const A = parseVersion(a);
|
|
10130
|
+
const B = parseVersion(b);
|
|
10131
|
+
for (let i2 = 0; i2 < 3; i2++) if (A.nums[i2] !== B.nums[i2]) return A.nums[i2] < B.nums[i2] ? -1 : 1;
|
|
10132
|
+
if (A.pre === B.pre) return 0;
|
|
10133
|
+
if (!A.pre) return 1;
|
|
10134
|
+
if (!B.pre) return -1;
|
|
10135
|
+
return A.pre < B.pre ? -1 : 1;
|
|
10136
|
+
}
|
|
10137
|
+
function isDevBuild(version2) {
|
|
10138
|
+
return version2.startsWith("0.0.0");
|
|
10139
|
+
}
|
|
10140
|
+
function registryBase() {
|
|
10141
|
+
const r = process.env.CODEX_TRACKER_REGISTRY || process.env.npm_config_registry || process.env.NPM_CONFIG_REGISTRY || "https://registry.npmjs.org";
|
|
10142
|
+
return r.replace(/\/+$/, "");
|
|
10143
|
+
}
|
|
10144
|
+
async function fetchLatestVersion(signal) {
|
|
10145
|
+
const ctrl = new AbortController();
|
|
10146
|
+
const timer = setTimeout(() => ctrl.abort(), REQUEST_TIMEOUT_MS);
|
|
10147
|
+
signal?.addEventListener("abort", () => ctrl.abort(), { once: true });
|
|
10148
|
+
try {
|
|
10149
|
+
const res = await fetch(`${registryBase()}/-/package/${NPM_PACKAGE}/dist-tags`, {
|
|
10150
|
+
signal: ctrl.signal,
|
|
10151
|
+
headers: { accept: "application/json" }
|
|
10152
|
+
});
|
|
10153
|
+
if (!res.ok) throw new Error(`registry responded ${res.status}`);
|
|
10154
|
+
const json = await res.json();
|
|
10155
|
+
const latest = json?.latest;
|
|
10156
|
+
if (typeof latest !== "string" || !latest) throw new Error("registry returned no `latest` tag");
|
|
10157
|
+
return latest;
|
|
10158
|
+
} finally {
|
|
10159
|
+
clearTimeout(timer);
|
|
10160
|
+
}
|
|
10161
|
+
}
|
|
10162
|
+
function detectPackageManager(installDir = import_node_path11.default.resolve(__dirname, "..")) {
|
|
10163
|
+
const p = installDir.replace(/\\/g, "/").toLowerCase();
|
|
10164
|
+
if (/\/\.?bun\//.test(p)) return "bun";
|
|
10165
|
+
if (/\/\.?pnpm[/-]/.test(p)) return "pnpm";
|
|
10166
|
+
if (/\/\.?yarn\//.test(p)) return "yarn";
|
|
10167
|
+
const ua = (process.env.npm_config_user_agent ?? "").toLowerCase();
|
|
10168
|
+
if (ua.startsWith("pnpm")) return "pnpm";
|
|
10169
|
+
if (ua.startsWith("yarn")) return "yarn";
|
|
10170
|
+
if (ua.startsWith("bun")) return "bun";
|
|
10171
|
+
return "npm";
|
|
10172
|
+
}
|
|
10173
|
+
function updateArgs(pm, spec = `${NPM_PACKAGE}@latest`) {
|
|
10174
|
+
switch (pm) {
|
|
10175
|
+
case "pnpm":
|
|
10176
|
+
return ["add", "-g", spec];
|
|
10177
|
+
case "yarn":
|
|
10178
|
+
return ["global", "add", spec];
|
|
10179
|
+
case "bun":
|
|
10180
|
+
return ["add", "-g", spec];
|
|
10181
|
+
default:
|
|
10182
|
+
return ["install", "-g", spec];
|
|
10183
|
+
}
|
|
10184
|
+
}
|
|
10185
|
+
function updateCommand(pm, spec) {
|
|
10186
|
+
return `${pm} ${updateArgs(pm, spec).join(" ")}`;
|
|
10187
|
+
}
|
|
10188
|
+
function cachePath() {
|
|
10189
|
+
return import_node_path11.default.join(configDir(), "update.json");
|
|
10190
|
+
}
|
|
10191
|
+
function readCache() {
|
|
10192
|
+
try {
|
|
10193
|
+
const raw = JSON.parse(import_node_fs9.default.readFileSync(cachePath(), "utf8"));
|
|
10194
|
+
if (typeof raw?.latest === "string" && typeof raw.checkedAt === "number") {
|
|
10195
|
+
return { latest: raw.latest, checkedAt: raw.checkedAt };
|
|
10196
|
+
}
|
|
10197
|
+
} catch {
|
|
10198
|
+
}
|
|
10199
|
+
return null;
|
|
10200
|
+
}
|
|
10201
|
+
function writeCache(c) {
|
|
10202
|
+
try {
|
|
10203
|
+
import_node_fs9.default.mkdirSync(import_node_path11.default.dirname(cachePath()), { recursive: true });
|
|
10204
|
+
import_node_fs9.default.writeFileSync(cachePath(), JSON.stringify(c, null, 2));
|
|
10205
|
+
} catch {
|
|
10206
|
+
}
|
|
10207
|
+
}
|
|
10208
|
+
function info(current, latest, checkedAt, error) {
|
|
10209
|
+
const pm = detectPackageManager();
|
|
10210
|
+
return {
|
|
10211
|
+
current,
|
|
10212
|
+
latest,
|
|
10213
|
+
available: Boolean(latest) && !isDevBuild(current) && compareVersions(latest, current) > 0,
|
|
10214
|
+
checkedAt,
|
|
10215
|
+
error,
|
|
10216
|
+
packageManager: pm,
|
|
10217
|
+
command: updateCommand(pm)
|
|
10218
|
+
};
|
|
10219
|
+
}
|
|
10220
|
+
async function checkForUpdate(opts = {}) {
|
|
10221
|
+
const current = opts.current ?? APP_VERSION;
|
|
10222
|
+
const cached = readCache();
|
|
10223
|
+
if (!opts.force && cached && Date.now() - cached.checkedAt < CHECK_TTL_MS) {
|
|
10224
|
+
return info(current, cached.latest, cached.checkedAt, null);
|
|
10225
|
+
}
|
|
10226
|
+
try {
|
|
10227
|
+
const latest = await fetchLatestVersion(opts.signal);
|
|
10228
|
+
const checkedAt = Date.now();
|
|
10229
|
+
writeCache({ latest, checkedAt });
|
|
10230
|
+
return info(current, latest, checkedAt, null);
|
|
10231
|
+
} catch (err) {
|
|
10232
|
+
return info(current, cached?.latest ?? null, cached?.checkedAt ?? null, err.message);
|
|
10233
|
+
}
|
|
10234
|
+
}
|
|
10235
|
+
function runUpdate(opts = {}) {
|
|
10236
|
+
const pm = detectPackageManager();
|
|
10237
|
+
const spec = opts.version ? `${NPM_PACKAGE}@${opts.version}` : `${NPM_PACKAGE}@latest`;
|
|
10238
|
+
const args = updateArgs(pm, spec);
|
|
10239
|
+
const command = `${pm} ${args.join(" ")}`;
|
|
10240
|
+
return new Promise((resolve) => {
|
|
10241
|
+
let output = "";
|
|
10242
|
+
const collect = (chunk) => {
|
|
10243
|
+
const text = chunk.toString();
|
|
10244
|
+
opts.onOutput?.(text);
|
|
10245
|
+
output = (output + text).slice(-LOG_TAIL_CHARS);
|
|
10246
|
+
};
|
|
10247
|
+
let child;
|
|
10248
|
+
try {
|
|
10249
|
+
child = (0, import_node_child_process4.spawn)(pm, args, {
|
|
10250
|
+
// Electron's bundled Node has no shell PATH resolution for `npm.cmd` on Windows.
|
|
10251
|
+
shell: process.platform === "win32",
|
|
10252
|
+
env: { ...process.env, ELECTRON_RUN_AS_NODE: void 0 },
|
|
10253
|
+
windowsHide: true
|
|
10254
|
+
});
|
|
10255
|
+
} catch (err) {
|
|
10256
|
+
resolve({ ok: false, code: null, command, output: err.message });
|
|
10257
|
+
return;
|
|
10258
|
+
}
|
|
10259
|
+
const timer = setTimeout(() => child.kill(), INSTALL_TIMEOUT_MS);
|
|
10260
|
+
child.stdout?.on("data", collect);
|
|
10261
|
+
child.stderr?.on("data", collect);
|
|
10262
|
+
child.on("error", (err) => {
|
|
10263
|
+
clearTimeout(timer);
|
|
10264
|
+
resolve({ ok: false, code: null, command, output: output + err.message });
|
|
10265
|
+
});
|
|
10266
|
+
child.on("close", (code2) => {
|
|
10267
|
+
clearTimeout(timer);
|
|
10268
|
+
resolve({ ok: code2 === 0, code: code2, command, output });
|
|
10269
|
+
});
|
|
10270
|
+
});
|
|
10271
|
+
}
|
|
9861
10272
|
|
|
9862
10273
|
// src/core/engine.ts
|
|
9863
10274
|
var SHALLOW_MS = 3e3;
|
|
@@ -9866,6 +10277,7 @@ var TICK_MS = 2e3;
|
|
|
9866
10277
|
var REMOTE_MS = 6e4;
|
|
9867
10278
|
var LIVE_LIMITS_DEBOUNCE_MS = 1e4;
|
|
9868
10279
|
var LIVE_LIMITS_MIN_GAP_MS = 2e4;
|
|
10280
|
+
var UPDATE_CHECK_MS = 6 * 60 * 60 * 1e3;
|
|
9869
10281
|
var Engine = class extends import_node_events.EventEmitter {
|
|
9870
10282
|
constructor(opts) {
|
|
9871
10283
|
super();
|
|
@@ -9902,6 +10314,9 @@ var Engine = class extends import_node_events.EventEmitter {
|
|
|
9902
10314
|
liveLimitsInFlight = false;
|
|
9903
10315
|
liveLimitsTimer = null;
|
|
9904
10316
|
lastLiveLimitsAttempt = 0;
|
|
10317
|
+
update = null;
|
|
10318
|
+
updateStatus = "idle";
|
|
10319
|
+
updateLog = null;
|
|
9905
10320
|
get heatmapWeeks() {
|
|
9906
10321
|
return this.opts.heatmapWeeks ?? 16;
|
|
9907
10322
|
}
|
|
@@ -9923,6 +10338,8 @@ var Engine = class extends import_node_events.EventEmitter {
|
|
|
9923
10338
|
return;
|
|
9924
10339
|
}
|
|
9925
10340
|
void this.refreshLiveLimits(true);
|
|
10341
|
+
void this.checkUpdate(false);
|
|
10342
|
+
this.timers.push(setInterval(() => void this.checkUpdate(false), UPDATE_CHECK_MS));
|
|
9926
10343
|
this.store.startWatching(() => void this.refresh(false));
|
|
9927
10344
|
this.timers.push(setInterval(() => void this.refresh(false), SHALLOW_MS));
|
|
9928
10345
|
this.timers.push(setInterval(() => void this.refresh(true), DEEP_MS));
|
|
@@ -10029,7 +10446,7 @@ var Engine = class extends import_node_events.EventEmitter {
|
|
|
10029
10446
|
if (!this.signedIn) return { buckets: 0, sessions: 0 };
|
|
10030
10447
|
if (!this.stats) this.recompute();
|
|
10031
10448
|
try {
|
|
10032
|
-
const r = await this.uploader.pushAll(this.stats.buckets, this.
|
|
10449
|
+
const r = await this.uploader.pushAll(this.stats.buckets, this.stats.sessions, this.stats.sessionCosts);
|
|
10033
10450
|
this.emitSnapshot();
|
|
10034
10451
|
return r;
|
|
10035
10452
|
} catch (err) {
|
|
@@ -10053,6 +10470,48 @@ var Engine = class extends import_node_events.EventEmitter {
|
|
|
10053
10470
|
await this.uploader.fetchRemote(this.heatmapWeeks);
|
|
10054
10471
|
this.recompute();
|
|
10055
10472
|
}
|
|
10473
|
+
/**
|
|
10474
|
+
* Ask the npm registry for the newest published version (cached for 6 h unless `force`).
|
|
10475
|
+
* Best-effort: a failure is recorded on the snapshot, never thrown.
|
|
10476
|
+
*/
|
|
10477
|
+
async checkUpdate(force) {
|
|
10478
|
+
if (!this.config.checkUpdates) {
|
|
10479
|
+
this.update = null;
|
|
10480
|
+
return null;
|
|
10481
|
+
}
|
|
10482
|
+
if (this.updateStatus === "checking" || this.updateStatus === "installing") return this.update;
|
|
10483
|
+
this.updateStatus = "checking";
|
|
10484
|
+
this.emitSnapshot();
|
|
10485
|
+
this.update = await checkForUpdate({ force });
|
|
10486
|
+
this.updateStatus = "idle";
|
|
10487
|
+
if (this.update.error) this.opts.log?.(`update check failed: ${this.update.error}`);
|
|
10488
|
+
this.emitSnapshot();
|
|
10489
|
+
return this.update;
|
|
10490
|
+
}
|
|
10491
|
+
/**
|
|
10492
|
+
* Install the newest version globally with the package manager this copy came from.
|
|
10493
|
+
* The new code only takes effect once the app is restarted, so the UI says so rather than
|
|
10494
|
+
* pretending to hot-swap itself.
|
|
10495
|
+
*/
|
|
10496
|
+
async installUpdate() {
|
|
10497
|
+
if (this.updateStatus === "installing") return false;
|
|
10498
|
+
if (!this.update?.available) await this.checkUpdate(true);
|
|
10499
|
+
if (!this.update?.available) return false;
|
|
10500
|
+
this.updateStatus = "installing";
|
|
10501
|
+
this.updateLog = null;
|
|
10502
|
+
this.emitSnapshot();
|
|
10503
|
+
const r = await runUpdate({ version: this.update.latest ?? void 0 });
|
|
10504
|
+
this.updateStatus = r.ok ? "installed" : "failed";
|
|
10505
|
+
this.updateLog = r.ok ? null : `${r.command}
|
|
10506
|
+
${r.output}`.trim();
|
|
10507
|
+
if (!r.ok) this.opts.log?.(`update install failed (${r.code}): ${r.output}`);
|
|
10508
|
+
this.emitSnapshot();
|
|
10509
|
+
return r.ok;
|
|
10510
|
+
}
|
|
10511
|
+
updateState() {
|
|
10512
|
+
if (!this.config.checkUpdates || !this.update) return null;
|
|
10513
|
+
return { ...this.update, status: this.updateStatus, log: this.updateLog };
|
|
10514
|
+
}
|
|
10056
10515
|
/** Start the device-code login flow (resolves when approved / denied / expired / cancelled). */
|
|
10057
10516
|
async login(openBrowser, onCode) {
|
|
10058
10517
|
if (this.pending) return { status: "cancelled" };
|
|
@@ -10145,6 +10604,7 @@ var Engine = class extends import_node_events.EventEmitter {
|
|
|
10145
10604
|
rateLimits: this.liveLimits ?? fromLogRateLimits(s.logRateLimits),
|
|
10146
10605
|
rateLimitsError: this.config.liveRateLimits ? this.liveLimitsError : null,
|
|
10147
10606
|
rateLimitsUpdatedAt: this.liveLimits ? this.liveLimitsAt : s.logRateLimits?.observedAt ?? null,
|
|
10607
|
+
update: this.updateState(),
|
|
10148
10608
|
modelsToday: s.modelsToday,
|
|
10149
10609
|
modelsMonth: s.modelsMonth,
|
|
10150
10610
|
byAgentToday: s.byAgentToday,
|
|
@@ -10203,23 +10663,60 @@ function applyDashboardFlag(flags) {
|
|
|
10203
10663
|
function electronBinary() {
|
|
10204
10664
|
try {
|
|
10205
10665
|
const p = require("electron");
|
|
10206
|
-
return typeof p === "string" && p ? p : null;
|
|
10666
|
+
return typeof p === "string" && p && import_node_fs10.default.existsSync(p) ? p : null;
|
|
10207
10667
|
} catch {
|
|
10208
10668
|
return null;
|
|
10209
10669
|
}
|
|
10210
10670
|
}
|
|
10211
|
-
function
|
|
10671
|
+
function electronPackageDir() {
|
|
10672
|
+
try {
|
|
10673
|
+
return import_node_path12.default.dirname(require.resolve("electron/package.json"));
|
|
10674
|
+
} catch {
|
|
10675
|
+
return null;
|
|
10676
|
+
}
|
|
10677
|
+
}
|
|
10678
|
+
async function ensureElectron() {
|
|
10679
|
+
const existing = electronBinary();
|
|
10680
|
+
if (existing) return existing;
|
|
10681
|
+
const dir = electronPackageDir();
|
|
10682
|
+
if (!dir) return null;
|
|
10683
|
+
const t2 = makeT(lang());
|
|
10684
|
+
console.log(t2("cliDownloadingElectron"));
|
|
10685
|
+
const log2 = { info: (m) => console.log(m), error: (m) => console.error(m) };
|
|
10686
|
+
let bin = null;
|
|
10687
|
+
try {
|
|
10688
|
+
bin = await installElectronBinary(dir, log2);
|
|
10689
|
+
} catch (err) {
|
|
10690
|
+
log2.error(` ${err.message}`);
|
|
10691
|
+
}
|
|
10692
|
+
if (!bin) {
|
|
10693
|
+
const installer = import_node_path12.default.join(dir, "install.js");
|
|
10694
|
+
if (import_node_fs10.default.existsSync(installer)) {
|
|
10695
|
+
const env = { ...process.env };
|
|
10696
|
+
delete env.ELECTRON_SKIP_BINARY_DOWNLOAD;
|
|
10697
|
+
(0, import_node_child_process5.spawnSync)(process.execPath, [installer], { stdio: "inherit", cwd: dir, env });
|
|
10698
|
+
}
|
|
10699
|
+
}
|
|
10700
|
+
try {
|
|
10701
|
+
delete require.cache[require.resolve("electron")];
|
|
10702
|
+
} catch {
|
|
10703
|
+
}
|
|
10704
|
+
const found = electronBinary();
|
|
10705
|
+
if (!found) console.error(t2("cliElectronDownloadFailed"));
|
|
10706
|
+
return found;
|
|
10707
|
+
}
|
|
10708
|
+
async function startMenubar(background) {
|
|
10212
10709
|
const t2 = makeT(lang());
|
|
10213
|
-
const bin =
|
|
10710
|
+
const bin = await ensureElectron();
|
|
10214
10711
|
if (!bin) {
|
|
10215
10712
|
console.error(t2("cliNoElectron"));
|
|
10216
10713
|
return 2;
|
|
10217
10714
|
}
|
|
10218
10715
|
const env = { ...process.env, CODEX_TRACKER_HOME: configDir() };
|
|
10219
10716
|
delete env.ELECTRON_RUN_AS_NODE;
|
|
10220
|
-
const mainPath =
|
|
10717
|
+
const mainPath = import_node_path12.default.join(__dirname, "main.js");
|
|
10221
10718
|
console.log(t2("cliStartingMenubar"));
|
|
10222
|
-
const child = (0,
|
|
10719
|
+
const child = (0, import_node_child_process5.spawn)(bin, [mainPath], {
|
|
10223
10720
|
stdio: background ? "ignore" : "inherit",
|
|
10224
10721
|
detached: background,
|
|
10225
10722
|
env,
|
|
@@ -10302,6 +10799,10 @@ async function runStatus(json) {
|
|
|
10302
10799
|
line(t2("cliStatusDirs"), s.sessionDirs.join("\n" + " ".repeat(15)) || "-");
|
|
10303
10800
|
const byAgent = Object.entries(s.counts.byAgent).sort((a, b) => b[1].sessions - a[1].sessions).map(([agent, c]) => `${agent} ${c.sessions}`).join(", ");
|
|
10304
10801
|
console.log(t2("sessions", { n: s.counts.sessions }) + " \xB7 " + t2("files", { n: s.counts.files }) + (byAgent ? ` (${byAgent})` : ""));
|
|
10802
|
+
if (cfg.checkUpdates) {
|
|
10803
|
+
const u = await checkForUpdate();
|
|
10804
|
+
if (u.available) console.log("\n" + t2("cliUpdateAvailable", { current: u.current, latest: u.latest ?? "?" }) + ` \u2014 ${u.command}`);
|
|
10805
|
+
}
|
|
10305
10806
|
return 0;
|
|
10306
10807
|
}
|
|
10307
10808
|
async function runLogin(flags) {
|
|
@@ -10451,6 +10952,31 @@ function runConfig(positional) {
|
|
|
10451
10952
|
console.log(t2("cliConfigDir", { dir: configDir() }));
|
|
10452
10953
|
return 0;
|
|
10453
10954
|
}
|
|
10955
|
+
async function runUpdateCommand(flags) {
|
|
10956
|
+
const t2 = makeT(lang());
|
|
10957
|
+
const info2 = await checkForUpdate({ force: true });
|
|
10958
|
+
if (info2.error && !info2.latest) {
|
|
10959
|
+
console.error(t2("cliUpdateCheckFailed", { message: info2.error }));
|
|
10960
|
+
return 1;
|
|
10961
|
+
}
|
|
10962
|
+
if (!info2.available) {
|
|
10963
|
+
console.log(t2("cliUpdateLatest", { version: info2.current }));
|
|
10964
|
+
return 0;
|
|
10965
|
+
}
|
|
10966
|
+
console.log(t2("cliUpdateAvailable", { current: info2.current, latest: info2.latest ?? "?" }));
|
|
10967
|
+
if (flags.check === true) {
|
|
10968
|
+
console.log(` ${info2.command}`);
|
|
10969
|
+
return 0;
|
|
10970
|
+
}
|
|
10971
|
+
console.log(t2("cliUpdateRunning", { command: info2.command }));
|
|
10972
|
+
const r = await runUpdate({ version: info2.latest ?? void 0, onOutput: (c) => process.stdout.write(c) });
|
|
10973
|
+
if (!r.ok) {
|
|
10974
|
+
console.error(t2("cliUpdateFailed", { code: r.code ?? "?", command: r.command }));
|
|
10975
|
+
return 1;
|
|
10976
|
+
}
|
|
10977
|
+
console.log(t2("cliUpdateDone", { version: info2.latest ?? "?" }));
|
|
10978
|
+
return 0;
|
|
10979
|
+
}
|
|
10454
10980
|
function runLang(positional) {
|
|
10455
10981
|
const value = positional[0];
|
|
10456
10982
|
if (!value || !["en", "zh", "auto"].includes(value)) {
|
|
@@ -10479,7 +11005,7 @@ async function main() {
|
|
|
10479
11005
|
console.log(t2("cliNoDisplay"));
|
|
10480
11006
|
return runAgent(args.flags);
|
|
10481
11007
|
}
|
|
10482
|
-
if (!
|
|
11008
|
+
if (!await ensureElectron()) {
|
|
10483
11009
|
console.log(t2("cliNoElectron"));
|
|
10484
11010
|
return runAgent(args.flags);
|
|
10485
11011
|
}
|
|
@@ -10508,6 +11034,9 @@ async function main() {
|
|
|
10508
11034
|
return runConfig(args.positional);
|
|
10509
11035
|
case "lang":
|
|
10510
11036
|
return runLang(args.positional);
|
|
11037
|
+
case "update":
|
|
11038
|
+
case "upgrade":
|
|
11039
|
+
return runUpdateCommand(args.flags);
|
|
10511
11040
|
default:
|
|
10512
11041
|
console.error(t2("cliUnknownCommand", { command: args.command }));
|
|
10513
11042
|
console.log(t2("cliUsage"));
|