@modelstatus/cli 0.1.32 → 0.1.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelstatus/cli",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
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/api.js CHANGED
@@ -63,6 +63,8 @@ export function createClient({ apiBase, apiKey }) {
63
63
  deleteUsage: (id) => req("DELETE", `/usages/${id}`),
64
64
  linkUsage: (id, modelId) => req("POST", `/usages/${id}/link`, { model_id: modelId }),
65
65
  bulkUpload: (projectId, usages) => req("POST", "/usages/bulk", { project_id: projectId, usages }),
66
+ // Clear inventory: all usages, or `{ all: true }` to also wipe projects + rules + feed.
67
+ clearUsages: ({ all = false, project } = {}) => req("DELETE", `/usages${qs({ all: all ? "true" : undefined, project_id: project })}`),
66
68
 
67
69
  // ci runs (Pro) — record a CI evaluation for the dashboard + alerts
68
70
  ciRun: (body) => req("POST", "/ci/runs", body),
package/src/index.js CHANGED
@@ -364,6 +364,39 @@ async function cmdCi(positional, flags) {
364
364
  if (failing.length) process.exit(1);
365
365
  }
366
366
 
367
+ /** y/N prompt; returns false on a non-TTY (caller requires --yes there). */
368
+ async function confirm(prompt) {
369
+ if (!process.stdin.isTTY) return false;
370
+ const readline = await import("node:readline");
371
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
372
+ const ans = await new Promise((res) => rl.question(prompt, res));
373
+ rl.close();
374
+ return /^y(es)?$/i.test(String(ans).trim());
375
+ }
376
+
377
+ /** Clear your cloud inventory. `--all` also wipes projects, alert rules + the
378
+ * in-app feed (a full reset). Destructive → confirms unless --yes/--force. */
379
+ async function cmdClear(_positional, flags) {
380
+ const { apiBase, apiKey } = resolveAuth(flags);
381
+ if (!apiKey) {
382
+ console.error("No API key. Run `mm login` or pass --key.");
383
+ process.exit(1);
384
+ }
385
+ const all = !!flags.all;
386
+ const scope = all ? "ALL usages, projects, alert rules + the in-app feed" : "ALL usages";
387
+ if (!flags.yes && !flags.force) {
388
+ if (!process.stdin.isTTY) {
389
+ console.error(`Refusing to delete ${scope} without confirmation. Re-run with --yes.`);
390
+ process.exit(1);
391
+ }
392
+ const ok = await confirm(`Delete ${scope} from your account? This cannot be undone. [y/N] `);
393
+ if (!ok) return console.log("Aborted — nothing deleted.");
394
+ }
395
+ const res = await createClient({ apiBase, apiKey }).clearUsages({ all });
396
+ if (flags.json) return console.log(JSON.stringify(res, null, 2));
397
+ console.log(`✓ Cleared ${res.usages ?? 0} usage(s)${res.projects ? ` + ${res.projects} project(s)` : ""}. Your inventory is clean — rescan to repopulate.`);
398
+ }
399
+
367
400
  /** List detection sources and whether each can run right now. */
368
401
  async function cmdSources(_positional, flags) {
369
402
  const dir = path.resolve(flags.dir || ".");
@@ -460,6 +493,7 @@ Usage:
460
493
  mm ci [dir] CI gate: fail the build on deprecated/retiring models (GitHub annotations)
461
494
  (--diff <base> limits findings to files changed vs base; auto on PRs via GITHUB_BASE_REF)
462
495
  mm sources List detection sources and whether each can run here
496
+ mm clear Delete all tracked usages from your inventory (--all also wipes projects/rules; --yes to skip the prompt)
463
497
  mm upgrade Open Stripe checkout and poll until Pro is active
464
498
  mm tui Force-launch the TUI (logs you in first if needed)
465
499
 
@@ -519,6 +553,7 @@ async function main() {
519
553
  else if (cmd === "ci") await cmdCi(positional, flags);
520
554
  else if (cmd === "status") await cmdStatus(positional, flags);
521
555
  else if (cmd === "sources") await cmdSources(positional, flags);
556
+ else if (cmd === "clear") await cmdClear(positional, flags);
522
557
  else if (cmd === "upgrade") await cmdUpgrade(positional, flags);
523
558
  else if (cmd === "tui" || !cmd) await launchTui(positional[1], flags);
524
559
  else if (cmd === "help" || flags.help) console.log(HELP);
@@ -19,6 +19,7 @@ export const meta = {
19
19
  { k: "e", label: "env" },
20
20
  { k: "c", label: "critical" },
21
21
  { k: "d", label: "delete" },
22
+ { k: "C", label: "clear all" },
22
23
  { k: "r", label: "rescan" },
23
24
  { k: "n", label: "new" },
24
25
  { k: "g", label: "refresh" },
@@ -77,6 +78,21 @@ export function InventoryView({ client, ui, dir = ".", active, width = 78, heigh
77
78
  if (input === "g") return q.reload();
78
79
  if (input === "r") return ui.switchTo("scan");
79
80
  if (input === "n") return ui.switchTo("add");
81
+ if (input === "C" && usages.length) {
82
+ return ui.askPrompt(`Type "clear" to delete ALL ${usages.length} usages`, {
83
+ onSubmit: (v) => {
84
+ if (String(v || "").trim().toLowerCase() !== "clear") return ui.showToast("clear cancelled");
85
+ client
86
+ .clearUsages({})
87
+ .then((res) => {
88
+ ui.showToast(`${GLYPH.check} cleared ${res.usages ?? usages.length} usages`);
89
+ setCursor(0);
90
+ q.reload();
91
+ })
92
+ .catch((e) => ui.showToast(e.message, "red"));
93
+ },
94
+ });
95
+ }
80
96
  if (!cur) return;
81
97
  if (input === "e") return patch(cur, { environment: ENV_ORDER[(ENV_ORDER.indexOf(cur.environment) + 1) % 4] }, "env updated");
82
98
  if (input === "c") return patch(cur, { is_critical: !cur.is_critical }, "critical toggled");