codex-token-tracker 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.md +63 -2
- package/dist/cli.js +261 -15
- package/dist/main.js +225 -18
- package/dist/preload.js +1 -0
- package/dist/renderer/renderer.js +110 -3
- package/dist/renderer/styles.css +99 -0
- package/package.json +14 -14
|
@@ -12973,6 +12973,7 @@
|
|
|
12973
12973
|
estimatedPricingHint: "Pricing estimated: model not in the price table",
|
|
12974
12974
|
other: "Other",
|
|
12975
12975
|
lastUpload: "Last upload {time}",
|
|
12976
|
+
lastSync: "Last full sync {time}",
|
|
12976
12977
|
neverUploaded: "Not uploaded yet",
|
|
12977
12978
|
uploadError: "Upload error: {message}",
|
|
12978
12979
|
allDevicesToday: "All devices today",
|
|
@@ -12991,10 +12992,26 @@
|
|
|
12991
12992
|
launchAtLogin: "Launch at login",
|
|
12992
12993
|
refresh: "Refresh",
|
|
12993
12994
|
quit: "Quit",
|
|
12995
|
+
// full sync
|
|
12996
|
+
syncNow: "Sync now",
|
|
12997
|
+
syncing: "Syncing\u2026",
|
|
12998
|
+
syncTitle: "Rescan every agent on this device and re-upload its full usage history",
|
|
12999
|
+
syncScanning: "Rescanning every agent's transcripts\u2026",
|
|
13000
|
+
syncComputing: "Recomputing token usage\u2026",
|
|
13001
|
+
syncUploading: "Re-uploading this device's history\u2026",
|
|
13002
|
+
syncDownloading: "Fetching other devices\u2026",
|
|
13003
|
+
syncLimits: "Refreshing rate limits\u2026",
|
|
13004
|
+
syncDone: "Synced {agents} \xB7 {files} files \xB7 {sessions} sessions \xB7 {buckets} hours re-uploaded",
|
|
13005
|
+
syncDoneLocal: "Rescanned {agents} \xB7 {files} files \xB7 {sessions} sessions \u2014 sign in to upload",
|
|
13006
|
+
syncFailed: "Sync failed: {message}",
|
|
12994
13007
|
sessions: "{n} sessions",
|
|
12995
13008
|
files: "{n} files",
|
|
12996
13009
|
noData: "No usage found yet. Run Codex and this will fill in.",
|
|
12997
13010
|
version: "v{version}",
|
|
13011
|
+
// dev builds
|
|
13012
|
+
devBuild: "DEV",
|
|
13013
|
+
devBuildTitle: "Local build \u2014 uploading to {url} (dev environment). Config: {dir}",
|
|
13014
|
+
devBuildMenu: "Dev build \u2192 {url}",
|
|
12998
13015
|
justNow: "just now",
|
|
12999
13016
|
never: "never",
|
|
13000
13017
|
local: "Local",
|
|
@@ -13022,6 +13039,7 @@ Commands:
|
|
|
13022
13039
|
login Connect this device to your dashboard (--dashboard <url>)
|
|
13023
13040
|
logout Disconnect this device
|
|
13024
13041
|
status Print today's usage, live session and rate limits
|
|
13042
|
+
sync Rescan every agent and re-upload this device's full history (calibrate)
|
|
13025
13043
|
paths Show detected session directories (Codex, pi, OpenCode, Cline/Roo/Kilo, Hermes, custom)
|
|
13026
13044
|
update Install the newest published version (--check only reports)
|
|
13027
13045
|
config get|set Read or change settings (config set uploadIntervalSec 30, config set sources.pi false)
|
|
@@ -13076,7 +13094,15 @@ Options:
|
|
|
13076
13094
|
cliUpdateRunning: "Running: {command}",
|
|
13077
13095
|
cliUpdateDone: "Updated to {version}. Restart the menu bar app (or rerun the CLI) to use it.",
|
|
13078
13096
|
cliUpdateFailed: "Update failed (exit {code}). Run it yourself: {command}",
|
|
13079
|
-
cliUpdateCheckFailed: "Could not reach the npm registry: {message}"
|
|
13097
|
+
cliUpdateCheckFailed: "Could not reach the npm registry: {message}",
|
|
13098
|
+
cliSyncStart: "Full sync: rescanning every agent on this device\u2026",
|
|
13099
|
+
cliSyncScanned: " scanned {files} files in {roots} directories ({agents})",
|
|
13100
|
+
cliSyncUploaded: " re-uploaded {buckets} hour buckets and {sessions} sessions",
|
|
13101
|
+
cliSyncLocal: " not signed in \u2014 nothing uploaded (run `codex-tracker login` first)",
|
|
13102
|
+
cliSyncDone: "Sync complete in {seconds}s \u2014 {sessions} sessions, today {tokens} tok \xB7 {cost}",
|
|
13103
|
+
cliSyncFailed: "Sync failed: {message}",
|
|
13104
|
+
cliChannelDev: "Dev build \u2014 dashboard {url} \xB7 config {dir}",
|
|
13105
|
+
cliUpdateDevBuild: "This is a local dev build; `update` would install the published package over it. Run `pnpm build` in the repo instead."
|
|
13080
13106
|
};
|
|
13081
13107
|
|
|
13082
13108
|
// src/i18n/zh.ts
|
|
@@ -13124,6 +13150,7 @@ Options:
|
|
|
13124
13150
|
estimatedPricingHint: "\u4EF7\u683C\u4E3A\u4F30\u7B97\u503C\uFF1A\u8BE5\u6A21\u578B\u4E0D\u5728\u4EF7\u76EE\u8868\u4E2D",
|
|
13125
13151
|
other: "\u5176\u4ED6",
|
|
13126
13152
|
lastUpload: "\u4E0A\u6B21\u4E0A\u4F20 {time}",
|
|
13153
|
+
lastSync: "\u4E0A\u6B21\u5B8C\u6574\u540C\u6B65 {time}",
|
|
13127
13154
|
neverUploaded: "\u5C1A\u672A\u4E0A\u4F20",
|
|
13128
13155
|
uploadError: "\u4E0A\u4F20\u5931\u8D25\uFF1A{message}",
|
|
13129
13156
|
allDevicesToday: "\u6240\u6709\u8BBE\u5907\u4ECA\u65E5",
|
|
@@ -13142,10 +13169,26 @@ Options:
|
|
|
13142
13169
|
launchAtLogin: "\u5F00\u673A\u81EA\u542F\u52A8",
|
|
13143
13170
|
refresh: "\u5237\u65B0",
|
|
13144
13171
|
quit: "\u9000\u51FA",
|
|
13172
|
+
// 完整同步
|
|
13173
|
+
syncNow: "\u7ACB\u5373\u540C\u6B65",
|
|
13174
|
+
syncing: "\u540C\u6B65\u4E2D\u2026",
|
|
13175
|
+
syncTitle: "\u91CD\u65B0\u626B\u63CF\u672C\u8BBE\u5907\u4E0A\u7684\u6240\u6709\u667A\u80FD\u4F53\uFF0C\u5E76\u91CD\u65B0\u4E0A\u4F20\u5B8C\u6574\u7528\u91CF\u5386\u53F2",
|
|
13176
|
+
syncScanning: "\u6B63\u5728\u91CD\u65B0\u626B\u63CF\u6240\u6709\u667A\u80FD\u4F53\u7684\u4F1A\u8BDD\u8BB0\u5F55\u2026",
|
|
13177
|
+
syncComputing: "\u6B63\u5728\u91CD\u65B0\u8BA1\u7B97 token \u7528\u91CF\u2026",
|
|
13178
|
+
syncUploading: "\u6B63\u5728\u91CD\u65B0\u4E0A\u4F20\u672C\u8BBE\u5907\u7684\u5386\u53F2\u6570\u636E\u2026",
|
|
13179
|
+
syncDownloading: "\u6B63\u5728\u62C9\u53D6\u5176\u4ED6\u8BBE\u5907\u7684\u6570\u636E\u2026",
|
|
13180
|
+
syncLimits: "\u6B63\u5728\u5237\u65B0\u9650\u989D\u2026",
|
|
13181
|
+
syncDone: "\u5DF2\u540C\u6B65 {agents} \xB7 {files} \u4E2A\u6587\u4EF6 \xB7 {sessions} \u4E2A\u4F1A\u8BDD \xB7 \u91CD\u65B0\u4E0A\u4F20 {buckets} \u4E2A\u5C0F\u65F6\u6BB5",
|
|
13182
|
+
syncDoneLocal: "\u5DF2\u91CD\u65B0\u626B\u63CF {agents} \xB7 {files} \u4E2A\u6587\u4EF6 \xB7 {sessions} \u4E2A\u4F1A\u8BDD \u2014 \u767B\u5F55\u540E\u53EF\u4E0A\u4F20",
|
|
13183
|
+
syncFailed: "\u540C\u6B65\u5931\u8D25\uFF1A{message}",
|
|
13145
13184
|
sessions: "{n} \u4E2A\u4F1A\u8BDD",
|
|
13146
13185
|
files: "{n} \u4E2A\u6587\u4EF6",
|
|
13147
13186
|
noData: "\u8FD8\u6CA1\u6709\u7528\u91CF\u6570\u636E\u3002\u8FD0\u884C Codex \u540E\u8FD9\u91CC\u4F1A\u81EA\u52A8\u66F4\u65B0\u3002",
|
|
13148
13187
|
version: "v{version}",
|
|
13188
|
+
// 开发版
|
|
13189
|
+
devBuild: "\u5F00\u53D1\u7248",
|
|
13190
|
+
devBuildTitle: "\u672C\u5730\u6784\u5EFA \u2014\u2014 \u6570\u636E\u4E0A\u4F20\u81F3 {url}\uFF08\u5F00\u53D1\u73AF\u5883\uFF09\u3002\u914D\u7F6E\u76EE\u5F55\uFF1A{dir}",
|
|
13191
|
+
devBuildMenu: "\u5F00\u53D1\u7248 \u2192 {url}",
|
|
13149
13192
|
justNow: "\u521A\u521A",
|
|
13150
13193
|
never: "\u4ECE\u672A",
|
|
13151
13194
|
local: "\u672C\u673A",
|
|
@@ -13171,6 +13214,7 @@ Options:
|
|
|
13171
13214
|
login \u5C06\u672C\u8BBE\u5907\u8FDE\u63A5\u5230\u4EEA\u8868\u76D8\uFF08--dashboard <url>\uFF09
|
|
13172
13215
|
logout \u65AD\u5F00\u672C\u8BBE\u5907
|
|
13173
13216
|
status \u6253\u5370\u4ECA\u65E5\u7528\u91CF\u3001\u5B9E\u65F6\u4F1A\u8BDD\u4E0E\u9650\u989D
|
|
13217
|
+
sync \u91CD\u65B0\u626B\u63CF\u6240\u6709\u667A\u80FD\u4F53\u5E76\u91CD\u65B0\u4E0A\u4F20\u672C\u8BBE\u5907\u5B8C\u6574\u5386\u53F2\uFF08\u6821\u51C6\uFF09
|
|
13174
13218
|
paths \u663E\u793A\u68C0\u6D4B\u5230\u7684\u4F1A\u8BDD\u76EE\u5F55\uFF08Codex\u3001pi\u3001OpenCode\u3001Cline/Roo/Kilo\u3001Hermes\u3001\u81EA\u5B9A\u4E49\uFF09
|
|
13175
13219
|
update \u5B89\u88C5\u6700\u65B0\u53D1\u5E03\u7248\u672C\uFF08--check \u53EA\u68C0\u67E5\u4E0D\u5B89\u88C5\uFF09
|
|
13176
13220
|
config get|set \u8BFB\u53D6\u6216\u4FEE\u6539\u8BBE\u7F6E\uFF08config set uploadIntervalSec 30\uFF0Cconfig set sources.pi false\uFF09
|
|
@@ -13225,7 +13269,15 @@ Options:
|
|
|
13225
13269
|
cliUpdateRunning: "\u6B63\u5728\u6267\u884C\uFF1A{command}",
|
|
13226
13270
|
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",
|
|
13227
13271
|
cliUpdateFailed: "\u66F4\u65B0\u5931\u8D25\uFF08\u9000\u51FA\u7801 {code}\uFF09\u3002\u8BF7\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
13228
|
-
cliUpdateCheckFailed: "\u65E0\u6CD5\u8BBF\u95EE npm registry\uFF1A{message}"
|
|
13272
|
+
cliUpdateCheckFailed: "\u65E0\u6CD5\u8BBF\u95EE npm registry\uFF1A{message}",
|
|
13273
|
+
cliSyncStart: "\u5B8C\u6574\u540C\u6B65\uFF1A\u6B63\u5728\u91CD\u65B0\u626B\u63CF\u672C\u8BBE\u5907\u4E0A\u7684\u6240\u6709\u667A\u80FD\u4F53\u2026",
|
|
13274
|
+
cliSyncScanned: " \u5DF2\u626B\u63CF {roots} \u4E2A\u76EE\u5F55\u4E2D\u7684 {files} \u4E2A\u6587\u4EF6\uFF08{agents}\uFF09",
|
|
13275
|
+
cliSyncUploaded: " \u5DF2\u91CD\u65B0\u4E0A\u4F20 {buckets} \u4E2A\u5C0F\u65F6\u6BB5\u4E0E {sessions} \u4E2A\u4F1A\u8BDD",
|
|
13276
|
+
cliSyncLocal: " \u672A\u767B\u5F55 \u2014 \u6CA1\u6709\u4E0A\u4F20\u4EFB\u4F55\u6570\u636E\uFF08\u8BF7\u5148\u8FD0\u884C `codex-tracker login`\uFF09",
|
|
13277
|
+
cliSyncDone: "\u540C\u6B65\u5B8C\u6210\uFF0C\u7528\u65F6 {seconds} \u79D2 \u2014 {sessions} \u4E2A\u4F1A\u8BDD\uFF0C\u4ECA\u65E5 {tokens} tok \xB7 {cost}",
|
|
13278
|
+
cliSyncFailed: "\u540C\u6B65\u5931\u8D25\uFF1A{message}",
|
|
13279
|
+
cliChannelDev: "\u5F00\u53D1\u7248 \u2014\u2014 \u4EEA\u8868\u76D8 {url} \xB7 \u914D\u7F6E\u76EE\u5F55 {dir}",
|
|
13280
|
+
cliUpdateDevBuild: "\u8FD9\u662F\u672C\u5730\u5F00\u53D1\u7248\uFF1B`update` \u4F1A\u7528\u5DF2\u53D1\u5E03\u7684\u5305\u8986\u76D6\u5B83\u3002\u8BF7\u6539\u4E3A\u5728\u4ED3\u5E93\u4E2D\u8FD0\u884C `pnpm build`\u3002"
|
|
13229
13281
|
};
|
|
13230
13282
|
|
|
13231
13283
|
// src/i18n/index.ts
|
|
@@ -13489,6 +13541,40 @@ Options:
|
|
|
13489
13541
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "primary", onClick: () => void bridge().installUpdate(), children: t2("update") })
|
|
13490
13542
|
] });
|
|
13491
13543
|
}
|
|
13544
|
+
function SyncIcon({ spinning }) {
|
|
13545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { className: `sync-icon${spinning ? " spinning" : ""}`, viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", "aria-hidden": true, children: [
|
|
13546
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M13.5 8a5.5 5.5 0 1 1-1.61-3.89" }),
|
|
13547
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M13.6 2.2v2.9h-2.9" })
|
|
13548
|
+
] });
|
|
13549
|
+
}
|
|
13550
|
+
var PHASE_KEY = {
|
|
13551
|
+
scanning: "syncScanning",
|
|
13552
|
+
computing: "syncComputing",
|
|
13553
|
+
uploading: "syncUploading",
|
|
13554
|
+
downloading: "syncDownloading",
|
|
13555
|
+
limits: "syncLimits"
|
|
13556
|
+
};
|
|
13557
|
+
function SyncBar({ sync, t: t2 }) {
|
|
13558
|
+
if (sync.status === "idle") return null;
|
|
13559
|
+
if (sync.status === "running") {
|
|
13560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "sync-bar", children: [
|
|
13561
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SyncIcon, { spinning: true }),
|
|
13562
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", children: t2(sync.phase ? PHASE_KEY[sync.phase] : "syncing") })
|
|
13563
|
+
] });
|
|
13564
|
+
}
|
|
13565
|
+
if (sync.status === "error") {
|
|
13566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "sync-bar failed", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", title: sync.error ?? "", children: t2("syncFailed", { message: sync.error ?? "?" }) }) });
|
|
13567
|
+
}
|
|
13568
|
+
const r = sync.last;
|
|
13569
|
+
if (!r) return null;
|
|
13570
|
+
const params = {
|
|
13571
|
+
agents: r.agents.length ? r.agents.join(", ") : "\u2014",
|
|
13572
|
+
files: r.files,
|
|
13573
|
+
sessions: r.sessions,
|
|
13574
|
+
buckets: r.uploadedBuckets
|
|
13575
|
+
};
|
|
13576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "sync-bar done", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", title: `${r.roots} dirs \xB7 ${(r.durationMs / 1e3).toFixed(1)}s`, children: t2(r.uploaded ? "syncDone" : "syncDoneLocal", params) }) });
|
|
13577
|
+
}
|
|
13492
13578
|
function App() {
|
|
13493
13579
|
const [snap, setSnap] = (0, import_react2.useState)(null);
|
|
13494
13580
|
const [, setClock] = (0, import_react2.useState)(0);
|
|
@@ -13519,18 +13605,31 @@ Options:
|
|
|
13519
13605
|
const agents = agentsPeriod === "today" ? snap.byAgentToday : snap.byAgentMonth;
|
|
13520
13606
|
const auth = snap.auth;
|
|
13521
13607
|
const rl = snap.rateLimits;
|
|
13608
|
+
const syncing = snap.sync.status === "running";
|
|
13522
13609
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app", children: [
|
|
13523
13610
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: "header", children: [
|
|
13524
13611
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "brand", children: [
|
|
13525
13612
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Logo, {}),
|
|
13526
13613
|
t2("appName")
|
|
13527
13614
|
] }),
|
|
13615
|
+
snap.channel === "dev" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "badge-dev", title: t2("devBuildTitle", { url: auth.dashboardUrl, dir: snap.configDir }), children: t2("devBuild") }) : null,
|
|
13528
13616
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "spacer" }),
|
|
13529
13617
|
live ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "pill live", title: live.projectName ?? "", children: [
|
|
13530
13618
|
live.tokensPerSecond.toFixed(1),
|
|
13531
13619
|
" ",
|
|
13532
13620
|
t2("tokensPerSec")
|
|
13533
13621
|
] }) : null,
|
|
13622
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
13623
|
+
"button",
|
|
13624
|
+
{
|
|
13625
|
+
className: "ghost icon-btn",
|
|
13626
|
+
onClick: () => void bridge().syncNow(),
|
|
13627
|
+
disabled: syncing,
|
|
13628
|
+
title: `${t2("syncTitle")}${snap.sync.last ? ` \xB7 ${t2("lastSync", { time: relativeTime(L, snap.sync.last.finishedAt, now) })}` : ""}`,
|
|
13629
|
+
"aria-label": t2("syncNow"),
|
|
13630
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SyncIcon, { spinning: syncing })
|
|
13631
|
+
}
|
|
13632
|
+
),
|
|
13534
13633
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "seg", role: "group", "aria-label": t2("language"), children: [
|
|
13535
13634
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: L === "en" ? "active" : "", onClick: () => void bridge().setLanguage("en"), children: "EN" }),
|
|
13536
13635
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: L === "zh" ? "active" : "", onClick: () => void bridge().setLanguage("zh"), children: "\u4E2D\u6587" })
|
|
@@ -13538,6 +13637,7 @@ Options:
|
|
|
13538
13637
|
] }),
|
|
13539
13638
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "scroll", children: [
|
|
13540
13639
|
snap.update ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UpdateBar, { update: snap.update, t: t2 }) : null,
|
|
13640
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SyncBar, { sync: snap.sync, t: t2 }),
|
|
13541
13641
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "card", children: [
|
|
13542
13642
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "card-title", children: [
|
|
13543
13643
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t2("today") }),
|
|
@@ -13736,7 +13836,14 @@ Options:
|
|
|
13736
13836
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "empty", style: { flex: 1 }, children: t2("signedOut") }),
|
|
13737
13837
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "primary", onClick: () => void bridge().login(), children: t2("signIn") })
|
|
13738
13838
|
] }),
|
|
13739
|
-
auth.error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "error", children: auth.error }) : null
|
|
13839
|
+
auth.error ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "error", children: auth.error }) : null,
|
|
13840
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "row sync-row", children: [
|
|
13841
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { className: "ghost sync-btn", onClick: () => void bridge().syncNow(), disabled: syncing, title: t2("syncTitle"), children: [
|
|
13842
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SyncIcon, { spinning: syncing }),
|
|
13843
|
+
syncing ? t2("syncing") : t2("syncNow")
|
|
13844
|
+
] }),
|
|
13845
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "hint", children: snap.sync.last ? t2("lastSync", { time: relativeTime(L, snap.sync.last.finishedAt, now) }) : snap.upload.lastUploadAt ? t2("lastUpload", { time: relativeTime(L, snap.upload.lastUploadAt, now) }) : t2("neverUploaded") })
|
|
13846
|
+
] })
|
|
13740
13847
|
] })
|
|
13741
13848
|
] }),
|
|
13742
13849
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("footer", { className: "footer", children: [
|
package/dist/renderer/styles.css
CHANGED
|
@@ -573,3 +573,102 @@ button.ghost:hover {
|
|
|
573
573
|
color: var(--muted);
|
|
574
574
|
user-select: text;
|
|
575
575
|
}
|
|
576
|
+
|
|
577
|
+
/* --- full sync --- */
|
|
578
|
+
button.icon-btn {
|
|
579
|
+
display: inline-flex;
|
|
580
|
+
align-items: center;
|
|
581
|
+
justify-content: center;
|
|
582
|
+
padding: 3px;
|
|
583
|
+
border-radius: 6px;
|
|
584
|
+
color: var(--muted);
|
|
585
|
+
}
|
|
586
|
+
button.icon-btn:hover:not(:disabled) {
|
|
587
|
+
color: var(--text);
|
|
588
|
+
}
|
|
589
|
+
button:disabled {
|
|
590
|
+
cursor: default;
|
|
591
|
+
opacity: 0.6;
|
|
592
|
+
}
|
|
593
|
+
.sync-icon {
|
|
594
|
+
width: 13px;
|
|
595
|
+
height: 13px;
|
|
596
|
+
flex: none;
|
|
597
|
+
}
|
|
598
|
+
.sync-icon.spinning {
|
|
599
|
+
animation: sync-spin 0.9s linear infinite;
|
|
600
|
+
color: var(--accent);
|
|
601
|
+
}
|
|
602
|
+
@keyframes sync-spin {
|
|
603
|
+
to {
|
|
604
|
+
transform: rotate(360deg);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
@media (prefers-reduced-motion: reduce) {
|
|
608
|
+
.sync-icon.spinning {
|
|
609
|
+
animation-duration: 2.4s;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
.sync-bar {
|
|
613
|
+
display: flex;
|
|
614
|
+
align-items: center;
|
|
615
|
+
gap: 8px;
|
|
616
|
+
margin: 10px 12px 0;
|
|
617
|
+
padding: 8px 10px;
|
|
618
|
+
border: 1px solid var(--border);
|
|
619
|
+
border-radius: 8px;
|
|
620
|
+
background: var(--accent-soft);
|
|
621
|
+
font-size: 11px;
|
|
622
|
+
color: var(--text);
|
|
623
|
+
}
|
|
624
|
+
.sync-bar .msg {
|
|
625
|
+
flex: 1;
|
|
626
|
+
min-width: 0;
|
|
627
|
+
overflow: hidden;
|
|
628
|
+
text-overflow: ellipsis;
|
|
629
|
+
white-space: nowrap;
|
|
630
|
+
}
|
|
631
|
+
.sync-bar.done { background: rgba(12, 163, 12, 0.1); }
|
|
632
|
+
.sync-bar.failed { background: rgba(208, 59, 59, 0.1); color: var(--critical); }
|
|
633
|
+
/* the result lists every agent it found — let it wrap onto a second line rather than truncate */
|
|
634
|
+
.sync-bar.done .msg,
|
|
635
|
+
.sync-bar.failed .msg {
|
|
636
|
+
white-space: normal;
|
|
637
|
+
overflow: visible;
|
|
638
|
+
}
|
|
639
|
+
.sync-row {
|
|
640
|
+
margin-top: 8px;
|
|
641
|
+
padding-top: 8px;
|
|
642
|
+
border-top: 1px solid var(--border);
|
|
643
|
+
}
|
|
644
|
+
button.sync-btn {
|
|
645
|
+
display: inline-flex;
|
|
646
|
+
align-items: center;
|
|
647
|
+
gap: 5px;
|
|
648
|
+
padding: 3px 8px;
|
|
649
|
+
border-color: var(--border);
|
|
650
|
+
}
|
|
651
|
+
.sync-row .hint {
|
|
652
|
+
flex: 1;
|
|
653
|
+
min-width: 0;
|
|
654
|
+
text-align: right;
|
|
655
|
+
font-size: 10.5px;
|
|
656
|
+
color: var(--muted);
|
|
657
|
+
overflow: hidden;
|
|
658
|
+
text-overflow: ellipsis;
|
|
659
|
+
white-space: nowrap;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/* --- dev build badge --- */
|
|
663
|
+
.badge-dev {
|
|
664
|
+
flex: none;
|
|
665
|
+
margin-left: 8px;
|
|
666
|
+
padding: 1px 6px;
|
|
667
|
+
border-radius: 5px;
|
|
668
|
+
font-size: 9.5px;
|
|
669
|
+
font-weight: 700;
|
|
670
|
+
letter-spacing: 0.06em;
|
|
671
|
+
text-transform: uppercase;
|
|
672
|
+
color: #fff;
|
|
673
|
+
background: var(--serious);
|
|
674
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-token-tracker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Menu bar / system tray app and headless agent that tracks OpenAI Codex token usage, cache hits, model mix and API-equivalent cost — and syncs it to your team dashboard.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Chen Li",
|
|
@@ -47,15 +47,6 @@
|
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|
|
49
49
|
},
|
|
50
|
-
"scripts": {
|
|
51
|
-
"build": "node scripts/build.mjs",
|
|
52
|
-
"dev": "node scripts/build.mjs --watch",
|
|
53
|
-
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
54
|
-
"start": "node bin/codex-tracker.js",
|
|
55
|
-
"prepublishOnly": "pnpm build && pnpm typecheck",
|
|
56
|
-
"build:icons": "node scripts/gen-icons.mjs",
|
|
57
|
-
"test": "node scripts/test.mjs"
|
|
58
|
-
},
|
|
59
50
|
"dependencies": {
|
|
60
51
|
"menubar": "^9.5.3"
|
|
61
52
|
},
|
|
@@ -63,8 +54,6 @@
|
|
|
63
54
|
"electron": "^38.8.6"
|
|
64
55
|
},
|
|
65
56
|
"devDependencies": {
|
|
66
|
-
"@codex-tracker/backend": "workspace:*",
|
|
67
|
-
"@codex-tracker/shared": "workspace:*",
|
|
68
57
|
"@types/node": "^22.15.0",
|
|
69
58
|
"@types/react": "^19.2.0",
|
|
70
59
|
"@types/react-dom": "^19.2.0",
|
|
@@ -72,6 +61,17 @@
|
|
|
72
61
|
"esbuild": "^0.28.2",
|
|
73
62
|
"react": "19.2.8",
|
|
74
63
|
"react-dom": "19.2.8",
|
|
75
|
-
"typescript": "~5.9.3"
|
|
64
|
+
"typescript": "~5.9.3",
|
|
65
|
+
"@codex-tracker/backend": "0.1.0",
|
|
66
|
+
"@codex-tracker/shared": "0.1.0"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "node scripts/build.mjs",
|
|
70
|
+
"build:release": "node scripts/build.mjs --release",
|
|
71
|
+
"dev": "node scripts/build.mjs --watch",
|
|
72
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
73
|
+
"start": "node bin/codex-tracker.js",
|
|
74
|
+
"build:icons": "node scripts/gen-icons.mjs",
|
|
75
|
+
"test": "node scripts/test.mjs"
|
|
76
76
|
}
|
|
77
|
-
}
|
|
77
|
+
}
|