@modelstatus/cli 0.1.81 → 0.1.82
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 +1 -1
- package/src/changelog-data.js +9 -0
- package/src/index.js +3 -3
- package/src/registry/local.js +4 -0
- package/src/tui/scan-stream.js +2 -2
- package/src/tui/ui.js +3 -1
- package/src/tui/views/alerts.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelstatus/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.82",
|
|
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/changelog-data.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/* GENERATED by scripts/gen-changelog.mjs from apps/web/lib/changelog.json — do not edit.
|
|
2
2
|
* Release notes baked into the binary (in-TUI + the on-load what's-new card). */
|
|
3
3
|
export const CHANGELOG = [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.82",
|
|
6
|
+
"date": "2026-06-13",
|
|
7
|
+
"title": "Withdrawn models",
|
|
8
|
+
"items": [
|
|
9
|
+
"When a provider pulls a model it released (an unplanned withdrawal, not a planned retirement), it now shows a distinct ⚠ withdrawn badge — in `mm status`, the TUI, and on its model page — instead of silently becoming an unrecognized id.",
|
|
10
|
+
"If you reference a withdrawn model, you get alerted (the same as the people who saw its announcement)."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"version": "0.1.81",
|
|
6
15
|
"date": "2026-06-12",
|
package/src/index.js
CHANGED
|
@@ -486,7 +486,7 @@ async function cmdCi(positional, flags) {
|
|
|
486
486
|
if (flags.json) {
|
|
487
487
|
console.log(JSON.stringify(report, null, 2));
|
|
488
488
|
} else {
|
|
489
|
-
const ICON = { ok: "🟢", deprecating: "🟡", retiring: "🟠", retired: "🔴" };
|
|
489
|
+
const ICON = { ok: "🟢", deprecating: "🟡", retiring: "🟠", retired: "🔴", withdrawn: "⛔" };
|
|
490
490
|
console.log(`LLM Status CI — scanned ${dir} (fail-on: ${failOn})`);
|
|
491
491
|
if (!findings.length) {
|
|
492
492
|
console.log("✓ No deprecated, retiring, or retired AI models found.");
|
|
@@ -709,8 +709,8 @@ async function cmdStatus(positional, flags) {
|
|
|
709
709
|
}
|
|
710
710
|
|
|
711
711
|
const today = new Date();
|
|
712
|
-
const ICON = { ok: "🟢", deprecating: "🟡", retiring: "🟠", retired: "🔴", custom: "⚪" };
|
|
713
|
-
const rank = { retired: 0, retiring: 1, deprecating: 2, ok: 3 };
|
|
712
|
+
const ICON = { ok: "🟢", deprecating: "🟡", retiring: "🟠", retired: "🔴", withdrawn: "⛔", custom: "⚪" };
|
|
713
|
+
const rank = { withdrawn: 0, retired: 0, retiring: 1, deprecating: 2, ok: 3 };
|
|
714
714
|
const rows = [...known.values()]
|
|
715
715
|
.map(({ model, count }) => ({ model, count, health: computeHealth(model, 90, today) }))
|
|
716
716
|
.sort((a, b) => rank[a.health] - rank[b.health] || String(a.model.retires_date || "9999").localeCompare(String(b.model.retires_date || "9999")));
|
package/src/registry/local.js
CHANGED
|
@@ -34,6 +34,10 @@ export function resolveLocal(snapshot, strings) {
|
|
|
34
34
|
/** Mirror of the server's computeHealth for a snapshot (slug-space) model. */
|
|
35
35
|
export function computeHealth(model, retiringWindowDays = 90, today = new Date()) {
|
|
36
36
|
if (!model) return "custom";
|
|
37
|
+
// Withdrawn (provider pulled it) is distinct from a planned retirement and is
|
|
38
|
+
// checked first. Snapshots also stamp retires_date on withdrawn models so an
|
|
39
|
+
// OLDER binary (which doesn't know this status) falls to "retired" below.
|
|
40
|
+
if (model.status === "withdrawn") return "withdrawn";
|
|
37
41
|
const ret = model.retires_date ? new Date(model.retires_date) : null;
|
|
38
42
|
if (model.status === "retired" || (ret && ret <= today)) return "retired";
|
|
39
43
|
if (ret) {
|
package/src/tui/scan-stream.js
CHANGED
|
@@ -15,7 +15,7 @@ import { resolveLocal, computeHealth, dropResolvedFragments } from "../registry/
|
|
|
15
15
|
import { scanFilesystemStreaming } from "../sources/filesystem.js";
|
|
16
16
|
import { compilePatterns } from "../detect/core.js";
|
|
17
17
|
|
|
18
|
-
export const HEALTH_RANK = { retired: 0, retiring: 1, deprecating: 2, ok: 3 };
|
|
18
|
+
export const HEALTH_RANK = { withdrawn: 0, retired: 0, retiring: 1, deprecating: 2, ok: 3 };
|
|
19
19
|
|
|
20
20
|
// dir → { snapshot, candidates, ... } for the last completed scan THIS session.
|
|
21
21
|
const SCAN_CACHE = new Map();
|
|
@@ -120,7 +120,7 @@ export function summarize(snapshot, candidates) {
|
|
|
120
120
|
|
|
121
121
|
/** Health distribution for the status-bar legend. */
|
|
122
122
|
export function countHealth(rows) {
|
|
123
|
-
const counts = { ok: 0, deprecating: 0, retiring: 0, retired: 0, custom: 0 };
|
|
123
|
+
const counts = { ok: 0, deprecating: 0, retiring: 0, retired: 0, withdrawn: 0, custom: 0 };
|
|
124
124
|
for (const r of rows) counts[r.health] = (counts[r.health] || 0) + 1;
|
|
125
125
|
return counts;
|
|
126
126
|
}
|
package/src/tui/ui.js
CHANGED
|
@@ -57,6 +57,7 @@ export const HEALTH_COLOR = {
|
|
|
57
57
|
deprecating: "#d97706",
|
|
58
58
|
retiring: "#ea580c",
|
|
59
59
|
retired: "#dc2626",
|
|
60
|
+
withdrawn: "#dc2626", // pulled by provider — as severe as retired
|
|
60
61
|
custom: "#9ca3af",
|
|
61
62
|
};
|
|
62
63
|
export const HEALTH_GLYPH = {
|
|
@@ -64,6 +65,7 @@ export const HEALTH_GLYPH = {
|
|
|
64
65
|
deprecating: GLYPH.deprecating,
|
|
65
66
|
retiring: GLYPH.retiring,
|
|
66
67
|
retired: GLYPH.retired,
|
|
68
|
+
withdrawn: GLYPH.warn, // ⚠ distinguishes a pull from a planned × retirement
|
|
67
69
|
custom: GLYPH.custom,
|
|
68
70
|
};
|
|
69
71
|
export const healthColor = (hh) => HEALTH_COLOR[hh] || C.FG;
|
|
@@ -292,7 +294,7 @@ export function ListRow({ active, selected, cells, width }) {
|
|
|
292
294
|
|
|
293
295
|
/** counts → [{text,color}] legend segments, all states in fixed order, zeros dimmed. */
|
|
294
296
|
export function legendSegments(counts = {}) {
|
|
295
|
-
const order = ["ok", "deprecating", "retiring", "retired"];
|
|
297
|
+
const order = ["ok", "deprecating", "retiring", "retired", "withdrawn"];
|
|
296
298
|
const segs = [];
|
|
297
299
|
order.forEach((k, i) => {
|
|
298
300
|
const n = counts[k] || 0;
|
package/src/tui/views/alerts.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "../ui.js";
|
|
7
7
|
|
|
8
8
|
const DELIVERY = ["immediate", "daily", "weekly"];
|
|
9
|
-
const DEFAULT_EVENTS = ["model_deprecated", "model_retired", "replacement_set", "price_changed", "new_model"];
|
|
9
|
+
const DEFAULT_EVENTS = ["model_deprecated", "model_retired", "model_withdrawn", "replacement_set", "price_changed", "new_model"];
|
|
10
10
|
const CHANNEL_KINDS = ["slack", "discord", "teams", "webhook", "sms"];
|
|
11
11
|
|
|
12
12
|
// Readable label for a rule even when it has no name (seed/sample rules do):
|