@aidriftline/driftline 0.1.12 → 0.1.14
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/registry/models.json +9 -1
- package/src/mcp.js +63 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aidriftline/driftline",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Finds OpenAI, Anthropic and Google model IDs in your code and warns before they are retired. CLI, GitHub Action and MCP server.",
|
|
5
5
|
"mcpName": "io.github.AI-Driftline/driftline",
|
|
6
6
|
"type": "module",
|
package/registry/models.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"checked": "2026-11-
|
|
2
|
+
"checked": "2026-11-12",
|
|
3
3
|
"note": "Hand-maintained from the official deprecation pages. Re-check before each release. Entries with a 'kind' other than text are scanned and reported but never drift-tested. gpt-4-1106-preview is listed on OpenAI's page under both 2026-03-26 and 2026-10-23; the later date is used so builds are not failed on a conflicting record. Entries with routes: true keep answering after the date, but the provider sends the requests to the replacement model (Google calls this automatic routing); the 'retires' date is the day routing starts. gemini-3.5-flash and gemini-3.7-flash already route to newer models with no date given, so they are left out.",
|
|
4
4
|
"sources": {
|
|
5
5
|
"openai": "https://developers.openai.com/api/docs/deprecations",
|
|
@@ -274,6 +274,14 @@
|
|
|
274
274
|
"replacement": "gpt-5.6-terra",
|
|
275
275
|
"announced": "2026-04-22"
|
|
276
276
|
},
|
|
277
|
+
{
|
|
278
|
+
"id": "ft-o4-mini-2025-04-16",
|
|
279
|
+
"provider": "openai",
|
|
280
|
+
"retires": "2026-10-23",
|
|
281
|
+
"qualifier": "exact",
|
|
282
|
+
"replacement": "gpt-5.6-terra",
|
|
283
|
+
"announced": "2026-04-22"
|
|
284
|
+
},
|
|
277
285
|
{
|
|
278
286
|
"id": "gpt-4",
|
|
279
287
|
"provider": "openai",
|
package/src/mcp.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Speaks JSON-RPC 2.0, one message per line on stdin/stdout (the MCP stdio transport). No dependencies.
|
|
4
4
|
// Data: the live registry at aidriftline.com/models.json (updated daily), or the copy bundled with this
|
|
5
5
|
// version when the site can't be reached or is older. Set DRIFTLINE_OFFLINE=1 to never fetch.
|
|
6
|
-
import { readFileSync } from "node:fs";
|
|
6
|
+
import { readFileSync, statSync } from "node:fs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { createInterface } from "node:readline";
|
|
9
9
|
import { REGISTRY_PATH, buildRegistry, classify, normalizeId } from "./registry.js";
|
|
@@ -55,27 +55,78 @@ function describe(rawId, reg, today) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
const STATUS_ENUM = ["retired", "at-risk", "deprecated", "active", "unknown"];
|
|
59
|
+
const MODEL_RESULT = {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
id: { type: "string", description: "The ID as given" },
|
|
63
|
+
normalized: { type: "string", description: "The ID after removing provider prefixes such as models/ or openai/" },
|
|
64
|
+
status: { type: "string", enum: STATUS_ENUM, description: "retired = calls fail (or are re-routed) now; at-risk = retires within 90 days; deprecated = retirement announced, further out; active = announced as active; unknown = no retirement published" },
|
|
65
|
+
provider: { type: "string" }, retires: { type: "string", description: "YYYY-MM-DD" },
|
|
66
|
+
qualifier: { type: "string", enum: ["exact", "earliest", "not-before"], description: "exact date, earliest possible shutdown (Google), or not before this date (Anthropic)" },
|
|
67
|
+
days_left: { type: "number", description: "Days until the date; 0 or negative when it has passed" },
|
|
68
|
+
replacement: { type: ["string", "null"] },
|
|
69
|
+
routes: { type: "boolean", description: "true: after the date the provider answers with the replacement model instead of failing" },
|
|
70
|
+
alias_of: { type: "string" }, page: { type: "string", description: "aidriftline.com page with the source for this date" },
|
|
71
|
+
advice: { type: "string", description: "One plain sentence to pass on to the user" },
|
|
72
|
+
},
|
|
73
|
+
required: ["id", "status", "advice"],
|
|
74
|
+
};
|
|
75
|
+
const META = {
|
|
76
|
+
registry_checked: { type: "string", description: "Date the registry was last checked against the provider pages" },
|
|
77
|
+
registry_source: { type: "string", enum: ["live", "bundled"], description: "live = aidriftline.com/models.json; bundled = the copy shipped in the package (used offline)" },
|
|
78
|
+
all_dates: { type: "string" },
|
|
79
|
+
next_step: { type: "string", description: "Present when a model has to move: how to test prompts on the replacement first" },
|
|
80
|
+
};
|
|
81
|
+
const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
|
|
82
|
+
|
|
58
83
|
const TOOLS = [
|
|
59
84
|
{
|
|
60
85
|
name: "check_models",
|
|
61
|
-
|
|
62
|
-
|
|
86
|
+
title: "Check model IDs for retirement",
|
|
87
|
+
description: "Look up OpenAI, Anthropic and Google model IDs and return, for each one, its retirement status, retirement date, days left and replacement. " +
|
|
88
|
+
"Use it before writing, changing or keeping a model ID in code, or when the user asks whether a model still works. " +
|
|
89
|
+
"To find the IDs inside a codebase use scan_project instead; to list everything retiring soon use upcoming_retirements. " +
|
|
90
|
+
"Read-only. Data comes from the providers' official deprecation pages, re-checked daily at aidriftline.com (fetched with a 4 s timeout, falling back to the copy in the package when offline). " +
|
|
91
|
+
"IDs not in the registry return status \"unknown\": no retirement is published for them, which does not prove the ID exists. Up to 100 IDs per call; extra IDs are ignored.",
|
|
92
|
+
inputSchema: { type: "object", properties: {
|
|
93
|
+
ids: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 100,
|
|
94
|
+
description: "Model IDs exactly as written in code, e.g. [\"gpt-4o\", \"claude-sonnet-4-5\", \"models/gemini-2.5-pro\"]. Provider prefixes (openai/, models/), Bedrock, Vertex and OpenRouter spellings and Claude short names are accepted and normalized." },
|
|
95
|
+
}, required: ["ids"] },
|
|
96
|
+
outputSchema: { type: "object", properties: { models: { type: "array", items: MODEL_RESULT }, ...META }, required: ["models", "registry_checked", "registry_source"] },
|
|
97
|
+
annotations: { title: "Check model IDs for retirement", ...READ_ONLY },
|
|
63
98
|
},
|
|
64
99
|
{
|
|
65
100
|
name: "scan_project",
|
|
66
|
-
|
|
101
|
+
title: "Scan a project for retiring model IDs",
|
|
102
|
+
description: "Scan a project folder for OpenAI, Anthropic and Google model IDs in code and config files and report the ones that are retired, retiring soon, deprecated or being re-routed, with file and line, most urgent first. " +
|
|
103
|
+
"Use it to audit a repository or before a release. For IDs you already know use check_models; for a time-based list use upcoming_retirements. " +
|
|
104
|
+
"Read-only: reads files on this machine and never changes them. Scans subfolders recursively, skips .git, node_modules, vendor, dist, build and similar folders, and files over 1 MB. " +
|
|
105
|
+
"Mentions in .md and .txt files are marked in_docs. Returns at most 200 findings (truncated: true when cut). Fails with an error if the path does not exist.",
|
|
67
106
|
inputSchema: { type: "object", properties: {
|
|
68
|
-
path: { type: "string", description: "
|
|
69
|
-
include_ok: { type: "boolean", description: "Also list models with no retirement announced (
|
|
107
|
+
path: { type: "string", description: "Absolute path of the folder to scan. Defaults to the server's working directory, which may not be the user's project, so pass it explicitly." },
|
|
108
|
+
include_ok: { type: "boolean", default: false, description: "Also list models with no retirement announced (status active or unknown). Default false." },
|
|
70
109
|
} },
|
|
110
|
+
outputSchema: { type: "object", properties: {
|
|
111
|
+
scanned: { type: "string" }, model_references: { type: "number", description: "All model ID mentions found, before filtering" },
|
|
112
|
+
flagged: { type: "number" }, truncated: { type: "boolean" },
|
|
113
|
+
findings: { type: "array", items: { ...MODEL_RESULT, properties: { file: { type: "string" }, line: { type: "number" }, in_docs: { type: "boolean" }, ...MODEL_RESULT.properties } } },
|
|
114
|
+
...META,
|
|
115
|
+
}, required: ["scanned", "flagged", "findings"] },
|
|
116
|
+
annotations: { title: "Scan a project for retiring model IDs", ...READ_ONLY },
|
|
71
117
|
},
|
|
72
118
|
{
|
|
73
119
|
name: "upcoming_retirements",
|
|
74
|
-
|
|
120
|
+
title: "List upcoming model retirements",
|
|
121
|
+
description: "List OpenAI, Anthropic and Google models whose retirement or re-routing date falls in the next N days, soonest first, with replacement for each. " +
|
|
122
|
+
"Use it to answer \"what is being retired soon?\" or to plan migrations. It does not look at code: use scan_project for that, or check_models for specific IDs. " +
|
|
123
|
+
"Read-only. Models already past their date are not included (check_models reports those as retired). Anthropic \"not before\" dates are left out because those models are still active.",
|
|
75
124
|
inputSchema: { type: "object", properties: {
|
|
76
|
-
days: { type: "number", description: "Look-ahead window in days
|
|
77
|
-
provider: { type: "string", enum: ["openai", "anthropic", "google"] },
|
|
125
|
+
days: { type: "number", minimum: 1, maximum: 3650, default: 90, description: "Look-ahead window in days, 1 to 3650. Default 90." },
|
|
126
|
+
provider: { type: "string", enum: ["openai", "anthropic", "google"], description: "Only this provider. Omit for all three." },
|
|
78
127
|
} },
|
|
128
|
+
outputSchema: { type: "object", properties: { days: { type: "number" }, count: { type: "number" }, models: { type: "array", items: MODEL_RESULT }, ...META }, required: ["days", "count", "models"] },
|
|
129
|
+
annotations: { title: "List upcoming model retirements", ...READ_ONLY },
|
|
79
130
|
},
|
|
80
131
|
];
|
|
81
132
|
|
|
@@ -98,6 +149,9 @@ export async function callTool(name, args = {}, { reg, source, today = new Date(
|
|
|
98
149
|
}
|
|
99
150
|
if (name === "scan_project") {
|
|
100
151
|
const root = resolve(args.path || ".");
|
|
152
|
+
let isDir = false;
|
|
153
|
+
try { isDir = statSync(root).isDirectory(); } catch { /* missing */ }
|
|
154
|
+
if (!isDir) throw new Error(`Not a folder: ${root}. Pass the absolute path of the project to scan.`);
|
|
101
155
|
const hits = scanDirectory(root, { known: reg.byId });
|
|
102
156
|
const rows = hits.map((h) => ({ file: h.file.split("\\").join("/"), line: h.line, in_docs: h.doc, ...describe(h.id, reg, today) }))
|
|
103
157
|
.filter((r) => args.include_ok || !["active", "unknown"].includes(r.status));
|