@xcanwin/manyoyo 5.9.11 → 5.10.3

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
@@ -21,6 +21,14 @@
21
21
 
22
22
  ---
23
23
 
24
+ ## 生态仓库
25
+
26
+ | 仓库 | 定位 |
27
+ |------|------|
28
+ | [manyoyo](https://github.com/xcanwin/manyoyo) | CLI + 容器运行时 + Web 服务 |
29
+ | [manyoyo-app](https://github.com/xcanwin/manyoyo-app) | Flutter 原生 UI 客户端(macOS / Windows / iOS / Android) |
30
+ | [manyoyo-studio](https://github.com/xcanwin/manyoyo-studio) | Electron 桌面端 + Capacitor 移动端 |
31
+
24
32
  ## 为什么是 MANYOYO
25
33
 
26
34
  AI Agent CLI 往往需要:
@@ -145,6 +153,9 @@ manyoyo build --iv 1.9.0-common
145
153
  # full 版本
146
154
  manyoyo build --iv 1.9.0-full
147
155
 
156
+ # 仅更新已有镜像内 Agent CLI 到 latest,不重建 Dockerfile
157
+ manyoyo build --iv 1.9.0-full --update-agents --yes
158
+
148
159
  # 自定义工具集
149
160
  manyoyo build --iba TOOL=go,codex,java,gemini
150
161
  ```
package/bin/manyoyo.js CHANGED
@@ -17,6 +17,7 @@ const { resolveAgentResumeArg, buildAgentResumeCommand } = require('../lib/agent
17
17
  const { runPluginCommand, createPlugin } = require('../lib/plugin');
18
18
  const { buildManyoyoLogPath } = require('../lib/log-path');
19
19
  const { resolveRuntimeConfig } = require('../lib/runtime-resolver');
20
+ const { resolveWorktreeSupport } = require('../lib/worktrees');
20
21
  const {
21
22
  parseEnvEntry: parseEnvEntryOrThrow,
22
23
  normalizeVolume
@@ -938,6 +939,22 @@ function normalizeShellFullArgv(argv) {
938
939
  }
939
940
  }
940
941
 
942
+ function normalizeWorktreeArgv(argv) {
943
+ for (let i = 0; i < argv.length; i += 1) {
944
+ if (argv[i] === '--wt') {
945
+ argv[i] = '--worktrees';
946
+ continue;
947
+ }
948
+ if (argv[i] === '--wtr') {
949
+ argv[i] = '--worktrees-root';
950
+ continue;
951
+ }
952
+ if (typeof argv[i] === 'string' && argv[i].startsWith('--wtr=')) {
953
+ argv[i] = `--worktrees-root=${argv[i].slice('--wtr='.length)}`;
954
+ }
955
+ }
956
+ }
957
+
941
958
  function appendArrayOption(command, flags, description) {
942
959
  return command.option(
943
960
  flags,
@@ -983,6 +1000,8 @@ function applyRunStyleOptions(command, options = {}) {
983
1000
  appendArrayOption(command, '-p, --port <port>', '设置端口映射 XXX:YYY (可多次使用)');
984
1001
 
985
1002
  command
1003
+ .option('--worktrees', '启用 Git worktrees 根目录自动挂载 (别名: --wt)')
1004
+ .option('--worktrees-root <path>', '指定项目级 Git worktrees 根目录 (仅支持绝对路径; 隐式启用 --worktrees; 别名: --wtr)')
986
1005
  .option('--sp, --shell-prefix <command>', '主命令前缀 (常用于临时环境变量)')
987
1006
  .option('-s, --shell <command>', '主命令')
988
1007
  .option('--ss, --shell-suffix <command>', '主命令后缀 (追加到 -s 之后,等价于 -- <args>)')
@@ -1156,6 +1175,7 @@ Notes:
1156
1175
  .option('-r, --run <name>', '加载运行配置 (从 ~/.manyoyo/manyoyo.json 的 runs.<name> 读取)')
1157
1176
  .option('--in, --image-name <name>', '指定镜像名称')
1158
1177
  .option('--iv, --image-ver <version>', '指定镜像版本 (格式: x.y.z-后缀,如 1.7.4-common)')
1178
+ .option('--update-agents', '仅更新已有镜像内 Agent CLI 到 latest (Claude/Codex/Gemini/OpenCode)')
1159
1179
  .option('--yes', '所有提示自动确认 (用于CI/脚本)');
1160
1180
  appendArrayOption(buildCommand, '--iba, --image-build-arg <arg>', '构建镜像时传参给dockerfile (可多次使用)');
1161
1181
  buildCommand.action(options => selectAction('build', options));
@@ -1260,6 +1280,7 @@ Notes:
1260
1280
 
1261
1281
  // Pre-handle -x/--shell-full: treat all following args as a single command
1262
1282
  normalizeShellFullArgv(process.argv);
1283
+ normalizeWorktreeArgv(process.argv);
1263
1284
 
1264
1285
  // Parse arguments
1265
1286
  program.allowUnknownOption(false);
@@ -1357,7 +1378,8 @@ Notes:
1357
1378
  normalizeCliEnvMap,
1358
1379
  mergeArrayConfig,
1359
1380
  normalizeVolume,
1360
- parseServerListen
1381
+ parseServerListen,
1382
+ resolveWorktreeSupport
1361
1383
  });
1362
1384
 
1363
1385
  HOST_PATH = resolvedRuntime.hostPath;
@@ -1432,6 +1454,10 @@ Notes:
1432
1454
  volumes: volumeList,
1433
1455
  ports: portList,
1434
1456
  imageBuildArgs: buildArgList,
1457
+ worktrees: resolvedRuntime.worktrees,
1458
+ worktreesRoot: resolvedRuntime.worktreesRoot,
1459
+ worktreeRepoRoot: resolvedRuntime.worktreeRepoRoot,
1460
+ worktreeMainRepoRoot: resolvedRuntime.worktreeMainRepoRoot,
1435
1461
  containerMode: contModeValue || "",
1436
1462
  shellPrefix: EXEC_COMMAND_PREFIX.trim(),
1437
1463
  shell: EXEC_COMMAND || "",
@@ -1482,6 +1508,7 @@ Notes:
1482
1508
  isServerRestart: isServerRestartMode,
1483
1509
  isServerDetach: Boolean(selectedAction === 'serve' && options.detach),
1484
1510
  isServerListenSpecified: Boolean(isServerMode && options.server !== true),
1511
+ updateAgents: Boolean(options.updateAgents),
1485
1512
  isPluginMode: false
1486
1513
  };
1487
1514
  }
@@ -2107,6 +2134,7 @@ async function main() {
2107
2134
  parseImageVersionTag,
2108
2135
  manyoyoName: MANYOYO_NAME,
2109
2136
  yesMode: Boolean(modeState.yesMode),
2137
+ updateAgents: Boolean(modeState.updateAgents),
2110
2138
  dockerCmd: DOCKER_CMD,
2111
2139
  rootDir: path.join(__dirname, '..'),
2112
2140
  loadConfig,
@@ -2115,7 +2143,9 @@ async function main() {
2115
2143
  pruneDanglingImages,
2116
2144
  colors: { RED, GREEN, YELLOW, BLUE, CYAN, NC }
2117
2145
  });
2118
- syncBuiltImageVersionToGlobalConfig(runtime.imageVersion);
2146
+ if (!modeState.updateAgents) {
2147
+ syncBuiltImageVersionToGlobalConfig(runtime.imageVersion);
2148
+ }
2119
2149
  process.exit(0);
2120
2150
  }
2121
2151
 
@@ -396,6 +396,159 @@ function runCmdPipeline(leftCmd, leftArgs, rightCmd, rightArgs, options = {}) {
396
396
  });
397
397
  }
398
398
 
399
+ const AGENT_UPDATE_PACKAGES = [
400
+ 'npm@latest',
401
+ '@anthropic-ai/claude-code@latest',
402
+ '@openai/codex@latest',
403
+ '@google/gemini-cli@latest',
404
+ 'opencode-ai@latest'
405
+ ];
406
+ const AGENT_VERSION_CHECK_COMMAND = [
407
+ 'printf "claude: " && claude --version',
408
+ 'printf "codex: " && codex --version',
409
+ 'printf "gemini: " && gemini --version',
410
+ 'printf "opencode: " && opencode --version',
411
+ 'printf "npm: " && npm --version'
412
+ ].join(' && ');
413
+
414
+ function createAgentUpdateContainerName() {
415
+ return `manyoyo-update-agents-${crypto.randomBytes(6).toString('hex')}`;
416
+ }
417
+
418
+ const MANYOYO_DEFAULT_CMD = ['supervisord', '-n', '-c', '/etc/supervisor/supervisord.conf'];
419
+
420
+ function parseImageConfig(rawConfig) {
421
+ if (!rawConfig || typeof rawConfig !== 'string') return {};
422
+ try {
423
+ const parsed = JSON.parse(rawConfig);
424
+ return parsed && typeof parsed === 'object' ? parsed : {};
425
+ } catch (e) {
426
+ return {};
427
+ }
428
+ }
429
+
430
+ function isAgentUpdateCommand(cmd) {
431
+ const text = Array.isArray(cmd) ? cmd.join(' ') : String(cmd || '');
432
+ return AGENT_UPDATE_PACKAGES.some(pkg => text.includes(pkg));
433
+ }
434
+
435
+ function buildImageImportChangeArgs(config) {
436
+ const changes = [];
437
+ const envList = Array.isArray(config.Env) ? config.Env : [];
438
+ envList.forEach(env => {
439
+ if (typeof env === 'string' && env.trim()) {
440
+ changes.push('--change', `ENV ${env}`);
441
+ }
442
+ });
443
+
444
+ if (config.Entrypoint) {
445
+ changes.push('--change', `ENTRYPOINT ${JSON.stringify(config.Entrypoint)}`);
446
+ }
447
+
448
+ const cmd = isAgentUpdateCommand(config.Cmd) ? MANYOYO_DEFAULT_CMD : (config.Cmd || MANYOYO_DEFAULT_CMD);
449
+ changes.push('--change', `CMD ${JSON.stringify(cmd)}`);
450
+
451
+ const workingDir = typeof config.WorkingDir === 'string' && config.WorkingDir.trim()
452
+ ? config.WorkingDir
453
+ : '/tmp';
454
+ changes.push('--change', `WORKDIR ${workingDir}`);
455
+
456
+ if (typeof config.User === 'string' && config.User.trim()) {
457
+ changes.push('--change', `USER ${config.User}`);
458
+ }
459
+
460
+ const exposedPorts = config.ExposedPorts && typeof config.ExposedPorts === 'object'
461
+ ? Object.keys(config.ExposedPorts)
462
+ : [];
463
+ exposedPorts.forEach(port => {
464
+ if (port) changes.push('--change', `EXPOSE ${port}`);
465
+ });
466
+
467
+ const labels = config.Labels && typeof config.Labels === 'object' ? config.Labels : {};
468
+ Object.entries(labels).forEach(([key, value]) => {
469
+ changes.push('--change', `LABEL ${key}=${value}`);
470
+ });
471
+
472
+ return changes;
473
+ }
474
+
475
+ async function updateAgentsInExistingImage(ctx, fullImageTag) {
476
+ const { RED, GREEN, YELLOW, BLUE, CYAN, NC } = ctx.colors;
477
+ const containerName = ctx.agentUpdateContainerName || createAgentUpdateContainerName();
478
+ const updateScript = [
479
+ 'echo "[manyoyo] Agent CLI versions before update:"',
480
+ AGENT_VERSION_CHECK_COMMAND,
481
+ `npm_config_update_notifier=false npm install -g ${AGENT_UPDATE_PACKAGES.map(quoteShellArg).join(' ')}`,
482
+ 'echo "[manyoyo] Agent CLI versions after update:"',
483
+ AGENT_VERSION_CHECK_COMMAND,
484
+ 'npm_config_update_notifier=false npm cache clean --force --loglevel=error',
485
+ 'rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.npm ~/.cache/node-gyp ~/.claude/plugins/cache ~/go/pkg/mod/cache',
486
+ 'rm -f /var/log/dpkg.log /var/log/bootstrap.log /var/lib/dpkg/status-old /var/cache/debconf/templates.dat-old'
487
+ ].join(' && ');
488
+ const runArgs = [
489
+ 'run',
490
+ '--name', containerName,
491
+ '--network', 'host',
492
+ fullImageTag,
493
+ '/bin/bash',
494
+ '-lc',
495
+ updateScript
496
+ ];
497
+ const cleanupArgs = ['rm', '-f', containerName];
498
+ let imageConfig = {};
499
+
500
+ ctx.log(`${CYAN}🔄 正在更新已有镜像内 Agent CLI: ${YELLOW}${fullImageTag}${NC}`);
501
+ ctx.log(`${BLUE}更新范围: Claude/Codex/Gemini/OpenCode CLI -> latest${NC}\n`);
502
+
503
+ try {
504
+ imageConfig = parseImageConfig(ctx.runCmd(ctx.dockerCmd, [
505
+ 'image',
506
+ 'inspect',
507
+ fullImageTag,
508
+ '--format',
509
+ '{{json .Config}}'
510
+ ], { stdio: 'pipe' }));
511
+ } catch (e) {
512
+ ctx.error(`${RED}错误: 找不到本地镜像 ${fullImageTag}${NC}`);
513
+ ctx.error(`${YELLOW}请先执行 ${ctx.manyoyoName} build --iv ${fullImageTag.split(':').pop()} --yes 构建镜像。${NC}`);
514
+ ctx.exit(1);
515
+ return;
516
+ }
517
+
518
+ const importArgs = [
519
+ 'import',
520
+ ...buildImageImportChangeArgs(imageConfig),
521
+ '-',
522
+ fullImageTag
523
+ ];
524
+
525
+ ctx.log(`${BLUE}准备执行命令:${NC}`);
526
+ ctx.log(`${ctx.dockerCmd} ${runArgs.map(quoteShellArg).join(' ')}`);
527
+ ctx.log(`${ctx.dockerCmd} export ${quoteShellArg(containerName)} | ${ctx.dockerCmd} ${importArgs.map(quoteShellArg).join(' ')}`);
528
+ ctx.log(`${ctx.dockerCmd} ${cleanupArgs.map(quoteShellArg).join(' ')}\n`);
529
+
530
+ if (!ctx.yesMode) {
531
+ await ctx.askQuestion('❔ 是否继续更新并覆盖当前镜像 tag? [ 直接回车=继续, ctrl+c=取消 ]: ');
532
+ ctx.log('');
533
+ }
534
+
535
+ const updateStartTime = Date.now();
536
+ try {
537
+ ctx.runCmd(ctx.dockerCmd, runArgs, { stdio: 'inherit' });
538
+ await ctx.runCmdPipeline(ctx.dockerCmd, ['export', containerName], ctx.dockerCmd, importArgs, { stdio: 'inherit' });
539
+ const updateDuration = ((Date.now() - updateStartTime) / 1000).toFixed(1);
540
+ ctx.log(`\n${GREEN}✅ Agent CLI 更新成功: ${fullImageTag}${NC}`);
541
+ ctx.log(`${GREEN}⏱️ 更新耗时: ${updateDuration} 秒${NC}`);
542
+ } catch (e) {
543
+ ctx.error(`${RED}错误: Agent CLI 更新失败${NC}`);
544
+ ctx.exit(1);
545
+ } finally {
546
+ try {
547
+ ctx.runCmd(ctx.dockerCmd, cleanupArgs, { stdio: 'inherit', ignoreError: true });
548
+ } catch (e) {}
549
+ }
550
+ }
551
+
399
552
  async function buildImage(options = {}) {
400
553
  const ctx = {
401
554
  imageBuildArgs: Array.isArray(options.imageBuildArgs) ? [...options.imageBuildArgs] : [],
@@ -406,6 +559,8 @@ async function buildImage(options = {}) {
406
559
  parseImageVersionTag: options.parseImageVersionTag,
407
560
  manyoyoName: options.manyoyoName || 'manyoyo',
408
561
  yesMode: Boolean(options.yesMode),
562
+ updateAgents: Boolean(options.updateAgents),
563
+ agentUpdateContainerName: options.agentUpdateContainerName || '',
409
564
  dockerCmd: options.dockerCmd || 'docker',
410
565
  rootDir: options.rootDir || process.cwd(),
411
566
  loadConfig: options.loadConfig || (() => ({})),
@@ -440,6 +595,11 @@ async function buildImage(options = {}) {
440
595
  }
441
596
 
442
597
  const fullImageTag = `${ctx.imageName}:${version}-${imageTool}`;
598
+ if (ctx.updateAgents) {
599
+ await updateAgentsInExistingImage(ctx, fullImageTag);
600
+ return;
601
+ }
602
+
443
603
  ctx.log(`${CYAN}🔨 正在构建镜像: ${YELLOW}${fullImageTag}${NC}`);
444
604
  ctx.log(`${BLUE}构建组件类型: ${imageTool}${NC}\n`);
445
605
 
@@ -23,6 +23,7 @@ function resolveRuntimeConfig(options = {}) {
23
23
  const mergeArrayConfig = params.mergeArrayConfig;
24
24
  const normalizeVolume = params.normalizeVolume;
25
25
  const parseServerListen = params.parseServerListen;
26
+ const resolveWorktreeSupport = params.resolveWorktreeSupport;
26
27
  const argv = Array.isArray(params.argv) ? params.argv : [];
27
28
  const isServerMode = params.isServerMode === true;
28
29
  const isServerStopMode = params.isServerStopMode === true;
@@ -104,7 +105,7 @@ function resolveRuntimeConfig(options = {}) {
104
105
  ...normalizeCliEnvMap(cliOptions.firstEnv)
105
106
  };
106
107
 
107
- const volumes = mergeArrayConfig(globalConfig.volumes, runConfig.volumes, cliOptions.volume)
108
+ let volumes = mergeArrayConfig(globalConfig.volumes, runConfig.volumes, cliOptions.volume)
108
109
  .map(volume => normalizeVolume(volume));
109
110
  const ports = mergeArrayConfig(globalConfig.ports, runConfig.ports, cliOptions.port);
110
111
  const imageBuildArgs = mergeArrayConfig(globalConfig.imageBuildArgs, runConfig.imageBuildArgs, cliOptions.imageBuildArg);
@@ -151,6 +152,25 @@ function resolveRuntimeConfig(options = {}) {
151
152
  hostPath = defaults.hostPath;
152
153
  }
153
154
 
155
+ let worktreeState = {
156
+ enabled: false,
157
+ worktreesRoot: null,
158
+ worktreeRepoRoot: null,
159
+ worktreeMainRepoRoot: null,
160
+ extraVolumes: []
161
+ };
162
+
163
+ if (typeof resolveWorktreeSupport === 'function') {
164
+ worktreeState = resolveWorktreeSupport({
165
+ enabled: Boolean(cliOptions.worktrees || cliOptions.worktreesRoot),
166
+ hostPath,
167
+ containerPath,
168
+ worktreesRoot: cliOptions.worktreesRoot,
169
+ volumes
170
+ });
171
+ volumes = volumes.concat(worktreeState.extraVolumes || []);
172
+ }
173
+
154
174
  return {
155
175
  hostPath,
156
176
  containerName,
@@ -164,6 +184,10 @@ function resolveRuntimeConfig(options = {}) {
164
184
  volumes,
165
185
  ports,
166
186
  imageBuildArgs,
187
+ worktrees: Boolean(worktreeState.enabled),
188
+ worktreesRoot: worktreeState.worktreesRoot,
189
+ worktreeRepoRoot: worktreeState.worktreeRepoRoot,
190
+ worktreeMainRepoRoot: worktreeState.worktreeMainRepoRoot,
167
191
  containerMode,
168
192
  yolo,
169
193
  quiet,
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawnSync } = require('child_process');
6
+
7
+ function defaultRunGitCommand(targetPath, args) {
8
+ const result = spawnSync('git', ['-C', targetPath, ...args], {
9
+ encoding: 'utf-8'
10
+ });
11
+
12
+ if (result.status !== 0) {
13
+ const stderr = String(result.stderr || '').trim();
14
+ throw new Error(stderr || `git ${args.join(' ')} 执行失败`);
15
+ }
16
+
17
+ return String(result.stdout || '').trim();
18
+ }
19
+
20
+ function normalizeAbsolutePath(targetPath) {
21
+ return path.resolve(String(targetPath || '').trim());
22
+ }
23
+
24
+ function isDescendantPath(parentPath, targetPath) {
25
+ const relativePath = path.relative(parentPath, targetPath);
26
+ return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
27
+ }
28
+
29
+ function detectGitWorktreeContext(targetPath, deps = {}) {
30
+ const fsApi = deps.fs || fs;
31
+ const pathApi = deps.path || path;
32
+ const runGitCommand = deps.runGitCommand || defaultRunGitCommand;
33
+ const absoluteTargetPath = normalizeAbsolutePath(targetPath);
34
+
35
+ if (!fsApi.existsSync(absoluteTargetPath)) {
36
+ throw new Error(`启用 --worktrees 时宿主机路径不存在: ${absoluteTargetPath}`);
37
+ }
38
+
39
+ const stats = fsApi.statSync(absoluteTargetPath);
40
+ if (!stats.isDirectory()) {
41
+ throw new Error(`启用 --worktrees 时宿主机路径必须为目录: ${absoluteTargetPath}`);
42
+ }
43
+
44
+ let repoRoot;
45
+ let commonDirRaw;
46
+ try {
47
+ repoRoot = pathApi.resolve(runGitCommand(absoluteTargetPath, ['rev-parse', '--show-toplevel']));
48
+ commonDirRaw = runGitCommand(absoluteTargetPath, ['rev-parse', '--git-common-dir']);
49
+ } catch (error) {
50
+ throw new Error(`启用 --worktrees 失败: ${absoluteTargetPath} 不在 Git 仓库内`);
51
+ }
52
+
53
+ const commonDir = pathApi.isAbsolute(commonDirRaw)
54
+ ? pathApi.normalize(commonDirRaw)
55
+ : pathApi.resolve(repoRoot, commonDirRaw);
56
+ const mainRepoRoot = pathApi.dirname(commonDir);
57
+ const projectName = pathApi.basename(mainRepoRoot);
58
+
59
+ return {
60
+ targetPath: absoluteTargetPath,
61
+ repoRoot,
62
+ mainRepoRoot,
63
+ isWorktree: repoRoot !== mainRepoRoot,
64
+ projectName,
65
+ defaultWorktreesRoot: pathApi.join(pathApi.dirname(mainRepoRoot), 'worktrees', projectName)
66
+ };
67
+ }
68
+
69
+ function shouldAddSamePathMount(hostPath, containerPath, targetPath) {
70
+ if (hostPath !== containerPath) {
71
+ return true;
72
+ }
73
+ return !isDescendantPath(hostPath, targetPath);
74
+ }
75
+
76
+ function resolveWorktreeSupport(options = {}, deps = {}) {
77
+ const fsApi = deps.fs || fs;
78
+ const pathApi = deps.path || path;
79
+ const enabled = options.enabled === true || Boolean(options.worktreesRoot);
80
+
81
+ if (!enabled) {
82
+ return {
83
+ enabled: false,
84
+ worktreesRoot: null,
85
+ worktreeRepoRoot: null,
86
+ worktreeMainRepoRoot: null,
87
+ extraVolumes: []
88
+ };
89
+ }
90
+
91
+ const hostPath = normalizeAbsolutePath(options.hostPath);
92
+ const containerPath = String(options.containerPath || hostPath).trim() || hostPath;
93
+ const detected = detectGitWorktreeContext(hostPath, deps);
94
+ let worktreesRoot = options.worktreesRoot;
95
+
96
+ if (worktreesRoot !== undefined && worktreesRoot !== null && String(worktreesRoot).trim() !== '') {
97
+ if (!pathApi.isAbsolute(worktreesRoot)) {
98
+ throw new Error(`--worktrees-root 仅支持绝对路径: ${worktreesRoot}`);
99
+ }
100
+ worktreesRoot = pathApi.resolve(worktreesRoot);
101
+ } else {
102
+ worktreesRoot = detected.defaultWorktreesRoot;
103
+ }
104
+
105
+ fsApi.mkdirSync(worktreesRoot, { recursive: true });
106
+
107
+ const existingVolumes = new Set((options.volumes || []).map(item => String(item)));
108
+ const extraVolumes = [];
109
+ [detected.mainRepoRoot, worktreesRoot].forEach(targetPath => {
110
+ if (!shouldAddSamePathMount(hostPath, containerPath, targetPath)) {
111
+ return;
112
+ }
113
+ const volume = `${targetPath}:${targetPath}`;
114
+ if (existingVolumes.has(volume) || extraVolumes.includes(volume)) {
115
+ return;
116
+ }
117
+ extraVolumes.push(volume);
118
+ });
119
+
120
+ return {
121
+ enabled: true,
122
+ worktreesRoot,
123
+ worktreeRepoRoot: detected.repoRoot,
124
+ worktreeMainRepoRoot: detected.mainRepoRoot,
125
+ extraVolumes
126
+ };
127
+ }
128
+
129
+ module.exports = {
130
+ detectGitWorktreeContext,
131
+ resolveWorktreeSupport
132
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcanwin/manyoyo",
3
- "version": "5.9.11",
3
+ "version": "5.10.3",
4
4
  "imageVersion": "1.9.0-common",
5
5
  "playwrightCliVersion": "0.1.1",
6
6
  "description": "AI Agent CLI Security Sandbox for Docker and Podman",
@@ -87,6 +87,7 @@
87
87
  "esbuild": "^0.25.12",
88
88
  "glob": "^13.0.6",
89
89
  "minimatch": "^10.2.2",
90
+ "postcss": "^8.5.10",
90
91
  "test-exclude": "^8.0.0",
91
92
  "vite": "^6.4.2"
92
93
  },