@ipv9/tokentracker-cli 0.39.37 → 0.39.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +41 -9
  2. package/dashboard/dist/assets/{Card-DKmo-v8a.js → Card-BbRYRVXf.js} +1 -1
  3. package/dashboard/dist/assets/DashboardPage-DYKHv0m3.js +60 -0
  4. package/dashboard/dist/assets/{FadeIn-Cf30rPTW.js → FadeIn-CjAacJ5t.js} +1 -1
  5. package/dashboard/dist/assets/{IpCheckPage-D3CL8XP0.js → IpCheckPage-BGALK8Vy.js} +1 -1
  6. package/dashboard/dist/assets/{LimitsPage-BqST7DxD.js → LimitsPage-LkZGPYvg.js} +2 -2
  7. package/dashboard/dist/assets/{LocalOnlyNotice-CqHzPooT.js → LocalOnlyNotice-Dxo2vFMQ.js} +1 -1
  8. package/dashboard/dist/assets/PopoverPopup-DP9fwdtl.js +1 -0
  9. package/dashboard/dist/assets/Select-DPE53NmM.js +1 -0
  10. package/dashboard/dist/assets/SelectItemText-D69v77is.js +12 -0
  11. package/dashboard/dist/assets/SettingsPage-Dxzqpi02.js +1 -0
  12. package/dashboard/dist/assets/SkillsPage-Bo7e39AB.js +1 -0
  13. package/dashboard/dist/assets/WidgetsPage-HHcIr_Eq.js +1 -0
  14. package/dashboard/dist/assets/WrappedPage-C-_jLXXU.js +1 -0
  15. package/dashboard/dist/assets/{arrow-up-right-BJzUcPig.js → arrow-up-right-BJvJHUGj.js} +1 -1
  16. package/dashboard/dist/assets/{download-CsXweDLj.js → download-CnzY_EgN.js} +1 -1
  17. package/dashboard/dist/assets/format-1w-q5n2f.js +1 -0
  18. package/dashboard/dist/assets/main-1kxAVM3m.css +1 -0
  19. package/dashboard/dist/assets/main-3gBEI6Jl.js +694 -0
  20. package/dashboard/dist/assets/mock-data-CJOMGcL9.js +1 -0
  21. package/dashboard/dist/assets/use-limits-display-prefs-D1nT9-mr.js +1 -0
  22. package/dashboard/dist/assets/use-native-settings-Dh6NzqjY.js +1 -0
  23. package/dashboard/dist/assets/use-usage-limits-BXR6lzGf.js +1 -0
  24. package/dashboard/dist/assets/useCurrency-Hhs5jbWw.js +1 -0
  25. package/dashboard/dist/index.html +16 -28
  26. package/dashboard/dist/llms.txt +0 -2
  27. package/dashboard/dist/sitemap.xml +0 -12
  28. package/package.json +7 -4
  29. package/src/cli.js +4 -13
  30. package/src/commands/doctor.js +1 -3
  31. package/src/commands/init.js +63 -53
  32. package/src/commands/serve.js +2 -50
  33. package/src/commands/status.js +1 -55
  34. package/src/commands/sync.js +134 -227
  35. package/src/lib/{browser-auth.js → browser-open.js} +3 -149
  36. package/src/lib/claude-categorizer.js +204 -32
  37. package/src/lib/codex-context-breakdown.js +83 -7
  38. package/src/lib/codex-rollout-parser.js +52 -10
  39. package/src/lib/diagnostics.js +1 -49
  40. package/src/lib/doctor.js +12 -87
  41. package/src/lib/file-identity.js +69 -0
  42. package/src/lib/init-flow.js +6 -3
  43. package/src/lib/local-api.js +6 -364
  44. package/src/lib/pricing/curated-overrides.json +1 -1
  45. package/src/lib/pricing/seed-snapshot.json +1 -1
  46. package/src/lib/rollout.js +392 -144
  47. package/src/lib/runtime-config.js +0 -32
  48. package/src/lib/sqlite-reader.js +83 -8
  49. package/dashboard/dist/assets/ActivityHeatmap-DPZ_Aj6E.js +0 -42
  50. package/dashboard/dist/assets/DashboardPage-C9Q5HCDh.js +0 -19
  51. package/dashboard/dist/assets/DevicePage-DhQFtROo.js +0 -1
  52. package/dashboard/dist/assets/DialogTitle-DduJCLiL.js +0 -1
  53. package/dashboard/dist/assets/InsforgeUserHeaderControls-CDqYVWBQ.js +0 -1
  54. package/dashboard/dist/assets/LandingPage-9345o0vr.js +0 -4356
  55. package/dashboard/dist/assets/LeaderboardAvatar-DNORVh3n.js +0 -1
  56. package/dashboard/dist/assets/LeaderboardPage-DBsdCCzY.js +0 -6
  57. package/dashboard/dist/assets/LeaderboardProfileModal-BuYdBVh_.js +0 -72
  58. package/dashboard/dist/assets/LeaderboardProfilePage-D4Fe1LOk.js +0 -1
  59. package/dashboard/dist/assets/LoginPage-BmcybmeK.js +0 -1
  60. package/dashboard/dist/assets/PopoverPopup-CDL7kqy5.js +0 -1
  61. package/dashboard/dist/assets/Select-BkzhG41t.js +0 -1
  62. package/dashboard/dist/assets/SelectItemText-BmRzs9ud.js +0 -1
  63. package/dashboard/dist/assets/SettingsPage-gXQl_o6s.js +0 -1
  64. package/dashboard/dist/assets/SkillsPage-BRXYEsKm.js +0 -1
  65. package/dashboard/dist/assets/WidgetsPage-CLHLz2UW.js +0 -1
  66. package/dashboard/dist/assets/WrappedPage-p4ZFwQjO.js +0 -1
  67. package/dashboard/dist/assets/index-BeoRn2gJ.js +0 -2
  68. package/dashboard/dist/assets/info-CuLr7ZIa.js +0 -1
  69. package/dashboard/dist/assets/main-CPGI-smk.js +0 -1004
  70. package/dashboard/dist/assets/main-DAcSUW2t.css +0 -1
  71. package/dashboard/dist/assets/use-limits-display-prefs-DOB09__q.js +0 -1
  72. package/dashboard/dist/assets/use-native-settings-JSylS2xS.js +0 -1
  73. package/dashboard/dist/assets/use-spring-qjUEvMQX.js +0 -1
  74. package/dashboard/dist/assets/use-usage-limits-B0bzPxNR.js +0 -1
  75. package/dashboard/dist/assets/useCurrency-BRorShq7.js +0 -1
  76. package/dashboard/dist/assets/useOpenInteractionType-DPP-6ZsL.js +0 -12
  77. package/dashboard/dist/share.html +0 -140
  78. package/src/commands/device-login.js +0 -161
  79. package/src/lib/upload-throttle.js +0 -148
