codex-token-tracker 0.1.1 → 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 +110 -0
- package/README.md +100 -8
- package/dist/cli.js +664 -80
- package/dist/main.js +604 -89
- package/dist/preload.js +3 -0
- package/dist/renderer/renderer.js +235 -5
- package/dist/renderer/styles.css +129 -0
- package/package.json +5 -2
package/dist/preload.js
CHANGED
|
@@ -16,6 +16,9 @@ var bridge = {
|
|
|
16
16
|
cancelLogin: () => import_electron.ipcRenderer.invoke("auth:cancel"),
|
|
17
17
|
logout: () => import_electron.ipcRenderer.invoke("auth:logout"),
|
|
18
18
|
refresh: () => import_electron.ipcRenderer.invoke("refresh"),
|
|
19
|
+
syncNow: () => import_electron.ipcRenderer.invoke("sync:now"),
|
|
20
|
+
checkUpdate: () => import_electron.ipcRenderer.invoke("update:check"),
|
|
21
|
+
installUpdate: () => import_electron.ipcRenderer.invoke("update:install"),
|
|
19
22
|
quit: () => import_electron.ipcRenderer.invoke("quit")
|
|
20
23
|
};
|
|
21
24
|
import_electron.contextBridge.exposeInMainWorld("codexTracker", bridge);
|
|
@@ -12758,6 +12758,70 @@
|
|
|
12758
12758
|
// src/renderer/App.tsx
|
|
12759
12759
|
var import_react2 = __toESM(require_react());
|
|
12760
12760
|
|
|
12761
|
+
// ../shared/src/pricing.ts
|
|
12762
|
+
var LONG_CONTEXT_THRESHOLD = 272e3;
|
|
12763
|
+
var long = (threshold, input, cachedInput, output) => ({
|
|
12764
|
+
threshold,
|
|
12765
|
+
input,
|
|
12766
|
+
cachedInput,
|
|
12767
|
+
output
|
|
12768
|
+
});
|
|
12769
|
+
var DEFAULT_PRICING = {
|
|
12770
|
+
// GPT-5.6 family
|
|
12771
|
+
"gpt-5.6-sol": { input: 4, cachedInput: 0.4, output: 20, long: long(LONG_CONTEXT_THRESHOLD, 8, 0.8, 30) },
|
|
12772
|
+
"gpt-5.6-sol-codex": { input: 4, cachedInput: 0.4, output: 20, long: long(LONG_CONTEXT_THRESHOLD, 8, 0.8, 30) },
|
|
12773
|
+
"gpt-5.6-terra": { input: 2, cachedInput: 0.2, output: 12, long: long(LONG_CONTEXT_THRESHOLD, 4, 0.4, 18) },
|
|
12774
|
+
"gpt-5.6-terra-codex": { input: 2, cachedInput: 0.2, output: 12, long: long(LONG_CONTEXT_THRESHOLD, 4, 0.4, 18) },
|
|
12775
|
+
"gpt-5.6-luna": { input: 0.2, cachedInput: 0.02, output: 1.2, long: long(LONG_CONTEXT_THRESHOLD, 0.4, 0.04, 1.8) },
|
|
12776
|
+
"gpt-5.6-cyber": { input: 12.5, cachedInput: 1.25, output: 75 },
|
|
12777
|
+
// GPT-5.5 family
|
|
12778
|
+
"gpt-5.5": { input: 5, cachedInput: 0.5, output: 30, long: long(LONG_CONTEXT_THRESHOLD, 10, 1, 45) },
|
|
12779
|
+
"gpt-5.5-codex": { input: 5, cachedInput: 0.5, output: 30, long: long(LONG_CONTEXT_THRESHOLD, 10, 1, 45) },
|
|
12780
|
+
"gpt-5.5-pro": { input: 30, cachedInput: 30, output: 180, long: long(LONG_CONTEXT_THRESHOLD, 60, 60, 270) },
|
|
12781
|
+
"gpt-5.5-cyber": { input: 12.5, cachedInput: 1.25, output: 75 },
|
|
12782
|
+
// GPT-5.4 family
|
|
12783
|
+
"gpt-5.4": { input: 2.5, cachedInput: 0.25, output: 15, long: long(LONG_CONTEXT_THRESHOLD, 5, 0.5, 22.5) },
|
|
12784
|
+
"gpt-5.4-codex": { input: 2.5, cachedInput: 0.25, output: 15, long: long(LONG_CONTEXT_THRESHOLD, 5, 0.5, 22.5) },
|
|
12785
|
+
"gpt-5.4-mini": { input: 0.75, cachedInput: 0.075, output: 4.5 },
|
|
12786
|
+
"gpt-5.4-nano": { input: 0.2, cachedInput: 0.02, output: 1.25 },
|
|
12787
|
+
"gpt-5.4-pro": { input: 30, cachedInput: 30, output: 180, long: long(LONG_CONTEXT_THRESHOLD, 60, 60, 270) },
|
|
12788
|
+
// GPT-5.3 family (Codex-only release)
|
|
12789
|
+
"gpt-5.3": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
12790
|
+
"gpt-5.3-codex": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
12791
|
+
// GPT-5.2 family
|
|
12792
|
+
"gpt-5.2": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
12793
|
+
"gpt-5.2-codex": { input: 1.75, cachedInput: 0.175, output: 14 },
|
|
12794
|
+
"gpt-5.2-pro": { input: 21, cachedInput: 21, output: 168 },
|
|
12795
|
+
// GPT-5.1 family
|
|
12796
|
+
"gpt-5.1": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
12797
|
+
"gpt-5.1-codex": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
12798
|
+
"gpt-5.1-codex-max": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
12799
|
+
"gpt-5.1-codex-mini": { input: 0.25, cachedInput: 0.025, output: 2 },
|
|
12800
|
+
// GPT-5 family
|
|
12801
|
+
"gpt-5": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
12802
|
+
"gpt-5-codex": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
12803
|
+
"gpt-5-mini": { input: 0.25, cachedInput: 0.025, output: 2 },
|
|
12804
|
+
"gpt-5-nano": { input: 0.05, cachedInput: 5e-3, output: 0.4 },
|
|
12805
|
+
"gpt-5-pro": { input: 15, cachedInput: 15, output: 120 },
|
|
12806
|
+
"gpt-5-search-api": { input: 1.25, cachedInput: 0.125, output: 10 },
|
|
12807
|
+
// ChatGPT-tuned endpoint
|
|
12808
|
+
"chat-latest": { input: 5, cachedInput: 0.5, output: 30 },
|
|
12809
|
+
// GPT-4.1 / 4o family
|
|
12810
|
+
"gpt-4.1": { input: 2, cachedInput: 0.5, output: 8 },
|
|
12811
|
+
"gpt-4.1-mini": { input: 0.4, cachedInput: 0.1, output: 1.6 },
|
|
12812
|
+
"gpt-4.1-nano": { input: 0.1, cachedInput: 0.025, output: 0.4 },
|
|
12813
|
+
"gpt-4o": { input: 2.5, cachedInput: 1.25, output: 10 },
|
|
12814
|
+
"gpt-4o-mini": { input: 0.15, cachedInput: 0.075, output: 0.6 },
|
|
12815
|
+
// Reasoning models
|
|
12816
|
+
o1: { input: 15, cachedInput: 7.5, output: 60 },
|
|
12817
|
+
"o1-pro": { input: 150, cachedInput: 150, output: 600 },
|
|
12818
|
+
o3: { input: 2, cachedInput: 0.5, output: 8 },
|
|
12819
|
+
"o3-pro": { input: 20, cachedInput: 20, output: 80 },
|
|
12820
|
+
"o3-mini": { input: 1.1, cachedInput: 0.55, output: 4.4 },
|
|
12821
|
+
"o4-mini": { input: 1.1, cachedInput: 0.275, output: 4.4 },
|
|
12822
|
+
"codex-mini-latest": { input: 1.5, cachedInput: 0.375, output: 6 }
|
|
12823
|
+
};
|
|
12824
|
+
|
|
12761
12825
|
// ../shared/src/time.ts
|
|
12762
12826
|
var SECOND = 1e3;
|
|
12763
12827
|
var MINUTE = 60 * SECOND;
|
|
@@ -12909,6 +12973,7 @@
|
|
|
12909
12973
|
estimatedPricingHint: "Pricing estimated: model not in the price table",
|
|
12910
12974
|
other: "Other",
|
|
12911
12975
|
lastUpload: "Last upload {time}",
|
|
12976
|
+
lastSync: "Last full sync {time}",
|
|
12912
12977
|
neverUploaded: "Not uploaded yet",
|
|
12913
12978
|
uploadError: "Upload error: {message}",
|
|
12914
12979
|
allDevicesToday: "All devices today",
|
|
@@ -12927,16 +12992,44 @@
|
|
|
12927
12992
|
launchAtLogin: "Launch at login",
|
|
12928
12993
|
refresh: "Refresh",
|
|
12929
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}",
|
|
12930
13007
|
sessions: "{n} sessions",
|
|
12931
13008
|
files: "{n} files",
|
|
12932
13009
|
noData: "No usage found yet. Run Codex and this will fill in.",
|
|
12933
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}",
|
|
12934
13015
|
justNow: "just now",
|
|
12935
13016
|
never: "never",
|
|
12936
13017
|
local: "Local",
|
|
12937
13018
|
remote: "Other devices",
|
|
13019
|
+
// updates
|
|
13020
|
+
update: "Update",
|
|
13021
|
+
updateAvailable: "Update to v{version}",
|
|
13022
|
+
updateNewVersion: "Version {version} is available (you have v{current})",
|
|
13023
|
+
updateUpToDate: "Up to date",
|
|
13024
|
+
updateChecking: "Checking\u2026",
|
|
13025
|
+
updateInstalling: "Installing\u2026",
|
|
13026
|
+
updateInstalled: "Updated to v{version} \u2014 restart to apply",
|
|
13027
|
+
updateFailed: "Update failed \u2014 run this yourself:",
|
|
13028
|
+
updateCheckFailed: "Update check failed: {message}",
|
|
13029
|
+
checkForUpdates: "Check for updates",
|
|
13030
|
+
releaseNotes: "Release notes",
|
|
12938
13031
|
// CLI
|
|
12939
|
-
cliUsage: `Usage: codex-tracker [command] [options]
|
|
13032
|
+
cliUsage: `Usage: codex-token-tracker [command] [options] (alias: codex-tracker)
|
|
12940
13033
|
|
|
12941
13034
|
Commands:
|
|
12942
13035
|
(none) Start the menu bar app (falls back to agent mode without a display)
|
|
@@ -12946,11 +13039,14 @@ Commands:
|
|
|
12946
13039
|
login Connect this device to your dashboard (--dashboard <url>)
|
|
12947
13040
|
logout Disconnect this device
|
|
12948
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)
|
|
12949
13043
|
paths Show detected session directories (Codex, pi, OpenCode, Cline/Roo/Kilo, Hermes, custom)
|
|
13044
|
+
update Install the newest published version (--check only reports)
|
|
12950
13045
|
config get|set Read or change settings (config set uploadIntervalSec 30, config set sources.pi false)
|
|
12951
13046
|
lang <en|zh|auto> Set the display language
|
|
12952
13047
|
|
|
12953
13048
|
Options:
|
|
13049
|
+
--check update: report the newest version without installing it
|
|
12954
13050
|
--dashboard <url> Dashboard URL (self-hosted)
|
|
12955
13051
|
--background Start the menu bar app detached and return
|
|
12956
13052
|
--version, -v Print version
|
|
@@ -12992,7 +13088,21 @@ Options:
|
|
|
12992
13088
|
cliConfigSet: "Set {key} = {value}",
|
|
12993
13089
|
cliConfigUnknownKey: "Unknown config key: {key}. Keys: {keys}",
|
|
12994
13090
|
cliLangSet: "Language set to {lang}",
|
|
12995
|
-
cliConfigDir: "Config: {dir}"
|
|
13091
|
+
cliConfigDir: "Config: {dir}",
|
|
13092
|
+
cliUpdateLatest: "codex-token-tracker {version} is the latest version.",
|
|
13093
|
+
cliUpdateAvailable: "Update available: {current} \u2192 {latest}",
|
|
13094
|
+
cliUpdateRunning: "Running: {command}",
|
|
13095
|
+
cliUpdateDone: "Updated to {version}. Restart the menu bar app (or rerun the CLI) to use it.",
|
|
13096
|
+
cliUpdateFailed: "Update failed (exit {code}). Run it yourself: {command}",
|
|
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."
|
|
12996
13106
|
};
|
|
12997
13107
|
|
|
12998
13108
|
// src/i18n/zh.ts
|
|
@@ -13040,6 +13150,7 @@ Options:
|
|
|
13040
13150
|
estimatedPricingHint: "\u4EF7\u683C\u4E3A\u4F30\u7B97\u503C\uFF1A\u8BE5\u6A21\u578B\u4E0D\u5728\u4EF7\u76EE\u8868\u4E2D",
|
|
13041
13151
|
other: "\u5176\u4ED6",
|
|
13042
13152
|
lastUpload: "\u4E0A\u6B21\u4E0A\u4F20 {time}",
|
|
13153
|
+
lastSync: "\u4E0A\u6B21\u5B8C\u6574\u540C\u6B65 {time}",
|
|
13043
13154
|
neverUploaded: "\u5C1A\u672A\u4E0A\u4F20",
|
|
13044
13155
|
uploadError: "\u4E0A\u4F20\u5931\u8D25\uFF1A{message}",
|
|
13045
13156
|
allDevicesToday: "\u6240\u6709\u8BBE\u5907\u4ECA\u65E5",
|
|
@@ -13058,15 +13169,42 @@ Options:
|
|
|
13058
13169
|
launchAtLogin: "\u5F00\u673A\u81EA\u542F\u52A8",
|
|
13059
13170
|
refresh: "\u5237\u65B0",
|
|
13060
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}",
|
|
13061
13184
|
sessions: "{n} \u4E2A\u4F1A\u8BDD",
|
|
13062
13185
|
files: "{n} \u4E2A\u6587\u4EF6",
|
|
13063
13186
|
noData: "\u8FD8\u6CA1\u6709\u7528\u91CF\u6570\u636E\u3002\u8FD0\u884C Codex \u540E\u8FD9\u91CC\u4F1A\u81EA\u52A8\u66F4\u65B0\u3002",
|
|
13064
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}",
|
|
13065
13192
|
justNow: "\u521A\u521A",
|
|
13066
13193
|
never: "\u4ECE\u672A",
|
|
13067
13194
|
local: "\u672C\u673A",
|
|
13068
13195
|
remote: "\u5176\u4ED6\u8BBE\u5907",
|
|
13069
|
-
|
|
13196
|
+
update: "\u66F4\u65B0",
|
|
13197
|
+
updateAvailable: "\u66F4\u65B0\u5230 v{version}",
|
|
13198
|
+
updateNewVersion: "\u6709\u65B0\u7248\u672C {version}\uFF08\u5F53\u524D v{current}\uFF09",
|
|
13199
|
+
updateUpToDate: "\u5DF2\u662F\u6700\u65B0\u7248\u672C",
|
|
13200
|
+
updateChecking: "\u68C0\u67E5\u4E2D\u2026",
|
|
13201
|
+
updateInstalling: "\u5B89\u88C5\u4E2D\u2026",
|
|
13202
|
+
updateInstalled: "\u5DF2\u66F4\u65B0\u5230 v{version} \u2014 \u91CD\u542F\u540E\u751F\u6548",
|
|
13203
|
+
updateFailed: "\u66F4\u65B0\u5931\u8D25 \u2014 \u8BF7\u624B\u52A8\u6267\u884C\uFF1A",
|
|
13204
|
+
updateCheckFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25\uFF1A{message}",
|
|
13205
|
+
checkForUpdates: "\u68C0\u67E5\u66F4\u65B0",
|
|
13206
|
+
releaseNotes: "\u66F4\u65B0\u65E5\u5FD7",
|
|
13207
|
+
cliUsage: `\u7528\u6CD5\uFF1Acodex-token-tracker [\u547D\u4EE4] [\u9009\u9879] \uFF08\u522B\u540D\uFF1Acodex-tracker\uFF09
|
|
13070
13208
|
|
|
13071
13209
|
\u547D\u4EE4\uFF1A
|
|
13072
13210
|
(\u65E0) \u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\uFF08\u65E0\u663E\u793A\u73AF\u5883\u65F6\u81EA\u52A8\u5207\u6362\u4E3A agent \u6A21\u5F0F\uFF09
|
|
@@ -13076,11 +13214,14 @@ Options:
|
|
|
13076
13214
|
login \u5C06\u672C\u8BBE\u5907\u8FDE\u63A5\u5230\u4EEA\u8868\u76D8\uFF08--dashboard <url>\uFF09
|
|
13077
13215
|
logout \u65AD\u5F00\u672C\u8BBE\u5907
|
|
13078
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
|
|
13079
13218
|
paths \u663E\u793A\u68C0\u6D4B\u5230\u7684\u4F1A\u8BDD\u76EE\u5F55\uFF08Codex\u3001pi\u3001OpenCode\u3001Cline/Roo/Kilo\u3001Hermes\u3001\u81EA\u5B9A\u4E49\uFF09
|
|
13219
|
+
update \u5B89\u88C5\u6700\u65B0\u53D1\u5E03\u7248\u672C\uFF08--check \u53EA\u68C0\u67E5\u4E0D\u5B89\u88C5\uFF09
|
|
13080
13220
|
config get|set \u8BFB\u53D6\u6216\u4FEE\u6539\u8BBE\u7F6E\uFF08config set uploadIntervalSec 30\uFF0Cconfig set sources.pi false\uFF09
|
|
13081
13221
|
lang <en|zh|auto> \u8BBE\u7F6E\u663E\u793A\u8BED\u8A00
|
|
13082
13222
|
|
|
13083
13223
|
\u9009\u9879\uFF1A
|
|
13224
|
+
--check update\uFF1A\u53EA\u62A5\u544A\u6700\u65B0\u7248\u672C\uFF0C\u4E0D\u6267\u884C\u5B89\u88C5
|
|
13084
13225
|
--dashboard <url> \u4EEA\u8868\u76D8\u5730\u5740\uFF08\u81EA\u6258\u7BA1\uFF09
|
|
13085
13226
|
--background \u4EE5\u540E\u53F0\u65B9\u5F0F\u542F\u52A8\u83DC\u5355\u680F\u5E94\u7528\u5E76\u7ACB\u5373\u8FD4\u56DE
|
|
13086
13227
|
--version, -v \u663E\u793A\u7248\u672C
|
|
@@ -13122,7 +13263,21 @@ Options:
|
|
|
13122
13263
|
cliConfigSet: "\u5DF2\u8BBE\u7F6E {key} = {value}",
|
|
13123
13264
|
cliConfigUnknownKey: "\u672A\u77E5\u914D\u7F6E\u9879\uFF1A{key}\u3002\u53EF\u7528\uFF1A{keys}",
|
|
13124
13265
|
cliLangSet: "\u8BED\u8A00\u5DF2\u8BBE\u7F6E\u4E3A {lang}",
|
|
13125
|
-
cliConfigDir: "\u914D\u7F6E\u76EE\u5F55\uFF1A{dir}"
|
|
13266
|
+
cliConfigDir: "\u914D\u7F6E\u76EE\u5F55\uFF1A{dir}",
|
|
13267
|
+
cliUpdateLatest: "codex-token-tracker {version} \u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
|
|
13268
|
+
cliUpdateAvailable: "\u53D1\u73B0\u65B0\u7248\u672C\uFF1A{current} \u2192 {latest}",
|
|
13269
|
+
cliUpdateRunning: "\u6B63\u5728\u6267\u884C\uFF1A{command}",
|
|
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",
|
|
13271
|
+
cliUpdateFailed: "\u66F4\u65B0\u5931\u8D25\uFF08\u9000\u51FA\u7801 {code}\uFF09\u3002\u8BF7\u624B\u52A8\u6267\u884C\uFF1A{command}",
|
|
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"
|
|
13126
13281
|
};
|
|
13127
13282
|
|
|
13128
13283
|
// src/i18n/index.ts
|
|
@@ -13367,6 +13522,59 @@ Options:
|
|
|
13367
13522
|
function levelClass(pct) {
|
|
13368
13523
|
return pct >= 85 ? "critical" : pct >= 60 ? "warning" : "";
|
|
13369
13524
|
}
|
|
13525
|
+
function UpdateBar({ update, t: t2 }) {
|
|
13526
|
+
if (update.status === "installed") {
|
|
13527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "update-bar done", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", children: t2("updateInstalled", { version: update.latest ?? "?" }) }) });
|
|
13528
|
+
}
|
|
13529
|
+
if (update.status === "failed") {
|
|
13530
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "update-bar failed", children: [
|
|
13531
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", title: update.log ?? "", children: t2("updateFailed") }),
|
|
13532
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "cmd", children: update.command })
|
|
13533
|
+
] });
|
|
13534
|
+
}
|
|
13535
|
+
if (update.status === "installing") {
|
|
13536
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "update-bar", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", children: t2("updateInstalling") }) });
|
|
13537
|
+
}
|
|
13538
|
+
if (!update.available) return null;
|
|
13539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "update-bar", children: [
|
|
13540
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "msg", children: t2("updateNewVersion", { version: update.latest ?? "?", current: update.current }) }),
|
|
13541
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "primary", onClick: () => void bridge().installUpdate(), children: t2("update") })
|
|
13542
|
+
] });
|
|
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
|
+
}
|
|
13370
13578
|
function App() {
|
|
13371
13579
|
const [snap, setSnap] = (0, import_react2.useState)(null);
|
|
13372
13580
|
const [, setClock] = (0, import_react2.useState)(0);
|
|
@@ -13397,24 +13605,39 @@ Options:
|
|
|
13397
13605
|
const agents = agentsPeriod === "today" ? snap.byAgentToday : snap.byAgentMonth;
|
|
13398
13606
|
const auth = snap.auth;
|
|
13399
13607
|
const rl = snap.rateLimits;
|
|
13608
|
+
const syncing = snap.sync.status === "running";
|
|
13400
13609
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "app", children: [
|
|
13401
13610
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: "header", children: [
|
|
13402
13611
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "brand", children: [
|
|
13403
13612
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Logo, {}),
|
|
13404
13613
|
t2("appName")
|
|
13405
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,
|
|
13406
13616
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "spacer" }),
|
|
13407
13617
|
live ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "pill live", title: live.projectName ?? "", children: [
|
|
13408
13618
|
live.tokensPerSecond.toFixed(1),
|
|
13409
13619
|
" ",
|
|
13410
13620
|
t2("tokensPerSec")
|
|
13411
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
|
+
),
|
|
13412
13633
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "seg", role: "group", "aria-label": t2("language"), children: [
|
|
13413
13634
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: L === "en" ? "active" : "", onClick: () => void bridge().setLanguage("en"), children: "EN" }),
|
|
13414
13635
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: L === "zh" ? "active" : "", onClick: () => void bridge().setLanguage("zh"), children: "\u4E2D\u6587" })
|
|
13415
13636
|
] })
|
|
13416
13637
|
] }),
|
|
13417
13638
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "scroll", children: [
|
|
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 }),
|
|
13418
13641
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "card", children: [
|
|
13419
13642
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "card-title", children: [
|
|
13420
13643
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t2("today") }),
|
|
@@ -13613,7 +13836,14 @@ Options:
|
|
|
13613
13836
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "empty", style: { flex: 1 }, children: t2("signedOut") }),
|
|
13614
13837
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "primary", onClick: () => void bridge().login(), children: t2("signIn") })
|
|
13615
13838
|
] }),
|
|
13616
|
-
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
|
+
] })
|
|
13617
13847
|
] })
|
|
13618
13848
|
] }),
|
|
13619
13849
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("footer", { className: "footer", children: [
|
package/dist/renderer/styles.css
CHANGED
|
@@ -543,3 +543,132 @@ button.ghost:hover {
|
|
|
543
543
|
.source-line .dot { width: 6px; height: 6px; border-radius: 50%; background: #0ca30c; flex: none; }
|
|
544
544
|
.source-line.stale .dot { background: #fab219; }
|
|
545
545
|
.source-line .err-hint { color: #d03b3b; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
|
|
546
|
+
|
|
547
|
+
/* --- update banner --- */
|
|
548
|
+
.update-bar {
|
|
549
|
+
display: flex;
|
|
550
|
+
align-items: center;
|
|
551
|
+
gap: 8px;
|
|
552
|
+
margin: 10px 12px 0;
|
|
553
|
+
padding: 8px 10px;
|
|
554
|
+
border: 1px solid var(--border);
|
|
555
|
+
border-radius: 8px;
|
|
556
|
+
background: rgba(99, 102, 241, 0.09);
|
|
557
|
+
font-size: 11px;
|
|
558
|
+
color: var(--text);
|
|
559
|
+
}
|
|
560
|
+
.update-bar .msg {
|
|
561
|
+
flex: 1;
|
|
562
|
+
min-width: 0;
|
|
563
|
+
overflow: hidden;
|
|
564
|
+
text-overflow: ellipsis;
|
|
565
|
+
white-space: nowrap;
|
|
566
|
+
}
|
|
567
|
+
.update-bar.failed { background: rgba(208, 59, 59, 0.1); }
|
|
568
|
+
.update-bar.done { background: rgba(12, 163, 12, 0.1); }
|
|
569
|
+
.update-bar button { flex: none; }
|
|
570
|
+
.update-bar .cmd {
|
|
571
|
+
font-family: var(--mono);
|
|
572
|
+
font-size: 10px;
|
|
573
|
+
color: var(--muted);
|
|
574
|
+
user-select: text;
|
|
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.
|
|
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",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"convex"
|
|
26
26
|
],
|
|
27
27
|
"bin": {
|
|
28
|
+
"codex-token-tracker": "bin/codex-tracker.js",
|
|
28
29
|
"codex-tracker": "bin/codex-tracker.js"
|
|
29
30
|
},
|
|
30
31
|
"main": "dist/main.js",
|
|
@@ -32,7 +33,8 @@
|
|
|
32
33
|
"bin",
|
|
33
34
|
"dist",
|
|
34
35
|
"assets",
|
|
35
|
-
"README.md"
|
|
36
|
+
"README.md",
|
|
37
|
+
"CHANGELOG.md"
|
|
36
38
|
],
|
|
37
39
|
"engines": {
|
|
38
40
|
"node": ">=20"
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
},
|
|
66
68
|
"scripts": {
|
|
67
69
|
"build": "node scripts/build.mjs",
|
|
70
|
+
"build:release": "node scripts/build.mjs --release",
|
|
68
71
|
"dev": "node scripts/build.mjs --watch",
|
|
69
72
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
70
73
|
"start": "node bin/codex-tracker.js",
|