@xcanwin/manyoyo 3.5.7 → 3.7.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.
- package/README.md +97 -17
- package/bin/manyoyo.js +217 -228
- package/config.example.json +21 -0
- package/docker/manyoyo.Dockerfile +1 -1
- package/docs/README_EN.md +95 -15
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ npm install -g .
|
|
|
40
40
|
## 2. 安装 podman
|
|
41
41
|
|
|
42
42
|
2.1 安装 [podman](https://podman.io/docs/installation)
|
|
43
|
+
|
|
43
44
|
2.2 拉取基础镜像
|
|
44
45
|
|
|
45
46
|
```bash
|
|
@@ -100,31 +101,40 @@ manyoyo --irm
|
|
|
100
101
|
|
|
101
102
|
# 静默显示执行命令
|
|
102
103
|
manyoyo -q full -x echo "hello world"
|
|
104
|
+
manyoyo -q tip -q cmd -x echo "hello world" # 多次使用静默选项
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
### 环境变量
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
给容器内CLI传递BASE_URL和TOKEN等。
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
# 直接传递
|
|
111
|
-
manyoyo -e "VAR=value" -x env
|
|
111
|
+
#### 字符串形式
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
manyoyo -e "
|
|
113
|
+
```bash
|
|
114
|
+
manyoyo -e "ANTHROPIC_BASE_URL=https://xxxx" -e "ANTHROPIC_AUTH_TOKEN=your-key" -x claude
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
####
|
|
117
|
+
#### 文件形式
|
|
118
|
+
|
|
119
|
+
环境文件使用 `.env` 格式,支持注释(以 `#` 开头的行):
|
|
118
120
|
|
|
119
121
|
```bash
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
export ANTHROPIC_BASE_URL="https://xxxx"
|
|
123
|
+
AUTH_TOANTHROPIC_AUTH_TOKENKEN=your-key
|
|
124
|
+
# MESSAGE="Hello World" # 注释会被忽略
|
|
125
|
+
TESTPATH='/usr/local/bin'
|
|
122
126
|
```
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
**环境文件路径规则**:
|
|
129
|
+
- `manyoyo --ef myconfig` → 加载 `~/.manyoyo/env/myconfig.env`
|
|
130
|
+
- `manyoyo --ef ./myconfig.env` → 加载当前目录的 `myconfig.env`
|
|
125
131
|
|
|
126
132
|
```bash
|
|
127
|
-
#
|
|
133
|
+
# 创建环境文件目录
|
|
134
|
+
mkdir -p ~/.manyoyo/env/
|
|
135
|
+
|
|
136
|
+
# 示例:创建 Claude 环境文件
|
|
137
|
+
cat > ~/.manyoyo/env/claude.env << 'EOF'
|
|
128
138
|
export ANTHROPIC_BASE_URL="https://api.anthropic.com"
|
|
129
139
|
# export CLAUDE_CODE_OAUTH_TOKEN="sk-xxxxxxxx"
|
|
130
140
|
export ANTHROPIC_AUTH_TOKEN="sk-xxxxxxxx"
|
|
@@ -134,13 +144,82 @@ export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-opus-4-5"
|
|
|
134
144
|
export ANTHROPIC_DEFAULT_SONNET_MODEL="claude-sonnet-4-5"
|
|
135
145
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-haiku-4-5"
|
|
136
146
|
export CLAUDE_CODE_SUBAGENT_MODEL="claude-sonnet-4-5"
|
|
147
|
+
EOF
|
|
148
|
+
|
|
149
|
+
# 在任意目录下使用环境文件
|
|
150
|
+
manyoyo --ef claude -x claude
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 配置文件
|
|
154
|
+
|
|
155
|
+
简化MANYOYO命令行操作。配置文件使用 **JSON5 格式**,支持注释、尾随逗号等特性。
|
|
156
|
+
|
|
157
|
+
#### 配置文件路径规则
|
|
158
|
+
|
|
159
|
+
- `manyoyo -r myconfig` → 加载 `~/.manyoyo/run/myconfig.json`
|
|
160
|
+
- `manyoyo -r ./myconfig.json` → 加载当前目录的 `myconfig.json`
|
|
161
|
+
- `manyoyo [任何选项]` → 始终会加载全局配置 `~/.manyoyo/manyoyo.json`
|
|
137
162
|
|
|
138
|
-
|
|
139
|
-
API_KEY=your-api-key-here
|
|
163
|
+
#### 配置选项
|
|
140
164
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
参考 `config.example.json` 文件查看所有可配置项:
|
|
166
|
+
|
|
167
|
+
```json5
|
|
168
|
+
{
|
|
169
|
+
// 容器基础配置
|
|
170
|
+
"containerName": "myy-dev", // 默认容器名称
|
|
171
|
+
"hostPath": "/path/to/project", // 默认宿主机工作目录
|
|
172
|
+
"containerPath": "/path/to/project", // 默认容器工作目录
|
|
173
|
+
"imageName": "localhost/xcanwin/manyoyo", // 默认镜像名称
|
|
174
|
+
"imageVersion": "1.6.3-full", // 默认镜像版本
|
|
175
|
+
"containerMode": "common", // 容器嵌套模式 (common, dind, sock)
|
|
176
|
+
|
|
177
|
+
// 环境变量配置
|
|
178
|
+
"envFile": [
|
|
179
|
+
"claude" // 对应 ~/.manyoyo/env/claude.env
|
|
180
|
+
],
|
|
181
|
+
"env": [], // 默认环境变量数组
|
|
182
|
+
|
|
183
|
+
// 其他配置
|
|
184
|
+
"volumes": [], // 默认挂载卷数组
|
|
185
|
+
"shellPrefix": "", // 默认命令前缀
|
|
186
|
+
"shell": "", // 默认执行命令
|
|
187
|
+
"yolo": "", // 默认 YOLO 模式 (c, gm, cx, oc)
|
|
188
|
+
"quiet": [], // 默认静默选项数组 (支持 ["tip", "cmd"] 格式)
|
|
189
|
+
"imageBuildArgs": [] // 默认镜像构建参数
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### 优先级
|
|
194
|
+
|
|
195
|
+
- **覆盖型参数**:命令行 > 运行配置 > 全局配置 > 默认值
|
|
196
|
+
- **合并型参数**:全局配置 + 运行配置 + 命令行(按顺序累加)
|
|
197
|
+
|
|
198
|
+
覆盖型参数包括:`containerName`, `hostPath`, `containerPath`, `imageName`, `imageVersion`, `containerMode`, `shellPrefix`, `shell`, `yolo`, `quiet`
|
|
199
|
+
|
|
200
|
+
合并型参数包括:`envFile`, `env`, `volumes`, `imageBuildArgs`
|
|
201
|
+
|
|
202
|
+
#### 常用样例
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# 创建运行配置目录
|
|
206
|
+
mkdir -p ~/.manyoyo/run/
|
|
207
|
+
|
|
208
|
+
# 创建 Claude 运行配置
|
|
209
|
+
cat > ~/.manyoyo/run/c.json << 'EOF'
|
|
210
|
+
{
|
|
211
|
+
// Claude Code 快捷配置
|
|
212
|
+
"imageName": "localhost/xcanwin/manyoyo",
|
|
213
|
+
"imageVersion": "1.6.3-full",
|
|
214
|
+
"envFile": [
|
|
215
|
+
"claude" // 自动加载 ~/.manyoyo/env/claude.env
|
|
216
|
+
],
|
|
217
|
+
"yolo": "c"
|
|
218
|
+
}
|
|
219
|
+
EOF
|
|
220
|
+
|
|
221
|
+
# 在任意目录下使用运行配置
|
|
222
|
+
manyoyo -r c
|
|
144
223
|
```
|
|
145
224
|
|
|
146
225
|
### AI CLI 快捷方式(跳过权限确认)
|
|
@@ -212,7 +291,7 @@ docker ps -a # 现在可以在容器内使用 docker 命令
|
|
|
212
291
|
| `--iba XXX=YYY` | `--image-build-arg` | 构建镜像时传参给dockerfile |
|
|
213
292
|
| `--irm` | `--image-remove` | 清理悬空镜像和 `<none>` 镜像 |
|
|
214
293
|
| `-e STRING` | `--env` | 设置环境变量 |
|
|
215
|
-
| `--ef FILE` | `--env-file` |
|
|
294
|
+
| `--ef FILE` | `--env-file` | 从文件加载环境变量(支持 `name` 或 `./path.env`) |
|
|
216
295
|
| `-v STRING` | `--volume` | 绑定挂载卷 |
|
|
217
296
|
| `--sp CMD` | `--shell-prefix` | 临时环境变量(作为 -s 的前缀) |
|
|
218
297
|
| `-s CMD` | `--shell` | 指定要执行的命令 |
|
|
@@ -221,6 +300,7 @@ docker ps -a # 现在可以在容器内使用 docker 命令
|
|
|
221
300
|
| `-y CLI` | `--yolo` | 无需确认运行 AI 智能体 |
|
|
222
301
|
| `--install NAME` | | 安装 manyoyo 命令 |
|
|
223
302
|
| `-q LIST` | `--quiet` | 静默显示 |
|
|
303
|
+
| `-r NAME` | `--run` | 加载运行配置(支持 `name` 或 `./path.json`) |
|
|
224
304
|
| `-V` | `--version` | 显示版本 |
|
|
225
305
|
| `-h` | `--help` | 显示帮助 |
|
|
226
306
|
|
package/bin/manyoyo.js
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
const { execSync, spawnSync } = require('child_process');
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
10
11
|
const readline = require('readline');
|
|
12
|
+
const { Command } = require('commander');
|
|
13
|
+
const JSON5 = require('json5');
|
|
11
14
|
const { version: BIN_VERSION, imageVersion: IMAGE_VERSION_BASE } = require('../package.json');
|
|
12
15
|
|
|
13
16
|
// Helper function to format date like bash $(date +%m%d-%H%M)
|
|
@@ -59,59 +62,61 @@ function sleep(ms) {
|
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
// ==============================================================================
|
|
62
|
-
//
|
|
65
|
+
// Configuration File Functions
|
|
63
66
|
// ==============================================================================
|
|
64
67
|
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.log(" -l|--cl|--cont-list 列举容器");
|
|
78
|
-
console.log(" --crm|--cont-remove 删除-n指定容器");
|
|
79
|
-
console.log(" -m|--cm|--cont-mode STRING 设置容器嵌套容器模式");
|
|
80
|
-
console.log(" 例如 common, dind, sock");
|
|
81
|
-
console.log(" --in|--image-name NAME 指定镜像名称");
|
|
82
|
-
console.log(" --iv|--image-ver VERSION 指定镜像版本");
|
|
83
|
-
console.log(" --ib|--image-build 构建镜像");
|
|
84
|
-
console.log(" --iba|--image-build-arg XXX=YYY 构建镜像时传参给dockerfile");
|
|
85
|
-
console.log(" --irm|--image-remove 清理悬空镜像和 <none> 镜像");
|
|
86
|
-
console.log(" -e|--env XXX=YYY 设置环境变量");
|
|
87
|
-
console.log(" --ef|--env-file ENV_FILE 设置环境变量通过文件");
|
|
88
|
-
console.log(" -v|--volume XXX:YYY 绑定挂载卷");
|
|
89
|
-
console.log(" --sp|--shell-prefix COMMAND 临时环境变量 (作为-s前缀)");
|
|
90
|
-
console.log(" -s|--shell COMMAND 指定命令执行");
|
|
91
|
-
console.log(" --|--shell-suffix COMMAND 指定命令参数, --后面全部直传 (作为-s后缀)");
|
|
92
|
-
console.log(" -x|--shell-full COMMAND 指定完整命令执行, -x后面全部直传 (代替--sp和-s和--命令)");
|
|
93
|
-
console.log(" -y|--yolo CLI 使AGENT无需确认 (代替-s命令)");
|
|
94
|
-
console.log(" 例如 claude / c, gemini / gm, codex / cx, opencode / oc");
|
|
95
|
-
console.log(" --install NAME 安装manyoyo命令");
|
|
96
|
-
console.log(" 例如 docker-cli-plugin");
|
|
97
|
-
console.log(" -q|--quiet LIST 静默显示");
|
|
98
|
-
console.log(" 例如 cnew,crm,tip,cmd,full");
|
|
99
|
-
console.log(" -V|--version 显示版本");
|
|
100
|
-
console.log(" -h|--help 显示帮助");
|
|
101
|
-
console.log("");
|
|
102
|
-
console.log(`${BLUE}Example:${NC}`);
|
|
103
|
-
console.log(` ${MANYOYO_NAME} --ib 构建镜像`);
|
|
104
|
-
console.log(` ${MANYOYO_NAME} -n test --ef ./xxx.env -y c 设置环境变量并运行无需确认的AGENT`);
|
|
105
|
-
console.log(` ${MANYOYO_NAME} -n test -- -c 恢复之前会话`);
|
|
106
|
-
console.log(` ${MANYOYO_NAME} -x echo 123 指定命令执行`);
|
|
107
|
-
console.log(` ${MANYOYO_NAME} -n test --ef ./xxx.env -x claude 设置环境变量并运行`);
|
|
108
|
-
console.log(` ${MANYOYO_NAME} -n test -x claude -c 恢复之前会话`);
|
|
68
|
+
function loadConfig() {
|
|
69
|
+
const configPath = path.join(os.homedir(), '.manyoyo', 'manyoyo.json');
|
|
70
|
+
if (fs.existsSync(configPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const config = JSON5.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
73
|
+
return config;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
console.error(`${YELLOW}⚠️ 配置文件格式错误: ${configPath}${NC}`);
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {};
|
|
109
80
|
}
|
|
110
81
|
|
|
111
|
-
function
|
|
112
|
-
|
|
82
|
+
function loadRunConfig(name) {
|
|
83
|
+
// Check if name is a file path (contains path separator or extension)
|
|
84
|
+
const isFilePath = name.includes('/') || name.includes('\\') || path.extname(name);
|
|
85
|
+
|
|
86
|
+
if (isFilePath) {
|
|
87
|
+
// If it's a file path, only check that exact path
|
|
88
|
+
if (fs.existsSync(name)) {
|
|
89
|
+
try {
|
|
90
|
+
const config = JSON5.parse(fs.readFileSync(name, 'utf-8'));
|
|
91
|
+
return config;
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error(`${YELLOW}⚠️ 运行配置文件格式错误: ${name}${NC}`);
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
// If it's just a name, only check ~/.manyoyo/run/name.json
|
|
99
|
+
const configPath = path.join(os.homedir(), '.manyoyo', 'run', `${name}.json`);
|
|
100
|
+
if (fs.existsSync(configPath)) {
|
|
101
|
+
try {
|
|
102
|
+
const config = JSON5.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
103
|
+
return config;
|
|
104
|
+
} catch (e) {
|
|
105
|
+
console.error(`${YELLOW}⚠️ 运行配置文件格式错误: ${configPath}${NC}`);
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.error(`${RED}⚠️ 未找到运行配置: ${name}${NC}`);
|
|
112
|
+
return {};
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// ==============================================================================
|
|
116
|
+
// UI Functions
|
|
117
|
+
// ==============================================================================
|
|
118
|
+
|
|
119
|
+
|
|
115
120
|
function getHelloTip(containerName, defaultCommand) {
|
|
116
121
|
if ( !(QUIET.tip || QUIET.full) ) {
|
|
117
122
|
console.log("");
|
|
@@ -126,8 +131,12 @@ function getHelloTip(containerName, defaultCommand) {
|
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
function setQuiet(
|
|
130
|
-
|
|
134
|
+
function setQuiet(actions) {
|
|
135
|
+
// Support both string and array input
|
|
136
|
+
const actionArray = Array.isArray(actions) ? actions : [actions];
|
|
137
|
+
actionArray.forEach(action => {
|
|
138
|
+
// Remove comma splitting - each action should be a single quiet option
|
|
139
|
+
const ac = action.trim();
|
|
131
140
|
switch (ac) {
|
|
132
141
|
case 'cnew':
|
|
133
142
|
QUIET.cnew = 1;
|
|
@@ -149,7 +158,6 @@ function setQuiet(action) {
|
|
|
149
158
|
break;
|
|
150
159
|
}
|
|
151
160
|
});
|
|
152
|
-
// process.exit(0);
|
|
153
161
|
}
|
|
154
162
|
|
|
155
163
|
async function askQuestion(prompt) {
|
|
@@ -175,9 +183,21 @@ function addEnv(env) {
|
|
|
175
183
|
}
|
|
176
184
|
|
|
177
185
|
function addEnvFile(envFile) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
186
|
+
// Check if envFile is a file path (contains path separator)
|
|
187
|
+
const isFilePath = envFile.includes('/') || envFile.includes('\\');
|
|
188
|
+
|
|
189
|
+
let filePath;
|
|
190
|
+
if (isFilePath) {
|
|
191
|
+
// If it's a file path, only check that exact path
|
|
192
|
+
filePath = envFile;
|
|
193
|
+
} else {
|
|
194
|
+
// If it's just a name, only check ~/.manyoyo/env/name.env
|
|
195
|
+
filePath = path.join(os.homedir(), '.manyoyo', 'env', `${envFile}.env`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
ENV_FILE = filePath;
|
|
199
|
+
if (fs.existsSync(filePath)) {
|
|
200
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
181
201
|
const lines = content.split('\n');
|
|
182
202
|
|
|
183
203
|
for (let line of lines) {
|
|
@@ -203,7 +223,10 @@ function addEnvFile(envFile) {
|
|
|
203
223
|
}
|
|
204
224
|
}
|
|
205
225
|
}
|
|
226
|
+
return {};
|
|
206
227
|
}
|
|
228
|
+
console.error(`${RED}⚠️ 未找到环境文件: ${envFile}${NC}`);
|
|
229
|
+
return {};
|
|
207
230
|
}
|
|
208
231
|
|
|
209
232
|
function addVolume(volume) {
|
|
@@ -231,7 +254,7 @@ function setYolo(cli) {
|
|
|
231
254
|
EXEC_COMMAND = "opencode";
|
|
232
255
|
break;
|
|
233
256
|
default:
|
|
234
|
-
console.log(`${RED}⚠️
|
|
257
|
+
console.log(`${RED}⚠️ 未知LLM CLI: ${cli}${NC}`);
|
|
235
258
|
process.exit(0);
|
|
236
259
|
}
|
|
237
260
|
}
|
|
@@ -251,10 +274,10 @@ function setContMode(mode) {
|
|
|
251
274
|
case 'sock':
|
|
252
275
|
case 's':
|
|
253
276
|
CONT_MODE = "--privileged --volume /var/run/docker.sock:/var/run/docker.sock --env DOCKER_HOST=unix:///var/run/docker.sock --env CONTAINER_HOST=unix:///var/run/docker.sock";
|
|
254
|
-
console.log(`${RED}⚠️
|
|
277
|
+
console.log(`${RED}⚠️ 开启危险的容器嵌套容器模式, 危害: 容器可访问宿主机文件${NC}`);
|
|
255
278
|
break;
|
|
256
279
|
default:
|
|
257
|
-
console.log(`${RED}⚠️
|
|
280
|
+
console.log(`${RED}⚠️ 未知模式: ${mode}${NC}`);
|
|
258
281
|
process.exit(0);
|
|
259
282
|
}
|
|
260
283
|
}
|
|
@@ -565,188 +588,157 @@ async function buildImage(IMAGE_BUILD_ARGS, imageName, imageVersion) {
|
|
|
565
588
|
// Main Function Helpers
|
|
566
589
|
// ==============================================================================
|
|
567
590
|
|
|
568
|
-
function
|
|
569
|
-
//
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
591
|
+
function setupCommander() {
|
|
592
|
+
// Load config file
|
|
593
|
+
const config = loadConfig();
|
|
594
|
+
|
|
595
|
+
const program = new Command();
|
|
596
|
+
|
|
597
|
+
program
|
|
598
|
+
.name(MANYOYO_NAME)
|
|
599
|
+
.version(BIN_VERSION, '-V, --version', '显示版本')
|
|
600
|
+
.description('MANYOYO - AI Agent CLI Sandbox\nhttps://github.com/xcanwin/manyoyo')
|
|
601
|
+
.addHelpText('after', `
|
|
602
|
+
配置文件:
|
|
603
|
+
~/.manyoyo/manyoyo.json 全局配置文件 (JSON5格式,支持注释)
|
|
604
|
+
~/.manyoyo/run/c.json 运行配置示例
|
|
605
|
+
|
|
606
|
+
路径规则:
|
|
607
|
+
-r name → ~/.manyoyo/run/name.json
|
|
608
|
+
-r ./file.json → 当前目录的 file.json
|
|
609
|
+
--ef name → ~/.manyoyo/env/name.env
|
|
610
|
+
--ef ./file.env → 当前目录的 file.env
|
|
611
|
+
|
|
612
|
+
示例:
|
|
613
|
+
${MANYOYO_NAME} --ib 构建镜像
|
|
614
|
+
${MANYOYO_NAME} -r c 使用 ~/.manyoyo/run/c.json 配置
|
|
615
|
+
${MANYOYO_NAME} -r ./myconfig.json 使用当前目录 ./myconfig.json 配置
|
|
616
|
+
${MANYOYO_NAME} -n test --ef claude -y c 使用 ~/.manyoyo/env/claude.env 环境变量文件
|
|
617
|
+
${MANYOYO_NAME} -n test --ef ./myenv.env -y c 使用当前目录 ./myenv.env 环境变量文件
|
|
618
|
+
${MANYOYO_NAME} -n test -- -c 恢复之前会话
|
|
619
|
+
${MANYOYO_NAME} -x echo 123 指定命令执行
|
|
620
|
+
${MANYOYO_NAME} -n test -q tip -q cmd 多次使用静默选项
|
|
621
|
+
`);
|
|
622
|
+
|
|
623
|
+
// Options
|
|
624
|
+
program
|
|
625
|
+
.option('-r, --run <name>', '加载运行配置 (name → ~/.manyoyo/run/name.json, ./file.json → 当前目录文件)')
|
|
626
|
+
.option('--hp, --host-path <path>', '设置宿主机工作目录 (默认当前路径)')
|
|
627
|
+
.option('-n, --cont-name <name>', '设置容器名称')
|
|
628
|
+
.option('--cp, --cont-path <path>', '设置容器工作目录')
|
|
629
|
+
.option('-l, --cont-list', '列举容器')
|
|
630
|
+
.option('--crm, --cont-remove', '删除-n指定容器')
|
|
631
|
+
.option('-m, --cont-mode <mode>', '设置容器嵌套容器模式 (common, dind, sock)')
|
|
632
|
+
.option('--in, --image-name <name>', '指定镜像名称')
|
|
633
|
+
.option('--iv, --image-ver <version>', '指定镜像版本')
|
|
634
|
+
.option('--ib, --image-build', '构建镜像')
|
|
635
|
+
.option('--iba, --image-build-arg <arg>', '构建镜像时传参给dockerfile (可多次使用)', (value, previous) => [...(previous || []), value], [])
|
|
636
|
+
.option('--irm, --image-remove', '清理悬空镜像和 <none> 镜像')
|
|
637
|
+
.option('-e, --env <env>', '设置环境变量 XXX=YYY (可多次使用)', (value, previous) => [...(previous || []), value], [])
|
|
638
|
+
.option('--ef, --env-file <file>', '设置环境变量通过文件 (name → ~/.manyoyo/env/name.env, ./file.env → 当前目录文件)', (value, previous) => [...(previous || []), value], [])
|
|
639
|
+
.option('-v, --volume <volume>', '绑定挂载卷 XXX:YYY (可多次使用)', (value, previous) => [...(previous || []), value], [])
|
|
640
|
+
.option('--sp, --shell-prefix <command>', '临时环境变量 (作为-s前缀)')
|
|
641
|
+
.option('-s, --shell <command>', '指定命令执行')
|
|
642
|
+
.option('-x, --shell-full <command...>', '指定完整命令执行 (代替--sp和-s和--命令)')
|
|
643
|
+
.option('-y, --yolo <cli>', '使AGENT无需确认 (claude/c, gemini/gm, codex/cx, opencode/oc)')
|
|
644
|
+
.option('--install <name>', '安装manyoyo命令 (docker-cli-plugin)')
|
|
645
|
+
.option('-q, --quiet <item>', '静默显示 (可多次使用: cnew,crm,tip,cmd,full)', (value, previous) => [...(previous || []), value], []);
|
|
646
|
+
|
|
647
|
+
// Docker CLI plugin metadata check
|
|
579
648
|
if (process.argv[2] === 'docker-cli-plugin-metadata') {
|
|
580
|
-
|
|
649
|
+
console.log(JSON.stringify({
|
|
581
650
|
"SchemaVersion": "0.1.0",
|
|
582
651
|
"Vendor": "xcanwin",
|
|
583
652
|
"Version": "v1.0.0",
|
|
584
653
|
"Description": "AI Agent CLI Sandbox"
|
|
585
|
-
};
|
|
586
|
-
console.log(JSON.stringify(metadata, null, 2));
|
|
654
|
+
}, null, 2));
|
|
587
655
|
process.exit(0);
|
|
588
656
|
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function parseArguments(argv) {
|
|
592
|
-
// Parse arguments
|
|
593
|
-
let args = argv.slice(2);
|
|
594
657
|
|
|
595
658
|
// Docker CLI plugin mode - remove first arg if running as plugin
|
|
596
659
|
const dockerPluginPath = path.join(process.env.HOME || '', '.docker/cli-plugins/docker-manyoyo');
|
|
597
|
-
if (argv[1] === dockerPluginPath &&
|
|
598
|
-
|
|
660
|
+
if (process.argv[1] === dockerPluginPath && process.argv[2] === 'manyoyo') {
|
|
661
|
+
process.argv.splice(2, 1);
|
|
599
662
|
}
|
|
600
663
|
|
|
601
|
-
//
|
|
602
|
-
|
|
603
|
-
while (i < args.length) {
|
|
604
|
-
const arg = args[i];
|
|
605
|
-
|
|
606
|
-
switch (arg) {
|
|
607
|
-
case '-q':
|
|
608
|
-
case '--quiet':
|
|
609
|
-
setQuiet(args[i + 1]);
|
|
610
|
-
i += 2;
|
|
611
|
-
break;
|
|
612
|
-
|
|
613
|
-
case '--hp':
|
|
614
|
-
case '--host-path':
|
|
615
|
-
HOST_PATH = args[i + 1];
|
|
616
|
-
i += 2;
|
|
617
|
-
break;
|
|
618
|
-
|
|
619
|
-
case '-n':
|
|
620
|
-
case '--cn':
|
|
621
|
-
case '--cont-name':
|
|
622
|
-
CONTAINER_NAME = args[i + 1];
|
|
623
|
-
i += 2;
|
|
624
|
-
break;
|
|
625
|
-
|
|
626
|
-
case '--cp':
|
|
627
|
-
case '--cont-path':
|
|
628
|
-
CONTAINER_PATH = args[i + 1];
|
|
629
|
-
i += 2;
|
|
630
|
-
break;
|
|
631
|
-
|
|
632
|
-
case '-l':
|
|
633
|
-
case '--cl':
|
|
634
|
-
case '--cont-list':
|
|
635
|
-
getContList();
|
|
636
|
-
process.exit(0);
|
|
637
|
-
|
|
638
|
-
case '--crm':
|
|
639
|
-
case '--cont-remove':
|
|
640
|
-
SHOULD_REMOVE = true;
|
|
641
|
-
i += 1;
|
|
642
|
-
break;
|
|
643
|
-
|
|
644
|
-
case '--in':
|
|
645
|
-
case '--image-name':
|
|
646
|
-
IMAGE_NAME = args[i + 1];
|
|
647
|
-
i += 2;
|
|
648
|
-
break;
|
|
649
|
-
|
|
650
|
-
case '--iv':
|
|
651
|
-
case '--image-ver':
|
|
652
|
-
IMAGE_VERSION = args[i + 1];
|
|
653
|
-
i += 2;
|
|
654
|
-
break;
|
|
655
|
-
|
|
656
|
-
case '-e':
|
|
657
|
-
case '--env':
|
|
658
|
-
addEnv(args[i + 1]);
|
|
659
|
-
i += 2;
|
|
660
|
-
break;
|
|
661
|
-
|
|
662
|
-
case '--ef':
|
|
663
|
-
case '--env-file':
|
|
664
|
-
addEnvFile(args[i + 1]);
|
|
665
|
-
i += 2;
|
|
666
|
-
break;
|
|
664
|
+
// Ensure docker/podman is available
|
|
665
|
+
ensureDocker();
|
|
667
666
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
i += 2;
|
|
672
|
-
break;
|
|
667
|
+
// Parse arguments
|
|
668
|
+
program.allowUnknownOption(false);
|
|
669
|
+
program.parse(process.argv);
|
|
673
670
|
|
|
674
|
-
|
|
675
|
-
case '--shell-prefix':
|
|
676
|
-
EXEC_COMMAND_PREFIX = args[i + 1] + " ";
|
|
677
|
-
i += 2;
|
|
678
|
-
break;
|
|
671
|
+
const options = program.opts();
|
|
679
672
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
EXEC_COMMAND = args[i + 1];
|
|
683
|
-
i += 2;
|
|
684
|
-
break;
|
|
673
|
+
// Load run config if specified
|
|
674
|
+
const runConfig = options.run ? loadRunConfig(options.run) : {};
|
|
685
675
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
676
|
+
// Merge configs: command line > run config > global config > defaults
|
|
677
|
+
// Override mode (scalar values): use first defined value
|
|
678
|
+
HOST_PATH = options.hostPath || runConfig.hostPath || config.hostPath || HOST_PATH;
|
|
679
|
+
if (options.contName || runConfig.containerName || config.containerName) {
|
|
680
|
+
CONTAINER_NAME = options.contName || runConfig.containerName || config.containerName;
|
|
681
|
+
}
|
|
682
|
+
if (options.contPath || runConfig.containerPath || config.containerPath) {
|
|
683
|
+
CONTAINER_PATH = options.contPath || runConfig.containerPath || config.containerPath;
|
|
684
|
+
}
|
|
685
|
+
IMAGE_NAME = options.imageName || runConfig.imageName || config.imageName || IMAGE_NAME;
|
|
686
|
+
if (options.imageVer || runConfig.imageVersion || config.imageVersion) {
|
|
687
|
+
IMAGE_VERSION = options.imageVer || runConfig.imageVersion || config.imageVersion;
|
|
688
|
+
}
|
|
689
|
+
if (options.shellPrefix || runConfig.shellPrefix || config.shellPrefix) {
|
|
690
|
+
EXEC_COMMAND_PREFIX = (options.shellPrefix || runConfig.shellPrefix || config.shellPrefix) + " ";
|
|
691
|
+
}
|
|
692
|
+
if (options.shell || runConfig.shell || config.shell) {
|
|
693
|
+
EXEC_COMMAND = options.shell || runConfig.shell || config.shell;
|
|
694
|
+
}
|
|
692
695
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
696
|
+
// Merge mode (array values): concatenate all sources
|
|
697
|
+
const toArray = (val) => Array.isArray(val) ? val : (val ? [val] : []);
|
|
698
|
+
const envFileList = [
|
|
699
|
+
...toArray(config.envFile),
|
|
700
|
+
...toArray(runConfig.envFile),
|
|
701
|
+
...(options.envFile || [])
|
|
702
|
+
].filter(Boolean);
|
|
703
|
+
envFileList.forEach(ef => addEnvFile(ef));
|
|
699
704
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
setYolo(args[i + 1]);
|
|
703
|
-
i += 2;
|
|
704
|
-
break;
|
|
705
|
+
const envList = [...(config.env || []), ...(runConfig.env || []), ...(options.env || [])];
|
|
706
|
+
envList.forEach(e => addEnv(e));
|
|
705
707
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
case '--cont-mode':
|
|
709
|
-
setContMode(args[i + 1]);
|
|
710
|
-
i += 2;
|
|
711
|
-
break;
|
|
708
|
+
const volumeList = [...(config.volumes || []), ...(runConfig.volumes || []), ...(options.volume || [])];
|
|
709
|
+
volumeList.forEach(v => addVolume(v));
|
|
712
710
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
IMAGE_BUILD_NEED = true;
|
|
716
|
-
i += 1;
|
|
717
|
-
break;
|
|
711
|
+
const buildArgList = [...(config.imageBuildArgs || []), ...(runConfig.imageBuildArgs || []), ...(options.imageBuildArg || [])];
|
|
712
|
+
buildArgList.forEach(arg => addImageBuildArg(arg));
|
|
718
713
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
i += 2;
|
|
723
|
-
break;
|
|
714
|
+
// Override mode for special options
|
|
715
|
+
const yoloValue = options.yolo || runConfig.yolo || config.yolo;
|
|
716
|
+
if (yoloValue) setYolo(yoloValue);
|
|
724
717
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
pruneDanglingImages();
|
|
728
|
-
process.exit(0);
|
|
718
|
+
const contModeValue = options.contMode || runConfig.containerMode || config.containerMode;
|
|
719
|
+
if (contModeValue) setContMode(contModeValue);
|
|
729
720
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
process.exit(0);
|
|
721
|
+
const quietValue = options.quiet || runConfig.quiet || config.quiet;
|
|
722
|
+
if (quietValue) setQuiet(quietValue);
|
|
733
723
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
724
|
+
if (options.contList) { getContList(); process.exit(0); }
|
|
725
|
+
if (options.contRemove) SHOULD_REMOVE = true;
|
|
726
|
+
if (options.imageBuild) IMAGE_BUILD_NEED = true;
|
|
727
|
+
if (options.imageRemove) { pruneDanglingImages(); process.exit(0); }
|
|
728
|
+
if (options.install) { installManyoyo(options.install); process.exit(0); }
|
|
738
729
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
730
|
+
// Handle shell-full (variadic arguments)
|
|
731
|
+
if (options.shellFull) {
|
|
732
|
+
EXEC_COMMAND = options.shellFull.join(' ');
|
|
733
|
+
}
|
|
743
734
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
}
|
|
735
|
+
// Handle -- suffix arguments
|
|
736
|
+
const doubleDashIndex = process.argv.indexOf('--');
|
|
737
|
+
if (doubleDashIndex !== -1 && doubleDashIndex < process.argv.length - 1) {
|
|
738
|
+
EXEC_COMMAND_SUFFIX = " " + process.argv.slice(doubleDashIndex + 1).join(' ');
|
|
749
739
|
}
|
|
740
|
+
|
|
741
|
+
return program;
|
|
750
742
|
}
|
|
751
743
|
|
|
752
744
|
function handleRemoveContainer() {
|
|
@@ -755,10 +747,10 @@ function handleRemoveContainer() {
|
|
|
755
747
|
if (containerExists(CONTAINER_NAME)) {
|
|
756
748
|
removeContainer(CONTAINER_NAME);
|
|
757
749
|
} else {
|
|
758
|
-
console.log(`${RED}⚠️
|
|
750
|
+
console.log(`${RED}⚠️ 错误: 未找到名为 ${CONTAINER_NAME} 的容器。${NC}`);
|
|
759
751
|
}
|
|
760
752
|
} catch (e) {
|
|
761
|
-
console.log(`${RED}⚠️
|
|
753
|
+
console.log(`${RED}⚠️ 错误: 未找到名为 ${CONTAINER_NAME} 的容器。${NC}`);
|
|
762
754
|
}
|
|
763
755
|
process.exit(0);
|
|
764
756
|
}
|
|
@@ -768,7 +760,7 @@ function validateHostPath() {
|
|
|
768
760
|
const realHostPath = fs.realpathSync(HOST_PATH);
|
|
769
761
|
const homeDir = process.env.HOME || '/home';
|
|
770
762
|
if (realHostPath === '/' || realHostPath === '/home' || realHostPath === homeDir) {
|
|
771
|
-
console.log(`${RED}⚠️
|
|
763
|
+
console.log(`${RED}⚠️ 错误: 不允许挂载根目录或home目录。${NC}`);
|
|
772
764
|
process.exit(1);
|
|
773
765
|
}
|
|
774
766
|
}
|
|
@@ -785,7 +777,7 @@ async function waitForContainerReady(containerName) {
|
|
|
785
777
|
}
|
|
786
778
|
|
|
787
779
|
if (status === 'exited') {
|
|
788
|
-
console.log(`${RED}⚠️
|
|
780
|
+
console.log(`${RED}⚠️ 错误: 容器启动后立即退出。${NC}`);
|
|
789
781
|
dockerExec(`${DOCKER_CMD} logs "${containerName}"`, { stdio: 'inherit' });
|
|
790
782
|
process.exit(1);
|
|
791
783
|
}
|
|
@@ -794,7 +786,7 @@ async function waitForContainerReady(containerName) {
|
|
|
794
786
|
count++;
|
|
795
787
|
|
|
796
788
|
if (count >= MAX_RETRIES) {
|
|
797
|
-
console.log(`${RED}⚠️
|
|
789
|
+
console.log(`${RED}⚠️ 错误: 容器启动超时(当前状态: ${status})。${NC}`);
|
|
798
790
|
dockerExec(`${DOCKER_CMD} logs "${containerName}"`, { stdio: 'inherit' });
|
|
799
791
|
process.exit(1);
|
|
800
792
|
}
|
|
@@ -802,7 +794,7 @@ async function waitForContainerReady(containerName) {
|
|
|
802
794
|
await sleep(100);
|
|
803
795
|
count++;
|
|
804
796
|
if (count >= MAX_RETRIES) {
|
|
805
|
-
console.log(`${RED}⚠️
|
|
797
|
+
console.log(`${RED}⚠️ 错误: 容器启动超时。${NC}`);
|
|
806
798
|
process.exit(1);
|
|
807
799
|
}
|
|
808
800
|
}
|
|
@@ -917,31 +909,28 @@ async function handlePostExit(defaultCommand) {
|
|
|
917
909
|
|
|
918
910
|
async function main() {
|
|
919
911
|
try {
|
|
920
|
-
// 1.
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
// 2. Parse command-line arguments
|
|
924
|
-
parseArguments(process.argv);
|
|
912
|
+
// 1. Setup commander and parse arguments
|
|
913
|
+
setupCommander();
|
|
925
914
|
|
|
926
|
-
//
|
|
915
|
+
// 2. Handle image build operation
|
|
927
916
|
if (IMAGE_BUILD_NEED) {
|
|
928
917
|
await buildImage(IMAGE_BUILD_ARGS, IMAGE_NAME, IMAGE_VERSION.split('-')[0]);
|
|
929
918
|
process.exit(0);
|
|
930
919
|
}
|
|
931
920
|
|
|
932
|
-
//
|
|
921
|
+
// 3. Handle remove container operation
|
|
933
922
|
handleRemoveContainer();
|
|
934
923
|
|
|
935
|
-
//
|
|
924
|
+
// 4. Validate host path safety
|
|
936
925
|
validateHostPath();
|
|
937
926
|
|
|
938
|
-
//
|
|
927
|
+
// 5. Setup container (create or connect)
|
|
939
928
|
const defaultCommand = await setupContainer();
|
|
940
929
|
|
|
941
|
-
//
|
|
930
|
+
// 6. Execute command in container
|
|
942
931
|
executeInContainer(defaultCommand);
|
|
943
932
|
|
|
944
|
-
//
|
|
933
|
+
// 7. Handle post-exit interactions
|
|
945
934
|
await handlePostExit(defaultCommand);
|
|
946
935
|
|
|
947
936
|
} catch (e) {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
// MANYOYO 全局配置文件,保存到 ~/.manyoyo/manyoyo.json
|
|
3
|
+
|
|
4
|
+
// 覆盖型参数(命令行 > 运行配置 > 全局配置)
|
|
5
|
+
"containerName": "myy-dev",
|
|
6
|
+
"hostPath": "/path/to/your/project",
|
|
7
|
+
"containerPath": "/path/to/your/project",
|
|
8
|
+
"imageName": "localhost/xcanwin/manyoyo",
|
|
9
|
+
"imageVersion": "1.6.4-full",
|
|
10
|
+
"containerMode": "common",
|
|
11
|
+
"shellPrefix": "",
|
|
12
|
+
"shell": "",
|
|
13
|
+
"yolo": "",
|
|
14
|
+
|
|
15
|
+
// 合并型参数(配置文件和命令行会累加)
|
|
16
|
+
"env": ["IS_SANDBOX=1"],
|
|
17
|
+
"envFile": [],
|
|
18
|
+
"volumes": [],
|
|
19
|
+
"imageBuildArgs": [],
|
|
20
|
+
"quiet": ["tip", "cmd"]
|
|
21
|
+
}
|
|
@@ -192,7 +192,7 @@ EOF
|
|
|
192
192
|
mkdir -p ~/.config/opencode/
|
|
193
193
|
cat > ~/.config/opencode/opencode.json <<EOF
|
|
194
194
|
{
|
|
195
|
-
"
|
|
195
|
+
"\$schema": "https://opencode.ai/config.json",
|
|
196
196
|
"autoupdate": false,
|
|
197
197
|
"permission": "allow",
|
|
198
198
|
"model": "myprovider/{env:ANTHROPIC_MODEL}",
|
package/docs/README_EN.md
CHANGED
|
@@ -40,6 +40,7 @@ npm install -g .
|
|
|
40
40
|
## 2. Install podman
|
|
41
41
|
|
|
42
42
|
2.1 Install [podman](https://podman.io/docs/installation)
|
|
43
|
+
|
|
43
44
|
2.2 Pull base image
|
|
44
45
|
|
|
45
46
|
```bash
|
|
@@ -100,31 +101,40 @@ manyoyo --irm
|
|
|
100
101
|
|
|
101
102
|
# Execute custom command with quiet output
|
|
102
103
|
manyoyo -q full -x echo "hello world"
|
|
104
|
+
manyoyo -q tip -q cmd -x echo "hello world" # Multiple quiet options
|
|
103
105
|
```
|
|
104
106
|
|
|
105
107
|
### Environment Variables
|
|
106
108
|
|
|
109
|
+
给容器内CLI传递BASE_URL和TOKEN等。
|
|
110
|
+
|
|
107
111
|
#### String Format
|
|
108
112
|
|
|
109
113
|
```bash
|
|
110
|
-
|
|
111
|
-
manyoyo -e "VAR=value" -x env
|
|
112
|
-
|
|
113
|
-
# Multiple
|
|
114
|
-
manyoyo -e "A=1" -e "B=2" -x env
|
|
114
|
+
manyoyo -e "ANTHROPIC_BASE_URL=https://xxxx" -e "ANTHROPIC_AUTH_TOKEN=your-key" -x claude
|
|
115
115
|
```
|
|
116
116
|
|
|
117
117
|
#### File Format
|
|
118
118
|
|
|
119
|
+
Environment files use `.env` format and support comments (lines starting with `#`):
|
|
120
|
+
|
|
119
121
|
```bash
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
export ANTHROPIC_BASE_URL="https://xxxx"
|
|
123
|
+
ANTHROPIC_AUTH_TOKEN=your-key
|
|
124
|
+
# MESSAGE="Hello World" # Comments will be ignored
|
|
125
|
+
TESTPATH='/usr/local/bin'
|
|
122
126
|
```
|
|
123
127
|
|
|
124
|
-
Environment
|
|
128
|
+
**Environment file path rules**:
|
|
129
|
+
- `manyoyo --ef myconfig` → loads `~/.manyoyo/env/myconfig.env`
|
|
130
|
+
- `manyoyo --ef ./myconfig.env` → loads `myconfig.env` from current directory
|
|
125
131
|
|
|
126
132
|
```bash
|
|
127
|
-
#
|
|
133
|
+
# Create environment file directory
|
|
134
|
+
mkdir -p ~/.manyoyo/env/
|
|
135
|
+
|
|
136
|
+
# Example: Create Claude environment file
|
|
137
|
+
cat > ~/.manyoyo/env/claude.env << 'EOF'
|
|
128
138
|
export ANTHROPIC_BASE_URL="https://api.anthropic.com"
|
|
129
139
|
# export CLAUDE_CODE_OAUTH_TOKEN="sk-xxxxxxxx"
|
|
130
140
|
export ANTHROPIC_AUTH_TOKEN="sk-xxxxxxxx"
|
|
@@ -134,13 +144,82 @@ export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-opus-4-5"
|
|
|
134
144
|
export ANTHROPIC_DEFAULT_SONNET_MODEL="claude-sonnet-4-5"
|
|
135
145
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-haiku-4-5"
|
|
136
146
|
export CLAUDE_CODE_SUBAGENT_MODEL="claude-sonnet-4-5"
|
|
147
|
+
EOF
|
|
148
|
+
|
|
149
|
+
# Use environment file from any directory
|
|
150
|
+
manyoyo --ef claude -x claude
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Configuration Files
|
|
154
|
+
|
|
155
|
+
Simplify MANYOYO command-line operations. Configuration files use **JSON5 format**, which supports comments, trailing commas, and other features.
|
|
156
|
+
|
|
157
|
+
#### Configuration file path rules
|
|
158
|
+
|
|
159
|
+
- `manyoyo -r myconfig` → loads `~/.manyoyo/run/myconfig.json`
|
|
160
|
+
- `manyoyo -r ./myconfig.json` → loads `myconfig.json` from current directory
|
|
161
|
+
- `manyoyo [any option]` → always loads global configuration `~/.manyoyo/manyoyo.json`
|
|
137
162
|
|
|
138
|
-
|
|
139
|
-
API_KEY=your-api-key-here
|
|
163
|
+
#### Configuration Options
|
|
140
164
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
Refer to `config.example.json` for all available options:
|
|
166
|
+
|
|
167
|
+
```json5
|
|
168
|
+
{
|
|
169
|
+
// Container basic configuration
|
|
170
|
+
"containerName": "myy-dev", // Default container name
|
|
171
|
+
"hostPath": "/path/to/project", // Default host working directory
|
|
172
|
+
"containerPath": "/path/to/project", // Default container working directory
|
|
173
|
+
"imageName": "localhost/xcanwin/manyoyo", // Default image name
|
|
174
|
+
"imageVersion": "1.6.3-full", // Default image version
|
|
175
|
+
"containerMode": "common", // Container nesting mode (common, dind, sock)
|
|
176
|
+
|
|
177
|
+
// Environment variable configuration
|
|
178
|
+
"envFile": [
|
|
179
|
+
"claude" // Corresponds to ~/.manyoyo/env/claude.env
|
|
180
|
+
],
|
|
181
|
+
"env": [], // Default environment variables array
|
|
182
|
+
|
|
183
|
+
// Other configuration
|
|
184
|
+
"volumes": [], // Default volume mounts array
|
|
185
|
+
"shellPrefix": "", // Default command prefix
|
|
186
|
+
"shell": "", // Default execute command
|
|
187
|
+
"yolo": "", // Default YOLO mode (c, gm, cx, oc)
|
|
188
|
+
"quiet": [], // Default quiet options array (supports ["tip", "cmd"] format)
|
|
189
|
+
"imageBuildArgs": [] // Default image build arguments
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Priority
|
|
194
|
+
|
|
195
|
+
- **Override parameters**: Command line > Run config > Global config > Defaults
|
|
196
|
+
- **Merge parameters**: Global config + Run config + Command line (concatenated in order)
|
|
197
|
+
|
|
198
|
+
Override parameters include: `containerName`, `hostPath`, `containerPath`, `imageName`, `imageVersion`, `containerMode`, `shellPrefix`, `shell`, `yolo`, `quiet`
|
|
199
|
+
|
|
200
|
+
Merge parameters include: `envFile`, `env`, `volumes`, `imageBuildArgs`
|
|
201
|
+
|
|
202
|
+
#### Common Examples
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Create run configuration directory
|
|
206
|
+
mkdir -p ~/.manyoyo/run/
|
|
207
|
+
|
|
208
|
+
# Create Claude run configuration
|
|
209
|
+
cat > ~/.manyoyo/run/c.json << 'EOF'
|
|
210
|
+
{
|
|
211
|
+
// Claude Code quick configuration
|
|
212
|
+
"imageName": "localhost/xcanwin/manyoyo",
|
|
213
|
+
"imageVersion": "1.6.3-full",
|
|
214
|
+
"envFile": [
|
|
215
|
+
"claude" // Automatically loads ~/.manyoyo/env/claude.env
|
|
216
|
+
],
|
|
217
|
+
"yolo": "c"
|
|
218
|
+
}
|
|
219
|
+
EOF
|
|
220
|
+
|
|
221
|
+
# Use run configuration from any directory
|
|
222
|
+
manyoyo -r c
|
|
144
223
|
```
|
|
145
224
|
|
|
146
225
|
### AI CLI Shortcuts (skip permissions)
|
|
@@ -212,7 +291,7 @@ docker ps -a # Now you can use docker commands inside the container
|
|
|
212
291
|
| `--iba` | `--image-build-arg` | Pass arguments to a Dockerfile during image build |
|
|
213
292
|
| `--irm` | `--image-remove` | Clean dangling images and `<none>` images |
|
|
214
293
|
| `-e STRING` | `--env` | Set environment variable |
|
|
215
|
-
| `--ef FILE` | `--env-file` | Load environment variables from file |
|
|
294
|
+
| `--ef FILE` | `--env-file` | Load environment variables from file (supports `name` or `./path.env`) |
|
|
216
295
|
| `-v STRING` | `--volume` | Bind mount volume |
|
|
217
296
|
| `--sp CMD` | `--shell-prefix` | Temporary environment variable (prefix for -s) |
|
|
218
297
|
| `-s CMD` | `--shell` | Specify command to execute |
|
|
@@ -221,6 +300,7 @@ docker ps -a # Now you can use docker commands inside the container
|
|
|
221
300
|
| `-y CLI` | `--yolo` | Run AI agent without confirmation |
|
|
222
301
|
| `--install NAME` | | Install manyoyo command |
|
|
223
302
|
| `-q LIST` | `--quiet` | Quiet output |
|
|
303
|
+
| `-r NAME` | `--run` | Load run configuration (supports `name` or `./path.json`) |
|
|
224
304
|
| `-V` | `--version` | Show version |
|
|
225
305
|
| `-h` | `--help` | Show help |
|
|
226
306
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcanwin/manyoyo",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"imageVersion": "1.6.
|
|
3
|
+
"version": "3.7.0",
|
|
4
|
+
"imageVersion": "1.6.4",
|
|
5
5
|
"description": "AI Agent CLI Security Sandbox",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai", "agent", "sandbox", "docker", "cli", "container", "development"
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
"README.md",
|
|
32
32
|
"docs/README_EN.md",
|
|
33
33
|
"LICENSE",
|
|
34
|
-
"docker/manyoyo.Dockerfile"
|
|
35
|
-
|
|
34
|
+
"docker/manyoyo.Dockerfile",
|
|
35
|
+
"config.example.json"
|
|
36
|
+
],
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"commander": "^12.0.0",
|
|
39
|
+
"json5": "^2.2.3"
|
|
40
|
+
}
|
|
36
41
|
}
|