dep-oracle 1.2.0 → 1.3.0

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.
Files changed (41) hide show
  1. package/README.md +33 -8
  2. package/dist/action/index.js +398 -16
  3. package/dist/badge-5Z3WAD2B.js +89 -0
  4. package/dist/badge-5Z3WAD2B.js.map +1 -0
  5. package/dist/chunk-32B3QIPY.js +1505 -0
  6. package/dist/chunk-32B3QIPY.js.map +1 -0
  7. package/dist/chunk-7DST6SNA.js +258 -0
  8. package/dist/chunk-7DST6SNA.js.map +1 -0
  9. package/dist/{chunk-TXSNFX3N.js → chunk-DLWG22RC.js} +403 -17
  10. package/dist/chunk-DLWG22RC.js.map +1 -0
  11. package/dist/chunk-HX6MGNBD.js +271 -0
  12. package/dist/chunk-HX6MGNBD.js.map +1 -0
  13. package/dist/chunk-IVXGOPRU.js +145 -0
  14. package/dist/chunk-IVXGOPRU.js.map +1 -0
  15. package/dist/chunk-SP3VYPXX.js +218 -0
  16. package/dist/chunk-SP3VYPXX.js.map +1 -0
  17. package/dist/chunk-T5EVLWZM.js +4234 -0
  18. package/dist/chunk-T5EVLWZM.js.map +1 -0
  19. package/dist/chunk-UMB5MJHL.js +239 -0
  20. package/dist/chunk-UMB5MJHL.js.map +1 -0
  21. package/dist/cli/index.js +163 -6499
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/index.js +9 -84
  24. package/dist/index.js.map +1 -1
  25. package/dist/mcp/server.js +33 -12
  26. package/dist/mcp/server.js.map +1 -1
  27. package/dist/npm-UB54H37N.js +9 -0
  28. package/dist/npm-UB54H37N.js.map +1 -0
  29. package/dist/orchestrator-VOOYKDPT.js +8 -0
  30. package/dist/orchestrator-VOOYKDPT.js.map +1 -0
  31. package/dist/python-U4G2GK4J.js +9 -0
  32. package/dist/python-U4G2GK4J.js.map +1 -0
  33. package/dist/server-WONIBSG4.js +640 -0
  34. package/dist/server-WONIBSG4.js.map +1 -0
  35. package/dist/store-Z5UANEBB.js +8 -0
  36. package/dist/store-Z5UANEBB.js.map +1 -0
  37. package/dist/trust-score-YXYDFVPZ.js +8 -0
  38. package/dist/trust-score-YXYDFVPZ.js.map +1 -0
  39. package/package.json +1 -1
  40. package/server.json +2 -2
  41. package/dist/chunk-TXSNFX3N.js.map +0 -1
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  MigrationAdvisor,
10
10
  NpmParser,
11
11
  PythonParser,
12
+ RateLimiter,
12
13
  ScanResultSchema,
13
14
  TrustMetricsSchema,
14
15
  TrustReportSchema,
@@ -25,11 +26,14 @@ import {
25
26
  createLogger,
26
27
  getBlastRadius,
27
28
  getImportingFiles,
29
+ githubRateLimiter,
28
30
  isDebug,
29
31
  isVerbose,
30
32
  logger,
33
+ npmRateLimiter,
34
+ pypiRateLimiter,
31
35
  setVerbose
32
- } from "./chunk-TXSNFX3N.js";
36
+ } from "./chunk-DLWG22RC.js";
33
37
 
34
38
  // src/analyzers/trend-predictor.ts
35
39
  var HIGH_CONFIDENCE_THRESHOLD = 3;
@@ -182,87 +186,6 @@ function monthsBetween(from, to) {
182
186
  return years * 12 + months + (days < 0 ? -0.5 : 0);
183
187
  }
184
188
 
185
- // src/utils/rate-limiter.ts
186
- var RateLimiter = class {
187
- tokens;
188
- maxTokens;
189
- refillIntervalMs;
190
- lastRefill;
191
- waitQueue = [];
192
- /**
193
- * @param maxRequests Maximum number of requests allowed in the window
194
- * @param windowMs Window duration in milliseconds
195
- */
196
- constructor(maxRequests, windowMs) {
197
- this.maxTokens = maxRequests;
198
- this.tokens = maxRequests;
199
- this.refillIntervalMs = windowMs;
200
- this.lastRefill = Date.now();
201
- }
202
- /**
203
- * Acquire a token. Resolves immediately when tokens are available,
204
- * otherwise waits until the bucket is refilled.
205
- */
206
- async acquire() {
207
- this.refill();
208
- if (this.tokens > 0) {
209
- this.tokens--;
210
- return;
211
- }
212
- return new Promise((resolve2) => {
213
- this.waitQueue.push(resolve2);
214
- this.scheduleRefill();
215
- });
216
- }
217
- /**
218
- * Return the number of tokens currently available (without waiting).
219
- */
220
- get remaining() {
221
- this.refill();
222
- return this.tokens;
223
- }
224
- /**
225
- * Return the number of milliseconds until the next refill.
226
- */
227
- get msUntilRefill() {
228
- const elapsed = Date.now() - this.lastRefill;
229
- return Math.max(0, this.refillIntervalMs - elapsed);
230
- }
231
- // -----------------------------------------------------------------------
232
- // Internal
233
- // -----------------------------------------------------------------------
234
- refill() {
235
- const now = Date.now();
236
- const elapsed = now - this.lastRefill;
237
- if (elapsed >= this.refillIntervalMs) {
238
- const periods = Math.floor(elapsed / this.refillIntervalMs);
239
- this.tokens = Math.min(this.maxTokens, this.tokens + periods * this.maxTokens);
240
- this.lastRefill = now - elapsed % this.refillIntervalMs;
241
- this.drainWaitQueue();
242
- }
243
- }
244
- scheduleRefill() {
245
- const delay = this.msUntilRefill;
246
- if (delay <= 0) {
247
- this.refill();
248
- return;
249
- }
250
- setTimeout(() => {
251
- this.refill();
252
- }, delay);
253
- }
254
- drainWaitQueue() {
255
- while (this.waitQueue.length > 0 && this.tokens > 0) {
256
- this.tokens--;
257
- const resolve2 = this.waitQueue.shift();
258
- resolve2?.();
259
- }
260
- }
261
- };
262
- var githubRateLimiter = new RateLimiter(5e3, 36e5);
263
- var npmRateLimiter = new RateLimiter(300, 6e4);
264
- var pypiRateLimiter = new RateLimiter(100, 6e4);
265
-
266
189
  // src/cli/config.ts
267
190
  import { cosmiconfig } from "cosmiconfig";
268
191
  var explorer = cosmiconfig("dep-oracle");
