@ipv9/tokentracker-cli 0.39.33 → 0.39.35

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 (49) hide show
  1. package/README.md +48 -5
  2. package/dashboard/dist/assets/{ActivityHeatmap-B8dR4C_0.js → ActivityHeatmap-rUm9bSZ9.js} +1 -1
  3. package/dashboard/dist/assets/{Card-UnYzwQJR.js → Card-YWr91b5b.js} +1 -1
  4. package/dashboard/dist/assets/DashboardPage-DdJOVJuw.js +19 -0
  5. package/dashboard/dist/assets/{DevicePage-CZFnVVjk.js → DevicePage-CQMG1bh8.js} +1 -1
  6. package/dashboard/dist/assets/{DialogTitle-BZl-I43S.js → DialogTitle-BM0Hz4Pq.js} +1 -1
  7. package/dashboard/dist/assets/{FadeIn-0EQh6r0x.js → FadeIn-D8xysY3i.js} +1 -1
  8. package/dashboard/dist/assets/{InsforgeUserHeaderControls-BeA5yiNr.js → InsforgeUserHeaderControls-CsAafXyj.js} +1 -1
  9. package/dashboard/dist/assets/{IpCheckPage-BkjSnzmW.js → IpCheckPage-Bbg9erkT.js} +1 -1
  10. package/dashboard/dist/assets/{LandingPage-CCj37Lvh.js → LandingPage-BCMsWOwJ.js} +1 -1
  11. package/dashboard/dist/assets/{LeaderboardAvatar-B4viOzHY.js → LeaderboardAvatar-CkQa4EX2.js} +1 -1
  12. package/dashboard/dist/assets/{LeaderboardPage-CQV2N36e.js → LeaderboardPage-CyDX3tDL.js} +3 -3
  13. package/dashboard/dist/assets/{LeaderboardProfileModal-CNKo1dRR.js → LeaderboardProfileModal-CMctMA-p.js} +1 -1
  14. package/dashboard/dist/assets/{LeaderboardProfilePage-FvRutqM5.js → LeaderboardProfilePage-DTZ_6dBt.js} +1 -1
  15. package/dashboard/dist/assets/LimitsPage-V7939U8b.js +2 -0
  16. package/dashboard/dist/assets/{LocalOnlyNotice-BH_BM61g.js → LocalOnlyNotice-dODOM3SB.js} +1 -1
  17. package/dashboard/dist/assets/{LoginPage-BOs7Vxww.js → LoginPage-CE5GBEI2.js} +1 -1
  18. package/dashboard/dist/assets/{PopoverPopup-Dd2SS-kl.js → PopoverPopup-CRzMMFsC.js} +1 -1
  19. package/dashboard/dist/assets/{Select-DKPyTgtq.js → Select-m6edgfye.js} +1 -1
  20. package/dashboard/dist/assets/{SelectItemText-JqKVOv-o.js → SelectItemText-DIoTPvZM.js} +1 -1
  21. package/dashboard/dist/assets/{SettingsPage-CJfhq1dP.js → SettingsPage-DObaSleW.js} +1 -1
  22. package/dashboard/dist/assets/{SkillsPage-C6tkEm2L.js → SkillsPage-BSi26dZo.js} +1 -1
  23. package/dashboard/dist/assets/{WidgetsPage-CL32K7OS.js → WidgetsPage-CTl7OtvF.js} +1 -1
  24. package/dashboard/dist/assets/{WrappedPage-7AnZnCu_.js → WrappedPage-BXwhD9aq.js} +1 -1
  25. package/dashboard/dist/assets/{arrow-up-right-2_t43gcm.js → arrow-up-right-DxdoYoxg.js} +1 -1
  26. package/dashboard/dist/assets/{download-INj0jPNk.js → download-BZkwGDg8.js} +1 -1
  27. package/dashboard/dist/assets/{info-D7mMY73X.js → info-DRaQWOpM.js} +1 -1
  28. package/dashboard/dist/assets/{main-D2nNo9eK.js → main-CuaPRwHu.js} +5 -2
  29. package/dashboard/dist/assets/main-DAcSUW2t.css +1 -0
  30. package/dashboard/dist/assets/use-limits-display-prefs-5cj2WfP-.js +1 -0
  31. package/dashboard/dist/assets/{use-native-settings-Dacr_YkP.js → use-native-settings-BfydbPtC.js} +1 -1
  32. package/dashboard/dist/assets/{use-spring-CE3dBlxJ.js → use-spring-BxDSGjR_.js} +1 -1
  33. package/dashboard/dist/assets/{use-usage-limits-BOjNomCE.js → use-usage-limits-D_5PMCyF.js} +1 -1
  34. package/dashboard/dist/assets/{useCurrency-V1KRKBNl.js → useCurrency-DQhzQX2k.js} +1 -1
  35. package/dashboard/dist/assets/{useOpenInteractionType-DVyniqcO.js → useOpenInteractionType--grUNSll.js} +1 -1
  36. package/dashboard/dist/index.html +2 -2
  37. package/dashboard/dist/share.html +2 -2
  38. package/package.json +1 -1
  39. package/src/cli.js +3 -3
  40. package/src/commands/doctor.js +5 -0
  41. package/src/commands/serve.js +3 -1
  42. package/src/lib/browser-auth.js +40 -13
  43. package/src/lib/doctor.js +104 -1
  44. package/src/lib/pricing/seed-snapshot.json +1 -1
  45. package/src/lib/usage-limits.js +133 -1
  46. package/dashboard/dist/assets/DashboardPage-CB24v0ry.js +0 -19
  47. package/dashboard/dist/assets/LimitsPage-By5T54_9.js +0 -2
  48. package/dashboard/dist/assets/main-CGmdZkmj.css +0 -1
  49. package/dashboard/dist/assets/use-limits-display-prefs-CbW7JdJz.js +0 -1
