myagent-ai 1.16.1 → 1.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/start.js +127 -18
package/package.json
CHANGED
package/start.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* - 先尝试 pip install -r requirements.txt (批量安装)
|
|
12
12
|
* - 如果失败,逐包安装: 核心包失败=报错,可选包失败=跳过
|
|
13
13
|
* - 确保即使部分可选包编译失败,核心功能也能正常运行
|
|
14
|
+
*
|
|
15
|
+
* [v1.16.1] 更新策略:
|
|
16
|
+
* - 版本变化不触发全量依赖重装
|
|
17
|
+
* - 只在 requirements.txt 哈希变化时检查新增/变更的包
|
|
18
|
+
* - 用 pip show 快速判断已安装的包,只安装缺失的
|
|
14
19
|
*/
|
|
15
20
|
"use strict";
|
|
16
21
|
|
|
@@ -18,6 +23,7 @@ const { spawn, execSync, execFileSync } = require("child_process");
|
|
|
18
23
|
const path = require("path");
|
|
19
24
|
const fs = require("fs");
|
|
20
25
|
const os = require("os");
|
|
26
|
+
const crypto = require("crypto");
|
|
21
27
|
|
|
22
28
|
const IS_WIN = process.platform === "win32";
|
|
23
29
|
const PKG_NAME = "myagent-ai";
|
|
@@ -331,19 +337,117 @@ function installAllDeps(venvPython, pkgDir) {
|
|
|
331
337
|
return { ok: true, failedOptional };
|
|
332
338
|
}
|
|
333
339
|
|
|
334
|
-
//
|
|
335
|
-
|
|
340
|
+
// ── Sentinel(依赖安装标记) ─────────────────────────────────
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* [v1.16.1] 计算依赖哈希(requirements.txt 的 MD5 前 8 位)
|
|
344
|
+
*/
|
|
345
|
+
function getDepsHash(pkgDir) {
|
|
346
|
+
const reqFile = path.join(pkgDir, "requirements.txt");
|
|
347
|
+
if (!fs.existsSync(reqFile)) return "";
|
|
348
|
+
try {
|
|
349
|
+
return crypto.createHash("md5").update(fs.readFileSync(reqFile, "utf8")).digest("hex").slice(0, 8);
|
|
350
|
+
} catch (_) {
|
|
351
|
+
return "";
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* 标记依赖已安装
|
|
357
|
+
* [v1.16.1] 写入 "版本号|依赖哈希" 格式
|
|
358
|
+
* 这样版本变化时 isDepsInstalled 仍返回 true,不会全量重装
|
|
359
|
+
*/
|
|
360
|
+
function markDepsInstalled(version, pkgDir) {
|
|
336
361
|
try {
|
|
337
362
|
const dir = getDataDir();
|
|
338
363
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
339
|
-
|
|
364
|
+
const depsHash = pkgDir ? getDepsHash(pkgDir) : "";
|
|
365
|
+
fs.writeFileSync(getSentinelFile(), `${version}|${depsHash}`);
|
|
340
366
|
} catch (_) {}
|
|
341
367
|
}
|
|
342
368
|
|
|
369
|
+
/**
|
|
370
|
+
* [v1.16.1] 检查依赖是否已安装
|
|
371
|
+
* - 无 sentinel 文件 → false(首次安装)
|
|
372
|
+
* - 新格式(含 |)→ true(只要 sentinel 存在就算已安装)
|
|
373
|
+
* - 旧格式(纯版本号)→ 精确匹配版本号
|
|
374
|
+
*/
|
|
343
375
|
function isDepsInstalled(pkgVersion) {
|
|
344
376
|
try {
|
|
345
377
|
if (!fs.existsSync(getSentinelFile())) return false;
|
|
346
|
-
|
|
378
|
+
const sentinel = fs.readFileSync(getSentinelFile(), "utf8").trim();
|
|
379
|
+
if (sentinel.includes("|")) {
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
return sentinel === pkgVersion;
|
|
383
|
+
} catch (_) {
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* [v1.16.1] 检查依赖是否有变更并只安装新增/变更的包
|
|
390
|
+
* 对比 sentinel 中的依赖哈希与当前 requirements.txt 的哈希,
|
|
391
|
+
* 如果不同则用 pip show 逐包检查,只安装缺失或需要升级的包。
|
|
392
|
+
* @param {string} pkgDir 包目录路径
|
|
393
|
+
* @returns {boolean} 是否有依赖变更
|
|
394
|
+
*/
|
|
395
|
+
function checkAndInstallChangedDeps(pkgDir) {
|
|
396
|
+
const venvPython = getVenvPython(getVenvDir());
|
|
397
|
+
if (!fs.existsSync(venvPython)) return false;
|
|
398
|
+
|
|
399
|
+
const reqFile = path.join(pkgDir, "requirements.txt");
|
|
400
|
+
if (!fs.existsSync(reqFile)) return false;
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
const sentinel = fs.readFileSync(getSentinelFile(), "utf8").trim();
|
|
404
|
+
const currentHash = getDepsHash(pkgDir);
|
|
405
|
+
|
|
406
|
+
// 解析旧的哈希
|
|
407
|
+
const oldHash = sentinel.includes("|") ? sentinel.split("|")[1] : "";
|
|
408
|
+
if (oldHash && oldHash === currentHash) {
|
|
409
|
+
// 依赖没变,什么都不做
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// 依赖有变更,找出新增/缺失的包并安装
|
|
414
|
+
const packages = parseRequirements(reqFile);
|
|
415
|
+
const newPkgs = [];
|
|
416
|
+
for (const pkg of packages) {
|
|
417
|
+
const baseName = pkgBaseName(pkg);
|
|
418
|
+
try {
|
|
419
|
+
// 用 pip show 检查包是否已安装(快速,不需要网络)
|
|
420
|
+
execFileSync(venvPython, [
|
|
421
|
+
"-m", "pip", "show", baseName, "--disable-pip-version-check"
|
|
422
|
+
], { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"], timeout: 10000 });
|
|
423
|
+
} catch (_) {
|
|
424
|
+
// pip show 失败 = 包未安装,需要安装
|
|
425
|
+
newPkgs.push(pkg);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (newPkgs.length === 0) return false;
|
|
430
|
+
|
|
431
|
+
console.log("");
|
|
432
|
+
console.log(` \x1b[36m检测到 ${newPkgs.length} 个新增依赖,正在安装...\x1b[0m`);
|
|
433
|
+
for (const pkg of newPkgs) {
|
|
434
|
+
const baseName = pkgBaseName(pkg);
|
|
435
|
+
const isCore = CORE_DEPS.includes(baseName);
|
|
436
|
+
process.stdout.write(` ${pkg} ... `);
|
|
437
|
+
const result = isCore ? pipInstallWithFallback(venvPython, pkg, true) : pipInstall(venvPython, pkg, true);
|
|
438
|
+
if (result.ok) {
|
|
439
|
+
const note = result.err === "(--no-deps)" ? " (部分子依赖跳过)" : "";
|
|
440
|
+
console.log("\x1b[32m✓\x1b[0m" + note);
|
|
441
|
+
} else {
|
|
442
|
+
if (isCore) {
|
|
443
|
+
console.log("\x1b[31m✗\x1b[0m");
|
|
444
|
+
console.log("\x1b[31m " + result.err.split("\n").join("\n ") + "\x1b[0m");
|
|
445
|
+
} else {
|
|
446
|
+
console.log("\x1b[33m✗ 跳过\x1b[0m");
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return true;
|
|
347
451
|
} catch (_) {
|
|
348
452
|
return false;
|
|
349
453
|
}
|
|
@@ -359,7 +463,7 @@ function cmdInstall(pkgDir) {
|
|
|
359
463
|
installAllDeps(venvPython, pkgDir);
|
|
360
464
|
let ver = "";
|
|
361
465
|
try { ver = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
362
|
-
markDepsInstalled(ver);
|
|
466
|
+
markDepsInstalled(ver, pkgDir);
|
|
363
467
|
console.log("");
|
|
364
468
|
console.log(" \x1b[32m✓ 安装完成,可以启动了: myagent-ai web\x1b[0m");
|
|
365
469
|
console.log("");
|
|
@@ -378,7 +482,7 @@ function cmdReinstall(pkgDir) {
|
|
|
378
482
|
installAllDeps(venvPython, pkgDir);
|
|
379
483
|
let ver = "";
|
|
380
484
|
try { ver = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
381
|
-
markDepsInstalled(ver);
|
|
485
|
+
markDepsInstalled(ver, pkgDir);
|
|
382
486
|
console.log("");
|
|
383
487
|
console.log(" \x1b[32m✓ 重装完成\x1b[0m");
|
|
384
488
|
console.log("");
|
|
@@ -390,7 +494,7 @@ function cmdUpdate(pkgDir) {
|
|
|
390
494
|
try { curVer = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
391
495
|
|
|
392
496
|
console.log("");
|
|
393
|
-
console.log(`
|
|
497
|
+
console.log(` MyAgent 更新${curVer ? " v" + curVer : "到最新版"}`);
|
|
394
498
|
console.log("");
|
|
395
499
|
|
|
396
500
|
// 1. npm 升级全局包(--force 确保跳过缓存)
|
|
@@ -400,14 +504,14 @@ function cmdUpdate(pkgDir) {
|
|
|
400
504
|
encoding: "utf8", stdio: "inherit", timeout: 120000,
|
|
401
505
|
});
|
|
402
506
|
} catch (e) {
|
|
403
|
-
console.error("
|
|
507
|
+
console.error("npm 更新失败,请尝试手动运行: npm install -g myagent-ai@latest --force");
|
|
404
508
|
process.exit(1);
|
|
405
509
|
}
|
|
406
510
|
|
|
407
511
|
// 2. 重新定位 pkgDir(npm 更新后路径可能变化)
|
|
408
512
|
const newPkgDir = resolvePackageDir();
|
|
409
513
|
if (!fs.existsSync(path.join(newPkgDir, "main.py"))) {
|
|
410
|
-
console.error("
|
|
514
|
+
console.error("更新后找不到 main.py,请重新安装: npm install -g myagent-ai@latest");
|
|
411
515
|
process.exit(1);
|
|
412
516
|
}
|
|
413
517
|
|
|
@@ -417,11 +521,16 @@ function cmdUpdate(pkgDir) {
|
|
|
417
521
|
// 2.5 清除 __pycache__(防止旧的 .pyc 字节码覆盖新的 .py 源码)
|
|
418
522
|
cleanPycache(newPkgDir);
|
|
419
523
|
|
|
420
|
-
//
|
|
524
|
+
// [v1.16.1] 3. 更新后只检查依赖是否有变更,不全量重装
|
|
525
|
+
checkAndInstallChangedDeps(newPkgDir);
|
|
526
|
+
markDepsInstalled(newVer, newPkgDir);
|
|
527
|
+
|
|
528
|
+
// 4. 如果版本变了,用新的 start.js 重新执行自身,确保运行最新代码
|
|
421
529
|
if (curVer && newVer && curVer !== newVer) {
|
|
422
530
|
const newStartJs = path.join(newPkgDir, "start.js");
|
|
423
531
|
if (fs.existsSync(newStartJs)) {
|
|
424
|
-
console.log(
|
|
532
|
+
console.log("");
|
|
533
|
+
console.log(` 已下载 v${newVer},正在切换到新版本...`);
|
|
425
534
|
console.log("");
|
|
426
535
|
const { spawn } = require("child_process");
|
|
427
536
|
const webChild = spawn(process.execPath, [newStartJs, "web"], {
|
|
@@ -432,13 +541,11 @@ function cmdUpdate(pkgDir) {
|
|
|
432
541
|
}
|
|
433
542
|
}
|
|
434
543
|
|
|
435
|
-
markDepsInstalled(newVer);
|
|
436
|
-
|
|
437
544
|
console.log("");
|
|
438
545
|
if (newVer) {
|
|
439
|
-
console.log(`
|
|
546
|
+
console.log(` ✓ 已是最新版 v${newVer}`);
|
|
440
547
|
} else {
|
|
441
|
-
console.log(`
|
|
548
|
+
console.log(` ✓ 更新完成`);
|
|
442
549
|
}
|
|
443
550
|
console.log(` 正在启动...`);
|
|
444
551
|
console.log("");
|
|
@@ -448,7 +555,6 @@ function cmdUpdate(pkgDir) {
|
|
|
448
555
|
}
|
|
449
556
|
|
|
450
557
|
|
|
451
|
-
|
|
452
558
|
// ── 清除 __pycache__ ──────────────────────────────────────
|
|
453
559
|
|
|
454
560
|
function cleanPycache(pkgDir) {
|
|
@@ -525,14 +631,17 @@ function cmdRun(pkgDir, userArgs) {
|
|
|
525
631
|
let ver = "";
|
|
526
632
|
try { ver = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8")).version || ""; } catch (_) {}
|
|
527
633
|
|
|
528
|
-
//
|
|
634
|
+
// [v1.16.1] 首次运行: 无 sentinel → 全量安装;有 sentinel → 只检查增量
|
|
529
635
|
if (!isDepsInstalled(ver)) {
|
|
530
636
|
console.log("");
|
|
531
637
|
console.log(" \x1b[36m首次运行,正在安装依赖...\x1b[0m");
|
|
532
638
|
console.log("");
|
|
533
639
|
installAllDeps(venvPython, pkgDir);
|
|
534
|
-
markDepsInstalled(ver);
|
|
640
|
+
markDepsInstalled(ver, pkgDir);
|
|
535
641
|
console.log("");
|
|
642
|
+
} else {
|
|
643
|
+
// sentinel 存在,检查依赖是否有变更(新增或升级)
|
|
644
|
+
checkAndInstallChangedDeps(pkgDir);
|
|
536
645
|
}
|
|
537
646
|
|
|
538
647
|
// 显示版本
|