@@ -1,161 +0,0 @@
1
- "use strict";
2
-
3
- const os = require("node:os");
4
- const path = require("node:path");
5
- const fs = require("node:fs/promises");
6
-
7
- const { readJson, writeJson } = require("../lib/fs");
8
- const { resolveTrackerPaths } = require("../lib/tracker-paths");
9
-
10
- const DEFAULT_BASE_URL = "https://srctyff5.us-east.insforge.app";
11
- const POLL_INTERVAL_MS = 5_000;
12
- const ABSOLUTE_TIMEOUT_MS = 16 * 60 * 1000; // matches the 15-min server window with a small buffer
13
-
14
- function readBaseUrl(config) {
15
- return (
16
- process.env.TOKENTRACKER_BASE_URL ||
17
- process.env.TOKENTRACKER_API_URL ||
18
- config?.baseUrl ||
19
- DEFAULT_BASE_URL
20
- );
21
- }
22
-
23
- async function authorize({ baseUrl, clientInfo }) {
24
- const res = await fetch(`${baseUrl}/functions/tokentracker-device-flow-authorize`, {
25
- method: "POST",
26
- headers: { "Content-Type": "application/json" },
27
- body: JSON.stringify({ client_info: clientInfo }),
28
- });
29
- if (!res.ok) {
30
- const text = await res.text();
31
- throw new Error(`authorize failed (HTTP ${res.status}): ${text.slice(0, 200)}`);
32
- }
33
- return res.json();
34
- }
35
-
36
- async function pollOnce({ baseUrl, deviceCode }) {
37
- const res = await fetch(`${baseUrl}/functions/tokentracker-device-flow-poll`, {
38
- method: "POST",
39
- headers: { "Content-Type": "application/json" },
40
- body: JSON.stringify({ device_code: deviceCode }),
41
- });
42
- const data = await res.json().catch(() => ({}));
43
- // 404 = unknown, 410 = expired, 200 = {status, user_id?, device_token?}.
44
- // Anything else (502 from a misconfigured edge, 5xx during deploy, …) must
45
- // bubble up as a network-style error — masking it as "unknown" would tell
46
- // the user their device_code was evicted when it wasn't.
47
- if (!res.ok && res.status !== 404 && res.status !== 410) {
48
- throw new Error(`poll HTTP ${res.status}: ${(data?.error ?? "").toString().slice(0, 200)}`);
49
- }
50
- return {
51
- status: data.status ?? "unknown",
52
- user_id: data.user_id ?? null,
53
- deviceToken: data.device_token ?? data.deviceToken ?? null,
54
- deviceId: data.device_id ?? data.deviceId ?? null,
55
- httpStatus: res.status,
56
- };
57
- }
58
-
59
- function sleep(ms) {
60
- return new Promise((resolve) => setTimeout(resolve, ms));
61
- }
62
-
63
- async function cmdDeviceLogin(argv = [], options = {}) {
64
- const opts = parseArgs(argv);
65
- const home = options.home || os.homedir();
66
- const sleepFn = options.sleep || sleep;
67
- const { trackerDir } = await resolveTrackerPaths({ home });
68
- const configPath = path.join(trackerDir, "config.json");
69
- const config = (await readJson(configPath)) || {};
70
- const baseUrl = opts.baseUrl || readBaseUrl(config);
71
-
72
- const clientInfo = `${os.platform()}-${os.arch()} ${os.hostname()}`;
73
- process.stdout.write(`Requesting device code from ${baseUrl}...\n`);
74
- const authResp = await authorize({ baseUrl, clientInfo });
75
-
76
- if (opts.json) {
77
- process.stdout.write(JSON.stringify(authResp, null, 2) + "\n");
78
- } else {
79
- process.stdout.write(
80
- [
81
- "",
82
- " Sign in from a browser:",
83
- ` ${authResp.verification_uri_complete || authResp.verification_uri}`,
84
- "",
85
- ` Or visit ${authResp.verification_uri} and enter the code:`,
86
- "",
87
- ` ${authResp.user_code}`,
88
- "",
89
- ` This code expires in ${Math.round(authResp.expires_in / 60)} minutes.`,
90
- " Polling every 5 seconds until you approve…",
91
- "",
92
- ].join("\n"),
93
- );
94
- }
95
-
96
- const startedAt = Date.now();
97
- let consecutiveErrors = 0;
98
- const MAX_BACKOFF_MS = 30_000;
99
- while (Date.now() - startedAt < ABSOLUTE_TIMEOUT_MS) {
100
- // Exponential backoff on consecutive network failures (capped at 30s) so
101
- // a flaky network doesn't hammer the server at the full 5s cadence for
102
- // the entire 15-minute window. ±20% jitter on retries prevents
103
- // thundering-herd reconnects when many CLIs lose connectivity at once.
104
- let wait =
105
- consecutiveErrors === 0
106
- ? POLL_INTERVAL_MS
107
- : Math.min(POLL_INTERVAL_MS * Math.pow(2, consecutiveErrors - 1), MAX_BACKOFF_MS);
108
- if (consecutiveErrors > 0) {
109
- const jitter = wait * 0.2 * (Math.random() * 2 - 1);
110
- wait = Math.max(POLL_INTERVAL_MS, wait + jitter);
111
- }
112
- await sleepFn(wait);
113
- let result;
114
- try {
115
- result = await pollOnce({ baseUrl, deviceCode: authResp.device_code });
116
- consecutiveErrors = 0;
117
- } catch (e) {
118
- consecutiveErrors++;
119
- process.stderr.write(`poll error (retry ${consecutiveErrors}): ${e?.message || e}\n`);
120
- continue;
121
- }
122
- if (result.status === "approved" && result.user_id) {
123
- if (!result.deviceToken) {
124
- throw new Error("device login approved but server did not return a device token");
125
- }
126
- const next = {
127
- ...config,
128
- baseUrl,
129
- user_id: result.user_id,
130
- deviceToken: result.deviceToken,
131
- deviceId: result.deviceId || config.deviceId,
132
- device_login_at: new Date().toISOString(),
133
- };
134
- await writeJson(configPath, next);
135
- process.stdout.write(`\n✓ Approved. device token written to ${configPath}\n`);
136
- return;
137
- }
138
- if (result.status === "expired") {
139
- throw new Error("device_code expired — re-run `tracker device-login`");
140
- }
141
- if (result.status === "unknown") {
142
- throw new Error("device_code is unknown — server may have evicted it");
143
- }
144
- // status === "pending" → just keep polling silently
145
- }
146
- throw new Error("device-login timed out without approval");
147
- }
148
-
149
- function parseArgs(argv) {
150
- const out = { json: false, baseUrl: null };
151
- for (let i = 0; i < argv.length; i++) {
152
- const a = argv[i];
153
- if (a === "--json") out.json = true;
154
- else if (a === "--base-url") {
155
- out.baseUrl = argv[++i] || null;
156
- } else throw new Error(`Unknown option: ${a}`);
157
- }
158
- return out;
159
- }
160
-
161
- module.exports = { cmdDeviceLogin, authorize, pollOnce };
@@ -1,148 +0,0 @@
1
- const DEFAULTS = {
2
- intervalMs: 30 * 60_000,
3
- jitterMsMax: 60_000,
4
- backlogBytes: 1_000_000,
5
- batchSize: 300,
6
- maxBatchesSmall: 2,
7
- maxBatchesLarge: 4,
8
- backoffInitialMs: 60_000,
9
- backoffMaxMs: 30 * 60_000,
10
- };
11
-
12
- function normalizeState(raw) {
13
- const s = raw && typeof raw === "object" ? raw : {};
14
- return {
15
- version: 1,
16
- lastSuccessMs: toSafeInt(s.lastSuccessMs),
17
- nextAllowedAtMs: toSafeInt(s.nextAllowedAtMs),
18
- backoffUntilMs: toSafeInt(s.backoffUntilMs),
19
- backoffStep: toSafeInt(s.backoffStep),
20
- lastErrorAt: typeof s.lastErrorAt === "string" ? s.lastErrorAt : null,
21
- lastError: typeof s.lastError === "string" ? s.lastError : null,
22
- updatedAt: typeof s.updatedAt === "string" ? s.updatedAt : null,
23
- };
24
- }
25
-
26
- function decideAutoUpload({ nowMs, pendingBytes, state, config }) {
27
- const cfg = { ...DEFAULTS, ...(config || {}) };
28
- const s = normalizeState(state);
29
- const pending = Number(pendingBytes || 0);
30
-
31
- if (pending <= 0) {
32
- return {
33
- allowed: false,
34
- reason: "no-pending",
35
- maxBatches: 0,
36
- batchSize: cfg.batchSize,
37
- blockedUntilMs: 0,
38
- };
39
- }
40
-
41
- const blockedUntilMs = Math.max(s.nextAllowedAtMs || 0, s.backoffUntilMs || 0);
42
- if (blockedUntilMs > 0 && nowMs < blockedUntilMs) {
43
- return {
44
- allowed: false,
45
- reason: "throttled",
46
- maxBatches: 0,
47
- batchSize: cfg.batchSize,
48
- blockedUntilMs,
49
- };
50
- }
51
-
52
- const maxBatches = pending >= cfg.backlogBytes ? cfg.maxBatchesLarge : cfg.maxBatchesSmall;
53
- return {
54
- allowed: true,
55
- reason: "allowed",
56
- maxBatches,
57
- batchSize: cfg.batchSize,
58
- blockedUntilMs: 0,
59
- };
60
- }
61
-
62
- function recordUploadSuccess({ nowMs, state, config, randInt }) {
63
- const cfg = { ...DEFAULTS, ...(config || {}) };
64
- const s = normalizeState(state);
65
- const jitter =
66
- typeof randInt === "function" ? randInt(0, cfg.jitterMsMax) : randomInt(0, cfg.jitterMsMax);
67
- const nextAllowedAtMs = nowMs + cfg.intervalMs + jitter;
68
-
69
- return {
70
- ...s,
71
- lastSuccessMs: nowMs,
72
- nextAllowedAtMs,
73
- backoffUntilMs: 0,
74
- backoffStep: 0,
75
- lastErrorAt: null,
76
- lastError: null,
77
- updatedAt: new Date(nowMs).toISOString(),
78
- };
79
- }
80
-
81
- function recordUploadFailure({ nowMs, state, error, config }) {
82
- const cfg = { ...DEFAULTS, ...(config || {}) };
83
- const s = normalizeState(state);
84
-
85
- const retryAfterMs = toSafeInt(error?.retryAfterMs);
86
- const status = toSafeInt(error?.status);
87
-
88
- let backoffMs = 0;
89
- if (retryAfterMs > 0) {
90
- backoffMs = Math.min(cfg.backoffMaxMs, Math.max(cfg.backoffInitialMs, retryAfterMs));
91
- } else {
92
- const step = Math.min(10, Math.max(0, s.backoffStep || 0));
93
- backoffMs = Math.min(cfg.backoffMaxMs, cfg.backoffInitialMs * Math.pow(2, step));
94
- }
95
-
96
- const backoffUntilMs = nowMs + backoffMs;
97
- const nextAllowedAtMs = Math.max(s.nextAllowedAtMs || 0, backoffUntilMs);
98
-
99
- return {
100
- ...s,
101
- nextAllowedAtMs,
102
- backoffUntilMs,
103
- backoffStep: Math.min(20, (s.backoffStep || 0) + 1),
104
- lastErrorAt: new Date(nowMs).toISOString(),
105
- lastError: truncate(String(error?.message || "upload failed"), 200),
106
- updatedAt: new Date(nowMs).toISOString(),
107
- };
108
- }
109
-
110
- function parseRetryAfterMs(headerValue, nowMs = Date.now()) {
111
- if (typeof headerValue !== "string" || headerValue.trim().length === 0) return null;
112
- const v = headerValue.trim();
113
- const seconds = Number(v);
114
- if (Number.isFinite(seconds) && seconds >= 0) return Math.floor(seconds * 1000);
115
- const d = new Date(v);
116
- if (Number.isNaN(d.getTime())) return null;
117
- const delta = d.getTime() - nowMs;
118
- return delta > 0 ? delta : 0;
119
- }
120
-
121
- function randomInt(min, maxInclusive) {
122
- const lo = Math.floor(min);
123
- const hi = Math.floor(maxInclusive);
124
- if (hi <= lo) return lo;
125
- return lo + Math.floor(Math.random() * (hi - lo + 1));
126
- }
127
-
128
- function toSafeInt(v) {
129
- const n = Number(v);
130
- if (!Number.isFinite(n)) return 0;
131
- if (n <= 0) return 0;
132
- return Math.floor(n);
133
- }
134
-
135
- function truncate(s, maxLen) {
136
- if (typeof s !== "string") return "";
137
- if (s.length <= maxLen) return s;
138
- return s.slice(0, maxLen - 1) + "…";
139
- }
140
-
141
- module.exports = {
142
- DEFAULTS,
143
- normalizeState,
144
- decideAutoUpload,
145
- recordUploadSuccess,
146
- recordUploadFailure,
147
- parseRetryAfterMs,
148
- };