@@ -28,6 +28,11 @@ async function cmdDoctor(argv = []) {
28
28
  runtime,
29
29
  diagnostics,
30
30
  fetch: globalThis.fetch,
31
+ system: {
32
+ nodeVersion: process.version,
33
+ platform: process.platform,
34
+ env: process.env,
35
+ },
31
36
  paths: {
32
37
  trackerDir,
33
38
  configPath,
@@ -334,7 +334,7 @@ function parseArgs(argv, env = process.env) {
334
334
  const opts = {
335
335
  port: envPort || DEFAULT_PORT,
336
336
  portExplicit: Boolean(envPort),
337
- open: true,
337
+ open: false,
338
338
  sync: false,
339
339
  };
340
340
  for (let i = 0; i < argv.length; i++) {
@@ -347,6 +347,8 @@ function parseArgs(argv, env = process.env) {
347
347
  }
348
348
  } else if (arg === "--no-open") {
349
349
  opts.open = false;
350
+ } else if (arg === "--open") {
351
+ opts.open = true;
350
352
  } else if (arg === "--sync") {
351
353
  opts.sync = true;
352
354
  } else if (arg === "--no-sync") {
@@ -158,7 +158,40 @@ function buildBrowserList() {
158
158
  return [def, ...all.filter((b) => b !== def)];
159
159
  }
160
160
 
161
- function openInBrowser(url) {
161
+ function buildBrowserOpenErrorMessage({ url, command, error, platform = process.platform } = {}) {
162
+ const commandName =
163
+ command || (platform === "win32" ? "cmd" : platform === "darwin" ? "open" : "xdg-open");
164
+ const reason =
165
+ error?.code === "ENOENT" ? `${commandName} not found` : error?.message || String(error || "unknown error");
166
+ const lines = [
167
+ `Could not open browser automatically: ${reason}.`,
168
+ `Open manually: ${url}`,
169
+ ];
170
+ if (platform === "linux" && commandName === "xdg-open") {
171
+ lines.push("Linux fix: sudo apt install -y xdg-utils");
172
+ }
173
+ return `${lines.join("\n")}\n`;
174
+ }
175
+
176
+ function spawnBrowserCommand(
177
+ command,
178
+ args,
179
+ { url, platform = process.platform, stderr = process.stderr, spawn = cp.spawn } = {},
180
+ ) {
181
+ try {
182
+ const child = spawn(command, args, { stdio: "ignore", detached: true });
183
+ child.on("error", (error) => {
184
+ stderr.write(buildBrowserOpenErrorMessage({ url, command, error, platform }));
185
+ });
186
+ child.unref();
187
+ return child;
188
+ } catch (error) {
189
+ stderr.write(buildBrowserOpenErrorMessage({ url, command, error, platform }));
190
+ return null;
191
+ }
192
+ }
193
+
194
+ function openInBrowser(url, { stderr = process.stderr } = {}) {
162
195
  const platform = process.platform;
163
196
 
164
197
  if (platform === "darwin") {
@@ -230,15 +263,10 @@ else
230
263
  open location "${url}"
231
264
  end if
232
265
  `;
233
- try {
234
- const child = cp.spawn("osascript", ["-e", script], { stdio: "ignore", detached: true });
235
- child.unref();
236
- } catch (_e) {
266
+ const child = spawnBrowserCommand("osascript", ["-e", script], { url, platform, stderr });
267
+ if (!child) {
237
268
  // Fallback to plain open
238
- try {
239
- const child = cp.spawn("open", [url], { stdio: "ignore", detached: true });
240
- child.unref();
241
- } catch (_e2) {}
269
+ spawnBrowserCommand("open", [url], { url, platform, stderr });
242
270
  }
243
271
  return;
244
272
  }
@@ -254,10 +282,7 @@ end if
254
282
  args = [url];
255
283
  }
256
284
 
257
- try {
258
- const child = cp.spawn(cmd, args, { stdio: "ignore", detached: true });
259
- child.unref();
260
- } catch (_e) {}
285
+ spawnBrowserCommand(cmd, args, { url, platform, stderr });
261
286
  }
262
287
 
263
288
  function resolvePostAuthRedirect({ dashboardUrl, authUrl }) {
@@ -278,4 +303,6 @@ function resolvePostAuthRedirect({ dashboardUrl, authUrl }) {
278
303
  module.exports = {
279
304
  beginBrowserAuth,
280
305
  openInBrowser,
306
+ buildBrowserOpenErrorMessage,
307
+ spawnBrowserCommand,
281
308
  };
package/src/lib/doctor.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const fs = require("node:fs/promises");
2
2
  const { constants } = require("node:fs");
3
+ const path = require("node:path");
3
4
 
4
5
  const { readJsonStrict } = require("./fs");
5
6
 
@@ -9,10 +10,14 @@ async function buildDoctorReport({
9
10
  fetch = globalThis.fetch,
10
11
  now = () => new Date(),
11
12
  paths = {},
13
+ system = null,
12
14
  } = {}) {
13
15
  const checks = [];
14
16
 
15
17
  checks.push(...buildRuntimeChecks(runtime));
18
+ if (system) {
19
+ checks.push(...(await buildSystemChecks(system)));
20
+ }
16
21
 
17
22
  if (paths.trackerDir) {
18
23
  checks.push(await checkTrackerDir(paths.trackerDir));
@@ -42,6 +47,99 @@ async function buildDoctorReport({
42
47
  };
43
48
  }
44
49
 
50
+ async function buildSystemChecks({
51
+ nodeVersion = process.version,
52
+ platform = process.platform,
53
+ env = process.env,
54
+ commandExists = commandExistsOnPath,
55
+ } = {}) {
56
+ return [
57
+ buildNodeVersionCheck(nodeVersion),
58
+ await buildBrowserOpenerCheck({ platform, env, commandExists }),
59
+ ];
60
+ }
61
+
62
+ function buildNodeVersionCheck(nodeVersion) {
63
+ const major = parseNodeMajor(nodeVersion);
64
+ const ok = Number.isFinite(major) && major >= 20;
65
+ return {
66
+ id: "runtime.node_version",
67
+ status: ok ? "ok" : "fail",
68
+ detail: ok ? `Node.js ${nodeVersion} satisfies >=20` : `Node.js ${nodeVersion || "unknown"} is below required >=20`,
69
+ critical: !ok,
70
+ meta: {
71
+ node_version: nodeVersion || null,
72
+ required_major: 20,
73
+ },
74
+ };
75
+ }
76
+
77
+ async function buildBrowserOpenerCheck({ platform = process.platform, env = process.env, commandExists }) {
78
+ const headless = isHeadlessEnvironment({ platform, env });
79
+ if (headless) {
80
+ return {
81
+ id: "browser.opener",
82
+ status: "warn",
83
+ detail: "headless/session environment detected; use --no-open or open the printed URL manually",
84
+ critical: false,
85
+ meta: { platform, command: null, headless: true },
86
+ };
87
+ }
88
+
89
+ if (platform === "win32") {
90
+ return {
91
+ id: "browser.opener",
92
+ status: "ok",
93
+ detail: "Windows browser opener uses cmd /c start",
94
+ critical: false,
95
+ meta: { platform, command: "cmd", headless: false },
96
+ };
97
+ }
98
+
99
+ const command = platform === "darwin" ? "open" : "xdg-open";
100
+ const exists = await commandExists(command, env);
101
+ return {
102
+ id: "browser.opener",
103
+ status: exists ? "ok" : "warn",
104
+ detail: exists
105
+ ? `${command} available`
106
+ : command === "xdg-open"
107
+ ? "xdg-open missing; install xdg-utils or use --no-open"
108
+ : `${command} missing; use --no-open`,
109
+ critical: false,
110
+ meta: {
111
+ platform,
112
+ command,
113
+ headless: false,
114
+ linux_fix: command === "xdg-open" && !exists ? "sudo apt install -y xdg-utils" : null,
115
+ },
116
+ };
117
+ }
118
+
119
+ function parseNodeMajor(nodeVersion) {
120
+ const match = String(nodeVersion || "").match(/^v?(\d+)/);
121
+ return match ? Number(match[1]) : NaN;
122
+ }
123
+
124
+ function isHeadlessEnvironment({ platform, env = {} } = {}) {
125
+ if (env.CI === "true" || env.HEADLESS === "true" || env.TOKENTRACKER_HEADLESS === "1") return true;
126
+ if (platform === "linux" && !env.DISPLAY && !env.WAYLAND_DISPLAY && !env.WSL_DISTRO_NAME) return true;
127
+ return false;
128
+ }
129
+
130
+ async function commandExistsOnPath(command, env = process.env) {
131
+ const pathValue = env.PATH || "";
132
+ const dirs = pathValue.split(path.delimiter).filter(Boolean);
133
+ for (const dir of dirs) {
134
+ const candidate = path.join(dir, command);
135
+ try {
136
+ await fs.access(candidate, constants.X_OK);
137
+ return true;
138
+ } catch (_e) {}
139
+ }
140
+ return false;
141
+ }
142
+
45
143
  function buildRuntimeChecks(runtime = {}) {
46
144
  const checks = [];
47
145
  const baseUrl =
@@ -335,4 +433,9 @@ function summarizeChecks(checks = []) {
335
433
  return summary;
336
434
  }
337
435
 
338
- module.exports = { buildDoctorReport };
436
+ module.exports = {
437
+ buildDoctorReport,
438
+ buildBrowserOpenerCheck,
439
+ buildNodeVersionCheck,
440
+ isHeadlessEnvironment,
441
+ };