@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13
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/CHANGELOG.md +101 -0
- package/LICENSE.md +559 -186
- package/README.md +18 -0
- package/dist/bin.js +1 -9
- package/dist/config/index.d.ts +477 -556
- package/dist/config/index.js +1 -2
- package/dist/generate/index.js +1 -3
- package/dist/packem_chunks/applyDefaults.js +2 -336
- package/dist/packem_chunks/bin.js +234 -9552
- package/dist/packem_chunks/doctor-probe.js +2 -112
- package/dist/packem_chunks/fix.js +11 -234
- package/dist/packem_chunks/handler.js +1 -99
- package/dist/packem_chunks/handler10.js +2 -53
- package/dist/packem_chunks/handler11.js +1 -32
- package/dist/packem_chunks/handler12.js +5 -100
- package/dist/packem_chunks/handler13.js +1 -25
- package/dist/packem_chunks/handler14.js +18 -916
- package/dist/packem_chunks/handler15.js +15 -201
- package/dist/packem_chunks/handler16.js +1 -124
- package/dist/packem_chunks/handler17.js +1 -13
- package/dist/packem_chunks/handler18.js +1 -106
- package/dist/packem_chunks/handler19.js +1 -19
- package/dist/packem_chunks/handler2.js +2 -75
- package/dist/packem_chunks/handler20.js +5 -29
- package/dist/packem_chunks/handler21.js +1 -222
- package/dist/packem_chunks/handler22.js +1 -237
- package/dist/packem_chunks/handler23.js +5 -101
- package/dist/packem_chunks/handler24.js +1 -110
- package/dist/packem_chunks/handler25.js +3 -402
- package/dist/packem_chunks/handler26.js +1 -13
- package/dist/packem_chunks/handler27.js +1 -63
- package/dist/packem_chunks/handler28.js +7 -34
- package/dist/packem_chunks/handler29.js +21 -456
- package/dist/packem_chunks/handler3.js +4 -95
- package/dist/packem_chunks/handler30.js +3 -170
- package/dist/packem_chunks/handler31.js +1 -530
- package/dist/packem_chunks/handler32.js +2 -214
- package/dist/packem_chunks/handler33.js +25 -119
- package/dist/packem_chunks/handler34.js +2 -630
- package/dist/packem_chunks/handler35.js +3 -283
- package/dist/packem_chunks/handler36.js +22 -542
- package/dist/packem_chunks/handler37.js +410 -744
- package/dist/packem_chunks/handler38.js +22 -989
- package/dist/packem_chunks/handler39.js +22 -574
- package/dist/packem_chunks/handler4.js +2 -90
- package/dist/packem_chunks/handler40.js +22 -1685
- package/dist/packem_chunks/handler41.js +6 -1088
- package/dist/packem_chunks/handler42.js +5 -797
- package/dist/packem_chunks/handler43.js +10 -2658
- package/dist/packem_chunks/handler44.js +51 -3784
- package/dist/packem_chunks/handler45.js +25 -2574
- package/dist/packem_chunks/handler46.js +3 -3769
- package/dist/packem_chunks/handler47.js +21 -1485
- package/dist/packem_chunks/handler48.js +42 -0
- package/dist/packem_chunks/handler5.js +8 -174
- package/dist/packem_chunks/handler6.js +1 -95
- package/dist/packem_chunks/handler7.js +1 -115
- package/dist/packem_chunks/handler8.js +1 -12
- package/dist/packem_chunks/handler9.js +1 -29
- package/dist/packem_chunks/heal-accept.js +10 -522
- package/dist/packem_chunks/heal.js +14 -673
- package/dist/packem_chunks/index.js +7 -873
- package/dist/packem_chunks/loader.js +1 -23
- package/dist/packem_chunks/tar.js +3 -0
- package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
- package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
- package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
- package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
- package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
- package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
- package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
- package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
- package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
- package/dist/packem_shared/index-DH-5hsrC.js +1 -0
- package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
- package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
- package/dist/packem_shared/registry-CkubDdiY.js +2 -0
- package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
- package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
- package/dist/packem_shared/selectors-BylODRiM.js +3 -0
- package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
- package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
- package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
- package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
- package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
- package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
- package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
- package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
- package/index.js +556 -727
- package/package.json +19 -29
- package/schemas/project.schema.json +739 -297
- package/schemas/vis-config.schema.json +3365 -384
- package/templates/buildkite-ci/template.yml +20 -20
- package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
- package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
- package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
- package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
- package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
- package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
- package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
- package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
- package/dist/packem_shared/docker-2iZzc280.js +0 -181
- package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
- package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
- package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
- package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
- package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
- package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
- package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
- package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
- package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
- package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
- package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
- package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
- package/dist/packem_shared/utils-CthVdBPS.js +0 -40
- package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
import { runProvider, PROVIDER_NAMES, detectProvider, detectAvailableProviders } from '@visulima/find-ai-runner';
|
|
2
|
-
import { renderToString, Box, Text, Table } from '@visulima/tui';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import { b as buildCacheKey, a as getCachedAnalysis, s as setCachedAnalysis, d as getTtlForAnalysisType } from './ai-cache-Be_jexe4.js';
|
|
5
|
-
|
|
6
|
-
const AI_TIMEOUT_MS = 12e4;
|
|
7
|
-
const MAX_RETRIES = 2;
|
|
8
|
-
const RETRY_BASE_DELAY_MS = 1e3;
|
|
9
|
-
const sleep = (ms) => new Promise((resolve) => {
|
|
10
|
-
setTimeout(resolve, ms);
|
|
11
|
-
});
|
|
12
|
-
const runWithRetry = async (provider, prompt, retries = MAX_RETRIES) => {
|
|
13
|
-
let lastError;
|
|
14
|
-
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
15
|
-
try {
|
|
16
|
-
const result = await runProvider(provider, prompt, { timeoutMs: AI_TIMEOUT_MS });
|
|
17
|
-
return result.stdout;
|
|
18
|
-
} catch (error) {
|
|
19
|
-
lastError = error instanceof Error ? error : new Error(String(error));
|
|
20
|
-
if (lastError.message.includes("timed out")) {
|
|
21
|
-
throw lastError;
|
|
22
|
-
}
|
|
23
|
-
if (attempt < retries) {
|
|
24
|
-
const delay = RETRY_BASE_DELAY_MS * 2 ** attempt;
|
|
25
|
-
await sleep(delay);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
throw lastError ?? new Error("AI request failed after retries");
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const DEFAULT_PRIORITY = {
|
|
33
|
-
amp: 30,
|
|
34
|
-
claude: 80,
|
|
35
|
-
codex: 60,
|
|
36
|
-
copilot: 50,
|
|
37
|
-
crush: 35,
|
|
38
|
-
cursor: 40,
|
|
39
|
-
droid: 20,
|
|
40
|
-
gemini: 100,
|
|
41
|
-
kimi: 25,
|
|
42
|
-
opencode: 35,
|
|
43
|
-
qwen: 30
|
|
44
|
-
};
|
|
45
|
-
const resolveProvider = (config) => {
|
|
46
|
-
if (config?.provider) {
|
|
47
|
-
if (!PROVIDER_NAMES.includes(config.provider)) {
|
|
48
|
-
return void 0;
|
|
49
|
-
}
|
|
50
|
-
const provider = detectProvider(config.provider);
|
|
51
|
-
return provider.available ? provider : void 0;
|
|
52
|
-
}
|
|
53
|
-
const available = detectAvailableProviders();
|
|
54
|
-
if (available.length === 0) {
|
|
55
|
-
return void 0;
|
|
56
|
-
}
|
|
57
|
-
const priority = { ...DEFAULT_PRIORITY, ...config?.priority };
|
|
58
|
-
return available.toSorted((a, b) => (priority[b.name] ?? 0) - (priority[a.name] ?? 0))[0];
|
|
59
|
-
};
|
|
60
|
-
const VALID_ACTIONS = /* @__PURE__ */ new Set(["defer", "review", "skip", "update"]);
|
|
61
|
-
const VALID_RISK_LEVELS = /* @__PURE__ */ new Set(["critical", "high", "low", "medium"]);
|
|
62
|
-
const VALID_EFFORTS = /* @__PURE__ */ new Set(["high", "low", "medium"]);
|
|
63
|
-
const CHUNK_THRESHOLD = 50;
|
|
64
|
-
const CHUNK_SIZE = 30;
|
|
65
|
-
const buildPackageList = (outdated) => outdated.map((entry) => {
|
|
66
|
-
const vulnInfo = entry.vulnerabilities && entry.vulnerabilities.length > 0 ? ` [VULNERABILITIES: ${entry.vulnerabilities.map((v) => `${v.severity} ${v.id}`).join(", ")}]` : "";
|
|
67
|
-
let socketInfo = "";
|
|
68
|
-
if (entry.socketReport) {
|
|
69
|
-
const score = Math.round(entry.socketReport.score.overall * 100);
|
|
70
|
-
const parts = [`score:${String(score)}%`];
|
|
71
|
-
if (entry.socketReport.alerts.length > 0) {
|
|
72
|
-
const alertsByLevel = entry.socketReport.alerts.reduce((accumulator, a) => {
|
|
73
|
-
accumulator[a.severity] = (accumulator[a.severity] ?? 0) + 1;
|
|
74
|
-
return accumulator;
|
|
75
|
-
}, {});
|
|
76
|
-
const alertSummary = Object.entries(alertsByLevel).map(([s, c]) => `${String(c)} ${s}`).join(", ");
|
|
77
|
-
parts.push(`alerts: ${alertSummary}`);
|
|
78
|
-
}
|
|
79
|
-
parts.push(`supply-chain:${String(Math.round(entry.socketReport.score.supplyChain * 100))}%`);
|
|
80
|
-
parts.push(`quality:${String(Math.round(entry.socketReport.score.quality * 100))}%`);
|
|
81
|
-
socketInfo = ` [SOCKET.DEV: ${parts.join(", ")}]`;
|
|
82
|
-
}
|
|
83
|
-
return `- ${entry.packageName}: ${entry.currentRange} → ${entry.newRange} (${entry.updateType})${vulnInfo}${socketInfo}`;
|
|
84
|
-
}).join("\n");
|
|
85
|
-
const JSON_RESPONSE_SCHEMA = `Respond ONLY with valid JSON in this exact structure:
|
|
86
|
-
{
|
|
87
|
-
"summary": "Brief overall summary",
|
|
88
|
-
"recommendations": [
|
|
89
|
-
{
|
|
90
|
-
"package": "package-name",
|
|
91
|
-
"action": "update|skip|review|defer",
|
|
92
|
-
"reason": "explanation",
|
|
93
|
-
"riskLevel": "low|medium|high|critical",
|
|
94
|
-
"breakingChanges": ["change1"],
|
|
95
|
-
"effort": "low|medium|high"
|
|
96
|
-
}
|
|
97
|
-
],
|
|
98
|
-
"warnings": ["warning1"]
|
|
99
|
-
}`;
|
|
100
|
-
const PROMPTS = {
|
|
101
|
-
compatibility: (packageList) => `Analyze the compatibility of these package updates:
|
|
102
|
-
|
|
103
|
-
${packageList}
|
|
104
|
-
|
|
105
|
-
For each package:
|
|
106
|
-
1. Check peer dependency compatibility
|
|
107
|
-
2. Identify potential conflicts with other packages in the list
|
|
108
|
-
3. Assess API compatibility between current and target versions
|
|
109
|
-
4. Check for deprecated features being removed
|
|
110
|
-
5. Evaluate Node.js version requirements
|
|
111
|
-
|
|
112
|
-
${JSON_RESPONSE_SCHEMA}`,
|
|
113
|
-
impact: (packageList) => `Analyze the impact of updating these npm packages:
|
|
114
|
-
|
|
115
|
-
${packageList}
|
|
116
|
-
|
|
117
|
-
For each package, provide:
|
|
118
|
-
1. Risk level (low/medium/high/critical)
|
|
119
|
-
2. Recommended action (update/skip/review/defer)
|
|
120
|
-
3. Reason for recommendation
|
|
121
|
-
4. Known breaking changes (if any)
|
|
122
|
-
5. Estimated migration effort (low/medium/high)
|
|
123
|
-
|
|
124
|
-
${JSON_RESPONSE_SCHEMA}`,
|
|
125
|
-
recommend: (packageList) => `Provide smart recommendations for updating these packages:
|
|
126
|
-
|
|
127
|
-
${packageList}
|
|
128
|
-
|
|
129
|
-
Consider:
|
|
130
|
-
1. Update priority based on security, features, and stability
|
|
131
|
-
2. Grouping related packages for atomic updates
|
|
132
|
-
3. Best practices for the specific package ecosystem
|
|
133
|
-
4. Risk vs. benefit analysis
|
|
134
|
-
5. Suggested update order
|
|
135
|
-
6. If Socket.dev scores are provided, prioritize packages with low supply chain or quality scores
|
|
136
|
-
|
|
137
|
-
${JSON_RESPONSE_SCHEMA}`,
|
|
138
|
-
security: (packageList) => `Analyze the security implications of these package updates:
|
|
139
|
-
|
|
140
|
-
${packageList}
|
|
141
|
-
|
|
142
|
-
For each package:
|
|
143
|
-
1. Check if the update fixes known vulnerabilities (use the vulnerability data above)
|
|
144
|
-
2. Assess if the new version introduces security risks
|
|
145
|
-
3. Evaluate if this is a security-sensitive package (auth, crypto, session, etc.)
|
|
146
|
-
4. Recommend urgency of the update based on vulnerability severity
|
|
147
|
-
5. Flag any packages where skipping the update poses security risk
|
|
148
|
-
6. If Socket.dev scores are provided, factor in supply chain and quality scores — low scores indicate higher risk
|
|
149
|
-
|
|
150
|
-
${JSON_RESPONSE_SCHEMA}`
|
|
151
|
-
};
|
|
152
|
-
const VALID_ANALYSIS_TYPES = /* @__PURE__ */ new Set(["compatibility", "impact", "recommend", "security"]);
|
|
153
|
-
const validateAnalysisType = (type) => {
|
|
154
|
-
if (VALID_ANALYSIS_TYPES.has(type)) {
|
|
155
|
-
return type;
|
|
156
|
-
}
|
|
157
|
-
return "impact";
|
|
158
|
-
};
|
|
159
|
-
const buildAnalysisPrompt = (outdated, analysisType = "impact") => {
|
|
160
|
-
const packageList = buildPackageList(outdated);
|
|
161
|
-
return PROMPTS[analysisType](packageList);
|
|
162
|
-
};
|
|
163
|
-
const JSON_BLOCK_REGEX = /```(?:json)?\s*([\s\S]*?)```/;
|
|
164
|
-
const JSON_OBJECT_REGEX = /\{[\s\S]*\}/;
|
|
165
|
-
const extractJson = (text) => {
|
|
166
|
-
try {
|
|
167
|
-
return JSON.parse(text);
|
|
168
|
-
} catch {
|
|
169
|
-
}
|
|
170
|
-
const blockMatch = JSON_BLOCK_REGEX.exec(text);
|
|
171
|
-
if (blockMatch?.[1]) {
|
|
172
|
-
try {
|
|
173
|
-
return JSON.parse(blockMatch[1]);
|
|
174
|
-
} catch {
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const objectMatch = JSON_OBJECT_REGEX.exec(text);
|
|
178
|
-
if (objectMatch?.[0]) {
|
|
179
|
-
try {
|
|
180
|
-
return JSON.parse(objectMatch[0]);
|
|
181
|
-
} catch {
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return void 0;
|
|
185
|
-
};
|
|
186
|
-
const normalizeRecommendation = (raw) => {
|
|
187
|
-
return {
|
|
188
|
-
action: VALID_ACTIONS.has(raw.action) ? raw.action : "review",
|
|
189
|
-
breakingChanges: Array.isArray(raw.breakingChanges) ? raw.breakingChanges : [],
|
|
190
|
-
effort: VALID_EFFORTS.has(raw.effort) ? raw.effort : "medium",
|
|
191
|
-
package: typeof raw.package === "string" ? raw.package : "",
|
|
192
|
-
reason: typeof raw.reason === "string" ? raw.reason : "",
|
|
193
|
-
riskLevel: VALID_RISK_LEVELS.has(raw.riskLevel) ? raw.riskLevel : "medium"
|
|
194
|
-
};
|
|
195
|
-
};
|
|
196
|
-
const parseAiResponse = (text, provider, analysisType) => {
|
|
197
|
-
const parsed = extractJson(text);
|
|
198
|
-
if (!parsed || typeof parsed !== "object") {
|
|
199
|
-
return { analysisType, provider, recommendations: [], summary: "Failed to parse AI response.", warnings: ["AI response was not valid JSON."] };
|
|
200
|
-
}
|
|
201
|
-
const data = parsed;
|
|
202
|
-
const rawRecs = Array.isArray(data.recommendations) ? data.recommendations : [];
|
|
203
|
-
return {
|
|
204
|
-
analysisType,
|
|
205
|
-
provider,
|
|
206
|
-
recommendations: rawRecs.map((rec) => normalizeRecommendation(rec)),
|
|
207
|
-
summary: typeof data.summary === "string" ? data.summary : "",
|
|
208
|
-
warnings: Array.isArray(data.warnings) ? data.warnings : []
|
|
209
|
-
};
|
|
210
|
-
};
|
|
211
|
-
const KNOWN_BREAKING = {
|
|
212
|
-
eslint: ["ESLint 9.0: Flat config required", "ESLint 8.0+: New rule formats"],
|
|
213
|
-
next: ["Next.js 13+: App router changes", "Next.js 14+: Server components default"],
|
|
214
|
-
react: ["React 17 to 18: Concurrent features", "React 18+: Strict mode changes"],
|
|
215
|
-
typescript: ["TypeScript 5.0: New decorators", "TypeScript 4.7+: ESM changes"],
|
|
216
|
-
vite: ["Vite 5: Node.js 18+ required"],
|
|
217
|
-
vue: ["Vue 3: Composition API", "Vue 3: Breaking template changes"],
|
|
218
|
-
webpack: ["Webpack 5: Node.js polyfills removed"]
|
|
219
|
-
};
|
|
220
|
-
const SECURITY_SENSITIVE = /* @__PURE__ */ new Set(["bcrypt", "cors", "crypto-js", "express-session", "helmet", "jose", "jsonwebtoken", "node-forge", "oauth", "passport"]);
|
|
221
|
-
const ruleBasedAnalysis = (outdated, analysisType) => {
|
|
222
|
-
const recommendations = outdated.map((entry) => {
|
|
223
|
-
const hasVulnerabilities = entry.vulnerabilities && entry.vulnerabilities.length > 0;
|
|
224
|
-
const isSecuritySensitive = SECURITY_SENSITIVE.has(entry.packageName);
|
|
225
|
-
const breakingChanges = KNOWN_BREAKING[entry.packageName] ?? [];
|
|
226
|
-
let riskLevel = "low";
|
|
227
|
-
let action = "update";
|
|
228
|
-
let effort = "low";
|
|
229
|
-
let reason = "Patch/minor update, safe to apply.";
|
|
230
|
-
if (entry.updateType === "major") {
|
|
231
|
-
riskLevel = "high";
|
|
232
|
-
action = breakingChanges.length > 0 ? "review" : "update";
|
|
233
|
-
effort = "medium";
|
|
234
|
-
reason = breakingChanges.length > 0 ? `Major update with known breaking changes: ${breakingChanges[0]}` : "Major version update, review changelog before applying.";
|
|
235
|
-
} else if (entry.updateType === "minor") {
|
|
236
|
-
riskLevel = "medium";
|
|
237
|
-
reason = "Minor update, generally safe.";
|
|
238
|
-
}
|
|
239
|
-
if (hasVulnerabilities) {
|
|
240
|
-
riskLevel = "high";
|
|
241
|
-
action = "update";
|
|
242
|
-
reason = "Security update — current version has known vulnerabilities.";
|
|
243
|
-
}
|
|
244
|
-
if (isSecuritySensitive && entry.updateType === "major") {
|
|
245
|
-
action = "review";
|
|
246
|
-
reason = "Security-sensitive package with major update, careful review needed.";
|
|
247
|
-
effort = "high";
|
|
248
|
-
}
|
|
249
|
-
return { action, breakingChanges, effort, package: entry.packageName, reason, riskLevel };
|
|
250
|
-
});
|
|
251
|
-
return {
|
|
252
|
-
analysisType,
|
|
253
|
-
provider: "rule-engine",
|
|
254
|
-
recommendations,
|
|
255
|
-
summary: `Rule-based ${analysisType} analysis for ${String(outdated.length)} packages.`,
|
|
256
|
-
warnings: ["No AI provider available — using built-in rule engine."]
|
|
257
|
-
};
|
|
258
|
-
};
|
|
259
|
-
const analyzeChunk = async (provider, chunk, analysisType) => {
|
|
260
|
-
const prompt = buildAnalysisPrompt(chunk, analysisType);
|
|
261
|
-
const stdout = await runWithRetry(provider, prompt);
|
|
262
|
-
return parseAiResponse(stdout, provider.name, analysisType);
|
|
263
|
-
};
|
|
264
|
-
const mergeResults = (results, provider, analysisType) => {
|
|
265
|
-
const recommendations = [];
|
|
266
|
-
const warnings = [];
|
|
267
|
-
const summaries = [];
|
|
268
|
-
for (const result of results) {
|
|
269
|
-
recommendations.push(...result.recommendations);
|
|
270
|
-
warnings.push(...result.warnings);
|
|
271
|
-
if (result.summary) {
|
|
272
|
-
summaries.push(result.summary);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return {
|
|
276
|
-
analysisType,
|
|
277
|
-
provider,
|
|
278
|
-
recommendations,
|
|
279
|
-
summary: summaries.length === 1 ? summaries[0] ?? "" : `Analyzed ${String(recommendations.length)} packages in ${String(results.length)} batches.`,
|
|
280
|
-
warnings: [...new Set(warnings)]
|
|
281
|
-
};
|
|
282
|
-
};
|
|
283
|
-
const ANALYSIS_TYPE_LABELS = {
|
|
284
|
-
compatibility: "Compatibility",
|
|
285
|
-
impact: "Impact",
|
|
286
|
-
recommend: "Recommendations",
|
|
287
|
-
security: "Security"
|
|
288
|
-
};
|
|
289
|
-
const formatAiAnalysis = (result) => {
|
|
290
|
-
const typeLabel = ANALYSIS_TYPE_LABELS[result.analysisType] ?? result.analysisType;
|
|
291
|
-
const header = `${typeLabel} Analysis (${result.provider})`;
|
|
292
|
-
const tableData = result.recommendations.flatMap((rec) => {
|
|
293
|
-
const rows = [
|
|
294
|
-
{ action: rec.action, effort: rec.effort, package: rec.package, reason: rec.reason, risk: rec.riskLevel }
|
|
295
|
-
];
|
|
296
|
-
if (rec.breakingChanges.length > 0) {
|
|
297
|
-
rows.push({ action: "", effort: "", package: "", reason: `Breaking: ${rec.breakingChanges.join("; ")}`, risk: "" });
|
|
298
|
-
}
|
|
299
|
-
return rows;
|
|
300
|
-
});
|
|
301
|
-
const columns = process.stdout.columns || 80;
|
|
302
|
-
return renderToString(
|
|
303
|
-
React.createElement(
|
|
304
|
-
Box,
|
|
305
|
-
{ borderStyle: "round", flexDirection: "column", paddingLeft: 1, paddingRight: 1 },
|
|
306
|
-
React.createElement(Text, { bold: true }, header),
|
|
307
|
-
React.createElement(Text, null, ""),
|
|
308
|
-
React.createElement(Text, null, result.summary),
|
|
309
|
-
React.createElement(Text, null, ""),
|
|
310
|
-
React.createElement(Table, { borderStyle: "none", data: tableData }),
|
|
311
|
-
...result.warnings.length > 0 ? [
|
|
312
|
-
React.createElement(Text, null, ""),
|
|
313
|
-
...result.warnings.map((warning, i) => React.createElement(Text, { dimColor: true, key: String(i) }, ` ${warning}`))
|
|
314
|
-
] : []
|
|
315
|
-
),
|
|
316
|
-
{ columns }
|
|
317
|
-
);
|
|
318
|
-
};
|
|
319
|
-
const runAiAnalysis = async (outdated, logger, config, analysisType = "impact") => {
|
|
320
|
-
const provider = resolveProvider(config);
|
|
321
|
-
if (!provider) {
|
|
322
|
-
logger.info("No AI CLI tool found, using rule-based analysis.\n");
|
|
323
|
-
return ruleBasedAnalysis(outdated, analysisType);
|
|
324
|
-
}
|
|
325
|
-
const cacheKey = buildCacheKey(provider.name, analysisType, outdated);
|
|
326
|
-
const cached = getCachedAnalysis(cacheKey);
|
|
327
|
-
if (cached) {
|
|
328
|
-
logger.info(`Using cached ${analysisType} analysis from ${cached.provider}.
|
|
329
|
-
`);
|
|
330
|
-
return cached;
|
|
331
|
-
}
|
|
332
|
-
const typeLabel = ANALYSIS_TYPE_LABELS[analysisType] ?? analysisType;
|
|
333
|
-
logger.info(`Running ${typeLabel.toLowerCase()} analysis with ${provider.name}...
|
|
334
|
-
`);
|
|
335
|
-
try {
|
|
336
|
-
let result;
|
|
337
|
-
if (outdated.length > CHUNK_THRESHOLD) {
|
|
338
|
-
logger.info(`Splitting ${String(outdated.length)} packages into batches of ${String(CHUNK_SIZE)}...
|
|
339
|
-
`);
|
|
340
|
-
const chunks = [];
|
|
341
|
-
for (let index = 0; index < outdated.length; index += CHUNK_SIZE) {
|
|
342
|
-
chunks.push(outdated.slice(index, index + CHUNK_SIZE));
|
|
343
|
-
}
|
|
344
|
-
const results = [];
|
|
345
|
-
for (let index = 0; index < chunks.length; index += 1) {
|
|
346
|
-
logger.info(` Batch ${String(index + 1)}/${String(chunks.length)}...`);
|
|
347
|
-
const chunk = chunks[index];
|
|
348
|
-
if (chunk) {
|
|
349
|
-
results.push(await analyzeChunk(provider, chunk, analysisType));
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
result = mergeResults(results, provider.name, analysisType);
|
|
353
|
-
} else {
|
|
354
|
-
const stdout = await runWithRetry(provider, buildAnalysisPrompt(outdated, analysisType));
|
|
355
|
-
result = parseAiResponse(stdout, provider.name, analysisType);
|
|
356
|
-
}
|
|
357
|
-
setCachedAnalysis(cacheKey, result, getTtlForAnalysisType(analysisType, config?.cacheTtl));
|
|
358
|
-
return result;
|
|
359
|
-
} catch (error) {
|
|
360
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
361
|
-
logger.warn(`AI analysis failed (${message}), falling back to rule engine.
|
|
362
|
-
`);
|
|
363
|
-
return ruleBasedAnalysis(outdated, analysisType);
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
export { DEFAULT_PRIORITY as D, resolveProvider as a, runWithRetry as b, extractJson as e, formatAiAnalysis as f, runAiAnalysis as r, validateAnalysisType as v };
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
4
|
-
|
|
5
|
-
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
6
|
-
|
|
7
|
-
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
-
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
-
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
-
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
-
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
-
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
-
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
// Fallback to createRequire
|
|
17
|
-
return __cjs_require(module);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const {
|
|
21
|
-
rmSync,
|
|
22
|
-
writeFileSync,
|
|
23
|
-
readdirSync,
|
|
24
|
-
statSync
|
|
25
|
-
} = __cjs_getBuiltinModule("node:fs");
|
|
26
|
-
const {
|
|
27
|
-
homedir
|
|
28
|
-
} = __cjs_getBuiltinModule("node:os");
|
|
29
|
-
import { x as xxh3Hash } from './xxh3-Ck8mXNg1.js';
|
|
30
|
-
import { isAccessibleSync, readJsonSync, ensureDirSync } from '@visulima/fs';
|
|
31
|
-
import { join } from '@visulima/path';
|
|
32
|
-
|
|
33
|
-
const getCacheDirectory = () => join(homedir(), ".vis", "cache", "ai");
|
|
34
|
-
const DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
35
|
-
const SECURITY_TTL_MS = 30 * 60 * 1e3;
|
|
36
|
-
const ensureCacheDirectory = () => {
|
|
37
|
-
ensureDirSync(getCacheDirectory());
|
|
38
|
-
};
|
|
39
|
-
const buildCacheKey = (provider, analysisType, outdated) => {
|
|
40
|
-
const packages = outdated.map((entry) => {
|
|
41
|
-
return { currentRange: entry.currentRange, name: entry.packageName, targetVersion: entry.targetVersion };
|
|
42
|
-
}).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
43
|
-
const payload = JSON.stringify({ analysisType, packages, provider });
|
|
44
|
-
return xxh3Hash(Buffer.from(payload));
|
|
45
|
-
};
|
|
46
|
-
const getCachedAnalysis = (cacheKey) => {
|
|
47
|
-
const filePath = join(getCacheDirectory(), `${cacheKey}.json`);
|
|
48
|
-
if (!isAccessibleSync(filePath)) {
|
|
49
|
-
return void 0;
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
const entry = readJsonSync(filePath);
|
|
53
|
-
if (Date.now() - entry.createdAt > entry.ttlMs) {
|
|
54
|
-
rmSync(filePath, { force: true });
|
|
55
|
-
return void 0;
|
|
56
|
-
}
|
|
57
|
-
return entry.result;
|
|
58
|
-
} catch {
|
|
59
|
-
rmSync(filePath, { force: true });
|
|
60
|
-
return void 0;
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
const setCachedAnalysis = (cacheKey, result, ttlMs) => {
|
|
64
|
-
ensureCacheDirectory();
|
|
65
|
-
const cacheDirectory = getCacheDirectory();
|
|
66
|
-
const entry = {
|
|
67
|
-
createdAt: Date.now(),
|
|
68
|
-
result,
|
|
69
|
-
ttlMs
|
|
70
|
-
};
|
|
71
|
-
writeFileSync(join(cacheDirectory, `${cacheKey}.json`), JSON.stringify(entry, void 0, 2), "utf8");
|
|
72
|
-
};
|
|
73
|
-
const getTtlForAnalysisType = (analysisType, configTtl) => {
|
|
74
|
-
if (configTtl !== void 0 && configTtl > 0) {
|
|
75
|
-
return configTtl;
|
|
76
|
-
}
|
|
77
|
-
return analysisType === "security" ? SECURITY_TTL_MS : DEFAULT_TTL_MS;
|
|
78
|
-
};
|
|
79
|
-
const getCacheStats = () => {
|
|
80
|
-
const cacheDirectory = getCacheDirectory();
|
|
81
|
-
if (!isAccessibleSync(cacheDirectory)) {
|
|
82
|
-
return { entries: 0, newestEntry: void 0, oldestEntry: void 0, totalSizeBytes: 0 };
|
|
83
|
-
}
|
|
84
|
-
const files = readdirSync(cacheDirectory).filter((f) => f.endsWith(".json"));
|
|
85
|
-
let totalSizeBytes = 0;
|
|
86
|
-
let oldest;
|
|
87
|
-
let newest;
|
|
88
|
-
for (const file of files) {
|
|
89
|
-
const filePath = join(cacheDirectory, file);
|
|
90
|
-
const stat = statSync(filePath);
|
|
91
|
-
totalSizeBytes += stat.size;
|
|
92
|
-
const { mtimeMs } = stat;
|
|
93
|
-
if (oldest === void 0 || mtimeMs < oldest) {
|
|
94
|
-
oldest = mtimeMs;
|
|
95
|
-
}
|
|
96
|
-
if (newest === void 0 || mtimeMs > newest) {
|
|
97
|
-
newest = mtimeMs;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return { entries: files.length, newestEntry: newest, oldestEntry: oldest, totalSizeBytes };
|
|
101
|
-
};
|
|
102
|
-
const buildHashCacheKey = (payload) => xxh3Hash(Buffer.from(JSON.stringify(payload)));
|
|
103
|
-
const getCachedJson = (cacheKey) => {
|
|
104
|
-
const filePath = join(getCacheDirectory(), `${cacheKey}.json`);
|
|
105
|
-
if (!isAccessibleSync(filePath)) {
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
const entry = readJsonSync(filePath);
|
|
110
|
-
if (Date.now() - entry.createdAt > entry.ttlMs) {
|
|
111
|
-
rmSync(filePath, { force: true });
|
|
112
|
-
return void 0;
|
|
113
|
-
}
|
|
114
|
-
return entry.result;
|
|
115
|
-
} catch {
|
|
116
|
-
rmSync(filePath, { force: true });
|
|
117
|
-
return void 0;
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
const setCachedJson = (cacheKey, result, ttlMs) => {
|
|
121
|
-
ensureCacheDirectory();
|
|
122
|
-
const cacheDirectory = getCacheDirectory();
|
|
123
|
-
const entry = {
|
|
124
|
-
createdAt: Date.now(),
|
|
125
|
-
result,
|
|
126
|
-
ttlMs
|
|
127
|
-
};
|
|
128
|
-
writeFileSync(join(cacheDirectory, `${cacheKey}.json`), JSON.stringify(entry, void 0, 2), "utf8");
|
|
129
|
-
};
|
|
130
|
-
const clearCache = () => {
|
|
131
|
-
const cacheDirectory = getCacheDirectory();
|
|
132
|
-
if (!isAccessibleSync(cacheDirectory)) {
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
const files = readdirSync(cacheDirectory).filter((f) => f.endsWith(".json"));
|
|
136
|
-
for (const file of files) {
|
|
137
|
-
rmSync(join(cacheDirectory, file), { force: true });
|
|
138
|
-
}
|
|
139
|
-
return files.length;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
export { getCachedAnalysis as a, buildCacheKey as b, clearCache as c, getTtlForAnalysisType as d, getCachedJson as e, setCachedJson as f, getCacheStats as g, buildHashCacheKey as h, setCachedAnalysis as s };
|