@xcanwin/manyoyo 4.2.0 → 4.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -119,6 +119,7 @@ manyoyo -y c # Claude Code(或 claude / cc)
119
119
  manyoyo -y gm # Gemini(或 gemini / g)
120
120
  manyoyo -y cx # Codex(或 codex)
121
121
  manyoyo -y oc # OpenCode(或 opencode)
122
+ manyoyo --update # 更新 MANYOYO(全局 npm 安装场景)
122
123
 
123
124
  # 容器管理
124
125
  manyoyo -l
package/bin/manyoyo.js CHANGED
@@ -640,6 +640,35 @@ function installManyoyo(name) {
640
640
  process.exit(0);
641
641
  }
642
642
 
643
+ function updateManyoyo() {
644
+ let isLocalFileInstall = false;
645
+ try {
646
+ const listOutput = runCmd('npm', ['ls', '-g', '@xcanwin/manyoyo', '--json', '--long'], { stdio: 'pipe' });
647
+ const listJson = JSON.parse(listOutput || '{}');
648
+ const dep = listJson && listJson.dependencies && listJson.dependencies['@xcanwin/manyoyo'];
649
+ const resolved = dep && typeof dep.resolved === 'string' ? dep.resolved : '';
650
+ const depPath = dep && typeof dep.path === 'string' ? dep.path : '';
651
+
652
+ if (resolved.startsWith('file:')) {
653
+ isLocalFileInstall = true;
654
+ } else if (depPath && fs.existsSync(depPath)) {
655
+ isLocalFileInstall = fs.lstatSync(depPath).isSymbolicLink();
656
+ }
657
+ } catch (e) {
658
+ // ignore detect errors and fallback to registry update
659
+ }
660
+
661
+ if (isLocalFileInstall) {
662
+ console.log(`${YELLOW}ℹ️ 检测到 MANYOYO 为本地 file 安装(npm install -g . / npm link),跳过在线更新。${NC}`);
663
+ console.log(`${YELLOW} 如需更新,请在本地仓库拉取最新代码后重新安装。${NC}`);
664
+ return;
665
+ }
666
+
667
+ console.log(`${CYAN}🔄 正在更新 ${MANYOYO_NAME} 到最新版本...${NC}`);
668
+ runCmd('npm', ['update', '-g', '@xcanwin/manyoyo'], { stdio: 'inherit' });
669
+ console.log(`${GREEN}✅ 更新完成,请重新执行 ${MANYOYO_NAME} --version 确认版本。${NC}`);
670
+ }
671
+
643
672
  function getContList() {
644
673
  try {
645
674
  const result = execSync(`${DOCKER_CMD} ps -a --size --filter "ancestor=manyoyo" --filter "ancestor=$(${DOCKER_CMD} images -a --format '{{.Repository}}:{{.Tag}}' | grep manyoyo)" --format "table {{.Names}}\\t{{.Status}}\\t{{.Size}}\\t{{.ID}}\\t{{.Image}}\\t{{.Ports}}\\t{{.Networks}}\\t{{.Mounts}}"`,
@@ -724,6 +753,7 @@ async function setupCommander() {
724
753
  -- <args...> → 直接透传命令后缀(优先级最高)
725
754
 
726
755
  示例:
756
+ ${MANYOYO_NAME} --update 更新 MANYOYO 到最新版本
727
757
  ${MANYOYO_NAME} --ib --iv ${IMAGE_VERSION_HELP_EXAMPLE} 构建镜像
728
758
  ${MANYOYO_NAME} --init-config all 从本机 Agent 配置初始化 ~/.manyoyo
729
759
  ${MANYOYO_NAME} -r claude 使用 manyoyo.json 的 runs.claude 快速启动
@@ -751,6 +781,7 @@ async function setupCommander() {
751
781
  .option('--ib, --image-build', '构建镜像')
752
782
  .option('--iba, --image-build-arg <arg>', '构建镜像时传参给dockerfile (可多次使用)', (value, previous) => [...(previous || []), value], [])
753
783
  .option('--init-config [agents]', '初始化 Agent 配置到 ~/.manyoyo (all 或逗号分隔: claude,codex,gemini,opencode)')
784
+ .option('--update', '更新 MANYOYO(若检测为本地 file 安装则跳过)')
754
785
  .option('--irm, --image-remove', '清理悬空镜像和 <none> 镜像')
755
786
  .option('-e, --env <env>', '设置环境变量 XXX=YYY (可多次使用)', (value, previous) => [...(previous || []), value], [])
756
787
  .option('--ef, --env-file <file>', '设置环境变量通过文件 (仅支持绝对路径,如 /abs/path.env)', (value, previous) => [...(previous || []), value], [])
@@ -784,8 +815,9 @@ async function setupCommander() {
784
815
  }
785
816
 
786
817
  const isInitConfigMode = process.argv.some(arg => arg === '--init-config' || arg.startsWith('--init-config='));
787
- // init-config 只处理本地文件,不依赖 docker/podman
788
- if (!isInitConfigMode) {
818
+ const isUpdateMode = process.argv.includes('--update');
819
+ // init-config/update 只处理本地文件或 npm,不依赖 docker/podman
820
+ if (!isInitConfigMode && !isUpdateMode) {
789
821
  // Ensure docker/podman is available
790
822
  ensureDocker();
791
823
  }
@@ -803,6 +835,11 @@ async function setupCommander() {
803
835
  YES_MODE = true;
804
836
  }
805
837
 
838
+ if (options.update) {
839
+ updateManyoyo();
840
+ process.exit(0);
841
+ }
842
+
806
843
  if (options.initConfig !== undefined) {
807
844
  await initAgentConfigs(options.initConfig, {
808
845
  yesMode: YES_MODE,
@@ -43,7 +43,7 @@ function createBuildCacheContext(ctx) {
43
43
  return {
44
44
  cacheDir,
45
45
  timestampFile,
46
- cacheTTLDays: config.cacheTTL || ctx.cacheTtlDays,
46
+ cacheTTLDays: config.cacheTTL ?? ctx.cacheTtlDays,
47
47
  nodeMirrors: [config.nodeMirror, 'https://mirrors.tencent.com/nodejs-release', 'https://nodejs.org/dist'].filter(Boolean),
48
48
  timestamps: loadBuildCacheTimestamps(timestampFile),
49
49
  now: new Date()
@@ -253,19 +253,50 @@ function extractBuildArgValues(args) {
253
253
  return values;
254
254
  }
255
255
 
256
- function buildPodmanBuildkitRunArgs(ctx, dockerfilePath, fullImageTag, imageBuildArgs) {
256
+ function isBuildCapabilityError(error) {
257
+ const combined = [
258
+ error && error.message ? error.message : '',
259
+ error && error.stderr ? String(error.stderr) : '',
260
+ error && error.stdout ? String(error.stdout) : ''
261
+ ].join('\n').toLowerCase();
262
+
263
+ const patterns = [
264
+ /unknown flag/,
265
+ /unknown shorthand flag/,
266
+ /unknown option/,
267
+ /unrecognized option/,
268
+ /unknown instruction:\s*"?((case|if|then|fi|for|while|do|done|esac))"?/,
269
+ /buildx .* not available/,
270
+ /buildx .* not found/,
271
+ /buildx .* not enabled/,
272
+ /'buildx' is not a docker command/,
273
+ /the --load option requires buildx/,
274
+ /unsupported.*--load/,
275
+ /does not support.*--load/,
276
+ /driver .* not supported/,
277
+ /no such plugin.*buildx/
278
+ ];
279
+ return patterns.some(pattern => pattern.test(combined));
280
+ }
281
+
282
+ function buildBuildkitRunArgs(ctx, dockerfilePath, fullImageTag, imageBuildArgs) {
257
283
  const dockerfileRelativePath = path.relative(ctx.rootDir, dockerfilePath).split(path.sep).join('/');
258
284
  const buildArgs = extractBuildArgValues(imageBuildArgs);
259
285
  const args = [
260
286
  'run', '--rm', '--privileged',
287
+ '--network', `host`,
261
288
  '--volume', `${ctx.rootDir}:/workspace`,
262
289
  '--entrypoint', 'buildctl-daemonless.sh',
263
- 'moby/buildkit:latest',
290
+ 'docker.io/moby/buildkit:latest',
264
291
  'build',
265
292
  '--frontend', 'dockerfile.v0',
266
293
  '--local', 'context=/workspace',
267
294
  '--local', 'dockerfile=/workspace',
268
- '--opt', `filename=${dockerfileRelativePath}`
295
+ '--opt', `filename=${dockerfileRelativePath}`,
296
+ '--opt', `build-arg:HTTP_PROXY=$HTTP_PROXY`,
297
+ '--opt', `build-arg:HTTPS_PROXY=$HTTPS_PROXY`,
298
+ '--opt', `build-arg:ALL_PROXY=$ALL_PROXY`,
299
+ '--opt', `build-arg:NO_PROXY=$NO_PROXY`
269
300
  ];
270
301
 
271
302
  for (const value of buildArgs) {
@@ -397,48 +428,53 @@ async function buildImage(options = {}) {
397
428
  '--progress=plain',
398
429
  '--no-cache'
399
430
  ];
431
+ const buildkitRunArgs = buildBuildkitRunArgs(ctx, dockerfilePath, fullImageTag, imageBuildArgs);
400
432
 
401
- ctx.log(`${BLUE}准备执行命令:${NC}`);
402
- const usePodmanBuildkit = ctx.dockerCmd === 'podman';
403
- const buildkitRunArgs = usePodmanBuildkit
404
- ? buildPodmanBuildkitRunArgs(ctx, dockerfilePath, fullImageTag, imageBuildArgs)
405
- : [];
406
- if (usePodmanBuildkit) {
407
- ctx.log(`${ctx.dockerCmd} ${buildkitRunArgs.map(quoteShellArg).join(' ')} | ${ctx.dockerCmd} load\n`);
408
- } else {
409
- ctx.log(`${ctx.dockerCmd} ${buildArgs.map(quoteShellArg).join(' ')}\n`);
433
+ function logBuildSuccess() {
434
+ ctx.log(`\n${GREEN}✅ 镜像构建成功: ${fullImageTag}${NC}`);
435
+ ctx.log(`${BLUE}使用镜像:${NC}`);
436
+ ctx.log(` ${ctx.manyoyoName} -n test --in ${ctx.imageName} --iv ${version}-${imageTool} -y c`);
437
+ ctx.pruneDanglingImages();
410
438
  }
411
439
 
440
+ ctx.log(`${BLUE}准备执行命令:${NC}`);
441
+ ctx.log(`${ctx.dockerCmd} ${buildArgs.map(quoteShellArg).join(' ')}\n`);
442
+
412
443
  if (!ctx.yesMode) {
413
444
  await ctx.askQuestion('❔ 是否继续构建? [ 直接回车=继续, ctrl+c=取消 ]: ');
414
445
  ctx.log('');
415
446
  }
416
447
 
417
- if (usePodmanBuildkit) {
418
- try {
419
- await ctx.runCmdPipeline(ctx.dockerCmd, buildkitRunArgs, ctx.dockerCmd, ['load'], { stdio: 'inherit' });
420
- ctx.log(`\n${GREEN}✅ 镜像构建成功: ${fullImageTag}${NC}`);
421
- ctx.log(`${BLUE}使用镜像:${NC}`);
422
- ctx.log(` ${ctx.manyoyoName} -n test --in ${ctx.imageName} --iv ${version}-${imageTool} -y c`);
423
- ctx.pruneDanglingImages();
448
+ try {
449
+ ctx.runCmd(ctx.dockerCmd, buildArgs, { stdio: 'inherit' });
450
+ logBuildSuccess();
451
+ return;
452
+ } catch (e) {
453
+ const stderrText = e && e.stderr ? String(e.stderr).trim() : '';
454
+ const stdoutText = e && e.stdout ? String(e.stdout).trim() : '';
455
+ const hasDiagnostics = Boolean(stderrText || stdoutText);
456
+ const capabilityError = isBuildCapabilityError(e);
457
+
458
+ if (!capabilityError && hasDiagnostics) {
459
+ ctx.error(`${RED}错误: 镜像构建失败${NC}`);
460
+ ctx.exit(1);
424
461
  return;
425
- } catch (e) {
426
- ctx.log(`${YELLOW}⚠️ BuildKit 构建失败,回退到 podman build...${NC}`);
427
- if (e && e.message) {
428
- ctx.log(`${YELLOW}原因: ${e.message}${NC}`);
429
- }
430
- ctx.log('');
431
- ctx.log(`${BLUE}回退命令:${NC}`);
432
- ctx.log(`${ctx.dockerCmd} ${buildArgs.map(quoteShellArg).join(' ')}\n`);
433
462
  }
463
+ if (!capabilityError && !hasDiagnostics) {
464
+ ctx.log(`${YELLOW}⚠️ 未捕获到构建器错误详情,尝试回退到 BuildKit...${NC}`);
465
+ }
466
+ ctx.log(`${YELLOW}⚠️ 直接 build 失败,回退到 BuildKit...${NC}`);
467
+ if (e && e.message) {
468
+ ctx.log(`${YELLOW}原因: ${e.message}${NC}`);
469
+ }
470
+ ctx.log('');
471
+ ctx.log(`${BLUE}回退命令:${NC}`);
472
+ ctx.log(`${ctx.dockerCmd} ${buildkitRunArgs.map(quoteShellArg).join(' ')} | ${ctx.dockerCmd} load\n`);
434
473
  }
435
474
 
436
475
  try {
437
- ctx.runCmd(ctx.dockerCmd, buildArgs, { stdio: 'inherit' });
438
- ctx.log(`\n${GREEN}✅ 镜像构建成功: ${fullImageTag}${NC}`);
439
- ctx.log(`${BLUE}使用镜像:${NC}`);
440
- ctx.log(` ${ctx.manyoyoName} -n test --in ${ctx.imageName} --iv ${version}-${imageTool} -y c`);
441
- ctx.pruneDanglingImages();
476
+ await ctx.runCmdPipeline(ctx.dockerCmd, buildkitRunArgs, ctx.dockerCmd, ['load'], { stdio: 'inherit' });
477
+ logBuildSuccess();
442
478
  } catch (e) {
443
479
  ctx.error(`${RED}错误: 镜像构建失败${NC}`);
444
480
  ctx.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "4.2.0",
3
+ "version": "4.2.4",
4
4
  "imageVersion": "1.8.0-common",
5
5
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",
6
6
  "keywords": [