@@ -319,7 +242,8 @@ async function scan(options) {
319
242
  const reports = [];
320
243
  for (const node of directNodes) {
321
244
  const blastRadius = getBlastRadius(node.name, importGraph);
322
- const results = await orchestrator.collectAll(node.name, node.version);
245
+ const nodeEcosystem = options?.ecosystem ?? node.registry;
246
+ const results = await orchestrator.collectAll(node.name, node.version, nodeEcosystem);
323
247
  const trustResult = trustEngine.calculate(results);
324
248
  const zombie = zombieDetector.detect(results.registry.data, results.github.data);
325
249
  const typosquat = typosquatDetector.check(node.name);
@@ -355,7 +279,8 @@ async function checkPackage(packageName, version, options) {
355
279
  const zombieDetector = new ZombieDetector();
356
280
  const migrationAdvisor = new MigrationAdvisor();
357
281
  const typosquatDetector = new TyposquatDetector();
358
- const results = await orchestrator.collectAll(packageName, version ?? "latest");
282
+ const ecosystem = options?.ecosystem ?? "npm";
283
+ const results = await orchestrator.collectAll(packageName, version ?? "latest", ecosystem);
359
284
  const trustResult = trustEngine.calculate(results);
360
285
  const zombie = zombieDetector.detect(results.registry.data, results.github.data);
361
286
  const typosquat = typosquatDetector.check(packageName);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/analyzers/trend-predictor.ts","../src/utils/rate-limiter.ts","../src/cli/config.ts","../src/index.ts"],"sourcesContent":["import type {\n RegistryData,\n PopularityData,\n GitHubData,\n} from '../parsers/schema.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type TrendDirection = 'rising' | 'stable' | 'declining' | 'unknown';\n\nexport interface TrendResult {\n /** Overall trend direction. */\n trend: TrendDirection;\n /** Confidence in the prediction (0-1). */\n confidence: number;\n /**\n * Estimated trust score change over the next 3 months.\n * Negative values indicate a projected decline (e.g. -5 means the score\n * is expected to drop by roughly 5 points).\n */\n riskProjection3m: number;\n /** Human-readable explanation of the trend assessment. */\n reason: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Minimum signals required for a high-confidence prediction. */\nconst HIGH_CONFIDENCE_THRESHOLD = 3;\n\n// ---------------------------------------------------------------------------\n// TrendPredictor\n// ---------------------------------------------------------------------------\n\nexport class TrendPredictor {\n /**\n * Predict the trajectory of a package based on registry, popularity,\n * and GitHub signals.\n */\n predict(\n registry: RegistryData | null,\n popularity: PopularityData | null,\n github: GitHubData | null,\n ): TrendResult {\n const signals: TrendSignal[] = [];\n const reasons: string[] = [];\n\n // -----------------------------------------------------------------------\n // Signal 1: Download trend from PopularityData\n // -----------------------------------------------------------------------\n if (popularity !== null) {\n switch (popularity.trend) {\n case 'rising':\n signals.push({ direction: 'rising', weight: 0.4 });\n reasons.push('Downloads are trending upward');\n break;\n case 'declining':\n signals.push({ direction: 'declining', weight: 0.4 });\n reasons.push('Downloads are trending downward');\n break;\n case 'stable':\n signals.push({ direction: 'stable', weight: 0.3 });\n reasons.push('Downloads are stable');\n break;\n }\n }\n\n // -----------------------------------------------------------------------\n // Signal 2: Commit frequency from GitHub\n // -----------------------------------------------------------------------\n if (github !== null) {\n const commits30d = github.recentCommitCount;\n\n if (commits30d >= 30) {\n signals.push({ direction: 'rising', weight: 0.3 });\n reasons.push(`High commit activity (${commits30d} commits in 30 days)`);\n } else if (commits30d >= 10) {\n signals.push({ direction: 'stable', weight: 0.2 });\n reasons.push(`Moderate commit activity (${commits30d} commits in 30 days)`);\n } else if (commits30d >= 1) {\n signals.push({ direction: 'stable', weight: 0.15 });\n reasons.push(`Low commit activity (${commits30d} commits in 30 days)`);\n } else {\n signals.push({ direction: 'declining', weight: 0.3 });\n reasons.push('No commits in the last 30 days');\n }\n }\n\n // -----------------------------------------------------------------------\n // Signal 3: Version release cadence from RegistryData\n // -----------------------------------------------------------------------\n if (registry !== null) {\n if (registry.lastPublishDate !== null) {\n const lastPublish = new Date(registry.lastPublishDate);\n const monthsSincePublish = monthsBetween(lastPublish, new Date());\n\n if (monthsSincePublish < 2) {\n signals.push({ direction: 'rising', weight: 0.2 });\n reasons.push('Recent version published within last 2 months');\n } else if (monthsSincePublish < 6) {\n signals.push({ direction: 'stable', weight: 0.15 });\n reasons.push(`Last publish ${Math.round(monthsSincePublish)} months ago`);\n } else if (monthsSincePublish < 12) {\n signals.push({ direction: 'declining', weight: 0.2 });\n reasons.push(`No new version in ${Math.round(monthsSincePublish)} months`);\n } else {\n signals.push({ direction: 'declining', weight: 0.3 });\n reasons.push(`No new version in ${Math.round(monthsSincePublish)} months — possibly abandoned`);\n }\n }\n\n // Version count as a maturity signal\n if (registry.versionCount > 50) {\n signals.push({ direction: 'stable', weight: 0.1 });\n reasons.push(`Mature package with ${registry.versionCount} versions`);\n }\n\n // Deprecated flag is a strong declining signal (string | null)\n if (registry.deprecated !== null) {\n signals.push({ direction: 'declining', weight: 0.5 });\n reasons.push('Package is marked as deprecated');\n }\n }\n\n // -----------------------------------------------------------------------\n // Signal 4: Star/fork ratio as community interest (GitHub)\n // -----------------------------------------------------------------------\n if (github !== null && github.stars > 0) {\n const forkRatio = github.forks / github.stars;\n if (forkRatio > 0.3) {\n // High fork ratio suggests active community interest\n signals.push({ direction: 'rising', weight: 0.1 });\n reasons.push('High fork-to-star ratio indicates active community');\n }\n }\n\n // -----------------------------------------------------------------------\n // Aggregate signals\n // -----------------------------------------------------------------------\n if (signals.length === 0) {\n return {\n trend: 'unknown',\n confidence: 0,\n riskProjection3m: 0,\n reason: 'Insufficient data to determine trend',\n };\n }\n\n const { trend, confidence } = this.aggregateSignals(signals);\n const riskProjection3m = this.calculateRiskProjection(trend, confidence, signals);\n\n return {\n trend,\n confidence: Math.round(confidence * 100) / 100,\n riskProjection3m: Math.round(riskProjection3m),\n reason: reasons.join('. ') + '.',\n };\n }\n\n // -------------------------------------------------------------------------\n // Internal helpers\n // -------------------------------------------------------------------------\n\n /**\n * Aggregate multiple trend signals into a single direction and confidence.\n * Uses weighted voting: each signal votes for its direction with its weight.\n */\n private aggregateSignals(signals: TrendSignal[]): {\n trend: TrendDirection;\n confidence: number;\n } {\n const votes: Record<Exclude<TrendDirection, 'unknown'>, number> = {\n rising: 0,\n stable: 0,\n declining: 0,\n };\n\n let totalWeight = 0;\n\n for (const signal of signals) {\n votes[signal.direction] += signal.weight;\n totalWeight += signal.weight;\n }\n\n // Find the winning direction\n let maxVote = 0;\n let trend: TrendDirection = 'stable';\n\n for (const [direction, vote] of Object.entries(votes)) {\n if (vote > maxVote) {\n maxVote = vote;\n trend = direction as TrendDirection;\n }\n }\n\n // Confidence is based on:\n // 1. How dominant the winning direction is (proportion of total weight)\n // 2. How many signals we have (more signals = higher confidence)\n const dominance = totalWeight > 0 ? maxVote / totalWeight : 0;\n const signalCountFactor = Math.min(signals.length / HIGH_CONFIDENCE_THRESHOLD, 1);\n const confidence = Math.min(dominance * signalCountFactor, 1);\n\n return { trend, confidence };\n }\n\n /**\n * Estimate how much the trust score might change in 3 months based on\n * the trend and confidence.\n */\n private calculateRiskProjection(\n trend: TrendDirection,\n confidence: number,\n signals: TrendSignal[],\n ): number {\n // Base projection ranges\n const projectionMap: Record<TrendDirection, number> = {\n rising: 5,\n stable: 0,\n declining: -10,\n unknown: 0,\n };\n\n let base = projectionMap[trend];\n\n // Scale by confidence\n base *= confidence;\n\n // Strong negative signals amplify the projection\n const decliningWeight = signals\n .filter((s) => s.direction === 'declining')\n .reduce((sum, s) => sum + s.weight, 0);\n\n if (decliningWeight > 0.5) {\n // Multiple strong declining signals — amplify the negative projection\n base -= Math.round(decliningWeight * 5);\n }\n\n // Clamp to reasonable range\n return Math.max(-20, Math.min(10, base));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\ninterface TrendSignal {\n direction: 'rising' | 'stable' | 'declining';\n weight: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction monthsBetween(from: Date, to: Date): number {\n const years = to.getFullYear() - from.getFullYear();\n const months = to.getMonth() - from.getMonth();\n const days = to.getDate() - from.getDate();\n return years * 12 + months + (days < 0 ? -0.5 : 0);\n}\n","/**\n * Token-bucket rate limiter for controlling outbound HTTP request frequency.\n *\n * Usage:\n * ```ts\n * const limiter = new RateLimiter(10, 60_000); // 10 requests per minute\n * await limiter.acquire(); // blocks if bucket is empty\n * await fetch(url);\n * ```\n */\nexport class RateLimiter {\n private tokens: number;\n private readonly maxTokens: number;\n private readonly refillIntervalMs: number;\n private lastRefill: number;\n private waitQueue: Array<() => void> = [];\n\n /**\n * @param maxRequests Maximum number of requests allowed in the window\n * @param windowMs Window duration in milliseconds\n */\n constructor(maxRequests: number, windowMs: number) {\n this.maxTokens = maxRequests;\n this.tokens = maxRequests;\n this.refillIntervalMs = windowMs;\n this.lastRefill = Date.now();\n }\n\n /**\n * Acquire a token. Resolves immediately when tokens are available,\n * otherwise waits until the bucket is refilled.\n */\n async acquire(): Promise<void> {\n this.refill();\n\n if (this.tokens > 0) {\n this.tokens--;\n return;\n }\n\n // No tokens available — wait for the next refill cycle\n return new Promise<void>((resolve) => {\n this.waitQueue.push(resolve);\n this.scheduleRefill();\n });\n }\n\n /**\n * Return the number of tokens currently available (without waiting).\n */\n get remaining(): number {\n this.refill();\n return this.tokens;\n }\n\n /**\n * Return the number of milliseconds until the next refill.\n */\n get msUntilRefill(): number {\n const elapsed = Date.now() - this.lastRefill;\n return Math.max(0, this.refillIntervalMs - elapsed);\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n private refill(): void {\n const now = Date.now();\n const elapsed = now - this.lastRefill;\n\n if (elapsed >= this.refillIntervalMs) {\n // Full refill\n const periods = Math.floor(elapsed / this.refillIntervalMs);\n this.tokens = Math.min(this.maxTokens, this.tokens + periods * this.maxTokens);\n this.lastRefill = now - (elapsed % this.refillIntervalMs);\n this.drainWaitQueue();\n }\n }\n\n private scheduleRefill(): void {\n const delay = this.msUntilRefill;\n if (delay <= 0) {\n this.refill();\n return;\n }\n\n setTimeout(() => {\n this.refill();\n }, delay);\n }\n\n private drainWaitQueue(): void {\n while (this.waitQueue.length > 0 && this.tokens > 0) {\n this.tokens--;\n const resolve = this.waitQueue.shift();\n resolve?.();\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pre-configured limiters\n// ---------------------------------------------------------------------------\n\n/**\n * GitHub API rate limiter: 5000 requests per hour (authenticated).\n *\n * GitHub's unauthenticated limit is 60/hr, but dep-oracle is designed to\n * work with a token so we use the authenticated limit.\n */\nexport const githubRateLimiter = new RateLimiter(5000, 3_600_000);\n\n/**\n * npm registry rate limiter: generous default of 300 requests per minute.\n * The npm registry does not publish official limits, but this is safe.\n */\nexport const npmRateLimiter = new RateLimiter(300, 60_000);\n\n/**\n * PyPI rate limiter: conservative 100 requests per minute.\n */\nexport const pypiRateLimiter = new RateLimiter(100, 60_000);\n","/**\n * Configuration loader for dep-oracle.\n *\n * Uses cosmiconfig to search for configuration in:\n * - package.json \"dep-oracle\" field\n * - .dep-oraclerc / .dep-oraclerc.json / .dep-oraclerc.yaml / .dep-oraclerc.yml\n * - dep-oracle.config.js / dep-oracle.config.cjs / dep-oracle.config.mjs\n *\n * Merge order: defaults < file config < environment variables < CLI overrides.\n */\n\nimport { cosmiconfig } from 'cosmiconfig';\nimport { ConfigSchema, type Config } from '../parsers/schema.js';\n\nconst explorer = cosmiconfig('dep-oracle');\n\n/**\n * Load and validate project configuration.\n *\n * @param overrides - Partial config values from CLI flags or programmatic use.\n * These take highest precedence.\n * @returns Fully validated Config object with defaults applied.\n */\nexport async function loadConfig(overrides?: Partial<Config>): Promise<Config> {\n let fileConfig: Record<string, unknown> = {};\n\n try {\n const result = await explorer.search();\n if (result?.config && typeof result.config === 'object') {\n fileConfig = result.config as Record<string, unknown>;\n }\n } catch {\n // Config file is optional -- continue with defaults if search fails\n }\n\n // Merge: file config < CLI overrides\n const merged: Record<string, unknown> = { ...fileConfig, ...overrides };\n\n // Read GitHub token from environment variables when not set by file or override\n if (!merged.githubToken && process.env.GITHUB_TOKEN) {\n merged.githubToken = process.env.GITHUB_TOKEN;\n }\n if (!merged.githubToken && process.env.DEP_ORACLE_GITHUB_TOKEN) {\n merged.githubToken = process.env.DEP_ORACLE_GITHUB_TOKEN;\n }\n\n return ConfigSchema.parse(merged);\n}\n","/**\n * dep-oracle -- public API for programmatic use.\n *\n * This module re-exports the key types, parsers, collectors, analyzers,\n * and cache infrastructure so consumers can integrate dep-oracle into\n * their own tooling without going through the CLI.\n *\n * Usage:\n * import { NpmParser, CollectorOrchestrator, TrustScoreEngine } from 'dep-oracle';\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type {\n DependencyTree,\n DependencyNode,\n TrustReport,\n ScanResult,\n Config,\n CollectorResult,\n CollectorStatus,\n TrustMetrics,\n Registry,\n RegistryData,\n GitHubData,\n SecurityData,\n FundingData,\n PopularityData,\n LicenseData,\n DownloadTrend,\n LicenseRisk,\n VulnerabilityEntry,\n} from './parsers/schema.js';\n\n// ---------------------------------------------------------------------------\n// Schema utilities\n// ---------------------------------------------------------------------------\n\nexport {\n ConfigSchema,\n DependencyNodeSchema,\n DependencyTreeSchema,\n TrustReportSchema,\n ScanResultSchema,\n TrustMetricsSchema,\n createDependencyNode,\n createDependencyTree,\n collectorSuccess,\n collectorError,\n collectorCached,\n collectorOffline,\n} from './parsers/schema.js';\n\n// ---------------------------------------------------------------------------\n// Parsers\n// ---------------------------------------------------------------------------\n\nexport { BaseParser } from './parsers/base.js';\nexport { NpmParser } from './parsers/npm.js';\nexport { PythonParser } from './parsers/python.js';\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\nexport { CacheManager } from './cache/store.js';\n\n// ---------------------------------------------------------------------------\n// Collectors\n// ---------------------------------------------------------------------------\n\nexport { CollectorOrchestrator } from './collectors/orchestrator.js';\nexport type { AllCollectorResults, OrchestratorOptions } from './collectors/orchestrator.js';\n\n// ---------------------------------------------------------------------------\n// Analyzers\n// ---------------------------------------------------------------------------\n\nexport { TrustScoreEngine } from './analyzers/trust-score.js';\nexport type { TrustScoreResult } from './analyzers/trust-score.js';\n\nexport { ZombieDetector } from './analyzers/zombie-detector.js';\nexport type { ZombieResult, ZombieSeverity } from './analyzers/zombie-detector.js';\n\nexport { BlastRadiusCalculator } from './analyzers/blast-radius.js';\nexport type { BlastRadiusResult } from './analyzers/blast-radius.js';\n\nexport { TyposquatDetector } from './analyzers/typosquat.js';\nexport type { TyposquatResult } from './analyzers/typosquat.js';\n\nexport { TrendPredictor } from './analyzers/trend-predictor.js';\nexport type { TrendResult, TrendDirection } from './analyzers/trend-predictor.js';\n\nexport { MigrationAdvisor } from './analyzers/migration-advisor.js';\nexport type { MigrationSuggestion, MigrationDifficulty } from './analyzers/migration-advisor.js';\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nexport { buildImportGraph, getBlastRadius, getImportingFiles } from './utils/graph.js';\nexport type { ImportGraph } from './utils/graph.js';\n\nexport { logger, createLogger, isDebug, setVerbose, isVerbose } from './utils/logger.js';\n\nexport { RateLimiter, githubRateLimiter, npmRateLimiter, pypiRateLimiter } from './utils/rate-limiter.js';\n\n// ---------------------------------------------------------------------------\n// Config loader\n// ---------------------------------------------------------------------------\n\nexport { loadConfig } from './cli/config.js';\n\n// ---------------------------------------------------------------------------\n// Convenience API — matches README documentation\n// ---------------------------------------------------------------------------\n\nimport { resolve } from 'node:path';\nimport type { DependencyTree, TrustReport, ScanResult } from './parsers/schema.js';\nimport { NpmParser } from './parsers/npm.js';\nimport { PythonParser } from './parsers/python.js';\nimport { CacheManager } from './cache/store.js';\nimport { CollectorOrchestrator } from './collectors/orchestrator.js';\nimport { TrustScoreEngine } from './analyzers/trust-score.js';\nimport { ZombieDetector } from './analyzers/zombie-detector.js';\nimport { MigrationAdvisor } from './analyzers/migration-advisor.js';\nimport { TyposquatDetector } from './analyzers/typosquat.js';\nimport { buildImportGraph, getBlastRadius } from './utils/graph.js';\n\nasync function detectProject(dir: string): Promise<DependencyTree | null> {\n const parsers = [new NpmParser(), new PythonParser()];\n for (const parser of parsers) {\n if (await parser.detect(dir)) return parser.parse(dir);\n }\n return null;\n}\n\n/**\n * Scan a project directory and return a full trust report.\n *\n * ```typescript\n * import { scan } from 'dep-oracle';\n * const report = await scan({ dir: './my-project' });\n * console.log(report.overallScore);\n * ```\n */\nexport async function scan(options?: {\n dir?: string;\n githubToken?: string;\n offline?: boolean;\n}): Promise<ScanResult> {\n const dir = resolve(options?.dir ?? process.cwd());\n const cache = new CacheManager();\n const orchestrator = new CollectorOrchestrator(cache, {\n githubToken: options?.githubToken,\n offline: options?.offline,\n });\n const trustEngine = new TrustScoreEngine();\n const zombieDetector = new ZombieDetector();\n const migrationAdvisor = new MigrationAdvisor();\n const typosquatDetector = new TyposquatDetector();\n\n const tree = await detectProject(dir);\n if (!tree) {\n return {\n tree: { root: dir, manifest: '', nodes: new Map(), totalDirect: 0, totalTransitive: 0 },\n reports: [],\n overallScore: 0,\n summary: 'No supported manifest file found.',\n };\n }\n\n const importGraph = await buildImportGraph(dir);\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const results = await orchestrator.collectAll(node.name, node.version);\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(results.registry.data, results.github.data);\n const typosquat = typosquatDetector.check(node.name);\n const alternatives = migrationAdvisor\n .suggest(node.name, zombie.isZombie ? 'zombie dependency' : 'low trust score')\n .map((a) => a.alternative);\n const trend = results.popularity.data?.trend ?? 'stable';\n\n reports.push({\n package: node.name,\n version: node.version,\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius,\n alternatives,\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n });\n }\n\n const overallScore =\n reports.length > 0\n ? Math.round(reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length)\n : 0;\n\n return {\n tree,\n reports,\n overallScore,\n summary:\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${reports.filter((r) => r.isZombie).length} zombie(s) detected.`,\n };\n}\n\n/**\n * Check a single package and return its trust report.\n *\n * ```typescript\n * import { checkPackage } from 'dep-oracle';\n * const result = await checkPackage('express');\n * console.log(result.trustScore); // 74\n * ```\n */\nexport async function checkPackage(\n packageName: string,\n version?: string,\n options?: { githubToken?: string; offline?: boolean },\n): Promise<TrustReport> {\n const cache = new CacheManager();\n const orchestrator = new CollectorOrchestrator(cache, {\n githubToken: options?.githubToken,\n offline: options?.offline,\n });\n const trustEngine = new TrustScoreEngine();\n const zombieDetector = new ZombieDetector();\n const migrationAdvisor = new MigrationAdvisor();\n const typosquatDetector = new TyposquatDetector();\n\n const results = await orchestrator.collectAll(packageName, version ?? 'latest');\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(results.registry.data, results.github.data);\n const typosquat = typosquatDetector.check(packageName);\n const alternatives = migrationAdvisor\n .suggest(packageName, zombie.isZombie ? 'zombie dependency' : 'low trust score')\n .map((a) => a.alternative);\n const trend = results.popularity.data?.trend ?? 'stable';\n\n return {\n package: packageName,\n version: version ?? 'latest',\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius: 0,\n alternatives,\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n };\n}\n"],"mappings":"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","names":["resolve"]}
1
+ {"version":3,"sources":["../src/analyzers/trend-predictor.ts","../src/cli/config.ts","../src/index.ts"],"sourcesContent":["import type {\n RegistryData,\n PopularityData,\n GitHubData,\n} from '../parsers/schema.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type TrendDirection = 'rising' | 'stable' | 'declining' | 'unknown';\n\nexport interface TrendResult {\n /** Overall trend direction. */\n trend: TrendDirection;\n /** Confidence in the prediction (0-1). */\n confidence: number;\n /**\n * Estimated trust score change over the next 3 months.\n * Negative values indicate a projected decline (e.g. -5 means the score\n * is expected to drop by roughly 5 points).\n */\n riskProjection3m: number;\n /** Human-readable explanation of the trend assessment. */\n reason: string;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Minimum signals required for a high-confidence prediction. */\nconst HIGH_CONFIDENCE_THRESHOLD = 3;\n\n// ---------------------------------------------------------------------------\n// TrendPredictor\n// ---------------------------------------------------------------------------\n\nexport class TrendPredictor {\n /**\n * Predict the trajectory of a package based on registry, popularity,\n * and GitHub signals.\n */\n predict(\n registry: RegistryData | null,\n popularity: PopularityData | null,\n github: GitHubData | null,\n ): TrendResult {\n const signals: TrendSignal[] = [];\n const reasons: string[] = [];\n\n // -----------------------------------------------------------------------\n // Signal 1: Download trend from PopularityData\n // -----------------------------------------------------------------------\n if (popularity !== null) {\n switch (popularity.trend) {\n case 'rising':\n signals.push({ direction: 'rising', weight: 0.4 });\n reasons.push('Downloads are trending upward');\n break;\n case 'declining':\n signals.push({ direction: 'declining', weight: 0.4 });\n reasons.push('Downloads are trending downward');\n break;\n case 'stable':\n signals.push({ direction: 'stable', weight: 0.3 });\n reasons.push('Downloads are stable');\n break;\n }\n }\n\n // -----------------------------------------------------------------------\n // Signal 2: Commit frequency from GitHub\n // -----------------------------------------------------------------------\n if (github !== null) {\n const commits30d = github.recentCommitCount;\n\n if (commits30d >= 30) {\n signals.push({ direction: 'rising', weight: 0.3 });\n reasons.push(`High commit activity (${commits30d} commits in 30 days)`);\n } else if (commits30d >= 10) {\n signals.push({ direction: 'stable', weight: 0.2 });\n reasons.push(`Moderate commit activity (${commits30d} commits in 30 days)`);\n } else if (commits30d >= 1) {\n signals.push({ direction: 'stable', weight: 0.15 });\n reasons.push(`Low commit activity (${commits30d} commits in 30 days)`);\n } else {\n signals.push({ direction: 'declining', weight: 0.3 });\n reasons.push('No commits in the last 30 days');\n }\n }\n\n // -----------------------------------------------------------------------\n // Signal 3: Version release cadence from RegistryData\n // -----------------------------------------------------------------------\n if (registry !== null) {\n if (registry.lastPublishDate !== null) {\n const lastPublish = new Date(registry.lastPublishDate);\n const monthsSincePublish = monthsBetween(lastPublish, new Date());\n\n if (monthsSincePublish < 2) {\n signals.push({ direction: 'rising', weight: 0.2 });\n reasons.push('Recent version published within last 2 months');\n } else if (monthsSincePublish < 6) {\n signals.push({ direction: 'stable', weight: 0.15 });\n reasons.push(`Last publish ${Math.round(monthsSincePublish)} months ago`);\n } else if (monthsSincePublish < 12) {\n signals.push({ direction: 'declining', weight: 0.2 });\n reasons.push(`No new version in ${Math.round(monthsSincePublish)} months`);\n } else {\n signals.push({ direction: 'declining', weight: 0.3 });\n reasons.push(`No new version in ${Math.round(monthsSincePublish)} months — possibly abandoned`);\n }\n }\n\n // Version count as a maturity signal\n if (registry.versionCount > 50) {\n signals.push({ direction: 'stable', weight: 0.1 });\n reasons.push(`Mature package with ${registry.versionCount} versions`);\n }\n\n // Deprecated flag is a strong declining signal (string | null)\n if (registry.deprecated !== null) {\n signals.push({ direction: 'declining', weight: 0.5 });\n reasons.push('Package is marked as deprecated');\n }\n }\n\n // -----------------------------------------------------------------------\n // Signal 4: Star/fork ratio as community interest (GitHub)\n // -----------------------------------------------------------------------\n if (github !== null && github.stars > 0) {\n const forkRatio = github.forks / github.stars;\n if (forkRatio > 0.3) {\n // High fork ratio suggests active community interest\n signals.push({ direction: 'rising', weight: 0.1 });\n reasons.push('High fork-to-star ratio indicates active community');\n }\n }\n\n // -----------------------------------------------------------------------\n // Aggregate signals\n // -----------------------------------------------------------------------\n if (signals.length === 0) {\n return {\n trend: 'unknown',\n confidence: 0,\n riskProjection3m: 0,\n reason: 'Insufficient data to determine trend',\n };\n }\n\n const { trend, confidence } = this.aggregateSignals(signals);\n const riskProjection3m = this.calculateRiskProjection(trend, confidence, signals);\n\n return {\n trend,\n confidence: Math.round(confidence * 100) / 100,\n riskProjection3m: Math.round(riskProjection3m),\n reason: reasons.join('. ') + '.',\n };\n }\n\n // -------------------------------------------------------------------------\n // Internal helpers\n // -------------------------------------------------------------------------\n\n /**\n * Aggregate multiple trend signals into a single direction and confidence.\n * Uses weighted voting: each signal votes for its direction with its weight.\n */\n private aggregateSignals(signals: TrendSignal[]): {\n trend: TrendDirection;\n confidence: number;\n } {\n const votes: Record<Exclude<TrendDirection, 'unknown'>, number> = {\n rising: 0,\n stable: 0,\n declining: 0,\n };\n\n let totalWeight = 0;\n\n for (const signal of signals) {\n votes[signal.direction] += signal.weight;\n totalWeight += signal.weight;\n }\n\n // Find the winning direction\n let maxVote = 0;\n let trend: TrendDirection = 'stable';\n\n for (const [direction, vote] of Object.entries(votes)) {\n if (vote > maxVote) {\n maxVote = vote;\n trend = direction as TrendDirection;\n }\n }\n\n // Confidence is based on:\n // 1. How dominant the winning direction is (proportion of total weight)\n // 2. How many signals we have (more signals = higher confidence)\n const dominance = totalWeight > 0 ? maxVote / totalWeight : 0;\n const signalCountFactor = Math.min(signals.length / HIGH_CONFIDENCE_THRESHOLD, 1);\n const confidence = Math.min(dominance * signalCountFactor, 1);\n\n return { trend, confidence };\n }\n\n /**\n * Estimate how much the trust score might change in 3 months based on\n * the trend and confidence.\n */\n private calculateRiskProjection(\n trend: TrendDirection,\n confidence: number,\n signals: TrendSignal[],\n ): number {\n // Base projection ranges\n const projectionMap: Record<TrendDirection, number> = {\n rising: 5,\n stable: 0,\n declining: -10,\n unknown: 0,\n };\n\n let base = projectionMap[trend];\n\n // Scale by confidence\n base *= confidence;\n\n // Strong negative signals amplify the projection\n const decliningWeight = signals\n .filter((s) => s.direction === 'declining')\n .reduce((sum, s) => sum + s.weight, 0);\n\n if (decliningWeight > 0.5) {\n // Multiple strong declining signals — amplify the negative projection\n base -= Math.round(decliningWeight * 5);\n }\n\n // Clamp to reasonable range\n return Math.max(-20, Math.min(10, base));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\ninterface TrendSignal {\n direction: 'rising' | 'stable' | 'declining';\n weight: number;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction monthsBetween(from: Date, to: Date): number {\n const years = to.getFullYear() - from.getFullYear();\n const months = to.getMonth() - from.getMonth();\n const days = to.getDate() - from.getDate();\n return years * 12 + months + (days < 0 ? -0.5 : 0);\n}\n","/**\n * Configuration loader for dep-oracle.\n *\n * Uses cosmiconfig to search for configuration in:\n * - package.json \"dep-oracle\" field\n * - .dep-oraclerc / .dep-oraclerc.json / .dep-oraclerc.yaml / .dep-oraclerc.yml\n * - dep-oracle.config.js / dep-oracle.config.cjs / dep-oracle.config.mjs\n *\n * Merge order: defaults < file config < environment variables < CLI overrides.\n */\n\nimport { cosmiconfig } from 'cosmiconfig';\nimport { ConfigSchema, type Config } from '../parsers/schema.js';\n\nconst explorer = cosmiconfig('dep-oracle');\n\n/**\n * Load and validate project configuration.\n *\n * @param overrides - Partial config values from CLI flags or programmatic use.\n * These take highest precedence.\n * @returns Fully validated Config object with defaults applied.\n */\nexport async function loadConfig(overrides?: Partial<Config>): Promise<Config> {\n let fileConfig: Record<string, unknown> = {};\n\n try {\n const result = await explorer.search();\n if (result?.config && typeof result.config === 'object') {\n fileConfig = result.config as Record<string, unknown>;\n }\n } catch {\n // Config file is optional -- continue with defaults if search fails\n }\n\n // Merge: file config < CLI overrides\n const merged: Record<string, unknown> = { ...fileConfig, ...overrides };\n\n // Read GitHub token from environment variables when not set by file or override\n if (!merged.githubToken && process.env.GITHUB_TOKEN) {\n merged.githubToken = process.env.GITHUB_TOKEN;\n }\n if (!merged.githubToken && process.env.DEP_ORACLE_GITHUB_TOKEN) {\n merged.githubToken = process.env.DEP_ORACLE_GITHUB_TOKEN;\n }\n\n return ConfigSchema.parse(merged);\n}\n","/**\n * dep-oracle -- public API for programmatic use.\n *\n * This module re-exports the key types, parsers, collectors, analyzers,\n * and cache infrastructure so consumers can integrate dep-oracle into\n * their own tooling without going through the CLI.\n *\n * Usage:\n * import { NpmParser, CollectorOrchestrator, TrustScoreEngine } from 'dep-oracle';\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type {\n DependencyTree,\n DependencyNode,\n TrustReport,\n ScanResult,\n Config,\n CollectorResult,\n CollectorStatus,\n TrustMetrics,\n Registry,\n RegistryData,\n GitHubData,\n SecurityData,\n FundingData,\n PopularityData,\n LicenseData,\n DownloadTrend,\n LicenseRisk,\n VulnerabilityEntry,\n} from './parsers/schema.js';\n\n// ---------------------------------------------------------------------------\n// Schema utilities\n// ---------------------------------------------------------------------------\n\nexport {\n ConfigSchema,\n DependencyNodeSchema,\n DependencyTreeSchema,\n TrustReportSchema,\n ScanResultSchema,\n TrustMetricsSchema,\n createDependencyNode,\n createDependencyTree,\n collectorSuccess,\n collectorError,\n collectorCached,\n collectorOffline,\n} from './parsers/schema.js';\n\n// ---------------------------------------------------------------------------\n// Parsers\n// ---------------------------------------------------------------------------\n\nexport { BaseParser } from './parsers/base.js';\nexport { NpmParser } from './parsers/npm.js';\nexport { PythonParser } from './parsers/python.js';\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\nexport { CacheManager } from './cache/store.js';\n\n// ---------------------------------------------------------------------------\n// Collectors\n// ---------------------------------------------------------------------------\n\nexport { CollectorOrchestrator } from './collectors/orchestrator.js';\nexport type { AllCollectorResults, OrchestratorOptions } from './collectors/orchestrator.js';\n\n// ---------------------------------------------------------------------------\n// Analyzers\n// ---------------------------------------------------------------------------\n\nexport { TrustScoreEngine } from './analyzers/trust-score.js';\nexport type { TrustScoreResult } from './analyzers/trust-score.js';\n\nexport { ZombieDetector } from './analyzers/zombie-detector.js';\nexport type { ZombieResult, ZombieSeverity } from './analyzers/zombie-detector.js';\n\nexport { BlastRadiusCalculator } from './analyzers/blast-radius.js';\nexport type { BlastRadiusResult } from './analyzers/blast-radius.js';\n\nexport { TyposquatDetector } from './analyzers/typosquat.js';\nexport type { TyposquatResult } from './analyzers/typosquat.js';\n\nexport { TrendPredictor } from './analyzers/trend-predictor.js';\nexport type { TrendResult, TrendDirection } from './analyzers/trend-predictor.js';\n\nexport { MigrationAdvisor } from './analyzers/migration-advisor.js';\nexport type { MigrationSuggestion, MigrationDifficulty } from './analyzers/migration-advisor.js';\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nexport { buildImportGraph, getBlastRadius, getImportingFiles } from './utils/graph.js';\nexport type { ImportGraph } from './utils/graph.js';\n\nexport { logger, createLogger, isDebug, setVerbose, isVerbose } from './utils/logger.js';\n\nexport { RateLimiter, githubRateLimiter, npmRateLimiter, pypiRateLimiter } from './utils/rate-limiter.js';\n\n// ---------------------------------------------------------------------------\n// Config loader\n// ---------------------------------------------------------------------------\n\nexport { loadConfig } from './cli/config.js';\n\n// ---------------------------------------------------------------------------\n// Convenience API — matches README documentation\n// ---------------------------------------------------------------------------\n\nimport { resolve } from 'node:path';\nimport type { DependencyTree, TrustReport, ScanResult } from './parsers/schema.js';\nimport { NpmParser } from './parsers/npm.js';\nimport { PythonParser } from './parsers/python.js';\nimport { CacheManager } from './cache/store.js';\nimport { CollectorOrchestrator } from './collectors/orchestrator.js';\nimport { TrustScoreEngine } from './analyzers/trust-score.js';\nimport { ZombieDetector } from './analyzers/zombie-detector.js';\nimport { MigrationAdvisor } from './analyzers/migration-advisor.js';\nimport { TyposquatDetector } from './analyzers/typosquat.js';\nimport { buildImportGraph, getBlastRadius } from './utils/graph.js';\n\nasync function detectProject(dir: string): Promise<DependencyTree | null> {\n const parsers = [new NpmParser(), new PythonParser()];\n for (const parser of parsers) {\n if (await parser.detect(dir)) return parser.parse(dir);\n }\n return null;\n}\n\n/**\n * Scan a project directory and return a full trust report.\n *\n * ```typescript\n * import { scan } from 'dep-oracle';\n * const report = await scan({ dir: './my-project' });\n * console.log(report.overallScore);\n * ```\n */\nexport async function scan(options?: {\n dir?: string;\n githubToken?: string;\n offline?: boolean;\n ecosystem?: 'npm' | 'pypi';\n}): Promise<ScanResult> {\n const dir = resolve(options?.dir ?? process.cwd());\n const cache = new CacheManager();\n const orchestrator = new CollectorOrchestrator(cache, {\n githubToken: options?.githubToken,\n offline: options?.offline,\n });\n const trustEngine = new TrustScoreEngine();\n const zombieDetector = new ZombieDetector();\n const migrationAdvisor = new MigrationAdvisor();\n const typosquatDetector = new TyposquatDetector();\n\n const tree = await detectProject(dir);\n if (!tree) {\n return {\n tree: { root: dir, manifest: '', nodes: new Map(), totalDirect: 0, totalTransitive: 0 },\n reports: [],\n overallScore: 0,\n summary: 'No supported manifest file found.',\n };\n }\n\n const importGraph = await buildImportGraph(dir);\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const nodeEcosystem = options?.ecosystem ?? node.registry;\n const results = await orchestrator.collectAll(node.name, node.version, nodeEcosystem);\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(results.registry.data, results.github.data);\n const typosquat = typosquatDetector.check(node.name);\n const alternatives = migrationAdvisor\n .suggest(node.name, zombie.isZombie ? 'zombie dependency' : 'low trust score')\n .map((a) => a.alternative);\n const trend = results.popularity.data?.trend ?? 'stable';\n\n reports.push({\n package: node.name,\n version: node.version,\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius,\n alternatives,\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n });\n }\n\n const overallScore =\n reports.length > 0\n ? Math.round(reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length)\n : 0;\n\n return {\n tree,\n reports,\n overallScore,\n summary:\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${reports.filter((r) => r.isZombie).length} zombie(s) detected.`,\n };\n}\n\n/**\n * Check a single package and return its trust report.\n *\n * ```typescript\n * import { checkPackage } from 'dep-oracle';\n * const result = await checkPackage('express');\n * console.log(result.trustScore); // 74\n * ```\n */\nexport async function checkPackage(\n packageName: string,\n version?: string,\n options?: { githubToken?: string; offline?: boolean; ecosystem?: 'npm' | 'pypi' },\n): Promise<TrustReport> {\n const cache = new CacheManager();\n const orchestrator = new CollectorOrchestrator(cache, {\n githubToken: options?.githubToken,\n offline: options?.offline,\n });\n const trustEngine = new TrustScoreEngine();\n const zombieDetector = new ZombieDetector();\n const migrationAdvisor = new MigrationAdvisor();\n const typosquatDetector = new TyposquatDetector();\n\n const ecosystem = options?.ecosystem ?? 'npm';\n const results = await orchestrator.collectAll(packageName, version ?? 'latest', ecosystem);\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(results.registry.data, results.github.data);\n const typosquat = typosquatDetector.check(packageName);\n const alternatives = migrationAdvisor\n .suggest(packageName, zombie.isZombie ? 'zombie dependency' : 'low trust score')\n .map((a) => a.alternative);\n const trend = results.popularity.data?.trend ?? 'stable';\n\n return {\n package: packageName,\n version: version ?? 'latest',\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius: 0,\n alternatives,\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n };\n}\n"],"mappings":"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","names":[]}
@@ -10,7 +10,7 @@ import {
10
10
  ZombieDetector,
11
11
  buildImportGraph,
12
12
  getBlastRadius
13
- } from "../chunk-TXSNFX3N.js";
13
+ } from "../chunk-DLWG22RC.js";
14
14
 
15
15
  // src/mcp/server.ts
16
16
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -117,6 +117,10 @@ var TOOLS = [
117
117
  dir: {
118
118
  type: "string",
119
119
  description: "Absolute path to the project directory to scan. Defaults to the current working directory if omitted."
120
+ },
121
+ ecosystem: {
122
+ type: "string",
123
+ description: 'Package ecosystem: "npm" or "pypi". Defaults to "npm". When scanning a project, the ecosystem is auto-detected from the manifest.'
120
124
  }
121
125
  },
122
126
  required: []
@@ -124,17 +128,21 @@ var TOOLS = [
124
128
  },
125
129
  {
126
130
  name: "dep_oracle_trust_score",
127
- description: "Calculate the trust score for a single npm package. Queries the npm registry, GitHub, OSV vulnerability database, and other sources to produce a weighted score (0-100) across 6 dimensions: security, maintainer, activity, popularity, funding, and license. Returns a TrustReport JSON with the overall score, per-dimension metrics, zombie status, and alternative suggestions.",
131
+ description: "Calculate the trust score for a single package. Queries the registry, GitHub, OSV vulnerability database, and other sources to produce a weighted score (0-100) across 6 dimensions: security, maintainer, activity, popularity, funding, and license. Returns a TrustReport JSON with the overall score, per-dimension metrics, zombie status, and alternative suggestions.",
128
132
  inputSchema: {
129
133
  type: "object",
130
134
  properties: {
131
135
  package: {
132
136
  type: "string",
133
- description: 'npm package name (e.g. "express", "@scope/pkg").'
137
+ description: 'Package name (e.g. "express", "@scope/pkg", "requests").'
134
138
  },
135
139
  version: {
136
140
  type: "string",
137
141
  description: 'Specific version to analyze (e.g. "4.18.2"). If omitted, the latest version is used.'
142
+ },
143
+ ecosystem: {
144
+ type: "string",
145
+ description: 'Package ecosystem: "npm" or "pypi". Defaults to "npm".'
138
146
  }
139
147
  },
140
148
  required: ["package"]
@@ -213,6 +221,10 @@ var TOOLS = [
213
221
  packageB: {
214
222
  type: "string",
215
223
  description: "Second package name to compare."
224
+ },
225
+ ecosystem: {
226
+ type: "string",
227
+ description: 'Package ecosystem: "npm" or "pypi". Defaults to "npm". Both packages must be from the same ecosystem.'
216
228
  }
217
229
  },
218
230
  required: ["packageA", "packageB"]
@@ -231,6 +243,10 @@ var TOOLS = [
231
243
  output: {
232
244
  type: "string",
233
245
  description: "Absolute path to write the report file. If omitted, the report content is returned directly."
246
+ },
247
+ ecosystem: {
248
+ type: "string",
249
+ description: 'Package ecosystem: "npm" or "pypi". Defaults to "npm".'
234
250
  }
235
251
  },
236
252
  required: []
@@ -290,8 +306,8 @@ async function parseProject(dir) {
290
306
  }
291
307
  return null;
292
308
  }
293
- async function buildTrustReport(packageName, version, blastRadius = 0) {
294
- const results = await orchestrator.collectAll(packageName, version);
309
+ async function buildTrustReport(packageName, version, blastRadius = 0, ecosystem = "npm") {
310
+ const results = await orchestrator.collectAll(packageName, version, ecosystem);
295
311
  const trustResult = trustEngine.calculate(results);
296
312
  const zombie = zombieDetector.detect(
297
313
  results.registry.data,
@@ -328,7 +344,8 @@ async function handleScan(args) {
328
344
  const reports = [];
329
345
  for (const node of directNodes) {
330
346
  const blastRadius = getBlastRadius(node.name, importGraph);
331
- const report = await buildTrustReport(node.name, node.version, blastRadius);
347
+ const nodeEcosystem = node.registry === "pypi" ? "pypi" : "npm";
348
+ const report = await buildTrustReport(node.name, node.version, blastRadius, nodeEcosystem);
332
349
  reports.push(report);
333
350
  }
334
351
  const overallScore = reports.length > 0 ? Math.round(
@@ -349,7 +366,8 @@ async function handleScan(args) {
349
366
  async function handleTrustScore(args) {
350
367
  const packageName = validatePackageName(String(args?.package ?? ""));
351
368
  const version = String(args?.version ?? "latest");
352
- const report = await buildTrustReport(packageName, version);
369
+ const ecosystem = args?.ecosystem === "pypi" ? "pypi" : "npm";
370
+ const report = await buildTrustReport(packageName, version, 0, ecosystem);
353
371
  return successResponse(report);
354
372
  }
355
373
  async function handleBlastRadius(args) {
@@ -369,7 +387,8 @@ async function handleZombies(args) {
369
387
  const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);
370
388
  const zombies = [];
371
389
  for (const node of directNodes) {
372
- const results = await orchestrator.collectAll(node.name, node.version);
390
+ const nodeEcosystem = node.registry === "pypi" ? "pypi" : "npm";
391
+ const results = await orchestrator.collectAll(node.name, node.version, nodeEcosystem);
373
392
  const zombie = zombieDetector.detect(
374
393
  results.registry.data,
375
394
  results.github.data
@@ -421,9 +440,10 @@ async function handleTyposquatCheck(args) {
421
440
  async function handleCompare(args) {
422
441
  const packageA = validatePackageName(String(args?.packageA ?? ""));
423
442
  const packageB = validatePackageName(String(args?.packageB ?? ""));
443
+ const ecosystem = args?.ecosystem === "pypi" ? "pypi" : "npm";
424
444
  const [reportA, reportB] = await Promise.all([
425
- buildTrustReport(packageA, "latest"),
426
- buildTrustReport(packageB, "latest")
445
+ buildTrustReport(packageA, "latest", 0, ecosystem),
446
+ buildTrustReport(packageB, "latest", 0, ecosystem)
427
447
  ]);
428
448
  return successResponse({
429
449
  comparison: {
@@ -448,7 +468,8 @@ async function handleReport(args) {
448
468
  const reports = [];
449
469
  for (const node of directNodes) {
450
470
  const blastRadius = getBlastRadius(node.name, importGraph);
451
- const report = await buildTrustReport(node.name, node.version, blastRadius);
471
+ const nodeEcosystem = node.registry === "pypi" ? "pypi" : "npm";
472
+ const report = await buildTrustReport(node.name, node.version, blastRadius, nodeEcosystem);
452
473
  reports.push(report);
453
474
  }
454
475
  const overallScore = reports.length > 0 ? Math.round(
@@ -483,7 +504,7 @@ var pkgVersion = (() => {
483
504
  try {
484
505
  return JSON.parse(readFileSync(pkgPath, "utf-8")).version;
485
506
  } catch {
486
- return "1.2.0";
507
+ return "1.3.0";
487
508
  }
488
509
  })();
489
510
  var server = new Server(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts","../../src/reporters/json.ts"],"sourcesContent":["/**\n * MCP stdio server entry point for dep-oracle.\n *\n * Exposes the full dep-oracle analysis engine as MCP tools so that\n * Claude (and other MCP clients) can scan projects, evaluate trust\n * scores, detect zombies, and more -- all through the standard\n * Model Context Protocol.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { registerTools } from './tools.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst pkgPath = join(__dirname, '..', '..', 'package.json');\nconst pkgVersion = (() => {\n try {\n return JSON.parse(readFileSync(pkgPath, 'utf-8')).version as string;\n } catch {\n return '1.2.0';\n }\n})();\n\nconst server = new Server(\n { name: 'dep-oracle', version: pkgVersion },\n { capabilities: { tools: {} } },\n);\n\nregisterTools(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","/**\n * MCP tool definitions for dep-oracle.\n *\n * Registers 8 tools that expose the full analysis pipeline:\n * 1. dep_oracle_scan -- full project scan\n * 2. dep_oracle_trust_score -- single package trust score\n * 3. dep_oracle_blast_radius -- import impact analysis\n * 4. dep_oracle_zombies -- list zombie dependencies\n * 5. dep_oracle_suggest_migration -- migration suggestions\n * 6. dep_oracle_typosquat_check -- typosquat risk check\n * 7. dep_oracle_compare -- side-by-side package comparison\n * 8. dep_oracle_report -- generate HTML report\n *\n * All tools share the same core engine (parsers, collectors, analyzers)\n * as the CLI.\n */\n\nimport { resolve } from 'node:path';\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n ListToolsRequestSchema,\n CallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { CacheManager } from '../cache/store.js';\nimport { NpmParser } from '../parsers/npm.js';\nimport { PythonParser } from '../parsers/python.js';\nimport type { DependencyTree, TrustReport, ScanResult } from '../parsers/schema.js';\nimport { CollectorOrchestrator } from '../collectors/orchestrator.js';\nimport { TrustScoreEngine } from '../analyzers/trust-score.js';\nimport { ZombieDetector } from '../analyzers/zombie-detector.js';\nimport { BlastRadiusCalculator } from '../analyzers/blast-radius.js';\nimport { MigrationAdvisor } from '../analyzers/migration-advisor.js';\nimport { TyposquatDetector } from '../analyzers/typosquat.js';\nimport { buildImportGraph, getBlastRadius } from '../utils/graph.js';\nimport { JsonReporter } from '../reporters/json.js';\n\n// ---------------------------------------------------------------------------\n// Security: path validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9._-~]*\\/)?[a-z0-9-~][a-z0-9._-~]*$/i;\n\nfunction validateDir(input: string | undefined): string {\n const dir = resolve(String(input ?? process.cwd()));\n // Block obvious path traversal patterns\n const normalized = dir.replace(/\\\\/g, '/');\n if (normalized.includes('/../') || normalized.endsWith('/..')) {\n throw new Error('Path traversal not allowed');\n }\n return dir;\n}\n\nfunction validateOutputPath(output: string, baseDir: string): string {\n const resolved = resolve(output);\n const base = resolve(baseDir);\n if (!resolved.startsWith(base)) {\n throw new Error('Output path must be within the project directory');\n }\n return resolved;\n}\n\nfunction validatePackageName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) throw new Error('Package name is required');\n if (trimmed.length > 214) throw new Error('Package name too long');\n if (!VALID_PACKAGE_NAME.test(trimmed)) {\n throw new Error(`Invalid package name: \"${trimmed}\"`);\n }\n return trimmed;\n}\n\n// ---------------------------------------------------------------------------\n// Shared instances (created once per MCP server lifetime)\n// ---------------------------------------------------------------------------\n\nconst cache = new CacheManager();\nconst orchestrator = new CollectorOrchestrator(cache, {\n githubToken: process.env.GITHUB_TOKEN,\n});\nconst trustEngine = new TrustScoreEngine();\nconst zombieDetector = new ZombieDetector();\nconst blastRadiusCalc = new BlastRadiusCalculator();\nconst migrationAdvisor = new MigrationAdvisor();\nconst typosquatDetector = new TyposquatDetector();\nconst jsonReporter = new JsonReporter();\nconst parsers = [new NpmParser(), new PythonParser()];\n\n// ---------------------------------------------------------------------------\n// Tool definitions\n// ---------------------------------------------------------------------------\n\nconst TOOLS = [\n {\n name: 'dep_oracle_scan',\n description:\n 'Perform a full dependency security scan on a project directory. ' +\n 'Parses the manifest (package.json, requirements.txt, etc.), collects data ' +\n 'from registries and GitHub, computes trust scores for every dependency, ' +\n 'detects zombies, typosquats, and calculates blast radius. Returns a complete ' +\n 'ScanResult JSON with per-package trust reports and an overall project score.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory to scan. ' +\n 'Defaults to the current working directory if omitted.',\n },\n },\n required: [] as string[],\n },\n },\n {\n name: 'dep_oracle_trust_score',\n description:\n 'Calculate the trust score for a single npm package. Queries the npm registry, ' +\n 'GitHub, OSV vulnerability database, and other sources to produce a weighted ' +\n 'score (0-100) across 6 dimensions: security, maintainer, activity, popularity, ' +\n 'funding, and license. Returns a TrustReport JSON with the overall score, ' +\n 'per-dimension metrics, zombie status, and alternative suggestions.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'npm package name (e.g. \"express\", \"@scope/pkg\").',\n },\n version: {\n type: 'string',\n description:\n 'Specific version to analyze (e.g. \"4.18.2\"). If omitted, the latest version is used.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_blast_radius',\n description:\n 'Analyze the blast radius (import impact) of a package within a project. ' +\n 'Scans all JS/TS source files to find how many files import the given package. ' +\n 'Returns the count of affected files, their paths, and the percentage of the ' +\n 'codebase impacted. Useful for understanding the risk if a dependency is ' +\n 'compromised or needs replacement.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to check import usage for.',\n },\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_zombies',\n description:\n 'Detect zombie (abandoned/unmaintained) dependencies in a project. ' +\n 'Parses the manifest, queries registry and GitHub for each dependency, and ' +\n 'flags packages that show signs of abandonment: deprecated, no commits in 12+ months, ' +\n 'no active maintainers, etc. Returns an array of zombie packages with severity ' +\n 'levels and reasons.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n },\n required: [] as string[],\n },\n },\n {\n name: 'dep_oracle_suggest_migration',\n description:\n 'Get migration suggestions for a given package. Looks up the package in a ' +\n 'curated knowledge base of common replacements and returns alternatives with ' +\n 'descriptions and difficulty ratings. Useful for replacing deprecated, abandoned, ' +\n 'or low-trust packages (e.g. moment -> dayjs, request -> got, lodash -> es-toolkit).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to find migration alternatives for.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_typosquat_check',\n description:\n 'Check whether a package name is a potential typosquat of a popular package. ' +\n 'Uses Levenshtein distance, homoglyph detection, and pattern analysis to identify ' +\n 'suspicious package names that closely resemble well-known packages. Returns a ' +\n 'risk assessment with similar package names and the edit distance.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to check for typosquatting risk.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_compare',\n description:\n 'Compare two packages side-by-side by computing trust scores for both. ' +\n 'Returns the full trust report for each package including scores, metrics, ' +\n 'zombie status, and trend direction. Useful for evaluating alternatives ' +\n 'or choosing between competing libraries.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n packageA: {\n type: 'string',\n description: 'First package name to compare.',\n },\n packageB: {\n type: 'string',\n description: 'Second package name to compare.',\n },\n },\n required: ['packageA', 'packageB'],\n },\n },\n {\n name: 'dep_oracle_report',\n description:\n 'Generate a JSON report for a project. Runs a full scan and outputs the results ' +\n 'as formatted JSON. Optionally writes the report to a file. Returns the JSON ' +\n 'content or the path to the generated file.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n output: {\n type: 'string',\n description:\n 'Absolute path to write the report file. If omitted, the report content is returned directly.',\n },\n },\n required: [] as string[],\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Tool registration\n// ---------------------------------------------------------------------------\n\nexport function registerTools(server: Server): void {\n // List all available tools\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOLS,\n }));\n\n // Handle tool invocations\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n switch (name) {\n case 'dep_oracle_scan':\n return await handleScan(args);\n case 'dep_oracle_trust_score':\n return await handleTrustScore(args);\n case 'dep_oracle_blast_radius':\n return await handleBlastRadius(args);\n case 'dep_oracle_zombies':\n return await handleZombies(args);\n case 'dep_oracle_suggest_migration':\n return await handleSuggestMigration(args);\n case 'dep_oracle_typosquat_check':\n return await handleTyposquatCheck(args);\n case 'dep_oracle_compare':\n return await handleCompare(args);\n case 'dep_oracle_report':\n return await handleReport(args);\n default:\n return errorResponse(`Unknown tool: ${name}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return errorResponse(`Tool \"${name}\" failed: ${message}`);\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build a successful text content response\n// ---------------------------------------------------------------------------\n\nfunction successResponse(data: unknown) {\n const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);\n return {\n content: [{ type: 'text' as const, text }],\n };\n}\n\nfunction errorResponse(message: string) {\n return {\n content: [{ type: 'text' as const, text: message }],\n isError: true,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared: parse project dependencies\n// ---------------------------------------------------------------------------\n\nasync function parseProject(dir: string): Promise<DependencyTree | null> {\n for (const parser of parsers) {\n if (await parser.detect(dir)) {\n return parser.parse(dir);\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Shared: build a full trust report for a single package\n// ---------------------------------------------------------------------------\n\nasync function buildTrustReport(\n packageName: string,\n version: string,\n blastRadius: number = 0,\n): Promise<TrustReport> {\n const results = await orchestrator.collectAll(packageName, version);\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(\n results.registry.data,\n results.github.data,\n );\n const typosquat = typosquatDetector.check(packageName);\n const alternatives = migrationAdvisor.suggest(\n packageName,\n zombie.isZombie ? 'zombie dependency' : 'low trust score',\n );\n\n // Determine trend from popularity data\n const trend = results.popularity.data?.trend ?? 'stable';\n\n return {\n package: packageName,\n version,\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius,\n alternatives: alternatives.map((a) => a.alternative),\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tool handlers\n// ---------------------------------------------------------------------------\n\nasync function handleScan(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\". ` +\n 'Supported: package.json, requirements.txt, pyproject.toml, Pipfile.',\n );\n }\n\n // Build import graph for blast radius\n const importGraph = await buildImportGraph(dir);\n\n // Collect trust reports for all direct dependencies\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const report = await buildTrustReport(node.name, node.version, blastRadius);\n reports.push(report);\n }\n\n // Overall score: weighted average\n const overallScore =\n reports.length > 0\n ? Math.round(\n reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length,\n )\n : 0;\n\n // Summary\n const zombieCount = reports.filter((r) => r.isZombie).length;\n const criticalCount = reports.filter((r) => r.trustScore < 50).length;\n const summary =\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${zombieCount} zombie(s) detected. ` +\n `${criticalCount} package(s) below trust threshold.`;\n\n const scanResult: ScanResult = {\n tree,\n reports,\n overallScore,\n summary,\n };\n\n // Serialize (handles Map objects)\n const serialized = jsonReporter.report(scanResult);\n return successResponse(serialized);\n}\n\nasync function handleTrustScore(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const version = String(args?.version ?? 'latest');\n const report = await buildTrustReport(packageName, version);\n return successResponse(report);\n}\n\nasync function handleBlastRadius(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n const dir = validateDir(args?.dir as string | undefined);\n const result = await blastRadiusCalc.calculate(packageName, dir);\n return successResponse(result);\n}\n\nasync function handleZombies(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\".`,\n );\n }\n\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const zombies: Array<{\n package: string;\n version: string;\n severity: string;\n reason: string;\n lastActivity: string | null;\n }> = [];\n\n for (const node of directNodes) {\n const results = await orchestrator.collectAll(node.name, node.version);\n const zombie = zombieDetector.detect(\n results.registry.data,\n results.github.data,\n );\n\n if (zombie.isZombie) {\n zombies.push({\n package: node.name,\n version: node.version,\n severity: zombie.severity,\n reason: zombie.reason,\n lastActivity: zombie.lastActivity?.toISOString() ?? null,\n });\n }\n }\n\n if (zombies.length === 0) {\n return successResponse({\n message: 'No zombie dependencies detected.',\n zombies: [],\n });\n }\n\n return successResponse({\n message: `Found ${zombies.length} zombie dependency(ies).`,\n zombies,\n });\n}\n\nasync function handleSuggestMigration(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const suggestions = migrationAdvisor.suggest(packageName, 'manual query');\n\n if (suggestions.length === 0) {\n return successResponse({\n package: packageName,\n message: `No migration suggestions found for \"${packageName}\". ` +\n 'This package may not have known alternatives in our database.',\n suggestions: [],\n });\n }\n\n return successResponse({\n package: packageName,\n suggestions,\n });\n}\n\nasync function handleTyposquatCheck(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const result = typosquatDetector.check(packageName);\n return successResponse({\n package: packageName,\n ...result,\n });\n}\n\nasync function handleCompare(args: Record<string, unknown> | undefined) {\n const packageA = validatePackageName(String(args?.packageA ?? ''));\n const packageB = validatePackageName(String(args?.packageB ?? ''));\n\n const [reportA, reportB] = await Promise.all([\n buildTrustReport(packageA, 'latest'),\n buildTrustReport(packageB, 'latest'),\n ]);\n\n return successResponse({\n comparison: {\n packageA: reportA,\n packageB: reportB,\n winner:\n reportA.trustScore > reportB.trustScore\n ? packageA\n : reportA.trustScore < reportB.trustScore\n ? packageB\n : 'tie',\n scoreDifference: Math.abs(reportA.trustScore - reportB.trustScore),\n },\n });\n}\n\nasync function handleReport(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n const output = args?.output ? validateOutputPath(String(args.output), dir) : null;\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\".`,\n );\n }\n\n // Build import graph for blast radius\n const importGraph = await buildImportGraph(dir);\n\n // Collect trust reports for direct dependencies\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const report = await buildTrustReport(node.name, node.version, blastRadius);\n reports.push(report);\n }\n\n const overallScore =\n reports.length > 0\n ? Math.round(\n reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length,\n )\n : 0;\n\n const zombieCount = reports.filter((r) => r.isZombie).length;\n const criticalCount = reports.filter((r) => r.trustScore < 50).length;\n const summary =\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${zombieCount} zombie(s) detected. ` +\n `${criticalCount} package(s) below trust threshold.`;\n\n const scanResult: ScanResult = {\n tree,\n reports,\n overallScore,\n summary,\n };\n\n const jsonContent = jsonReporter.report(scanResult);\n\n if (output) {\n const { writeFile } = await import('node:fs/promises');\n await writeFile(output, jsonContent, 'utf-8');\n return successResponse({\n message: `Report written to ${output}`,\n path: output,\n });\n }\n\n return successResponse(jsonContent);\n}\n","import type { ScanResult } from \"../parsers/schema.js\";\n\n// ---------------------------------------------------------------------------\n// JsonReporter\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a ScanResult as formatted JSON.\n *\n * The output is a self-contained JSON string suitable for piping into jq,\n * saving to a file, or passing to downstream tooling.\n */\nexport class JsonReporter {\n /**\n * Convert the scan result into a pretty-printed JSON string.\n *\n * Map objects (e.g. DependencyTree.nodes) are converted to plain\n * objects so that they survive JSON serialization.\n */\n report(result: ScanResult): string {\n return JSON.stringify(this.toSerializable(result), null, 2);\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n /**\n * Walk the value recursively and convert Map instances into plain objects\n * so JSON.stringify can handle them.\n */\n private toSerializable(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (value instanceof Map) {\n const obj: Record<string, unknown> = {};\n for (const [k, v] of value.entries()) {\n obj[String(k)] = this.toSerializable(v);\n }\n return obj;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.toSerializable(item));\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = this.toSerializable(v);\n }\n return result;\n }\n\n return value;\n }\n}\n"],"mappings":"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","names":["server","__filename","__dirname"]}
1
+ {"version":3,"sources":["../../src/mcp/server.ts","../../src/mcp/tools.ts","../../src/reporters/json.ts"],"sourcesContent":["/**\n * MCP stdio server entry point for dep-oracle.\n *\n * Exposes the full dep-oracle analysis engine as MCP tools so that\n * Claude (and other MCP clients) can scan projects, evaluate trust\n * scores, detect zombies, and more -- all through the standard\n * Model Context Protocol.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport { registerTools } from './tools.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst pkgPath = join(__dirname, '..', '..', 'package.json');\nconst pkgVersion = (() => {\n try {\n return JSON.parse(readFileSync(pkgPath, 'utf-8')).version as string;\n } catch {\n return '1.3.0';\n }\n})();\n\nconst server = new Server(\n { name: 'dep-oracle', version: pkgVersion },\n { capabilities: { tools: {} } },\n);\n\nregisterTools(server);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","/**\n * MCP tool definitions for dep-oracle.\n *\n * Registers 8 tools that expose the full analysis pipeline:\n * 1. dep_oracle_scan -- full project scan\n * 2. dep_oracle_trust_score -- single package trust score\n * 3. dep_oracle_blast_radius -- import impact analysis\n * 4. dep_oracle_zombies -- list zombie dependencies\n * 5. dep_oracle_suggest_migration -- migration suggestions\n * 6. dep_oracle_typosquat_check -- typosquat risk check\n * 7. dep_oracle_compare -- side-by-side package comparison\n * 8. dep_oracle_report -- generate HTML report\n *\n * All tools share the same core engine (parsers, collectors, analyzers)\n * as the CLI.\n */\n\nimport { resolve } from 'node:path';\nimport type { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n ListToolsRequestSchema,\n CallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport { CacheManager } from '../cache/store.js';\nimport { NpmParser } from '../parsers/npm.js';\nimport { PythonParser } from '../parsers/python.js';\nimport type { DependencyTree, TrustReport, ScanResult } from '../parsers/schema.js';\nimport { CollectorOrchestrator } from '../collectors/orchestrator.js';\nimport { TrustScoreEngine } from '../analyzers/trust-score.js';\nimport { ZombieDetector } from '../analyzers/zombie-detector.js';\nimport { BlastRadiusCalculator } from '../analyzers/blast-radius.js';\nimport { MigrationAdvisor } from '../analyzers/migration-advisor.js';\nimport { TyposquatDetector } from '../analyzers/typosquat.js';\nimport { buildImportGraph, getBlastRadius } from '../utils/graph.js';\nimport { JsonReporter } from '../reporters/json.js';\n\n// ---------------------------------------------------------------------------\n// Security: path validation\n// ---------------------------------------------------------------------------\n\nconst VALID_PACKAGE_NAME = /^(@[a-z0-9-~][a-z0-9._-~]*\\/)?[a-z0-9-~][a-z0-9._-~]*$/i;\n\nfunction validateDir(input: string | undefined): string {\n const dir = resolve(String(input ?? process.cwd()));\n // Block obvious path traversal patterns\n const normalized = dir.replace(/\\\\/g, '/');\n if (normalized.includes('/../') || normalized.endsWith('/..')) {\n throw new Error('Path traversal not allowed');\n }\n return dir;\n}\n\nfunction validateOutputPath(output: string, baseDir: string): string {\n const resolved = resolve(output);\n const base = resolve(baseDir);\n if (!resolved.startsWith(base)) {\n throw new Error('Output path must be within the project directory');\n }\n return resolved;\n}\n\nfunction validatePackageName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) throw new Error('Package name is required');\n if (trimmed.length > 214) throw new Error('Package name too long');\n if (!VALID_PACKAGE_NAME.test(trimmed)) {\n throw new Error(`Invalid package name: \"${trimmed}\"`);\n }\n return trimmed;\n}\n\n// ---------------------------------------------------------------------------\n// Shared instances (created once per MCP server lifetime)\n// ---------------------------------------------------------------------------\n\nconst cache = new CacheManager();\nconst orchestrator = new CollectorOrchestrator(cache, {\n githubToken: process.env.GITHUB_TOKEN,\n});\nconst trustEngine = new TrustScoreEngine();\nconst zombieDetector = new ZombieDetector();\nconst blastRadiusCalc = new BlastRadiusCalculator();\nconst migrationAdvisor = new MigrationAdvisor();\nconst typosquatDetector = new TyposquatDetector();\nconst jsonReporter = new JsonReporter();\nconst parsers = [new NpmParser(), new PythonParser()];\n\n// ---------------------------------------------------------------------------\n// Tool definitions\n// ---------------------------------------------------------------------------\n\nconst TOOLS = [\n {\n name: 'dep_oracle_scan',\n description:\n 'Perform a full dependency security scan on a project directory. ' +\n 'Parses the manifest (package.json, requirements.txt, etc.), collects data ' +\n 'from registries and GitHub, computes trust scores for every dependency, ' +\n 'detects zombies, typosquats, and calculates blast radius. Returns a complete ' +\n 'ScanResult JSON with per-package trust reports and an overall project score.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory to scan. ' +\n 'Defaults to the current working directory if omitted.',\n },\n ecosystem: {\n type: 'string',\n description:\n 'Package ecosystem: \"npm\" or \"pypi\". Defaults to \"npm\". ' +\n 'When scanning a project, the ecosystem is auto-detected from the manifest.',\n },\n },\n required: [] as string[],\n },\n },\n {\n name: 'dep_oracle_trust_score',\n description:\n 'Calculate the trust score for a single package. Queries the registry, ' +\n 'GitHub, OSV vulnerability database, and other sources to produce a weighted ' +\n 'score (0-100) across 6 dimensions: security, maintainer, activity, popularity, ' +\n 'funding, and license. Returns a TrustReport JSON with the overall score, ' +\n 'per-dimension metrics, zombie status, and alternative suggestions.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name (e.g. \"express\", \"@scope/pkg\", \"requests\").',\n },\n version: {\n type: 'string',\n description:\n 'Specific version to analyze (e.g. \"4.18.2\"). If omitted, the latest version is used.',\n },\n ecosystem: {\n type: 'string',\n description: 'Package ecosystem: \"npm\" or \"pypi\". Defaults to \"npm\".',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_blast_radius',\n description:\n 'Analyze the blast radius (import impact) of a package within a project. ' +\n 'Scans all JS/TS source files to find how many files import the given package. ' +\n 'Returns the count of affected files, their paths, and the percentage of the ' +\n 'codebase impacted. Useful for understanding the risk if a dependency is ' +\n 'compromised or needs replacement.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to check import usage for.',\n },\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_zombies',\n description:\n 'Detect zombie (abandoned/unmaintained) dependencies in a project. ' +\n 'Parses the manifest, queries registry and GitHub for each dependency, and ' +\n 'flags packages that show signs of abandonment: deprecated, no commits in 12+ months, ' +\n 'no active maintainers, etc. Returns an array of zombie packages with severity ' +\n 'levels and reasons.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n },\n required: [] as string[],\n },\n },\n {\n name: 'dep_oracle_suggest_migration',\n description:\n 'Get migration suggestions for a given package. Looks up the package in a ' +\n 'curated knowledge base of common replacements and returns alternatives with ' +\n 'descriptions and difficulty ratings. Useful for replacing deprecated, abandoned, ' +\n 'or low-trust packages (e.g. moment -> dayjs, request -> got, lodash -> es-toolkit).',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to find migration alternatives for.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_typosquat_check',\n description:\n 'Check whether a package name is a potential typosquat of a popular package. ' +\n 'Uses Levenshtein distance, homoglyph detection, and pattern analysis to identify ' +\n 'suspicious package names that closely resemble well-known packages. Returns a ' +\n 'risk assessment with similar package names and the edit distance.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n package: {\n type: 'string',\n description: 'Package name to check for typosquatting risk.',\n },\n },\n required: ['package'],\n },\n },\n {\n name: 'dep_oracle_compare',\n description:\n 'Compare two packages side-by-side by computing trust scores for both. ' +\n 'Returns the full trust report for each package including scores, metrics, ' +\n 'zombie status, and trend direction. Useful for evaluating alternatives ' +\n 'or choosing between competing libraries.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n packageA: {\n type: 'string',\n description: 'First package name to compare.',\n },\n packageB: {\n type: 'string',\n description: 'Second package name to compare.',\n },\n ecosystem: {\n type: 'string',\n description: 'Package ecosystem: \"npm\" or \"pypi\". Defaults to \"npm\". Both packages must be from the same ecosystem.',\n },\n },\n required: ['packageA', 'packageB'],\n },\n },\n {\n name: 'dep_oracle_report',\n description:\n 'Generate a JSON report for a project. Runs a full scan and outputs the results ' +\n 'as formatted JSON. Optionally writes the report to a file. Returns the JSON ' +\n 'content or the path to the generated file.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n dir: {\n type: 'string',\n description:\n 'Absolute path to the project directory. Defaults to cwd if omitted.',\n },\n output: {\n type: 'string',\n description:\n 'Absolute path to write the report file. If omitted, the report content is returned directly.',\n },\n ecosystem: {\n type: 'string',\n description: 'Package ecosystem: \"npm\" or \"pypi\". Defaults to \"npm\".',\n },\n },\n required: [] as string[],\n },\n },\n];\n\n// ---------------------------------------------------------------------------\n// Tool registration\n// ---------------------------------------------------------------------------\n\nexport function registerTools(server: Server): void {\n // List all available tools\n server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: TOOLS,\n }));\n\n // Handle tool invocations\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n switch (name) {\n case 'dep_oracle_scan':\n return await handleScan(args);\n case 'dep_oracle_trust_score':\n return await handleTrustScore(args);\n case 'dep_oracle_blast_radius':\n return await handleBlastRadius(args);\n case 'dep_oracle_zombies':\n return await handleZombies(args);\n case 'dep_oracle_suggest_migration':\n return await handleSuggestMigration(args);\n case 'dep_oracle_typosquat_check':\n return await handleTyposquatCheck(args);\n case 'dep_oracle_compare':\n return await handleCompare(args);\n case 'dep_oracle_report':\n return await handleReport(args);\n default:\n return errorResponse(`Unknown tool: ${name}`);\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return errorResponse(`Tool \"${name}\" failed: ${message}`);\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Helper: build a successful text content response\n// ---------------------------------------------------------------------------\n\nfunction successResponse(data: unknown) {\n const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);\n return {\n content: [{ type: 'text' as const, text }],\n };\n}\n\nfunction errorResponse(message: string) {\n return {\n content: [{ type: 'text' as const, text: message }],\n isError: true,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Shared: parse project dependencies\n// ---------------------------------------------------------------------------\n\nasync function parseProject(dir: string): Promise<DependencyTree | null> {\n for (const parser of parsers) {\n if (await parser.detect(dir)) {\n return parser.parse(dir);\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Shared: build a full trust report for a single package\n// ---------------------------------------------------------------------------\n\nasync function buildTrustReport(\n packageName: string,\n version: string,\n blastRadius: number = 0,\n ecosystem: 'npm' | 'pypi' = 'npm',\n): Promise<TrustReport> {\n const results = await orchestrator.collectAll(packageName, version, ecosystem);\n const trustResult = trustEngine.calculate(results);\n const zombie = zombieDetector.detect(\n results.registry.data,\n results.github.data,\n );\n const typosquat = typosquatDetector.check(packageName);\n const alternatives = migrationAdvisor.suggest(\n packageName,\n zombie.isZombie ? 'zombie dependency' : 'low trust score',\n );\n\n // Determine trend from popularity data\n const trend = results.popularity.data?.trend ?? 'stable';\n\n return {\n package: packageName,\n version,\n trustScore: trustResult.trustScore,\n metrics: trustResult.metrics,\n isZombie: zombie.isZombie,\n blastRadius,\n alternatives: alternatives.map((a) => a.alternative),\n trend,\n typosquatRisk: typosquat.isRisky ? 1.0 : 0.0,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tool handlers\n// ---------------------------------------------------------------------------\n\nasync function handleScan(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\". ` +\n 'Supported: package.json, requirements.txt, pyproject.toml, Pipfile.',\n );\n }\n\n // Build import graph for blast radius\n const importGraph = await buildImportGraph(dir);\n\n // Collect trust reports for all direct dependencies\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const nodeEcosystem = node.registry === 'pypi' ? 'pypi' as const : 'npm' as const;\n const report = await buildTrustReport(node.name, node.version, blastRadius, nodeEcosystem);\n reports.push(report);\n }\n\n // Overall score: weighted average\n const overallScore =\n reports.length > 0\n ? Math.round(\n reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length,\n )\n : 0;\n\n // Summary\n const zombieCount = reports.filter((r) => r.isZombie).length;\n const criticalCount = reports.filter((r) => r.trustScore < 50).length;\n const summary =\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${zombieCount} zombie(s) detected. ` +\n `${criticalCount} package(s) below trust threshold.`;\n\n const scanResult: ScanResult = {\n tree,\n reports,\n overallScore,\n summary,\n };\n\n // Serialize (handles Map objects)\n const serialized = jsonReporter.report(scanResult);\n return successResponse(serialized);\n}\n\nasync function handleTrustScore(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const version = String(args?.version ?? 'latest');\n const ecosystem = (args?.ecosystem === 'pypi' ? 'pypi' : 'npm') as 'npm' | 'pypi';\n const report = await buildTrustReport(packageName, version, 0, ecosystem);\n return successResponse(report);\n}\n\nasync function handleBlastRadius(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n const dir = validateDir(args?.dir as string | undefined);\n const result = await blastRadiusCalc.calculate(packageName, dir);\n return successResponse(result);\n}\n\nasync function handleZombies(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\".`,\n );\n }\n\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const zombies: Array<{\n package: string;\n version: string;\n severity: string;\n reason: string;\n lastActivity: string | null;\n }> = [];\n\n for (const node of directNodes) {\n const nodeEcosystem = node.registry === 'pypi' ? 'pypi' as const : 'npm' as const;\n const results = await orchestrator.collectAll(node.name, node.version, nodeEcosystem);\n const zombie = zombieDetector.detect(\n results.registry.data,\n results.github.data,\n );\n\n if (zombie.isZombie) {\n zombies.push({\n package: node.name,\n version: node.version,\n severity: zombie.severity,\n reason: zombie.reason,\n lastActivity: zombie.lastActivity?.toISOString() ?? null,\n });\n }\n }\n\n if (zombies.length === 0) {\n return successResponse({\n message: 'No zombie dependencies detected.',\n zombies: [],\n });\n }\n\n return successResponse({\n message: `Found ${zombies.length} zombie dependency(ies).`,\n zombies,\n });\n}\n\nasync function handleSuggestMigration(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const suggestions = migrationAdvisor.suggest(packageName, 'manual query');\n\n if (suggestions.length === 0) {\n return successResponse({\n package: packageName,\n message: `No migration suggestions found for \"${packageName}\". ` +\n 'This package may not have known alternatives in our database.',\n suggestions: [],\n });\n }\n\n return successResponse({\n package: packageName,\n suggestions,\n });\n}\n\nasync function handleTyposquatCheck(args: Record<string, unknown> | undefined) {\n const packageName = validatePackageName(String(args?.package ?? ''));\n\n const result = typosquatDetector.check(packageName);\n return successResponse({\n package: packageName,\n ...result,\n });\n}\n\nasync function handleCompare(args: Record<string, unknown> | undefined) {\n const packageA = validatePackageName(String(args?.packageA ?? ''));\n const packageB = validatePackageName(String(args?.packageB ?? ''));\n const ecosystem = (args?.ecosystem === 'pypi' ? 'pypi' : 'npm') as 'npm' | 'pypi';\n\n const [reportA, reportB] = await Promise.all([\n buildTrustReport(packageA, 'latest', 0, ecosystem),\n buildTrustReport(packageB, 'latest', 0, ecosystem),\n ]);\n\n return successResponse({\n comparison: {\n packageA: reportA,\n packageB: reportB,\n winner:\n reportA.trustScore > reportB.trustScore\n ? packageA\n : reportA.trustScore < reportB.trustScore\n ? packageB\n : 'tie',\n scoreDifference: Math.abs(reportA.trustScore - reportB.trustScore),\n },\n });\n}\n\nasync function handleReport(args: Record<string, unknown> | undefined) {\n const dir = validateDir(args?.dir as string | undefined);\n const output = args?.output ? validateOutputPath(String(args.output), dir) : null;\n\n const tree = await parseProject(dir);\n if (!tree) {\n return errorResponse(\n `No supported manifest file found in \"${dir}\".`,\n );\n }\n\n // Build import graph for blast radius\n const importGraph = await buildImportGraph(dir);\n\n // Collect trust reports for direct dependencies\n const directNodes = Array.from(tree.nodes.values()).filter((n) => n.isDirect);\n const reports: TrustReport[] = [];\n\n for (const node of directNodes) {\n const blastRadius = getBlastRadius(node.name, importGraph);\n const nodeEcosystem = node.registry === 'pypi' ? 'pypi' as const : 'npm' as const;\n const report = await buildTrustReport(node.name, node.version, blastRadius, nodeEcosystem);\n reports.push(report);\n }\n\n const overallScore =\n reports.length > 0\n ? Math.round(\n reports.reduce((sum, r) => sum + r.trustScore, 0) / reports.length,\n )\n : 0;\n\n const zombieCount = reports.filter((r) => r.isZombie).length;\n const criticalCount = reports.filter((r) => r.trustScore < 50).length;\n const summary =\n `Scanned ${reports.length} direct dependencies. ` +\n `Overall trust score: ${overallScore}/100. ` +\n `${zombieCount} zombie(s) detected. ` +\n `${criticalCount} package(s) below trust threshold.`;\n\n const scanResult: ScanResult = {\n tree,\n reports,\n overallScore,\n summary,\n };\n\n const jsonContent = jsonReporter.report(scanResult);\n\n if (output) {\n const { writeFile } = await import('node:fs/promises');\n await writeFile(output, jsonContent, 'utf-8');\n return successResponse({\n message: `Report written to ${output}`,\n path: output,\n });\n }\n\n return successResponse(jsonContent);\n}\n","import type { ScanResult } from \"../parsers/schema.js\";\n\n// ---------------------------------------------------------------------------\n// JsonReporter\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a ScanResult as formatted JSON.\n *\n * The output is a self-contained JSON string suitable for piping into jq,\n * saving to a file, or passing to downstream tooling.\n */\nexport class JsonReporter {\n /**\n * Convert the scan result into a pretty-printed JSON string.\n *\n * Map objects (e.g. DependencyTree.nodes) are converted to plain\n * objects so that they survive JSON serialization.\n */\n report(result: ScanResult): string {\n return JSON.stringify(this.toSerializable(result), null, 2);\n }\n\n // -------------------------------------------------------------------------\n // Internals\n // -------------------------------------------------------------------------\n\n /**\n * Walk the value recursively and convert Map instances into plain objects\n * so JSON.stringify can handle them.\n */\n private toSerializable(value: unknown): unknown {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (value instanceof Map) {\n const obj: Record<string, unknown> = {};\n for (const [k, v] of value.entries()) {\n obj[String(k)] = this.toSerializable(v);\n }\n return obj;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.toSerializable(item));\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = this.toSerializable(v);\n }\n return result;\n }\n\n return value;\n }\n}\n"],"mappings":"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","names":["server","__filename","__dirname"]}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ NpmParser
4
+ } from "./chunk-7DST6SNA.js";
5
+ import "./chunk-UMB5MJHL.js";
6
+ export {
7
+ NpmParser
8
+ };
9
+ //# sourceMappingURL=npm-UB54H37N.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ CollectorOrchestrator
4
+ } from "./chunk-32B3QIPY.js";
5
+ export {
6
+ CollectorOrchestrator
7
+ };
8
+ //# sourceMappingURL=orchestrator-VOOYKDPT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PythonParser
4
+ } from "./chunk-HX6MGNBD.js";
5
+ import "./chunk-UMB5MJHL.js";
6
+ export {
7
+ PythonParser
8
+ };
9
+ //# sourceMappingURL=python-U4G2GK4J.js.map