komodo-cli 2.2.0 → 2.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.
|
@@ -35,9 +35,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
35
35
|
));
|
|
36
36
|
|
|
37
37
|
// ../core/dist/index.js
|
|
38
|
-
import { randomUUID as
|
|
38
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
39
39
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
40
|
-
import { existsSync as
|
|
40
|
+
import { existsSync as existsSync4 } from "fs";
|
|
41
41
|
import { resolve } from "path";
|
|
42
42
|
import { execSync } from "child_process";
|
|
43
43
|
import os from "os";
|
|
@@ -52,6 +52,11 @@ import { randomUUID } from "crypto";
|
|
|
52
52
|
import { mkdir as mkdir2, writeFile as writeFile3, readFile as readFile3 } from "fs/promises";
|
|
53
53
|
import { existsSync as existsSync2 } from "fs";
|
|
54
54
|
import { join as join3 } from "path";
|
|
55
|
+
import { execa as execa3 } from "execa";
|
|
56
|
+
import { existsSync as existsSync3 } from "fs";
|
|
57
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
58
|
+
import { join as join4 } from "path";
|
|
59
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
55
60
|
|
|
56
61
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
57
62
|
var external_exports = {};
|
|
@@ -4095,10 +4100,12 @@ var coerce = {
|
|
|
4095
4100
|
var NEVER = INVALID;
|
|
4096
4101
|
|
|
4097
4102
|
// ../core/dist/index.js
|
|
4098
|
-
import { execa as
|
|
4099
|
-
import { existsSync as
|
|
4100
|
-
import {
|
|
4101
|
-
import {
|
|
4103
|
+
import { execa as execa4 } from "execa";
|
|
4104
|
+
import { existsSync as existsSync5 } from "fs";
|
|
4105
|
+
import { join as join6 } from "path";
|
|
4106
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4107
|
+
import { execa as execa5 } from "execa";
|
|
4108
|
+
import { execa as execa6 } from "execa";
|
|
4102
4109
|
function detectOs() {
|
|
4103
4110
|
const platform = os.platform();
|
|
4104
4111
|
if (platform === "darwin") return "darwin";
|
|
@@ -4938,6 +4945,16 @@ async function listSnapshots(basePath, environmentId) {
|
|
|
4938
4945
|
}
|
|
4939
4946
|
return state.snapshots;
|
|
4940
4947
|
}
|
|
4948
|
+
async function getLatestSnapshot(basePath, environmentId) {
|
|
4949
|
+
const snapshots = await listSnapshots(basePath, environmentId);
|
|
4950
|
+
if (snapshots.length === 0) {
|
|
4951
|
+
return void 0;
|
|
4952
|
+
}
|
|
4953
|
+
snapshots.sort(
|
|
4954
|
+
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
4955
|
+
);
|
|
4956
|
+
return snapshots[0];
|
|
4957
|
+
}
|
|
4941
4958
|
var DEFAULT_MODEL = "llama-3.3-70b";
|
|
4942
4959
|
var DEFAULT_MAX_TOKENS = 2048;
|
|
4943
4960
|
var DEFAULT_TEMPERATURE = 0.2;
|
|
@@ -5320,6 +5337,450 @@ Only include the JSON block if you're recommending specific package changes.`;
|
|
|
5320
5337
|
return result;
|
|
5321
5338
|
}
|
|
5322
5339
|
};
|
|
5340
|
+
var KNOWN_CONFLICTS = [
|
|
5341
|
+
{
|
|
5342
|
+
packages: ["tensorflow", "torch"],
|
|
5343
|
+
reason: "Both use significant GPU memory and may conflict",
|
|
5344
|
+
suggestion: "Use separate environments for TensorFlow and PyTorch projects"
|
|
5345
|
+
},
|
|
5346
|
+
{
|
|
5347
|
+
packages: ["numpy", "jax"],
|
|
5348
|
+
reason: "JAX includes its own numpy-like API that may conflict",
|
|
5349
|
+
suggestion: "Use jax.numpy instead of importing both"
|
|
5350
|
+
},
|
|
5351
|
+
{
|
|
5352
|
+
packages: ["PIL", "pillow"],
|
|
5353
|
+
reason: "PIL and Pillow are the same library, cannot coexist",
|
|
5354
|
+
suggestion: "Remove PIL and use Pillow instead"
|
|
5355
|
+
},
|
|
5356
|
+
{
|
|
5357
|
+
packages: ["opencv-python", "opencv-contrib-python"],
|
|
5358
|
+
reason: "These packages overlap and can cause import conflicts",
|
|
5359
|
+
suggestion: "Use opencv-contrib-python only (includes all opencv-python features)"
|
|
5360
|
+
},
|
|
5361
|
+
{
|
|
5362
|
+
packages: ["tensorflow", "tensorflow-gpu"],
|
|
5363
|
+
reason: "TensorFlow 2.x already includes GPU support",
|
|
5364
|
+
suggestion: "Remove tensorflow-gpu and use tensorflow only"
|
|
5365
|
+
},
|
|
5366
|
+
{
|
|
5367
|
+
packages: ["requests", "urllib3"],
|
|
5368
|
+
reason: "Requests includes urllib3, may cause version conflicts",
|
|
5369
|
+
suggestion: "Let requests manage its own urllib3 dependency"
|
|
5370
|
+
},
|
|
5371
|
+
{
|
|
5372
|
+
packages: ["pandas", "numpy"],
|
|
5373
|
+
reason: "Version mismatch between pandas and numpy can cause errors",
|
|
5374
|
+
suggestion: "Ensure compatible versions or let pandas manage numpy version"
|
|
5375
|
+
},
|
|
5376
|
+
{
|
|
5377
|
+
packages: ["jupyter", "ipython"],
|
|
5378
|
+
reason: "Conflicting IPython versions",
|
|
5379
|
+
suggestion: "Use jupyter only, which includes IPython"
|
|
5380
|
+
},
|
|
5381
|
+
{
|
|
5382
|
+
packages: ["scikit-learn", "sklearn"],
|
|
5383
|
+
reason: "sklearn is deprecated, scikit-learn is the correct package",
|
|
5384
|
+
suggestion: "Remove sklearn and use scikit-learn only"
|
|
5385
|
+
},
|
|
5386
|
+
{
|
|
5387
|
+
packages: ["pyyaml", "yaml"],
|
|
5388
|
+
reason: "Different YAML parsers can conflict",
|
|
5389
|
+
suggestion: "Use pyyaml only"
|
|
5390
|
+
},
|
|
5391
|
+
// Node.js conflicts
|
|
5392
|
+
{
|
|
5393
|
+
packages: ["moment", "dayjs"],
|
|
5394
|
+
reason: "Both date libraries serve same purpose, adds bundle bloat",
|
|
5395
|
+
suggestion: "Choose one date library (dayjs is lighter)"
|
|
5396
|
+
},
|
|
5397
|
+
{
|
|
5398
|
+
packages: ["axios", "node-fetch"],
|
|
5399
|
+
reason: "Both HTTP clients serve same purpose",
|
|
5400
|
+
suggestion: "Choose one HTTP client"
|
|
5401
|
+
},
|
|
5402
|
+
{
|
|
5403
|
+
packages: ["lodash", "underscore"],
|
|
5404
|
+
reason: "Both utility libraries serve same purpose",
|
|
5405
|
+
suggestion: "Choose one utility library (lodash is more feature-complete)"
|
|
5406
|
+
},
|
|
5407
|
+
{
|
|
5408
|
+
packages: ["express", "koa"],
|
|
5409
|
+
reason: "Both web frameworks, cannot be used together",
|
|
5410
|
+
suggestion: "Choose one web framework"
|
|
5411
|
+
},
|
|
5412
|
+
{
|
|
5413
|
+
packages: ["react", "vue"],
|
|
5414
|
+
reason: "Cannot use multiple frontend frameworks simultaneously",
|
|
5415
|
+
suggestion: "Choose one frontend framework"
|
|
5416
|
+
},
|
|
5417
|
+
{
|
|
5418
|
+
packages: ["webpack", "vite"],
|
|
5419
|
+
reason: "Both bundlers serve same purpose",
|
|
5420
|
+
suggestion: "Choose one bundler (vite is faster for development)"
|
|
5421
|
+
},
|
|
5422
|
+
{
|
|
5423
|
+
packages: ["eslint", "tslint"],
|
|
5424
|
+
reason: "TSLint is deprecated in favor of ESLint",
|
|
5425
|
+
suggestion: "Remove tslint and use eslint with @typescript-eslint"
|
|
5426
|
+
},
|
|
5427
|
+
{
|
|
5428
|
+
packages: ["node-sass", "sass"],
|
|
5429
|
+
reason: "node-sass is deprecated, sass is the new implementation",
|
|
5430
|
+
suggestion: "Remove node-sass and use sass (dart-sass)"
|
|
5431
|
+
}
|
|
5432
|
+
];
|
|
5433
|
+
async function getInstalledPackages(path, runtime) {
|
|
5434
|
+
if (runtime === "python") {
|
|
5435
|
+
return getPythonPackages(path);
|
|
5436
|
+
} else {
|
|
5437
|
+
return getNodePackages(path);
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
5440
|
+
async function getPythonPackages(path) {
|
|
5441
|
+
const venvPath = join4(path, ".venv");
|
|
5442
|
+
if (!existsSync3(venvPath)) {
|
|
5443
|
+
return [];
|
|
5444
|
+
}
|
|
5445
|
+
try {
|
|
5446
|
+
const pipPath = join4(venvPath, "bin", "pip");
|
|
5447
|
+
const result = await execa3(pipPath, ["list", "--format=json"], { cwd: path });
|
|
5448
|
+
const packages = JSON.parse(result.stdout);
|
|
5449
|
+
return packages.map((pkg) => ({
|
|
5450
|
+
name: pkg.name,
|
|
5451
|
+
version: pkg.version
|
|
5452
|
+
}));
|
|
5453
|
+
} catch {
|
|
5454
|
+
try {
|
|
5455
|
+
const result = await execa3(
|
|
5456
|
+
"uv",
|
|
5457
|
+
["pip", "list", "--format=json", "--python", join4(venvPath, "bin", "python")],
|
|
5458
|
+
{ cwd: path }
|
|
5459
|
+
);
|
|
5460
|
+
const packages = JSON.parse(result.stdout);
|
|
5461
|
+
return packages.map((pkg) => ({
|
|
5462
|
+
name: pkg.name,
|
|
5463
|
+
version: pkg.version
|
|
5464
|
+
}));
|
|
5465
|
+
} catch {
|
|
5466
|
+
return [];
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
async function getNodePackages(path) {
|
|
5471
|
+
const packageJsonPath = join4(path, "package.json");
|
|
5472
|
+
if (!existsSync3(packageJsonPath)) {
|
|
5473
|
+
return [];
|
|
5474
|
+
}
|
|
5475
|
+
try {
|
|
5476
|
+
const content = await readFile4(packageJsonPath, "utf-8");
|
|
5477
|
+
const packageJson = JSON.parse(content);
|
|
5478
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
5479
|
+
const packages = [];
|
|
5480
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
5481
|
+
packages.push({
|
|
5482
|
+
name,
|
|
5483
|
+
version: String(version).replace(/^[\^~]/, "")
|
|
5484
|
+
});
|
|
5485
|
+
}
|
|
5486
|
+
return packages;
|
|
5487
|
+
} catch {
|
|
5488
|
+
return [];
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
function detectConflicts(packages) {
|
|
5492
|
+
const conflicts = [];
|
|
5493
|
+
const packageNames = packages.map((p) => p.name.toLowerCase());
|
|
5494
|
+
for (const known of KNOWN_CONFLICTS) {
|
|
5495
|
+
const [pkg1, pkg2] = known.packages;
|
|
5496
|
+
if (packageNames.includes(pkg1) && packageNames.includes(pkg2)) {
|
|
5497
|
+
conflicts.push({
|
|
5498
|
+
package1: pkg1,
|
|
5499
|
+
package2: pkg2,
|
|
5500
|
+
reason: known.reason,
|
|
5501
|
+
severity: "warning",
|
|
5502
|
+
suggestion: known.suggestion
|
|
5503
|
+
});
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
return conflicts;
|
|
5507
|
+
}
|
|
5508
|
+
function analyzeHealth(packages, conflicts) {
|
|
5509
|
+
const issues = [];
|
|
5510
|
+
let score = 100;
|
|
5511
|
+
for (const conflict of conflicts) {
|
|
5512
|
+
issues.push({
|
|
5513
|
+
type: "conflict",
|
|
5514
|
+
severity: conflict.severity,
|
|
5515
|
+
title: `${conflict.package1} and ${conflict.package2} may conflict`,
|
|
5516
|
+
description: conflict.reason,
|
|
5517
|
+
fix: conflict.suggestion,
|
|
5518
|
+
autoFixable: false
|
|
5519
|
+
});
|
|
5520
|
+
score -= conflict.severity === "error" ? 20 : 10;
|
|
5521
|
+
}
|
|
5522
|
+
const commonOutdated = ["requests", "axios", "lodash"];
|
|
5523
|
+
for (const pkg of packages) {
|
|
5524
|
+
if (commonOutdated.includes(pkg.name.toLowerCase())) {
|
|
5525
|
+
}
|
|
5526
|
+
}
|
|
5527
|
+
return {
|
|
5528
|
+
healthy: issues.filter((i) => i.severity === "error").length === 0,
|
|
5529
|
+
issues,
|
|
5530
|
+
score: Math.max(0, score)
|
|
5531
|
+
};
|
|
5532
|
+
}
|
|
5533
|
+
async function detectRealConflicts(path, runtime) {
|
|
5534
|
+
const conflicts = [];
|
|
5535
|
+
if (runtime === "python") {
|
|
5536
|
+
const venvPath = join4(path, ".venv");
|
|
5537
|
+
if (!existsSync3(venvPath)) {
|
|
5538
|
+
return conflicts;
|
|
5539
|
+
}
|
|
5540
|
+
try {
|
|
5541
|
+
const pipPath = join4(venvPath, "bin", "pip");
|
|
5542
|
+
const result = await execa3(pipPath, ["check"], { cwd: path, reject: false });
|
|
5543
|
+
if (result.exitCode !== 0 && result.stdout) {
|
|
5544
|
+
const lines = result.stdout.split("\n");
|
|
5545
|
+
for (const line of lines) {
|
|
5546
|
+
const match = line.match(/(\S+)\s+[\d.]+\s+requires\s+(\S+)\s*([>=<]+[\d.]+)?.*but.*you.*have\s+(\S+)\s+([\d.]+)/);
|
|
5547
|
+
if (match) {
|
|
5548
|
+
const [, pkg1, pkg2, requiredVersion, , actualVersion] = match;
|
|
5549
|
+
if (pkg1 && pkg2) {
|
|
5550
|
+
conflicts.push({
|
|
5551
|
+
package1: pkg1,
|
|
5552
|
+
package2: pkg2,
|
|
5553
|
+
reason: `${pkg1} requires ${pkg2}${requiredVersion || ""}, but ${actualVersion || "unknown version"} is installed`,
|
|
5554
|
+
severity: "error",
|
|
5555
|
+
suggestion: `Upgrade ${pkg2} to meet ${pkg1}'s requirements`
|
|
5556
|
+
});
|
|
5557
|
+
}
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
} catch {
|
|
5562
|
+
}
|
|
5563
|
+
} else {
|
|
5564
|
+
try {
|
|
5565
|
+
const result = await execa3("npm", ["ls", "--json"], {
|
|
5566
|
+
cwd: path,
|
|
5567
|
+
reject: false
|
|
5568
|
+
});
|
|
5569
|
+
if (result.stdout) {
|
|
5570
|
+
try {
|
|
5571
|
+
const output = JSON.parse(result.stdout);
|
|
5572
|
+
if (output.problems && Array.isArray(output.problems)) {
|
|
5573
|
+
for (const problem of output.problems) {
|
|
5574
|
+
const problemStr = String(problem);
|
|
5575
|
+
const match = problemStr.match(/(\S+)@[\d.]+\s+requires\s+(\S+)@([^\s]+)/);
|
|
5576
|
+
if (match) {
|
|
5577
|
+
const [, pkg1, pkg2, version] = match;
|
|
5578
|
+
if (pkg1 && pkg2) {
|
|
5579
|
+
conflicts.push({
|
|
5580
|
+
package1: pkg1,
|
|
5581
|
+
package2: pkg2,
|
|
5582
|
+
reason: `${pkg1} requires ${pkg2}@${version || "unknown"}`,
|
|
5583
|
+
severity: "warning",
|
|
5584
|
+
suggestion: `Install compatible version of ${pkg2}`
|
|
5585
|
+
});
|
|
5586
|
+
}
|
|
5587
|
+
} else {
|
|
5588
|
+
conflicts.push({
|
|
5589
|
+
package1: "unknown",
|
|
5590
|
+
package2: "unknown",
|
|
5591
|
+
reason: problemStr,
|
|
5592
|
+
severity: "warning"
|
|
5593
|
+
});
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
} catch {
|
|
5598
|
+
}
|
|
5599
|
+
}
|
|
5600
|
+
} catch {
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
return conflicts;
|
|
5604
|
+
}
|
|
5605
|
+
async function checkPreInstallConflicts(path, runtime, newPackages) {
|
|
5606
|
+
const conflicts = [];
|
|
5607
|
+
const suggestions = [];
|
|
5608
|
+
const installed = await getInstalledPackages(path, runtime);
|
|
5609
|
+
const installedNames = installed.map((p) => p.name.toLowerCase());
|
|
5610
|
+
for (const newPkg of newPackages) {
|
|
5611
|
+
for (const known of KNOWN_CONFLICTS) {
|
|
5612
|
+
const [pkg1, pkg2] = known.packages;
|
|
5613
|
+
const newPkgName = newPkg.name.toLowerCase();
|
|
5614
|
+
if (newPkgName === pkg1 && installedNames.includes(pkg2)) {
|
|
5615
|
+
conflicts.push({
|
|
5616
|
+
package1: pkg1,
|
|
5617
|
+
package2: pkg2,
|
|
5618
|
+
reason: known.reason,
|
|
5619
|
+
severity: "warning",
|
|
5620
|
+
suggestion: known.suggestion
|
|
5621
|
+
});
|
|
5622
|
+
} else if (newPkgName === pkg2 && installedNames.includes(pkg1)) {
|
|
5623
|
+
conflicts.push({
|
|
5624
|
+
package1: pkg1,
|
|
5625
|
+
package2: pkg2,
|
|
5626
|
+
reason: known.reason,
|
|
5627
|
+
severity: "warning",
|
|
5628
|
+
suggestion: known.suggestion
|
|
5629
|
+
});
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
const newPackageNames = newPackages.map((p) => p.name.toLowerCase());
|
|
5634
|
+
for (const known of KNOWN_CONFLICTS) {
|
|
5635
|
+
const [pkg1, pkg2] = known.packages;
|
|
5636
|
+
if (newPackageNames.includes(pkg1) && newPackageNames.includes(pkg2)) {
|
|
5637
|
+
conflicts.push({
|
|
5638
|
+
package1: pkg1,
|
|
5639
|
+
package2: pkg2,
|
|
5640
|
+
reason: known.reason,
|
|
5641
|
+
severity: "warning",
|
|
5642
|
+
suggestion: known.suggestion
|
|
5643
|
+
});
|
|
5644
|
+
}
|
|
5645
|
+
}
|
|
5646
|
+
if (runtime === "python") {
|
|
5647
|
+
const venvPath = join4(path, ".venv");
|
|
5648
|
+
if (existsSync3(venvPath)) {
|
|
5649
|
+
try {
|
|
5650
|
+
const pipPath = join4(venvPath, "bin", "pip");
|
|
5651
|
+
const packageSpecs = newPackages.map(
|
|
5652
|
+
(p) => p.version ? `${p.name}==${p.version}` : p.name
|
|
5653
|
+
);
|
|
5654
|
+
const result = await execa3(
|
|
5655
|
+
pipPath,
|
|
5656
|
+
["install", "--dry-run", ...packageSpecs],
|
|
5657
|
+
{ cwd: path, reject: false }
|
|
5658
|
+
);
|
|
5659
|
+
if (result.exitCode !== 0 && result.stderr) {
|
|
5660
|
+
if (result.stderr.includes("ResolutionImpossible") || result.stderr.includes("incompatible")) {
|
|
5661
|
+
conflicts.push({
|
|
5662
|
+
package1: "new packages",
|
|
5663
|
+
package2: "existing packages",
|
|
5664
|
+
reason: "Package versions are incompatible",
|
|
5665
|
+
severity: "error",
|
|
5666
|
+
suggestion: "Review package version requirements"
|
|
5667
|
+
});
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
} catch {
|
|
5671
|
+
}
|
|
5672
|
+
}
|
|
5673
|
+
} else {
|
|
5674
|
+
try {
|
|
5675
|
+
const packageSpecs = newPackages.map(
|
|
5676
|
+
(p) => p.version ? `${p.name}@${p.version}` : p.name
|
|
5677
|
+
);
|
|
5678
|
+
const result = await execa3(
|
|
5679
|
+
"npm",
|
|
5680
|
+
["install", "--dry-run", ...packageSpecs],
|
|
5681
|
+
{ cwd: path, reject: false }
|
|
5682
|
+
);
|
|
5683
|
+
if (result.exitCode !== 0 && result.stderr) {
|
|
5684
|
+
if (result.stderr.includes("ERESOLVE") || result.stderr.includes("conflict")) {
|
|
5685
|
+
conflicts.push({
|
|
5686
|
+
package1: "new packages",
|
|
5687
|
+
package2: "existing packages",
|
|
5688
|
+
reason: "Dependency resolution conflict",
|
|
5689
|
+
severity: "error",
|
|
5690
|
+
suggestion: "Review peer dependencies"
|
|
5691
|
+
});
|
|
5692
|
+
}
|
|
5693
|
+
}
|
|
5694
|
+
} catch {
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
for (const conflict of conflicts) {
|
|
5698
|
+
if (conflict.package1 !== "unknown" && conflict.package2 !== "unknown") {
|
|
5699
|
+
suggestions.push({
|
|
5700
|
+
conflictId: randomUUID2(),
|
|
5701
|
+
strategy: "separate-env",
|
|
5702
|
+
packages: [],
|
|
5703
|
+
description: `Create separate environment for ${conflict.package1} and ${conflict.package2}`,
|
|
5704
|
+
friendlyDescription: "Use different projects for these packages",
|
|
5705
|
+
automated: false,
|
|
5706
|
+
risk: "safe"
|
|
5707
|
+
});
|
|
5708
|
+
}
|
|
5709
|
+
}
|
|
5710
|
+
return {
|
|
5711
|
+
willConflict: conflicts.length > 0,
|
|
5712
|
+
conflicts,
|
|
5713
|
+
suggestions
|
|
5714
|
+
};
|
|
5715
|
+
}
|
|
5716
|
+
async function resolveConflicts(path, runtime, conflicts, onProgress) {
|
|
5717
|
+
const resolved = [];
|
|
5718
|
+
const remaining = [];
|
|
5719
|
+
onProgress?.("Analyzing conflicts...");
|
|
5720
|
+
for (const conflict of conflicts) {
|
|
5721
|
+
if (conflict.package1 === "PIL" && conflict.package2 === "pillow") {
|
|
5722
|
+
onProgress?.("Removing deprecated PIL package...");
|
|
5723
|
+
try {
|
|
5724
|
+
if (runtime === "python") {
|
|
5725
|
+
const pipPath = join4(path, ".venv", "bin", "pip");
|
|
5726
|
+
await execa3(pipPath, ["uninstall", "-y", "PIL"], { cwd: path });
|
|
5727
|
+
resolved.push(`Removed ${conflict.package1}`);
|
|
5728
|
+
}
|
|
5729
|
+
} catch {
|
|
5730
|
+
remaining.push(conflict);
|
|
5731
|
+
}
|
|
5732
|
+
} else if (conflict.package1 === "tensorflow" && conflict.package2 === "tensorflow-gpu") {
|
|
5733
|
+
onProgress?.("Removing deprecated tensorflow-gpu...");
|
|
5734
|
+
try {
|
|
5735
|
+
if (runtime === "python") {
|
|
5736
|
+
const pipPath = join4(path, ".venv", "bin", "pip");
|
|
5737
|
+
await execa3(pipPath, ["uninstall", "-y", "tensorflow-gpu"], { cwd: path });
|
|
5738
|
+
resolved.push(`Removed ${conflict.package2}`);
|
|
5739
|
+
}
|
|
5740
|
+
} catch {
|
|
5741
|
+
remaining.push(conflict);
|
|
5742
|
+
}
|
|
5743
|
+
} else if (conflict.package1 === "sklearn" && conflict.package2 === "scikit-learn") {
|
|
5744
|
+
onProgress?.("Removing deprecated sklearn...");
|
|
5745
|
+
try {
|
|
5746
|
+
if (runtime === "python") {
|
|
5747
|
+
const pipPath = join4(path, ".venv", "bin", "pip");
|
|
5748
|
+
await execa3(pipPath, ["uninstall", "-y", "sklearn"], { cwd: path });
|
|
5749
|
+
resolved.push(`Removed ${conflict.package1}`);
|
|
5750
|
+
}
|
|
5751
|
+
} catch {
|
|
5752
|
+
remaining.push(conflict);
|
|
5753
|
+
}
|
|
5754
|
+
} else if (conflict.package1 === "tslint" && conflict.package2 === "eslint") {
|
|
5755
|
+
onProgress?.("Removing deprecated tslint...");
|
|
5756
|
+
try {
|
|
5757
|
+
if (runtime === "node") {
|
|
5758
|
+
await execa3("npm", ["uninstall", "tslint"], { cwd: path });
|
|
5759
|
+
resolved.push(`Removed ${conflict.package1}`);
|
|
5760
|
+
}
|
|
5761
|
+
} catch {
|
|
5762
|
+
remaining.push(conflict);
|
|
5763
|
+
}
|
|
5764
|
+
} else if (conflict.package1 === "node-sass" && conflict.package2 === "sass") {
|
|
5765
|
+
onProgress?.("Removing deprecated node-sass...");
|
|
5766
|
+
try {
|
|
5767
|
+
if (runtime === "node") {
|
|
5768
|
+
await execa3("npm", ["uninstall", "node-sass"], { cwd: path });
|
|
5769
|
+
resolved.push(`Removed ${conflict.package1}`);
|
|
5770
|
+
}
|
|
5771
|
+
} catch {
|
|
5772
|
+
remaining.push(conflict);
|
|
5773
|
+
}
|
|
5774
|
+
} else {
|
|
5775
|
+
remaining.push(conflict);
|
|
5776
|
+
}
|
|
5777
|
+
}
|
|
5778
|
+
return {
|
|
5779
|
+
success: remaining.length === 0,
|
|
5780
|
+
resolved,
|
|
5781
|
+
remaining
|
|
5782
|
+
};
|
|
5783
|
+
}
|
|
5323
5784
|
var Komodo = class {
|
|
5324
5785
|
hardware;
|
|
5325
5786
|
ai = null;
|
|
@@ -5417,6 +5878,31 @@ var Komodo = class {
|
|
|
5417
5878
|
aiAnalysis
|
|
5418
5879
|
};
|
|
5419
5880
|
}
|
|
5881
|
+
if (resolution.packages.length > 0 && existsSync4(targetPath)) {
|
|
5882
|
+
onProgress?.("Checking for potential conflicts...");
|
|
5883
|
+
try {
|
|
5884
|
+
const preCheck = await checkPreInstallConflicts(
|
|
5885
|
+
targetPath,
|
|
5886
|
+
resolution.runtime,
|
|
5887
|
+
resolution.packages
|
|
5888
|
+
);
|
|
5889
|
+
if (preCheck.willConflict) {
|
|
5890
|
+
for (const conflict of preCheck.conflicts) {
|
|
5891
|
+
const warningMsg = `Warning: ${conflict.package1} and ${conflict.package2} may conflict - ${conflict.reason}`;
|
|
5892
|
+
onWarning?.(warningMsg);
|
|
5893
|
+
resolution.warnings.push(warningMsg);
|
|
5894
|
+
}
|
|
5895
|
+
const criticalConflicts = preCheck.conflicts.filter((c) => c.severity === "error");
|
|
5896
|
+
if (criticalConflicts.length > 0) {
|
|
5897
|
+
resolution.explanations.push(
|
|
5898
|
+
`Found ${criticalConflicts.length} potential conflict(s). Installation may fail or cause issues.`
|
|
5899
|
+
);
|
|
5900
|
+
}
|
|
5901
|
+
}
|
|
5902
|
+
} catch (error) {
|
|
5903
|
+
onWarning?.("Could not check for conflicts, proceeding anyway");
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5420
5906
|
if (dryRun) {
|
|
5421
5907
|
return {
|
|
5422
5908
|
success: true,
|
|
@@ -5425,10 +5911,10 @@ var Komodo = class {
|
|
|
5425
5911
|
aiAnalysis
|
|
5426
5912
|
};
|
|
5427
5913
|
}
|
|
5428
|
-
if (!
|
|
5914
|
+
if (!existsSync4(targetPath)) {
|
|
5429
5915
|
await mkdir3(targetPath, { recursive: true });
|
|
5430
5916
|
}
|
|
5431
|
-
const environmentId =
|
|
5917
|
+
const environmentId = randomUUID3();
|
|
5432
5918
|
const environmentName = aiAnalysis?.goal ?? intent.slice(0, 50);
|
|
5433
5919
|
const environment = {
|
|
5434
5920
|
id: environmentId,
|
|
@@ -5591,118 +6077,6 @@ var GpuTypeSchema = external_exports.enum(["nvidia", "amd", "apple-silicon", "in
|
|
|
5591
6077
|
var OsSchema = external_exports.enum(["darwin", "linux", "windows"]);
|
|
5592
6078
|
var ArchSchema = external_exports.enum(["x64", "arm64"]);
|
|
5593
6079
|
var RuntimeSchema = external_exports.enum(["python", "node"]);
|
|
5594
|
-
var KNOWN_CONFLICTS = [
|
|
5595
|
-
{
|
|
5596
|
-
packages: ["tensorflow", "torch"],
|
|
5597
|
-
reason: "Both use significant GPU memory and may conflict",
|
|
5598
|
-
suggestion: "Use separate environments for TensorFlow and PyTorch projects"
|
|
5599
|
-
},
|
|
5600
|
-
{
|
|
5601
|
-
packages: ["numpy", "jax"],
|
|
5602
|
-
reason: "JAX includes its own numpy-like API that may conflict",
|
|
5603
|
-
suggestion: "Use jax.numpy instead of importing both"
|
|
5604
|
-
}
|
|
5605
|
-
];
|
|
5606
|
-
async function getInstalledPackages(path, runtime) {
|
|
5607
|
-
if (runtime === "python") {
|
|
5608
|
-
return getPythonPackages(path);
|
|
5609
|
-
} else {
|
|
5610
|
-
return getNodePackages(path);
|
|
5611
|
-
}
|
|
5612
|
-
}
|
|
5613
|
-
async function getPythonPackages(path) {
|
|
5614
|
-
const venvPath = join5(path, ".venv");
|
|
5615
|
-
if (!existsSync4(venvPath)) {
|
|
5616
|
-
return [];
|
|
5617
|
-
}
|
|
5618
|
-
try {
|
|
5619
|
-
const pipPath = join5(venvPath, "bin", "pip");
|
|
5620
|
-
const result = await execa3(pipPath, ["list", "--format=json"], { cwd: path });
|
|
5621
|
-
const packages = JSON.parse(result.stdout);
|
|
5622
|
-
return packages.map((pkg) => ({
|
|
5623
|
-
name: pkg.name,
|
|
5624
|
-
version: pkg.version
|
|
5625
|
-
}));
|
|
5626
|
-
} catch {
|
|
5627
|
-
try {
|
|
5628
|
-
const result = await execa3(
|
|
5629
|
-
"uv",
|
|
5630
|
-
["pip", "list", "--format=json", "--python", join5(venvPath, "bin", "python")],
|
|
5631
|
-
{ cwd: path }
|
|
5632
|
-
);
|
|
5633
|
-
const packages = JSON.parse(result.stdout);
|
|
5634
|
-
return packages.map((pkg) => ({
|
|
5635
|
-
name: pkg.name,
|
|
5636
|
-
version: pkg.version
|
|
5637
|
-
}));
|
|
5638
|
-
} catch {
|
|
5639
|
-
return [];
|
|
5640
|
-
}
|
|
5641
|
-
}
|
|
5642
|
-
}
|
|
5643
|
-
async function getNodePackages(path) {
|
|
5644
|
-
const packageJsonPath = join5(path, "package.json");
|
|
5645
|
-
if (!existsSync4(packageJsonPath)) {
|
|
5646
|
-
return [];
|
|
5647
|
-
}
|
|
5648
|
-
try {
|
|
5649
|
-
const content = await readFile4(packageJsonPath, "utf-8");
|
|
5650
|
-
const packageJson = JSON.parse(content);
|
|
5651
|
-
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
5652
|
-
const packages = [];
|
|
5653
|
-
for (const [name, version] of Object.entries(deps)) {
|
|
5654
|
-
packages.push({
|
|
5655
|
-
name,
|
|
5656
|
-
version: String(version).replace(/^[\^~]/, "")
|
|
5657
|
-
});
|
|
5658
|
-
}
|
|
5659
|
-
return packages;
|
|
5660
|
-
} catch {
|
|
5661
|
-
return [];
|
|
5662
|
-
}
|
|
5663
|
-
}
|
|
5664
|
-
function detectConflicts(packages) {
|
|
5665
|
-
const conflicts = [];
|
|
5666
|
-
const packageNames = packages.map((p) => p.name.toLowerCase());
|
|
5667
|
-
for (const known of KNOWN_CONFLICTS) {
|
|
5668
|
-
const [pkg1, pkg2] = known.packages;
|
|
5669
|
-
if (packageNames.includes(pkg1) && packageNames.includes(pkg2)) {
|
|
5670
|
-
conflicts.push({
|
|
5671
|
-
package1: pkg1,
|
|
5672
|
-
package2: pkg2,
|
|
5673
|
-
reason: known.reason,
|
|
5674
|
-
severity: "warning",
|
|
5675
|
-
suggestion: known.suggestion
|
|
5676
|
-
});
|
|
5677
|
-
}
|
|
5678
|
-
}
|
|
5679
|
-
return conflicts;
|
|
5680
|
-
}
|
|
5681
|
-
function analyzeHealth(packages, conflicts) {
|
|
5682
|
-
const issues = [];
|
|
5683
|
-
let score = 100;
|
|
5684
|
-
for (const conflict of conflicts) {
|
|
5685
|
-
issues.push({
|
|
5686
|
-
type: "conflict",
|
|
5687
|
-
severity: conflict.severity,
|
|
5688
|
-
title: `${conflict.package1} and ${conflict.package2} may conflict`,
|
|
5689
|
-
description: conflict.reason,
|
|
5690
|
-
fix: conflict.suggestion,
|
|
5691
|
-
autoFixable: false
|
|
5692
|
-
});
|
|
5693
|
-
score -= conflict.severity === "error" ? 20 : 10;
|
|
5694
|
-
}
|
|
5695
|
-
const commonOutdated = ["requests", "axios", "lodash"];
|
|
5696
|
-
for (const pkg of packages) {
|
|
5697
|
-
if (commonOutdated.includes(pkg.name.toLowerCase())) {
|
|
5698
|
-
}
|
|
5699
|
-
}
|
|
5700
|
-
return {
|
|
5701
|
-
healthy: issues.filter((i) => i.severity === "error").length === 0,
|
|
5702
|
-
issues,
|
|
5703
|
-
score: Math.max(0, score)
|
|
5704
|
-
};
|
|
5705
|
-
}
|
|
5706
6080
|
var KNOWN_VULNERABILITIES = {
|
|
5707
6081
|
"torch": [
|
|
5708
6082
|
{ versions: "<1.13.0", severity: "high", description: "Arbitrary code execution via pickle deserialization", fix: ">=2.0.0" }
|
|
@@ -7818,6 +8192,469 @@ function formatInsights(insights, colorize = true) {
|
|
|
7818
8192
|
}
|
|
7819
8193
|
return lines.join("\n");
|
|
7820
8194
|
}
|
|
8195
|
+
async function diagnoseEnvironment(path, runtime, onProgress) {
|
|
8196
|
+
const id = randomUUID4();
|
|
8197
|
+
const problems = [];
|
|
8198
|
+
let detectedRuntime = null;
|
|
8199
|
+
onProgress?.("Scanning environment...");
|
|
8200
|
+
if (!runtime) {
|
|
8201
|
+
if (existsSync5(join6(path, ".venv")) || existsSync5(join6(path, "requirements.txt"))) {
|
|
8202
|
+
detectedRuntime = "python";
|
|
8203
|
+
} else if (existsSync5(join6(path, "node_modules")) || existsSync5(join6(path, "package.json"))) {
|
|
8204
|
+
detectedRuntime = "node";
|
|
8205
|
+
}
|
|
8206
|
+
} else {
|
|
8207
|
+
detectedRuntime = runtime;
|
|
8208
|
+
}
|
|
8209
|
+
if (!detectedRuntime) {
|
|
8210
|
+
problems.push({
|
|
8211
|
+
id: randomUUID4(),
|
|
8212
|
+
severity: "critical",
|
|
8213
|
+
category: "missing-env",
|
|
8214
|
+
title: "No environment detected",
|
|
8215
|
+
friendlyTitle: "No project setup found",
|
|
8216
|
+
description: "Could not detect a Python or Node.js environment",
|
|
8217
|
+
technicalDetail: "No .venv, node_modules, requirements.txt, or package.json found"
|
|
8218
|
+
});
|
|
8219
|
+
return {
|
|
8220
|
+
id,
|
|
8221
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
8222
|
+
runtime: "python",
|
|
8223
|
+
// default fallback
|
|
8224
|
+
status: "missing",
|
|
8225
|
+
problems,
|
|
8226
|
+
repairPlan: [],
|
|
8227
|
+
estimatedTime: "Unknown",
|
|
8228
|
+
confidence: 0
|
|
8229
|
+
};
|
|
8230
|
+
}
|
|
8231
|
+
onProgress?.(`Checking ${detectedRuntime} environment...`);
|
|
8232
|
+
const envExists = await checkEnvironmentExists(path, detectedRuntime, problems);
|
|
8233
|
+
if (envExists) {
|
|
8234
|
+
await checkRuntimeFunctional(path, detectedRuntime, problems, onProgress);
|
|
8235
|
+
}
|
|
8236
|
+
if (envExists) {
|
|
8237
|
+
await checkDependencyConflicts(path, detectedRuntime, problems, onProgress);
|
|
8238
|
+
}
|
|
8239
|
+
if (envExists) {
|
|
8240
|
+
await checkLockfileDrift(path, detectedRuntime, problems, onProgress);
|
|
8241
|
+
}
|
|
8242
|
+
if (envExists) {
|
|
8243
|
+
await checkKnownConflicts(path, detectedRuntime, problems, onProgress);
|
|
8244
|
+
}
|
|
8245
|
+
const hasSnapshot = await checkSnapshotAvailable(path, onProgress);
|
|
8246
|
+
const criticalCount = problems.filter((p) => p.severity === "critical").length;
|
|
8247
|
+
const warningCount = problems.filter((p) => p.severity === "warning").length;
|
|
8248
|
+
let status;
|
|
8249
|
+
if (criticalCount > 0) {
|
|
8250
|
+
status = "broken";
|
|
8251
|
+
} else if (warningCount > 0) {
|
|
8252
|
+
status = "degraded";
|
|
8253
|
+
} else if (problems.length === 0) {
|
|
8254
|
+
status = "healthy";
|
|
8255
|
+
} else {
|
|
8256
|
+
status = "degraded";
|
|
8257
|
+
}
|
|
8258
|
+
const repairPlan = createRepairPlan({
|
|
8259
|
+
id,
|
|
8260
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
8261
|
+
runtime: detectedRuntime,
|
|
8262
|
+
status,
|
|
8263
|
+
problems,
|
|
8264
|
+
repairPlan: [],
|
|
8265
|
+
estimatedTime: "",
|
|
8266
|
+
confidence: 0
|
|
8267
|
+
});
|
|
8268
|
+
const estimatedTime = estimateRepairTime(repairPlan);
|
|
8269
|
+
const confidence = calculateConfidence(problems, hasSnapshot);
|
|
8270
|
+
return {
|
|
8271
|
+
id,
|
|
8272
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
8273
|
+
runtime: detectedRuntime,
|
|
8274
|
+
status,
|
|
8275
|
+
problems,
|
|
8276
|
+
repairPlan,
|
|
8277
|
+
estimatedTime,
|
|
8278
|
+
confidence
|
|
8279
|
+
};
|
|
8280
|
+
}
|
|
8281
|
+
function createRepairPlan(diagnosis) {
|
|
8282
|
+
const steps = [];
|
|
8283
|
+
let order = 1;
|
|
8284
|
+
const missingEnv = diagnosis.problems.find((p) => p.category === "missing-env");
|
|
8285
|
+
if (missingEnv) {
|
|
8286
|
+
steps.push({
|
|
8287
|
+
id: randomUUID4(),
|
|
8288
|
+
order: order++,
|
|
8289
|
+
action: "recreate-venv",
|
|
8290
|
+
description: `Create new ${diagnosis.runtime} virtual environment`,
|
|
8291
|
+
friendlyDescription: "Setting up your project from scratch...",
|
|
8292
|
+
automated: true,
|
|
8293
|
+
risk: "safe"
|
|
8294
|
+
});
|
|
8295
|
+
return steps;
|
|
8296
|
+
}
|
|
8297
|
+
const corruptVenv = diagnosis.problems.find((p) => p.category === "corrupt-venv");
|
|
8298
|
+
if (corruptVenv) {
|
|
8299
|
+
steps.push({
|
|
8300
|
+
id: randomUUID4(),
|
|
8301
|
+
order: order++,
|
|
8302
|
+
action: "recreate-venv",
|
|
8303
|
+
description: `Recreate corrupted ${diagnosis.runtime} environment`,
|
|
8304
|
+
friendlyDescription: "Rebuilding your project setup...",
|
|
8305
|
+
automated: true,
|
|
8306
|
+
risk: "moderate"
|
|
8307
|
+
});
|
|
8308
|
+
return steps;
|
|
8309
|
+
}
|
|
8310
|
+
const hasSnapshotProblem = diagnosis.problems.some(
|
|
8311
|
+
(p) => p.category === "broken-deps" || p.category === "version-conflict"
|
|
8312
|
+
);
|
|
8313
|
+
if (hasSnapshotProblem) {
|
|
8314
|
+
steps.push({
|
|
8315
|
+
id: randomUUID4(),
|
|
8316
|
+
order: order++,
|
|
8317
|
+
action: "restore-snapshot",
|
|
8318
|
+
description: "Restore from last known working state",
|
|
8319
|
+
friendlyDescription: "Going back to when things were working...",
|
|
8320
|
+
automated: true,
|
|
8321
|
+
risk: "safe"
|
|
8322
|
+
});
|
|
8323
|
+
}
|
|
8324
|
+
const versionConflicts = diagnosis.problems.filter(
|
|
8325
|
+
(p) => p.category === "version-conflict"
|
|
8326
|
+
);
|
|
8327
|
+
for (const conflict of versionConflicts) {
|
|
8328
|
+
steps.push({
|
|
8329
|
+
id: randomUUID4(),
|
|
8330
|
+
order: order++,
|
|
8331
|
+
action: "resolve-conflicts",
|
|
8332
|
+
description: conflict.description,
|
|
8333
|
+
friendlyDescription: "Fixing incompatible package versions...",
|
|
8334
|
+
automated: true,
|
|
8335
|
+
risk: "moderate"
|
|
8336
|
+
});
|
|
8337
|
+
}
|
|
8338
|
+
const staleLockfile = diagnosis.problems.find((p) => p.category === "stale-lockfile");
|
|
8339
|
+
if (staleLockfile) {
|
|
8340
|
+
steps.push({
|
|
8341
|
+
id: randomUUID4(),
|
|
8342
|
+
order: order++,
|
|
8343
|
+
action: "reinstall-deps",
|
|
8344
|
+
description: "Reinstall packages from lockfile",
|
|
8345
|
+
friendlyDescription: "Updating your packages...",
|
|
8346
|
+
automated: true,
|
|
8347
|
+
risk: "safe"
|
|
8348
|
+
});
|
|
8349
|
+
}
|
|
8350
|
+
return steps;
|
|
8351
|
+
}
|
|
8352
|
+
async function executeRepair(path, diagnosis, options) {
|
|
8353
|
+
const { autoBackup = true, dryRun = false, onProgress } = options || {};
|
|
8354
|
+
const { repairPlan, runtime } = diagnosis;
|
|
8355
|
+
if (repairPlan.length === 0) {
|
|
8356
|
+
return {
|
|
8357
|
+
success: true,
|
|
8358
|
+
stepsCompleted: 0,
|
|
8359
|
+
stepsTotal: 0,
|
|
8360
|
+
problemsFixed: [],
|
|
8361
|
+
problemsRemaining: []
|
|
8362
|
+
};
|
|
8363
|
+
}
|
|
8364
|
+
let snapshotId;
|
|
8365
|
+
const problemsFixed = [];
|
|
8366
|
+
const problemsRemaining = [];
|
|
8367
|
+
let stepsCompleted = 0;
|
|
8368
|
+
if (autoBackup && !dryRun) {
|
|
8369
|
+
try {
|
|
8370
|
+
const state = await loadState(path);
|
|
8371
|
+
if (state.activeEnvironmentId) {
|
|
8372
|
+
const env = state.environments.find((e) => e.id === state.activeEnvironmentId);
|
|
8373
|
+
if (env) {
|
|
8374
|
+
const snapshot = await createSnapshot(
|
|
8375
|
+
path,
|
|
8376
|
+
env,
|
|
8377
|
+
"Pre-repair backup"
|
|
8378
|
+
);
|
|
8379
|
+
snapshotId = snapshot.id;
|
|
8380
|
+
}
|
|
8381
|
+
}
|
|
8382
|
+
} catch {
|
|
8383
|
+
}
|
|
8384
|
+
}
|
|
8385
|
+
for (const step of repairPlan) {
|
|
8386
|
+
if (dryRun) {
|
|
8387
|
+
onProgress?.(step, "starting");
|
|
8388
|
+
onProgress?.(step, "complete");
|
|
8389
|
+
stepsCompleted++;
|
|
8390
|
+
continue;
|
|
8391
|
+
}
|
|
8392
|
+
try {
|
|
8393
|
+
onProgress?.(step, "starting");
|
|
8394
|
+
switch (step.action) {
|
|
8395
|
+
case "recreate-venv":
|
|
8396
|
+
await recreateEnvironment(path, runtime);
|
|
8397
|
+
problemsFixed.push("Recreated environment");
|
|
8398
|
+
break;
|
|
8399
|
+
case "restore-snapshot":
|
|
8400
|
+
await restoreFromSnapshot(path, runtime);
|
|
8401
|
+
problemsFixed.push("Restored from backup");
|
|
8402
|
+
break;
|
|
8403
|
+
case "reinstall-deps":
|
|
8404
|
+
await reinstallDependencies(path, runtime);
|
|
8405
|
+
problemsFixed.push("Reinstalled dependencies");
|
|
8406
|
+
break;
|
|
8407
|
+
case "resolve-conflicts":
|
|
8408
|
+
await resolveConflicts2(path, runtime);
|
|
8409
|
+
problemsFixed.push("Resolved conflicts");
|
|
8410
|
+
break;
|
|
8411
|
+
default:
|
|
8412
|
+
break;
|
|
8413
|
+
}
|
|
8414
|
+
onProgress?.(step, "complete");
|
|
8415
|
+
stepsCompleted++;
|
|
8416
|
+
} catch (error) {
|
|
8417
|
+
onProgress?.(step, "failed");
|
|
8418
|
+
problemsRemaining.push(
|
|
8419
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
8420
|
+
);
|
|
8421
|
+
}
|
|
8422
|
+
}
|
|
8423
|
+
return {
|
|
8424
|
+
success: stepsCompleted === repairPlan.length,
|
|
8425
|
+
stepsCompleted,
|
|
8426
|
+
stepsTotal: repairPlan.length,
|
|
8427
|
+
snapshotId,
|
|
8428
|
+
problemsFixed,
|
|
8429
|
+
problemsRemaining
|
|
8430
|
+
};
|
|
8431
|
+
}
|
|
8432
|
+
async function checkEnvironmentExists(path, runtime, problems) {
|
|
8433
|
+
if (runtime === "python") {
|
|
8434
|
+
const venvPath = join6(path, ".venv");
|
|
8435
|
+
if (!existsSync5(venvPath)) {
|
|
8436
|
+
problems.push({
|
|
8437
|
+
id: randomUUID4(),
|
|
8438
|
+
severity: "critical",
|
|
8439
|
+
category: "missing-env",
|
|
8440
|
+
title: "Virtual environment missing",
|
|
8441
|
+
friendlyTitle: "Project setup is missing",
|
|
8442
|
+
description: "No .venv directory found",
|
|
8443
|
+
technicalDetail: `Expected ${venvPath} to exist`
|
|
8444
|
+
});
|
|
8445
|
+
return false;
|
|
8446
|
+
}
|
|
8447
|
+
} else {
|
|
8448
|
+
const nodeModulesPath = join6(path, "node_modules");
|
|
8449
|
+
if (!existsSync5(nodeModulesPath)) {
|
|
8450
|
+
problems.push({
|
|
8451
|
+
id: randomUUID4(),
|
|
8452
|
+
severity: "critical",
|
|
8453
|
+
category: "missing-env",
|
|
8454
|
+
title: "node_modules missing",
|
|
8455
|
+
friendlyTitle: "Dependencies not installed",
|
|
8456
|
+
description: "No node_modules directory found",
|
|
8457
|
+
technicalDetail: `Expected ${nodeModulesPath} to exist`
|
|
8458
|
+
});
|
|
8459
|
+
return false;
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
return true;
|
|
8463
|
+
}
|
|
8464
|
+
async function checkRuntimeFunctional(path, runtime, problems, onProgress) {
|
|
8465
|
+
onProgress?.("Checking if runtime is functional...");
|
|
8466
|
+
try {
|
|
8467
|
+
if (runtime === "python") {
|
|
8468
|
+
const pythonPath = join6(path, ".venv", "bin", "python");
|
|
8469
|
+
await execa4(pythonPath, ["--version"], { cwd: path });
|
|
8470
|
+
} else {
|
|
8471
|
+
await execa4("node", ["--version"], { cwd: path });
|
|
8472
|
+
}
|
|
8473
|
+
} catch {
|
|
8474
|
+
problems.push({
|
|
8475
|
+
id: randomUUID4(),
|
|
8476
|
+
severity: "critical",
|
|
8477
|
+
category: "corrupt-venv",
|
|
8478
|
+
title: `${runtime} runtime not functional`,
|
|
8479
|
+
friendlyTitle: "Something is broken",
|
|
8480
|
+
description: `Cannot invoke ${runtime} from environment`,
|
|
8481
|
+
technicalDetail: runtime === "python" ? "Python executable in .venv/bin/python is not working" : "Node.js is not accessible"
|
|
8482
|
+
});
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
async function checkDependencyConflicts(path, runtime, problems, onProgress) {
|
|
8486
|
+
onProgress?.("Checking for dependency conflicts...");
|
|
8487
|
+
try {
|
|
8488
|
+
if (runtime === "python") {
|
|
8489
|
+
const pipPath = join6(path, ".venv", "bin", "pip");
|
|
8490
|
+
const result = await execa4(pipPath, ["check"], { cwd: path, reject: false });
|
|
8491
|
+
if (result.exitCode !== 0 && result.stdout) {
|
|
8492
|
+
const lines = result.stdout.split("\n").filter((l) => l.trim());
|
|
8493
|
+
for (const line of lines) {
|
|
8494
|
+
if (line.includes("requires") || line.includes("incompatible")) {
|
|
8495
|
+
problems.push({
|
|
8496
|
+
id: randomUUID4(),
|
|
8497
|
+
severity: "critical",
|
|
8498
|
+
category: "broken-deps",
|
|
8499
|
+
title: "Dependency conflict detected",
|
|
8500
|
+
friendlyTitle: "Packages don't work together",
|
|
8501
|
+
description: line,
|
|
8502
|
+
technicalDetail: "pip check failed"
|
|
8503
|
+
});
|
|
8504
|
+
}
|
|
8505
|
+
}
|
|
8506
|
+
}
|
|
8507
|
+
} else {
|
|
8508
|
+
const result = await execa4("npm", ["ls", "--json"], {
|
|
8509
|
+
cwd: path,
|
|
8510
|
+
reject: false
|
|
8511
|
+
});
|
|
8512
|
+
if (result.stdout) {
|
|
8513
|
+
try {
|
|
8514
|
+
const output = JSON.parse(result.stdout);
|
|
8515
|
+
if (output.problems && output.problems.length > 0) {
|
|
8516
|
+
for (const problem of output.problems) {
|
|
8517
|
+
problems.push({
|
|
8518
|
+
id: randomUUID4(),
|
|
8519
|
+
severity: "warning",
|
|
8520
|
+
category: "broken-deps",
|
|
8521
|
+
title: "Dependency issue detected",
|
|
8522
|
+
friendlyTitle: "Package issue found",
|
|
8523
|
+
description: String(problem),
|
|
8524
|
+
technicalDetail: "npm ls reported issues"
|
|
8525
|
+
});
|
|
8526
|
+
}
|
|
8527
|
+
}
|
|
8528
|
+
} catch {
|
|
8529
|
+
}
|
|
8530
|
+
}
|
|
8531
|
+
}
|
|
8532
|
+
} catch {
|
|
8533
|
+
}
|
|
8534
|
+
}
|
|
8535
|
+
async function checkLockfileDrift(path, runtime, problems, onProgress) {
|
|
8536
|
+
onProgress?.("Checking lockfile consistency...");
|
|
8537
|
+
try {
|
|
8538
|
+
if (runtime === "python") {
|
|
8539
|
+
const lockfilePath = join6(path, "requirements.lock");
|
|
8540
|
+
if (existsSync5(lockfilePath)) {
|
|
8541
|
+
const installed = await getInstalledPackages(path, runtime);
|
|
8542
|
+
if (installed.length === 0) {
|
|
8543
|
+
problems.push({
|
|
8544
|
+
id: randomUUID4(),
|
|
8545
|
+
severity: "warning",
|
|
8546
|
+
category: "stale-lockfile",
|
|
8547
|
+
title: "Lockfile exists but no packages installed",
|
|
8548
|
+
friendlyTitle: "Packages need to be installed",
|
|
8549
|
+
description: "requirements.lock exists but environment is empty"
|
|
8550
|
+
});
|
|
8551
|
+
}
|
|
8552
|
+
}
|
|
8553
|
+
} else {
|
|
8554
|
+
const lockfilePath = join6(path, "package-lock.json");
|
|
8555
|
+
if (existsSync5(lockfilePath)) {
|
|
8556
|
+
const nodeModulesPath = join6(path, "node_modules");
|
|
8557
|
+
if (!existsSync5(nodeModulesPath)) {
|
|
8558
|
+
problems.push({
|
|
8559
|
+
id: randomUUID4(),
|
|
8560
|
+
severity: "warning",
|
|
8561
|
+
category: "stale-lockfile",
|
|
8562
|
+
title: "Lockfile exists but node_modules missing",
|
|
8563
|
+
friendlyTitle: "Dependencies need installation",
|
|
8564
|
+
description: "package-lock.json exists but node_modules is missing"
|
|
8565
|
+
});
|
|
8566
|
+
}
|
|
8567
|
+
}
|
|
8568
|
+
}
|
|
8569
|
+
} catch {
|
|
8570
|
+
}
|
|
8571
|
+
}
|
|
8572
|
+
async function checkKnownConflicts(path, runtime, problems, onProgress) {
|
|
8573
|
+
onProgress?.("Checking for known conflicts...");
|
|
8574
|
+
try {
|
|
8575
|
+
const installed = await getInstalledPackages(path, runtime);
|
|
8576
|
+
const conflicts = detectConflicts(installed);
|
|
8577
|
+
for (const conflict of conflicts) {
|
|
8578
|
+
const severity = conflict.severity === "error" ? "critical" : conflict.severity;
|
|
8579
|
+
problems.push({
|
|
8580
|
+
id: randomUUID4(),
|
|
8581
|
+
severity,
|
|
8582
|
+
category: "version-conflict",
|
|
8583
|
+
title: `${conflict.package1} conflicts with ${conflict.package2}`,
|
|
8584
|
+
friendlyTitle: "Incompatible packages detected",
|
|
8585
|
+
description: conflict.reason,
|
|
8586
|
+
technicalDetail: conflict.suggestion
|
|
8587
|
+
});
|
|
8588
|
+
}
|
|
8589
|
+
} catch {
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8592
|
+
async function checkSnapshotAvailable(path, onProgress) {
|
|
8593
|
+
onProgress?.("Checking for backups...");
|
|
8594
|
+
try {
|
|
8595
|
+
const state = await loadState(path);
|
|
8596
|
+
if (state.activeEnvironmentId) {
|
|
8597
|
+
const snapshot = await getLatestSnapshot(path, state.activeEnvironmentId);
|
|
8598
|
+
return snapshot !== null;
|
|
8599
|
+
}
|
|
8600
|
+
return false;
|
|
8601
|
+
} catch {
|
|
8602
|
+
return false;
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
async function recreateEnvironment(path, runtime) {
|
|
8606
|
+
if (runtime === "python") {
|
|
8607
|
+
const venvPath = join6(path, ".venv");
|
|
8608
|
+
if (existsSync5(venvPath)) {
|
|
8609
|
+
await execa4("rm", ["-rf", venvPath], { cwd: path });
|
|
8610
|
+
}
|
|
8611
|
+
await execa4("python3", ["-m", "venv", ".venv"], { cwd: path });
|
|
8612
|
+
} else {
|
|
8613
|
+
await execa4("npm", ["install"], { cwd: path });
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
async function restoreFromSnapshot(path, runtime) {
|
|
8617
|
+
const state = await loadState(path);
|
|
8618
|
+
if (!state.activeEnvironmentId) {
|
|
8619
|
+
throw new Error("No active environment found");
|
|
8620
|
+
}
|
|
8621
|
+
const snapshot = await getLatestSnapshot(path, state.activeEnvironmentId);
|
|
8622
|
+
if (!snapshot) {
|
|
8623
|
+
throw new Error("No snapshot available");
|
|
8624
|
+
}
|
|
8625
|
+
if (runtime === "python") {
|
|
8626
|
+
await restorePythonEnvironment(path, snapshot.lockfileContent);
|
|
8627
|
+
} else {
|
|
8628
|
+
await restoreNodeEnvironment(path, snapshot.lockfileContent);
|
|
8629
|
+
}
|
|
8630
|
+
}
|
|
8631
|
+
async function reinstallDependencies(path, runtime) {
|
|
8632
|
+
if (runtime === "python") {
|
|
8633
|
+
const lockfilePath = join6(path, "requirements.lock");
|
|
8634
|
+
if (existsSync5(lockfilePath)) {
|
|
8635
|
+
const pipPath = join6(path, ".venv", "bin", "pip");
|
|
8636
|
+
await execa4(pipPath, ["install", "-r", "requirements.lock"], { cwd: path });
|
|
8637
|
+
}
|
|
8638
|
+
} else {
|
|
8639
|
+
await execa4("npm", ["ci"], { cwd: path });
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
async function resolveConflicts2(path, runtime) {
|
|
8643
|
+
await reinstallDependencies(path, runtime);
|
|
8644
|
+
}
|
|
8645
|
+
function estimateRepairTime(steps) {
|
|
8646
|
+
if (steps.length === 0) return "None needed";
|
|
8647
|
+
if (steps.length === 1) return "~1 minute";
|
|
8648
|
+
if (steps.length <= 3) return "~2 minutes";
|
|
8649
|
+
return "~5 minutes";
|
|
8650
|
+
}
|
|
8651
|
+
function calculateConfidence(problems, hasSnapshot) {
|
|
8652
|
+
if (problems.length === 0) return 1;
|
|
8653
|
+
const criticalCount = problems.filter((p) => p.severity === "critical").length;
|
|
8654
|
+
if (criticalCount > 2) return hasSnapshot ? 0.7 : 0.5;
|
|
8655
|
+
if (criticalCount > 0) return hasSnapshot ? 0.85 : 0.7;
|
|
8656
|
+
return 0.9;
|
|
8657
|
+
}
|
|
7821
8658
|
|
|
7822
8659
|
export {
|
|
7823
8660
|
__require,
|
|
@@ -7825,10 +8662,12 @@ export {
|
|
|
7825
8662
|
__toESM,
|
|
7826
8663
|
loadState,
|
|
7827
8664
|
KomodoChat,
|
|
7828
|
-
Komodo,
|
|
7829
8665
|
getInstalledPackages,
|
|
7830
8666
|
detectConflicts,
|
|
7831
8667
|
analyzeHealth,
|
|
8668
|
+
detectRealConflicts,
|
|
8669
|
+
resolveConflicts,
|
|
8670
|
+
Komodo,
|
|
7832
8671
|
runDoctor,
|
|
7833
8672
|
formatDoctorReport,
|
|
7834
8673
|
buildDependencyTree,
|
|
@@ -7849,5 +8688,7 @@ export {
|
|
|
7849
8688
|
analyzeRepoStructure,
|
|
7850
8689
|
formatRepoAnalysis,
|
|
7851
8690
|
analyzeEnvironment,
|
|
7852
|
-
formatInsights
|
|
8691
|
+
formatInsights,
|
|
8692
|
+
diagnoseEnvironment,
|
|
8693
|
+
executeRepair
|
|
7853
8694
|
};
|