@modelstatus/cli 0.1.77 → 0.1.78
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/README.md +1 -1
- package/package.json +1 -1
- package/src/index.js +15 -2
- package/src/telemetry.js +13 -8
- package/src/tui/views/account.js +3 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ The free CLI + TUI for [LLM Status](https://llmstatus.ai) — scans your repo fo
|
|
|
8
8
|
npx @modelstatus/cli status
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
That's it. No sign-in, no account, and the scan runs entirely on your machine — you get a snapshot of every model in your repo plus health badges and replacement suggestions. (
|
|
11
|
+
That's it. No sign-in, no account, and the scan runs entirely on your machine — you get a snapshot of every model in your repo plus health badges and replacement suggestions. (Anonymous usage analytics — event names + counts only, never code, model names, or paths — can be turned off anytime: `mm analytics off`, or `MM_NO_ANALYTICS=1`.)
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelstatus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.78",
|
|
4
4
|
"description": "Track which AI models you use, where, and never get surprised by a retirement. Free offline model-health for any repo (mm status), browser sign-in for cloud inventory + alerts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"llm",
|
package/src/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { redactValue } from "./redact.js";
|
|
|
14
14
|
import { assignProjects, buildUsages } from "./upload.js";
|
|
15
15
|
import { loginViaBrowser } from "./auth.js";
|
|
16
16
|
import { maybeCheckForUpdate, forceUpdate } from "./updater.js";
|
|
17
|
-
import { track,
|
|
17
|
+
import { track, analyticsState } from "./telemetry.js";
|
|
18
18
|
import { BUILD_VERSION } from "./version.js";
|
|
19
19
|
|
|
20
20
|
// TRUE BACKGROUND SCAN — hidden worker dispatch. MUST be the first executable
|
|
@@ -763,6 +763,7 @@ Usage:
|
|
|
763
763
|
mm login [api_key] Browser sign-in with polling (or paste a key)
|
|
764
764
|
mm signup Create an account in the browser, then poll
|
|
765
765
|
mm logout Forget the saved API key
|
|
766
|
+
mm analytics [on|off] Anonymous usage stats (event counts only — never code or paths)
|
|
766
767
|
mm scan [dir] Scan for model usage; interactive TUI, or --ci/--json for pipelines
|
|
767
768
|
mm fix [dir] Rewrite dying model ids to their replacement, in place (--dry-run previews; --model <slug> limits; --yes skips the confirm)
|
|
768
769
|
mm ci [dir] CI gate: fail the build on deprecated/retiring models (GitHub annotations)
|
|
@@ -853,7 +854,6 @@ async function main() {
|
|
|
853
854
|
|
|
854
855
|
// Anonymous, opt-out usage analytics (one-time disclosure, then a single
|
|
855
856
|
// event per invocation). No-op without a baked key / when opted out.
|
|
856
|
-
maybeAnalyticsNotice();
|
|
857
857
|
track("cli_command", { command: cmd || "tui" });
|
|
858
858
|
|
|
859
859
|
// Explicit self-update: `mm update` (command) or `--update` (flag on any
|
|
@@ -878,6 +878,19 @@ async function main() {
|
|
|
878
878
|
if (cmd === "login") await cmdLogin(positional, flags);
|
|
879
879
|
else if (cmd === "signup") await cmdSignup(positional, flags);
|
|
880
880
|
else if (cmd === "logout") cmdLogout();
|
|
881
|
+
else if (cmd === "analytics") {
|
|
882
|
+
const arg = (positional[1] || "").toLowerCase();
|
|
883
|
+
if (arg === "on" || arg === "off") {
|
|
884
|
+
const { setConfigValue } = await import("./config.js");
|
|
885
|
+
setConfigValue("analyticsOptOut", arg === "off");
|
|
886
|
+
console.log(`Anonymous usage analytics ${arg}.`);
|
|
887
|
+
} else {
|
|
888
|
+
const st = analyticsState();
|
|
889
|
+
console.log(`Anonymous usage analytics: ${st.on ? "on" : `off${st.reason ? ` — ${st.reason}` : ""}`}`);
|
|
890
|
+
console.log(" When on, mm sends event names + counts only — never code, model names, or paths.");
|
|
891
|
+
console.log(" Toggle: mm analytics on|off · also honored: MM_NO_ANALYTICS=1, DO_NOT_TRACK=1, CI=1");
|
|
892
|
+
}
|
|
893
|
+
}
|
|
881
894
|
else if (cmd === "scan") await cmdScan(positional, flags);
|
|
882
895
|
else if (cmd === "fix") await cmdFix(positional, flags);
|
|
883
896
|
else if (cmd === "ci") await cmdCi(positional, flags);
|
package/src/telemetry.js
CHANGED
|
@@ -14,7 +14,10 @@ import { BUILD_VERSION, UPDATE_CHANNEL } from "./version.js";
|
|
|
14
14
|
const POSTHOG_KEY = typeof __POSTHOG_KEY__ !== "undefined" ? __POSTHOG_KEY__ : (process.env.MM_POSTHOG_KEY || "");
|
|
15
15
|
const POSTHOG_HOST = (process.env.MM_POSTHOG_HOST || "https://us.i.posthog.com").replace(/\/$/, "");
|
|
16
16
|
|
|
17
|
-
const optedOut = () =>
|
|
17
|
+
const optedOut = () => {
|
|
18
|
+
if (process.env.MM_NO_ANALYTICS || process.env.DO_NOT_TRACK || process.env.CI) return true;
|
|
19
|
+
try { return !!loadConfig().analyticsOptOut; } catch { return false; }
|
|
20
|
+
};
|
|
18
21
|
const enabled = () => !!POSTHOG_KEY && !optedOut();
|
|
19
22
|
|
|
20
23
|
let cachedId = null;
|
|
@@ -27,13 +30,15 @@ function distinctId() {
|
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
/** One-time, pre-TUI stderr disclosure. Honors opt-out + only shows once. */
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
process.
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
/** Current analytics state + why — powers `mm analytics` and the Account tab.
|
|
34
|
+
* Disclosure lives there + README/docs/--help (not as a banner interrupting
|
|
35
|
+
* scan output). */
|
|
36
|
+
export function analyticsState() {
|
|
37
|
+
if (process.env.MM_NO_ANALYTICS || process.env.DO_NOT_TRACK || process.env.CI) {
|
|
38
|
+
return { on: false, reason: "env (MM_NO_ANALYTICS / DO_NOT_TRACK / CI)" };
|
|
39
|
+
}
|
|
40
|
+
try { if (loadConfig().analyticsOptOut) return { on: false, reason: "mm analytics off" }; } catch { /* ignore */ }
|
|
41
|
+
return { on: !!POSTHOG_KEY, reason: POSTHOG_KEY ? "" : "no key baked in" };
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/** Fire-and-forget capture. Never throws, never blocks the CLI meaningfully. */
|
package/src/tui/views/account.js
CHANGED
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { h, C, GLYPH } from "../ui.js";
|
|
4
4
|
import { loadConfig, configFilePath } from "../../config.js";
|
|
5
|
+
import { analyticsState } from "../../telemetry.js";
|
|
5
6
|
import { openUrl } from "../../openUrl.js";
|
|
6
7
|
|
|
7
8
|
const FREE_LIMITS = { projects: 1, usages: 15, channels: 1 };
|
|
@@ -15,7 +16,7 @@ function Row({ label, value, color = C.FG, bold = false }) {
|
|
|
15
16
|
return h(
|
|
16
17
|
Text,
|
|
17
18
|
{},
|
|
18
|
-
h(Text, { color: C.ACCENT }, label.padEnd(
|
|
19
|
+
h(Text, { color: C.ACCENT }, label.padEnd(10)),
|
|
19
20
|
h(Text, { color, bold }, value),
|
|
20
21
|
);
|
|
21
22
|
}
|
|
@@ -98,6 +99,7 @@ export function AccountView({ client, me, refreshMe, apiBase, ui, active }) {
|
|
|
98
99
|
h(Row, { label: "retiring", value: `${me?.retiring_window_days ?? 90} day window` }),
|
|
99
100
|
h(Row, { label: "endpoint", value: endpoint }),
|
|
100
101
|
h(Row, { label: "key", value: keyPrefix }),
|
|
102
|
+
h(Row, { label: "analytics", value: analyticsState().on ? "on · anonymous event counts only · mm analytics off" : "off", color: C.FG_FAINT }),
|
|
101
103
|
h(Row, { label: "config", value: configFilePath, color: C.FG_FAINT }),
|
|
102
104
|
),
|
|
103
105
|
h(Text, {}, ""),
|