@xcanwin/manyoyo 5.4.6 → 5.4.14
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 +3 -3
- package/bin/manyoyo.js +48 -3
- package/docker/manyoyo.Dockerfile +18 -19
- package/docker/res/playwright/playwright-cli-wrapper.sh +82 -0
- package/lib/global-config.js +228 -1
- package/lib/plugin/playwright.js +64 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ AI Agent CLI 往往需要:
|
|
|
53
53
|
```bash
|
|
54
54
|
npm install -g @xcanwin/manyoyo
|
|
55
55
|
podman pull ubuntu:24.04 # 仅 Podman 需要
|
|
56
|
-
manyoyo build --iv 1.8.
|
|
56
|
+
manyoyo build --iv 1.8.12-common
|
|
57
57
|
manyoyo init all
|
|
58
58
|
manyoyo run -r claude
|
|
59
59
|
```
|
|
@@ -137,10 +137,10 @@ manyoyo config command
|
|
|
137
137
|
|
|
138
138
|
```bash
|
|
139
139
|
# common 版本
|
|
140
|
-
manyoyo build --iv 1.8.
|
|
140
|
+
manyoyo build --iv 1.8.12-common
|
|
141
141
|
|
|
142
142
|
# full 版本
|
|
143
|
-
manyoyo build --iv 1.8.
|
|
143
|
+
manyoyo build --iv 1.8.12-full
|
|
144
144
|
|
|
145
145
|
# 自定义工具集
|
|
146
146
|
manyoyo build --iba TOOL=go,codex,java,gemini
|
package/bin/manyoyo.js
CHANGED
|
@@ -570,6 +570,42 @@ function addEnvFile(envFile) {
|
|
|
570
570
|
return addEnvFileTo(CONTAINER_ENVS, envFile);
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
+
function expandHomeAliasPath(filePath) {
|
|
574
|
+
const text = String(filePath || '').trim();
|
|
575
|
+
const homeDir = process.env.HOME || os.homedir();
|
|
576
|
+
|
|
577
|
+
if (text === '~') {
|
|
578
|
+
return homeDir;
|
|
579
|
+
}
|
|
580
|
+
if (text.startsWith('~/')) {
|
|
581
|
+
return path.join(homeDir, text.slice(2));
|
|
582
|
+
}
|
|
583
|
+
if (text === '$HOME') {
|
|
584
|
+
return homeDir;
|
|
585
|
+
}
|
|
586
|
+
if (text.startsWith('$HOME/')) {
|
|
587
|
+
return path.join(homeDir, text.slice('$HOME/'.length));
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return text;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function normalizeVolume(volume) {
|
|
594
|
+
const text = String(volume || '').trim();
|
|
595
|
+
if (!text.startsWith('~') && !text.startsWith('$HOME')) {
|
|
596
|
+
return text;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const separatorIndex = text.indexOf(':');
|
|
600
|
+
if (separatorIndex === -1) {
|
|
601
|
+
return expandHomeAliasPath(text);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const hostPath = text.slice(0, separatorIndex);
|
|
605
|
+
const rest = text.slice(separatorIndex);
|
|
606
|
+
return `${expandHomeAliasPath(hostPath)}${rest}`;
|
|
607
|
+
}
|
|
608
|
+
|
|
573
609
|
function hasEnvKey(targetEnvs, key) {
|
|
574
610
|
for (let i = 0; i < targetEnvs.length; i += 2) {
|
|
575
611
|
if (targetEnvs[i] !== '--env') {
|
|
@@ -1071,6 +1107,14 @@ async function setupCommander() {
|
|
|
1071
1107
|
host: options.host || ''
|
|
1072
1108
|
}, options));
|
|
1073
1109
|
|
|
1110
|
+
command.command('cli-add')
|
|
1111
|
+
.description('输出 playwright-cli skill 安装命令')
|
|
1112
|
+
.action(() => selectPluginAction({
|
|
1113
|
+
action: 'cli-add',
|
|
1114
|
+
pluginName: 'playwright',
|
|
1115
|
+
scene: 'all'
|
|
1116
|
+
}));
|
|
1117
|
+
|
|
1074
1118
|
command.command('ext-download')
|
|
1075
1119
|
.description('下载并解压 Playwright 扩展到 ~/.manyoyo/plugin/playwright/extensions/')
|
|
1076
1120
|
.option('--prodversion <ver>', 'CRX 下载使用的 Chrome 版本号 (默认 132.0.0.0)')
|
|
@@ -1109,8 +1153,8 @@ async function setupCommander() {
|
|
|
1109
1153
|
${MANYOYO_NAME} serve 127.0.0.1:3000 启动本机网页服务
|
|
1110
1154
|
${MANYOYO_NAME} serve 127.0.0.1:3000 -d 后台启动;未设密码时会打印本次随机密码
|
|
1111
1155
|
${MANYOYO_NAME} serve 0.0.0.0:3000 -U admin -P 123 -d 后台启动并监听全部网卡
|
|
1112
|
-
${MANYOYO_NAME} playwright up mcp-host-headless
|
|
1113
|
-
${MANYOYO_NAME}
|
|
1156
|
+
${MANYOYO_NAME} playwright up mcp-host-headless 启动 playwright MCP 宿主场景(默认/推荐)
|
|
1157
|
+
${MANYOYO_NAME} playwright up cli-host-headless 启动 playwright CLI 宿主场景(供容器内 playwright-cli 附着)
|
|
1114
1158
|
${MANYOYO_NAME} run -n test -q tip -q cmd 多次使用静默选项
|
|
1115
1159
|
`);
|
|
1116
1160
|
|
|
@@ -1372,7 +1416,8 @@ Notes:
|
|
|
1372
1416
|
|
|
1373
1417
|
applyPlaywrightCliSessionIntegration(config, runConfig);
|
|
1374
1418
|
|
|
1375
|
-
const volumeList = mergeArrayConfig(config.volumes, runConfig.volumes, options.volume)
|
|
1419
|
+
const volumeList = mergeArrayConfig(config.volumes, runConfig.volumes, options.volume)
|
|
1420
|
+
.map(normalizeVolume);
|
|
1376
1421
|
volumeList.forEach(v => addVolume(v));
|
|
1377
1422
|
|
|
1378
1423
|
const portList = mergeArrayConfig(config.ports, runConfig.ports, options.port);
|
|
@@ -127,6 +127,7 @@ EOX
|
|
|
127
127
|
|
|
128
128
|
# 从 cache-stage 复制 Node.js(缓存或下载)
|
|
129
129
|
COPY --from=cache-stage /opt/node /usr/local
|
|
130
|
+
COPY ./package.json /tmp/manyoyo-package.json
|
|
130
131
|
COPY ./docker/res/playwright/cli-cont-headless.init.js /app/config/cli-cont-headless.init.js
|
|
131
132
|
COPY ./docker/res/playwright/cli-cont-headless.json /app/config/cli-cont-headless.json
|
|
132
133
|
COPY ./docker/res/ /tmp/docker-res/
|
|
@@ -202,32 +203,30 @@ RUN <<EOX
|
|
|
202
203
|
;; esac
|
|
203
204
|
|
|
204
205
|
# 安装 Playwright CLI skills(不在镜像构建阶段下载浏览器)
|
|
205
|
-
npm install -g @playwright/cli@latest
|
|
206
206
|
PLAYWRIGHT_CLI_INSTALL_DIR=/tmp/playwright-cli-install
|
|
207
|
-
mkdir -p "$
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
"
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
cd "$
|
|
219
|
-
|
|
220
|
-
mkdir -p "$HOME/.codex/skills/playwright-cli" ~/.gemini/skills/playwright-cli
|
|
221
|
-
cp -R "${PLAYWRIGHT_CLI_INSTALL_DIR}/.claude/skills/playwright-cli/." "$HOME/.codex/skills/playwright-cli/"
|
|
222
|
-
cp -R "${PLAYWRIGHT_CLI_INSTALL_DIR}/.claude/skills/playwright-cli/." "$HOME/.gemini/skills/playwright-cli/"
|
|
223
|
-
cd $OLDPWD
|
|
224
|
-
rm -rf "${PLAYWRIGHT_CLI_INSTALL_DIR}"
|
|
207
|
+
mkdir -p "$PLAYWRIGHT_CLI_INSTALL_DIR/.playwright"
|
|
208
|
+
echo '{"browser":{"browserName":"chromium","launchOptions":{"channel":"chromium"}}}' > "${PLAYWRIGHT_CLI_INSTALL_DIR}/.playwright/cli.config.json"
|
|
209
|
+
cd "$PLAYWRIGHT_CLI_INSTALL_DIR"
|
|
210
|
+
PLAYWRIGHT_CLI_VERSION=$(node -p "const pkg = require('/tmp/manyoyo-package.json'); const value = String(pkg.playwrightCliVersion || '').trim(); if (!value) { throw new Error('package.json.playwrightCliVersion is required'); } value")
|
|
211
|
+
npm install -g "@playwright/cli@${PLAYWRIGHT_CLI_VERSION}"
|
|
212
|
+
playwright-cli --config="${PLAYWRIGHT_CLI_INSTALL_DIR}/.playwright/cli.config.json" install --skills
|
|
213
|
+
PLAYWRIGHT_CLI_SKILL_SOURCE="$PLAYWRIGHT_CLI_INSTALL_DIR/.claude/skills/playwright-cli"
|
|
214
|
+
for target in ~/.claude/skills/playwright-cli ~/.codex/skills/playwright-cli ~/.gemini/skills/playwright-cli; do
|
|
215
|
+
mkdir -p "$target"
|
|
216
|
+
cp -R "$PLAYWRIGHT_CLI_SKILL_SOURCE/." "$target/"
|
|
217
|
+
done
|
|
218
|
+
cd "$OLDPWD"
|
|
219
|
+
rm -rf "$PLAYWRIGHT_CLI_INSTALL_DIR"
|
|
225
220
|
|
|
226
221
|
# 清理
|
|
227
222
|
npm cache clean --force
|
|
223
|
+
rm -f /tmp/manyoyo-package.json
|
|
228
224
|
rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.npm ~/go/pkg/mod/cache
|
|
229
225
|
EOX
|
|
230
226
|
|
|
227
|
+
COPY ./docker/res/playwright/playwright-cli-wrapper.sh /usr/local/bin/playwright-cli
|
|
228
|
+
RUN chmod +x /usr/local/bin/playwright-cli
|
|
229
|
+
|
|
231
230
|
# 从 cache-stage 复制 JDT LSP(缓存或下载)
|
|
232
231
|
COPY --from=cache-stage /opt/jdtls /tmp/jdtls-cache
|
|
233
232
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
PLAYWRIGHT_NODE_BIN="${PLAYWRIGHT_NODE_BIN:-node}"
|
|
5
|
+
PLAYWRIGHT_CLI_ROOT="/usr/local/lib/node_modules/@playwright/cli/node_modules/playwright"
|
|
6
|
+
PLAYWRIGHT_CLI_PROGRAM="${PLAYWRIGHT_CLI_ROOT}/lib/cli/client/program.js"
|
|
7
|
+
PLAYWRIGHT_REAL_CLI="${PLAYWRIGHT_CLI_ROOT}/cli.js"
|
|
8
|
+
|
|
9
|
+
resolve_browser() {
|
|
10
|
+
browser=""
|
|
11
|
+
|
|
12
|
+
while [ "$#" -gt 0 ]; do
|
|
13
|
+
case "$1" in
|
|
14
|
+
--browser=*)
|
|
15
|
+
browser="${1#--browser=}"
|
|
16
|
+
;;
|
|
17
|
+
--browser)
|
|
18
|
+
shift
|
|
19
|
+
if [ "$#" -gt 0 ]; then
|
|
20
|
+
browser="$1"
|
|
21
|
+
shift
|
|
22
|
+
fi
|
|
23
|
+
continue
|
|
24
|
+
;;
|
|
25
|
+
--)
|
|
26
|
+
break
|
|
27
|
+
;;
|
|
28
|
+
-*)
|
|
29
|
+
;;
|
|
30
|
+
*)
|
|
31
|
+
if [ -z "$browser" ]; then
|
|
32
|
+
browser="$1"
|
|
33
|
+
fi
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
shift
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
if [ -n "$browser" ]; then
|
|
40
|
+
printf '%s\n' "$browser"
|
|
41
|
+
return 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
"$PLAYWRIGHT_NODE_BIN" <<'NODE'
|
|
45
|
+
const fs = require('fs');
|
|
46
|
+
const path = require('path');
|
|
47
|
+
|
|
48
|
+
const candidates = [
|
|
49
|
+
process.env.PLAYWRIGHT_MCP_CONFIG,
|
|
50
|
+
path.resolve('.playwright/cli.config.json')
|
|
51
|
+
].filter(Boolean);
|
|
52
|
+
|
|
53
|
+
for (const filePath of candidates) {
|
|
54
|
+
try {
|
|
55
|
+
const config = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
56
|
+
const browser = config && config.browser ? config.browser : {};
|
|
57
|
+
const launchOptions = browser.launchOptions || {};
|
|
58
|
+
const channel = String(launchOptions.channel || browser.browserName || '').trim();
|
|
59
|
+
if (channel) {
|
|
60
|
+
process.stdout.write(channel);
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
} catch (_) {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
process.stdout.write('chromium');
|
|
67
|
+
NODE
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if [ "${1-}" = "install-browser" ]; then
|
|
71
|
+
shift
|
|
72
|
+
for arg in "$@"; do
|
|
73
|
+
if [ "$arg" = "--help" ] || [ "$arg" = "-h" ]; then
|
|
74
|
+
exec "$PLAYWRIGHT_NODE_BIN" "$PLAYWRIGHT_CLI_PROGRAM" install-browser "$@"
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
BROWSER="$(resolve_browser "$@")"
|
|
79
|
+
exec "$PLAYWRIGHT_NODE_BIN" "$PLAYWRIGHT_REAL_CLI" install "$BROWSER"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
exec "$PLAYWRIGHT_NODE_BIN" "$PLAYWRIGHT_CLI_PROGRAM" "$@"
|
package/lib/global-config.js
CHANGED
|
@@ -36,6 +36,223 @@ function readManyoyoConfig(homeDir = os.homedir()) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function readQuotedString(text, startIndex) {
|
|
40
|
+
const quote = text[startIndex];
|
|
41
|
+
let value = '';
|
|
42
|
+
|
|
43
|
+
for (let i = startIndex + 1; i < text.length; i += 1) {
|
|
44
|
+
const ch = text[i];
|
|
45
|
+
if (ch === '\\') {
|
|
46
|
+
value += ch;
|
|
47
|
+
if (i + 1 < text.length) {
|
|
48
|
+
value += text[i + 1];
|
|
49
|
+
i += 1;
|
|
50
|
+
}
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (ch === quote) {
|
|
54
|
+
return {
|
|
55
|
+
value,
|
|
56
|
+
end: i + 1
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
value += ch;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isIdentifierStart(ch) {
|
|
66
|
+
return /[A-Za-z_$]/.test(ch);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isIdentifierPart(ch) {
|
|
70
|
+
return /[A-Za-z0-9_$]/.test(ch);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function skipWhitespace(text, index) {
|
|
74
|
+
let i = index;
|
|
75
|
+
while (i < text.length && /\s/.test(text[i])) {
|
|
76
|
+
i += 1;
|
|
77
|
+
}
|
|
78
|
+
return i;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function findTopLevelPropertyValueRange(text, propertyName) {
|
|
82
|
+
let depth = 0;
|
|
83
|
+
let inString = '';
|
|
84
|
+
let inLineComment = false;
|
|
85
|
+
let inBlockComment = false;
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
88
|
+
const ch = text[i];
|
|
89
|
+
const next = text[i + 1];
|
|
90
|
+
|
|
91
|
+
if (inLineComment) {
|
|
92
|
+
if (ch === '\n') inLineComment = false;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (inBlockComment) {
|
|
96
|
+
if (ch === '*' && next === '/') {
|
|
97
|
+
inBlockComment = false;
|
|
98
|
+
i += 1;
|
|
99
|
+
}
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (inString) {
|
|
103
|
+
if (ch === '\\') {
|
|
104
|
+
i += 1;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (ch === inString) inString = '';
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (ch === '/' && next === '/') {
|
|
112
|
+
inLineComment = true;
|
|
113
|
+
i += 1;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (ch === '/' && next === '*') {
|
|
117
|
+
inBlockComment = true;
|
|
118
|
+
i += 1;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (depth === 1 && !/\s|,/.test(ch)) {
|
|
122
|
+
let property = '';
|
|
123
|
+
let cursor = i;
|
|
124
|
+
if (ch === '"' || ch === '\'') {
|
|
125
|
+
const token = readQuotedString(text, i);
|
|
126
|
+
if (!token) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
property = token.value;
|
|
130
|
+
cursor = token.end;
|
|
131
|
+
} else if (isIdentifierStart(ch)) {
|
|
132
|
+
cursor = i + 1;
|
|
133
|
+
while (cursor < text.length && isIdentifierPart(text[cursor])) {
|
|
134
|
+
cursor += 1;
|
|
135
|
+
}
|
|
136
|
+
property = text.slice(i, cursor);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (property) {
|
|
140
|
+
const colonIndex = skipWhitespace(text, cursor);
|
|
141
|
+
if (text[colonIndex] === ':') {
|
|
142
|
+
if (property !== propertyName) {
|
|
143
|
+
i = colonIndex;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let valueStart = skipWhitespace(text, colonIndex + 1);
|
|
148
|
+
let valueEnd = valueStart;
|
|
149
|
+
let valueString = '';
|
|
150
|
+
let valueLineComment = false;
|
|
151
|
+
let valueBlockComment = false;
|
|
152
|
+
let valueDepth = 0;
|
|
153
|
+
|
|
154
|
+
for (; valueEnd < text.length; valueEnd += 1) {
|
|
155
|
+
const valueCh = text[valueEnd];
|
|
156
|
+
const valueNext = text[valueEnd + 1];
|
|
157
|
+
|
|
158
|
+
if (valueLineComment) {
|
|
159
|
+
if (valueCh === '\n') valueLineComment = false;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (valueBlockComment) {
|
|
163
|
+
if (valueCh === '*' && valueNext === '/') {
|
|
164
|
+
valueBlockComment = false;
|
|
165
|
+
valueEnd += 1;
|
|
166
|
+
}
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (valueString) {
|
|
170
|
+
if (valueCh === '\\') {
|
|
171
|
+
valueEnd += 1;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (valueCh === valueString) valueString = '';
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (valueCh === '/' && valueNext === '/') {
|
|
179
|
+
valueLineComment = true;
|
|
180
|
+
valueEnd += 1;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (valueCh === '/' && valueNext === '*') {
|
|
184
|
+
valueBlockComment = true;
|
|
185
|
+
valueEnd += 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (valueCh === '"' || valueCh === '\'') {
|
|
189
|
+
valueString = valueCh;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (valueCh === '{' || valueCh === '[' || valueCh === '(') {
|
|
193
|
+
valueDepth += 1;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (valueCh === '}' || valueCh === ']' || valueCh === ')') {
|
|
197
|
+
if (valueDepth === 0) {
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
valueDepth -= 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (valueDepth === 0 && valueCh === ',') {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
while (valueEnd > valueStart && /\s/.test(text[valueEnd - 1])) {
|
|
209
|
+
valueEnd -= 1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
start: valueStart,
|
|
214
|
+
end: valueEnd
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (ch === '"' || ch === '\'') {
|
|
221
|
+
inString = ch;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (ch === '{' || ch === '[') {
|
|
225
|
+
depth += 1;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (ch === '}' || ch === ']') {
|
|
229
|
+
depth -= 1;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function insertTopLevelImageVersion(text, imageVersion) {
|
|
238
|
+
const openBraceIndex = text.indexOf('{');
|
|
239
|
+
if (openBraceIndex === -1) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const newlineIndex = text.indexOf('\n', openBraceIndex);
|
|
244
|
+
const insertIndex = newlineIndex === -1 ? openBraceIndex + 1 : newlineIndex + 1;
|
|
245
|
+
return `${text.slice(0, insertIndex)} imageVersion: ${JSON.stringify(imageVersion)},\n${text.slice(insertIndex)}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function updateImageVersionText(text, imageVersion) {
|
|
249
|
+
const range = findTopLevelPropertyValueRange(text, 'imageVersion');
|
|
250
|
+
if (range) {
|
|
251
|
+
return `${text.slice(0, range.start)}${JSON.stringify(imageVersion)}${text.slice(range.end)}`;
|
|
252
|
+
}
|
|
253
|
+
return insertTopLevelImageVersion(text, imageVersion);
|
|
254
|
+
}
|
|
255
|
+
|
|
39
256
|
function syncGlobalImageVersion(imageVersion, options = {}) {
|
|
40
257
|
const homeDir = options.homeDir || os.homedir();
|
|
41
258
|
const result = readManyoyoConfig(homeDir);
|
|
@@ -72,7 +289,17 @@ function syncGlobalImageVersion(imageVersion, options = {}) {
|
|
|
72
289
|
};
|
|
73
290
|
|
|
74
291
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
75
|
-
|
|
292
|
+
if (result.exists) {
|
|
293
|
+
const currentText = fs.readFileSync(configPath, 'utf-8');
|
|
294
|
+
const updatedText = updateImageVersionText(currentText, imageVersion);
|
|
295
|
+
if (updatedText) {
|
|
296
|
+
fs.writeFileSync(configPath, updatedText.endsWith('\n') ? updatedText : `${updatedText}\n`);
|
|
297
|
+
} else {
|
|
298
|
+
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 4)}\n`);
|
|
299
|
+
}
|
|
300
|
+
} else {
|
|
301
|
+
fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 4)}\n`);
|
|
302
|
+
}
|
|
76
303
|
|
|
77
304
|
return {
|
|
78
305
|
updated: true,
|
package/lib/plugin/playwright.js
CHANGED
|
@@ -6,6 +6,7 @@ const os = require('os');
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const crypto = require('crypto');
|
|
8
8
|
const { spawn, spawnSync } = require('child_process');
|
|
9
|
+
const { playwrightCliVersion: PLAYWRIGHT_CLI_VERSION } = require('../../package.json');
|
|
9
10
|
|
|
10
11
|
const EXTENSIONS = [
|
|
11
12
|
['ublock-origin-lite', 'ddkjiahejlhfcafbddmgiahcphecmpfh'],
|
|
@@ -290,6 +291,7 @@ class PlaywrightPlugin {
|
|
|
290
291
|
const homeDir = os.homedir();
|
|
291
292
|
const pluginRootDir = path.join(homeDir, '.manyoyo', 'plugin', 'playwright');
|
|
292
293
|
const defaultConfig = {
|
|
294
|
+
homeDir,
|
|
293
295
|
runtime: 'mixed',
|
|
294
296
|
enabledScenes: [...SCENE_ORDER],
|
|
295
297
|
cliSessionScene: 'cli-host-headless',
|
|
@@ -379,6 +381,29 @@ class PlaywrightPlugin {
|
|
|
379
381
|
this.stderr.write(`${line}\n`);
|
|
380
382
|
}
|
|
381
383
|
|
|
384
|
+
remindCliSessionScene(sceneName) {
|
|
385
|
+
if (!isCliScene(sceneName)) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (sceneName !== 'cli-host-headed') {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (this.config.cliSessionScene === sceneName) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
this.writeStdout('[tip] 如果希望容器内 manyoyo run 自动附着到当前 CLI 宿主场景,请在 ~/.manyoyo/manyoyo.json 中设置:');
|
|
395
|
+
this.writeStdout('{');
|
|
396
|
+
this.writeStdout(' "volumes": [');
|
|
397
|
+
this.writeStdout(' "~/.manyoyo/.cache/ms-playwright:/root/.cache/ms-playwright"');
|
|
398
|
+
this.writeStdout(' ],');
|
|
399
|
+
this.writeStdout(' "plugins": {');
|
|
400
|
+
this.writeStdout(' "playwright": {');
|
|
401
|
+
this.writeStdout(' "cliSessionScene": "cli-host-headed"');
|
|
402
|
+
this.writeStdout(' }');
|
|
403
|
+
this.writeStdout(' }');
|
|
404
|
+
this.writeStdout('}');
|
|
405
|
+
}
|
|
406
|
+
|
|
382
407
|
randomAlnum(length = 16) {
|
|
383
408
|
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
384
409
|
let out = '';
|
|
@@ -605,11 +630,22 @@ class PlaywrightPlugin {
|
|
|
605
630
|
}
|
|
606
631
|
|
|
607
632
|
extensionDirPath() {
|
|
608
|
-
return path.join(
|
|
633
|
+
return path.join(this.config.homeDir, '.manyoyo', 'plugin', 'playwright', 'extensions');
|
|
609
634
|
}
|
|
610
635
|
|
|
611
636
|
extensionTmpDirPath() {
|
|
612
|
-
return path.join(
|
|
637
|
+
return path.join(this.config.homeDir, '.manyoyo', 'plugin', 'playwright', 'tmp-crx');
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
cliBrowserCacheDirPath() {
|
|
641
|
+
return path.join(this.config.homeDir, '.manyoyo', '.cache', 'ms-playwright');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
ensureCliHostHeadedCacheDir(sceneName) {
|
|
645
|
+
if (sceneName !== 'cli-host-headed') {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
fs.mkdirSync(this.cliBrowserCacheDirPath(), { recursive: true });
|
|
613
649
|
}
|
|
614
650
|
|
|
615
651
|
resolveTargets(sceneName = 'all') {
|
|
@@ -1204,6 +1240,7 @@ class PlaywrightPlugin {
|
|
|
1204
1240
|
|
|
1205
1241
|
async startHost(sceneName, options = {}) {
|
|
1206
1242
|
try {
|
|
1243
|
+
this.ensureCliHostHeadedCacheDir(sceneName);
|
|
1207
1244
|
this.ensureHostScenePrerequisites(sceneName);
|
|
1208
1245
|
} catch (error) {
|
|
1209
1246
|
this.writeStderr(`[up] ${sceneName} failed: ${error.message || String(error)}`);
|
|
@@ -1259,6 +1296,7 @@ class PlaywrightPlugin {
|
|
|
1259
1296
|
if (managedPids.length > 0) {
|
|
1260
1297
|
fs.writeFileSync(pidFile, `${managedPids[0]}`, 'utf8');
|
|
1261
1298
|
this.writeStdout(`[up] ${sceneName} ready on 127.0.0.1:${port} (pid(s) ${managedPids.join(' ')})`);
|
|
1299
|
+
this.remindCliSessionScene(sceneName);
|
|
1262
1300
|
return 0;
|
|
1263
1301
|
}
|
|
1264
1302
|
}
|
|
@@ -1539,6 +1577,26 @@ class PlaywrightPlugin {
|
|
|
1539
1577
|
return 0;
|
|
1540
1578
|
}
|
|
1541
1579
|
|
|
1580
|
+
printCliAdd() {
|
|
1581
|
+
const lines = [
|
|
1582
|
+
'PLAYWRIGHT_CLI_INSTALL_DIR="${TMPDIR:-/tmp}/manyoyo-playwright-cli-install-$$"',
|
|
1583
|
+
'mkdir -p "$PLAYWRIGHT_CLI_INSTALL_DIR/.playwright"',
|
|
1584
|
+
'echo \'{"browser":{"browserName":"chromium","launchOptions":{"channel":"chromium"}}}\' > "$PLAYWRIGHT_CLI_INSTALL_DIR/.playwright/cli.config.json"',
|
|
1585
|
+
'cd "$PLAYWRIGHT_CLI_INSTALL_DIR"',
|
|
1586
|
+
`npm install -g @playwright/cli@${PLAYWRIGHT_CLI_VERSION}`,
|
|
1587
|
+
'playwright-cli install --skills',
|
|
1588
|
+
'PLAYWRIGHT_CLI_SKILL_SOURCE="$PLAYWRIGHT_CLI_INSTALL_DIR/.claude/skills/playwright-cli"',
|
|
1589
|
+
'for target in ~/.claude/skills/playwright-cli ~/.codex/skills/playwright-cli ~/.gemini/skills/playwright-cli; do',
|
|
1590
|
+
' mkdir -p "$target"',
|
|
1591
|
+
' cp -R "$PLAYWRIGHT_CLI_SKILL_SOURCE/." "$target/"',
|
|
1592
|
+
'done',
|
|
1593
|
+
'cd "$OLDPWD"',
|
|
1594
|
+
'rm -rf "$PLAYWRIGHT_CLI_INSTALL_DIR"'
|
|
1595
|
+
];
|
|
1596
|
+
this.writeStdout(lines.join('\n'));
|
|
1597
|
+
return 0;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1542
1600
|
printSummary() {
|
|
1543
1601
|
const scenes = this.resolveTargets('all');
|
|
1544
1602
|
this.writeStdout(`playwright\truntime=${this.config.runtime}\tscenes=${scenes.join(',')}`);
|
|
@@ -1583,6 +1641,10 @@ class PlaywrightPlugin {
|
|
|
1583
1641
|
return this.printMcpAdd(host);
|
|
1584
1642
|
}
|
|
1585
1643
|
|
|
1644
|
+
if (action === 'cli-add') {
|
|
1645
|
+
return this.printCliAdd();
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1586
1648
|
if (action === 'ext-download') {
|
|
1587
1649
|
return await this.downloadExtensions({ prodversion });
|
|
1588
1650
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcanwin/manyoyo",
|
|
3
|
-
"version": "5.4.
|
|
4
|
-
"imageVersion": "1.8.
|
|
3
|
+
"version": "5.4.14",
|
|
4
|
+
"imageVersion": "1.8.12-common",
|
|
5
|
+
"playwrightCliVersion": "0.1.1",
|
|
5
6
|
"description": "AI Agent CLI Security Sandbox for Docker and Podman",
|
|
6
7
|
"keywords": [
|
|
7
8
|
"manyoyo",
|