arol-ai 0.1.5 → 0.1.7
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 +2 -0
- package/dist/cli.js +1 -8
- package/dist/data.js +3 -0
- package/dist/scanner.js +10 -5
- package/dist/status.js +14 -0
- package/package.json +5 -2
- package/src/data/deprecations.json +65 -27
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# arol-ai
|
|
2
2
|
|
|
3
|
+
[](https://github.com/benminor/arol/actions/workflows/ci.yml)
|
|
4
|
+
|
|
3
5
|
Scan a local code repo for usage of third-party APIs/SDKs that have **upcoming deprecations**, and print a clean report.
|
|
4
6
|
|
|
5
7
|
**Everything runs locally.** No network calls, no telemetry, no uploads, no auth. Your code never leaves your machine.
|
package/dist/cli.js
CHANGED
|
@@ -139,14 +139,7 @@ function runScan(targetPath, opts) {
|
|
|
139
139
|
const within = Number.isFinite(parsedWithin) && parsedWithin >= 0
|
|
140
140
|
? parsedWithin
|
|
141
141
|
: DEFAULT_WITHIN_DAYS;
|
|
142
|
-
const tripped = result.findings.some((f) =>
|
|
143
|
-
if (f.deprecation.severity === "high")
|
|
144
|
-
return true;
|
|
145
|
-
if ((0, status_1.effectiveStatus)(f.deprecation, now) !== "scheduled")
|
|
146
|
-
return false;
|
|
147
|
-
const days = (0, status_1.daysUntil)(f.deprecation.sunset_date, now);
|
|
148
|
-
return days !== null && days >= 0 && days <= within;
|
|
149
|
-
});
|
|
142
|
+
const tripped = result.findings.some((f) => (0, status_1.isActionable)(f.deprecation, now, within));
|
|
150
143
|
if (tripped)
|
|
151
144
|
process.exitCode = 1;
|
|
152
145
|
}
|
package/dist/data.js
CHANGED
|
@@ -107,6 +107,9 @@ function coerceDeprecation(raw) {
|
|
|
107
107
|
return null;
|
|
108
108
|
if ((match === "sdk" || match === "version") && sdk.length === 0)
|
|
109
109
|
return null;
|
|
110
|
+
// INVARIANT: every array below is always defined here — detect.{sdk,patterns,
|
|
111
|
+
// models} default to [] and applies_to to ["*"] (above). scanner.ts depends on
|
|
112
|
+
// this and also guards with `?? []` as defense in depth. Don't drop the defaults.
|
|
110
113
|
return {
|
|
111
114
|
id: r.id,
|
|
112
115
|
vendor: r.vendor,
|
package/dist/scanner.js
CHANGED
|
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.modelRegexSource = modelRegexSource;
|
|
39
40
|
exports.scanRepo = scanRepo;
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
@@ -104,7 +105,8 @@ function compileDeprecations(deprecations) {
|
|
|
104
105
|
return deprecations.map((deprecation) => {
|
|
105
106
|
const regexes = [];
|
|
106
107
|
// Raw patterns — code identifiers, endpoints, params.
|
|
107
|
-
|
|
108
|
+
// `?? []` guards against entries not produced by the loader (e.g. tests).
|
|
109
|
+
for (const pattern of deprecation.detect.patterns ?? []) {
|
|
108
110
|
try {
|
|
109
111
|
// Global so we can iterate every match and derive line numbers.
|
|
110
112
|
regexes.push(new RegExp(pattern, "g"));
|
|
@@ -114,7 +116,7 @@ function compileDeprecations(deprecations) {
|
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
// Model names — only matched inside string literals (quote-anchored).
|
|
117
|
-
for (const family of deprecation.detect.models) {
|
|
119
|
+
for (const family of deprecation.detect.models ?? []) {
|
|
118
120
|
try {
|
|
119
121
|
regexes.push(new RegExp(modelRegexSource(family), "g"));
|
|
120
122
|
}
|
|
@@ -122,7 +124,9 @@ function compileDeprecations(deprecations) {
|
|
|
122
124
|
// Defensive: a pathological family name must not crash the scan.
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
|
-
|
|
127
|
+
// Missing/empty applies_to means "applies everywhere" (["*"]), preserved here.
|
|
128
|
+
const declaredExts = deprecation.applies_to ?? [];
|
|
129
|
+
const appliesTo = new Set((declaredExts.length > 0 ? declaredExts : ["*"]).map((e) => e.toLowerCase()));
|
|
126
130
|
return { deprecation, regexes, appliesTo };
|
|
127
131
|
});
|
|
128
132
|
}
|
|
@@ -291,11 +295,12 @@ function scanContent(content, relPath, compiled, sink) {
|
|
|
291
295
|
function matchManifests(deprecations, refs) {
|
|
292
296
|
const byId = new Map();
|
|
293
297
|
for (const deprecation of deprecations) {
|
|
294
|
-
|
|
298
|
+
const sdks = deprecation.detect.sdk ?? [];
|
|
299
|
+
if (sdks.length === 0)
|
|
295
300
|
continue;
|
|
296
301
|
const matches = [];
|
|
297
302
|
const seen = new Set();
|
|
298
|
-
for (const sdk of
|
|
303
|
+
for (const sdk of sdks) {
|
|
299
304
|
for (const ref of refs) {
|
|
300
305
|
if (!(0, manifests_1.nameMatches)(sdk, ref.name))
|
|
301
306
|
continue;
|
package/dist/status.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.parseSunsetDate = parseSunsetDate;
|
|
4
4
|
exports.daysUntil = daysUntil;
|
|
5
5
|
exports.effectiveStatus = effectiveStatus;
|
|
6
|
+
exports.isActionable = isActionable;
|
|
6
7
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
7
8
|
/** Midnight-UTC timestamp for a Date's calendar day. */
|
|
8
9
|
function startOfDayUTC(d) {
|
|
@@ -43,3 +44,16 @@ function effectiveStatus(d, now) {
|
|
|
43
44
|
return "deprecated";
|
|
44
45
|
return t < startOfDayUTC(now) ? "retired" : "scheduled";
|
|
45
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Whether a finding should fail the CI gate (non-zero exit): any high-severity
|
|
49
|
+
* finding, or a scheduled finding landing within `within` days. Dateless
|
|
50
|
+
* "deprecated" and non-imminent medium/low findings are warn-only.
|
|
51
|
+
*/
|
|
52
|
+
function isActionable(d, now, within) {
|
|
53
|
+
if (d.severity === "high")
|
|
54
|
+
return true;
|
|
55
|
+
if (effectiveStatus(d, now) !== "scheduled")
|
|
56
|
+
return false;
|
|
57
|
+
const days = daysUntil(d.sunset_date, now);
|
|
58
|
+
return days !== null && days >= 0 && days <= within;
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arol-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Scan a local repo for upcoming third-party API/SDK deprecations. Fully local — no network, no telemetry, your code never leaves the machine.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deprecation",
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsc",
|
|
31
31
|
"scan": "node dist/cli.js scan",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
32
34
|
"prepublishOnly": "npm run build"
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
@@ -37,6 +39,7 @@
|
|
|
37
39
|
},
|
|
38
40
|
"devDependencies": {
|
|
39
41
|
"@types/node": "^22.10.0",
|
|
40
|
-
"typescript": "^5.7.2"
|
|
42
|
+
"typescript": "^5.7.2",
|
|
43
|
+
"vitest": "^4.1.8"
|
|
41
44
|
}
|
|
42
45
|
}
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"detect": {
|
|
11
11
|
"sdk": ["openai"],
|
|
12
12
|
"patterns": [
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"\\bbeta\\.assistants\\b",
|
|
14
|
+
"\\bbeta\\.threads\\b",
|
|
15
15
|
"/v1/assistants",
|
|
16
16
|
"/v1/threads"
|
|
17
17
|
]
|
|
@@ -180,13 +180,13 @@
|
|
|
180
180
|
"detect": {
|
|
181
181
|
"sdk": ["@stripe/stripe-js", "stripe"],
|
|
182
182
|
"patterns": [
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"createSource",
|
|
189
|
-
"retrieveSource"
|
|
183
|
+
"\\bhandleCardPayment\\b",
|
|
184
|
+
"\\bconfirmPaymentIntent\\b",
|
|
185
|
+
"\\bhandleFpxPayment\\b",
|
|
186
|
+
"\\bhandleCardSetup\\b",
|
|
187
|
+
"\\bconfirmSetupIntent\\b",
|
|
188
|
+
"\\bstripe\\.createSource\\b",
|
|
189
|
+
"\\bstripe\\.retrieveSource\\b"
|
|
190
190
|
]
|
|
191
191
|
},
|
|
192
192
|
"migration_url": "https://docs.stripe.com/changelog/dahlia/2026-03-25/remove-legacy-stripejs-methods",
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
"sunset_date": "2024-05-15",
|
|
204
204
|
"detect": {
|
|
205
205
|
"sdk": ["stripe"],
|
|
206
|
-
"patterns": ["\\.sources\\.create", "charges\\.create", "Charge\\.create"]
|
|
206
|
+
"patterns": ["\\bstripe\\.sources\\.create", "\\bstripe\\.charges\\.create", "\\bstripe\\.Charge\\.create"]
|
|
207
207
|
},
|
|
208
208
|
"migration_url": "https://docs.stripe.com/payments/older-apis",
|
|
209
209
|
"summary": "The Sources API is deprecated (local payment methods stopped being accepted May 15, 2024) and the Charges API is legacy. Migrate to the PaymentMethods + PaymentIntents APIs.",
|
|
@@ -216,15 +216,14 @@
|
|
|
216
216
|
"severity": "high",
|
|
217
217
|
"match": "pattern",
|
|
218
218
|
"applies_to": ["py", "js", "ts", "jsx", "tsx", "mjs"],
|
|
219
|
-
"sunset_date": "
|
|
220
|
-
"date_confidence": "verify",
|
|
219
|
+
"sunset_date": "2025-12-31",
|
|
221
220
|
"detect": {
|
|
222
221
|
"sdk": ["twilio"],
|
|
223
|
-
"patterns": ["
|
|
222
|
+
"patterns": ["\\bnotify\\.v1\\b", "\\.notify\\.services\\b"]
|
|
224
223
|
},
|
|
225
|
-
"migration_url": "https://
|
|
226
|
-
"summary": "Twilio Notify
|
|
227
|
-
"source": "https://www.
|
|
224
|
+
"migration_url": "https://support.twilio.com/hc/en-us/articles/9198083260571-Transitioning-off-Notify",
|
|
225
|
+
"summary": "Twilio Notify reached end of life December 31, 2025; Notify API requests now fail. No 1:1 replacement — rebuild with Programmable Messaging / Conversations.",
|
|
226
|
+
"source": "https://www.twilio.com/en-us/changelog/notify-api-end-of-life-further-extension-notice"
|
|
228
227
|
},
|
|
229
228
|
{
|
|
230
229
|
"id": "twilio-programmable-chat-retired",
|
|
@@ -236,12 +235,28 @@
|
|
|
236
235
|
"sunset_date": "2022-07-25",
|
|
237
236
|
"detect": {
|
|
238
237
|
"sdk": ["twilio", "twilio-chat"],
|
|
239
|
-
"patterns": ["
|
|
238
|
+
"patterns": ["\\bchat\\.v2\\b", "\\bIpMessaging\\b"]
|
|
240
239
|
},
|
|
241
240
|
"migration_url": "https://www.twilio.com/docs/conversations/migrating-chat-conversations",
|
|
242
241
|
"summary": "The standalone Programmable Chat API was sunset July 25, 2022 (Programmable Chat in Flex ended June 1, 2026). Migrate to the Conversations API.",
|
|
243
242
|
"source": "https://www.twilio.com/en-us/changelog/programmable-chat-end-of-life-notice"
|
|
244
243
|
},
|
|
244
|
+
{
|
|
245
|
+
"id": "twilio-chat-package-eol",
|
|
246
|
+
"vendor": "Twilio",
|
|
247
|
+
"title": "twilio-chat SDK package (Programmable Chat)",
|
|
248
|
+
"severity": "high",
|
|
249
|
+
"match": "sdk",
|
|
250
|
+
"applies_to": ["js", "ts", "jsx", "tsx", "mjs"],
|
|
251
|
+
"sunset_date": "2022-07-25",
|
|
252
|
+
"detect": {
|
|
253
|
+
"sdk": ["twilio-chat"],
|
|
254
|
+
"patterns": []
|
|
255
|
+
},
|
|
256
|
+
"migration_url": "https://www.twilio.com/docs/conversations/migrating-chat-conversations",
|
|
257
|
+
"summary": "The twilio-chat npm package is the client SDK for the retired Programmable Chat API (sunset July 25, 2022). Remove the dependency and migrate to @twilio/conversations.",
|
|
258
|
+
"source": "https://www.twilio.com/en-us/changelog/programmable-chat-end-of-life-notice"
|
|
259
|
+
},
|
|
245
260
|
{
|
|
246
261
|
"id": "aws-sdk-js-v2-eol",
|
|
247
262
|
"vendor": "AWS",
|
|
@@ -285,10 +300,10 @@
|
|
|
285
300
|
"detect": {
|
|
286
301
|
"sdk": ["openai"],
|
|
287
302
|
"patterns": [
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
291
|
-
"
|
|
303
|
+
"\\bopenai\\.ChatCompletion\\b",
|
|
304
|
+
"\\bopenai\\.Completion\\.create\\b",
|
|
305
|
+
"\\bopenai\\.Embedding\\.create\\b",
|
|
306
|
+
"\\bopenai\\.Moderation\\.create\\b"
|
|
292
307
|
]
|
|
293
308
|
},
|
|
294
309
|
"migration_url": "https://github.com/openai/openai-python/discussions/742",
|
|
@@ -308,9 +323,9 @@
|
|
|
308
323
|
"patterns": [
|
|
309
324
|
"from ['\"]ai/react['\"]",
|
|
310
325
|
"from ['\"]ai/openai['\"]",
|
|
311
|
-
"
|
|
312
|
-
"
|
|
313
|
-
"
|
|
326
|
+
"\\bexperimental_streamText\\b",
|
|
327
|
+
"\\bexperimental_generateText\\b",
|
|
328
|
+
"\\bStreamingTextResponse\\b"
|
|
314
329
|
]
|
|
315
330
|
},
|
|
316
331
|
"migration_url": "https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0",
|
|
@@ -331,8 +346,8 @@
|
|
|
331
346
|
"from langchain\\.llms import",
|
|
332
347
|
"from langchain\\.chat_models import",
|
|
333
348
|
"from langchain\\.embeddings import",
|
|
334
|
-
"
|
|
335
|
-
"
|
|
349
|
+
"\\binitialize_agent\\b",
|
|
350
|
+
"\\bLLMChain\\b"
|
|
336
351
|
]
|
|
337
352
|
},
|
|
338
353
|
"migration_url": "https://python.langchain.com/docs/versions/v0_2/",
|
|
@@ -350,10 +365,33 @@
|
|
|
350
365
|
"sunset_date": null,
|
|
351
366
|
"detect": {
|
|
352
367
|
"sdk": ["resend"],
|
|
353
|
-
"patterns": ["\\.audiences\\.", "\\.Audiences\\."]
|
|
368
|
+
"patterns": ["\\bresend\\.audiences\\.", "\\bresend\\.Audiences\\."]
|
|
354
369
|
},
|
|
355
370
|
"migration_url": "https://resend.com/docs/dashboard/segments/migrating-from-audiences-to-segments",
|
|
356
371
|
"summary": "Resend's Audiences API is deprecated in favor of Segments. The endpoints still work but will be removed in the future (no date announced). Migrate the audiences.* calls to the Segments API.",
|
|
357
372
|
"source": "https://resend.com/docs/api-reference/audiences/create-audience"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"id": "clerk-redirect-to-user-profile-component",
|
|
376
|
+
"vendor": "Clerk",
|
|
377
|
+
"title": "<RedirectToUserProfile /> control component deprecated",
|
|
378
|
+
"severity": "low",
|
|
379
|
+
"match": "pattern",
|
|
380
|
+
"applies_to": ["jsx", "tsx", "js", "ts"],
|
|
381
|
+
"sunset_date": null,
|
|
382
|
+
"status": "deprecated",
|
|
383
|
+
"detect": {
|
|
384
|
+
"sdk": [
|
|
385
|
+
"@clerk/nextjs",
|
|
386
|
+
"@clerk/clerk-react",
|
|
387
|
+
"@clerk/nuxt",
|
|
388
|
+
"@clerk/vue"
|
|
389
|
+
],
|
|
390
|
+
"patterns": ["\\bRedirectToUserProfile\\b"],
|
|
391
|
+
"models": []
|
|
392
|
+
},
|
|
393
|
+
"migration_url": "https://clerk.com/docs/reference/objects/clerk#redirect-to-user-profile",
|
|
394
|
+
"summary": "Clerk's <RedirectToUserProfile /> control component is deprecated in favor of the redirectToUserProfile() method on the Clerk object. No removal date announced.",
|
|
395
|
+
"source": "https://clerk.com/docs/nextjs/reference/components/control/redirect-to-user-profile"
|
|
358
396
|
}
|
|
359
397
|
]
|