helixlife-v5-cli 1.1.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 +87 -0
- package/helix-cli.js +16 -0
- package/lib/cli.js +223 -0
- package/package.json +34 -0
- package/references/vip.helixlife.cn.site-ops.md +1205 -0
- package/scripts/analysis-detail-upload-both.js +45 -0
- package/scripts/analysis-detail-upload-file.js +52 -0
- package/scripts/chrome-pdf-enumerate-a11y.js +16 -0
- package/scripts/chrome-pdf-goto-page.js +30 -0
- package/scripts/chrome-pdf-zoom-out.js +14 -0
- package/scripts/jigsaw-inspect-dom.js +15 -0
- package/scripts/jigsaw-verify-bio-tools.js +11 -0
- package/scripts/jigsaw-verify-drag-tool.js +23 -0
- package/scripts/jigsaw-verify-enumerate.js +54 -0
- package/scripts/jigsaw-verify-reference-lines.js +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# helixlife-v5-cli
|
|
2
|
+
|
|
3
|
+
全局安装 **`helixlife-v5-cli`** 后即可直接操作网页。该工具默认会把命令行参数原样透传到底层浏览器自动化引擎,同时附带 **Helix** 站点一键流程 `helix home` / `helix doctor`。
|
|
4
|
+
|
|
5
|
+
## 安装(仅 npm 全局安装)
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g helixlife-v5-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> 不再支持 / 不建议项目级本地安装与 `npx helixlife-v5-cli`,以避免污染用户工程的 `node_modules` 与 lockfile。
|
|
12
|
+
|
|
13
|
+
全局安装后直接:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
helixlife-v5-cli --help
|
|
17
|
+
helixlife-v5-cli list
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 首次执行前的自检(建议)
|
|
21
|
+
|
|
22
|
+
每次新会话第一次使用前,按下面 3 步确认环境就绪(与 Cursor skill 中代理执行的逻辑一致):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 1. 是否已安装;未安装时执行 npm install -g helixlife-v5-cli
|
|
26
|
+
helixlife-v5-cli --version
|
|
27
|
+
|
|
28
|
+
# 2. 取得线上最新版本
|
|
29
|
+
npm view helixlife-v5-cli version
|
|
30
|
+
|
|
31
|
+
# 3. 与本地不一致时升级到最新
|
|
32
|
+
npm install -g helixlife-v5-cli@latest
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`PATH` 不含 `npm bin -g` 时(nvm / Volta / 自定义 prefix 常见),临时兜底:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
node "$(npm root -g)/helixlife-v5-cli/helix-cli.js" --version
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 本地开发(与全局命令等价)
|
|
42
|
+
|
|
43
|
+
仓库克隆后,**仅在开发本仓库时**使用 `node helix-cli.js`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install
|
|
47
|
+
node helix-cli.js --help
|
|
48
|
+
node helix-cli.js list
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Helix 快捷命令
|
|
52
|
+
|
|
53
|
+
封装了「打开会话 → `goto` 到 HELIX_BASE_URL → `eval` 标题」,对应原 skill 文档里分步执行的启动流程:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
helixlife-v5-cli helix home
|
|
57
|
+
helixlife-v5-cli helix home --headless
|
|
58
|
+
helixlife-v5-cli helix doctor
|
|
59
|
+
helixlife-v5-cli helix doctor --cdp-url http://127.0.0.1:9222
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
包装器自带的用法摘要(不参加透传):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
helixlife-v5-cli --helix-help
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 环境与兼容说明
|
|
69
|
+
|
|
70
|
+
- **Windows / macOS**:通过当前 Node 直接引用包内引擎脚本启动子进程,不依赖 Shell、`npx` 或 PATH,便于 CI 与 Agent 调用。
|
|
71
|
+
- **stdin/stdout/stderr**:子进程继承 `stdio: 'inherit'`,交互行为与直接调用引擎一致。
|
|
72
|
+
- **退出码**:透传底层引擎退出码;包装器在无参数、`helix` 子命令错误时返回非零码。
|
|
73
|
+
|
|
74
|
+
### 环境变量
|
|
75
|
+
|
|
76
|
+
- `HELIX_BASE_URL`:目标地址,默认 `https://vip.helixlife.cn/`
|
|
77
|
+
- `HELIX_CDP_URL`:`helix doctor` 时在日志中输出的 CDP 参考地址,默认 `http://127.0.0.1:9222`
|
|
78
|
+
- `HELIX_PROFILE_DIR`:`helix home/doctor` 默认使用的 profile 目录,默认 `<当前目录>/.helixlife-profile`
|
|
79
|
+
- `HELIX_OUTPUT_DIR`:快照、下载、trace 等工作区目录,默认 `<当前目录>/.helixlife-v5-cli`(通过底层 `PLAYWRIGHT_MCP_OUTPUT_DIR` 生效;也可直接设置后者覆盖)
|
|
80
|
+
|
|
81
|
+
## 命令说明
|
|
82
|
+
|
|
83
|
+
详见 Cursor skill **helixlife-v5-cli**(`.cursor/skills/helixlife-v5-cli/SKILL.md`,通常不随 npm 包发布);站点流程以包内 **`references/vip.helixlife.cn.site-ops.md`** 为准。
|
|
84
|
+
|
|
85
|
+
## 依赖
|
|
86
|
+
|
|
87
|
+
运行时依赖:`@playwright/cli` ^0.1.9(要求 Node ≥ 18)。
|
package/helix-cli.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* helixlife-v5-cli 入口:将参数透传给底层浏览器自动化引擎。
|
|
4
|
+
*/
|
|
5
|
+
'use strict';
|
|
6
|
+
|
|
7
|
+
const { main } = require('./lib/cli');
|
|
8
|
+
|
|
9
|
+
if (require.main === module) {
|
|
10
|
+
main()
|
|
11
|
+
.then((code) => process.exit(typeof code === 'number' ? code : 0))
|
|
12
|
+
.catch((e) => {
|
|
13
|
+
console.error(e);
|
|
14
|
+
process.exit(2);
|
|
15
|
+
});
|
|
16
|
+
}
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
|
+
|
|
6
|
+
const PACKAGE_JSON = require(path.join(__dirname, '..', 'package.json'));
|
|
7
|
+
|
|
8
|
+
/** 默认快照/下载等工作区目录名(相对 cwd),替代引擎默认的 .playwright-cli */
|
|
9
|
+
const DEFAULT_OUTPUT_DIR_NAME = '.helixlife-v5-cli';
|
|
10
|
+
|
|
11
|
+
function playwrightCliEntry() {
|
|
12
|
+
return require.resolve('@playwright/cli/playwright-cli.js');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 解析 CLI 输出目录(快照、下载、trace 等)。优先 PLAYWRIGHT_MCP_OUTPUT_DIR,其次 HELIX_OUTPUT_DIR。
|
|
17
|
+
*/
|
|
18
|
+
function resolveOutputDir() {
|
|
19
|
+
const explicit =
|
|
20
|
+
process.env.PLAYWRIGHT_MCP_OUTPUT_DIR || process.env.HELIX_OUTPUT_DIR;
|
|
21
|
+
const dir = explicit || DEFAULT_OUTPUT_DIR_NAME;
|
|
22
|
+
return path.isAbsolute(dir) ? dir : path.resolve(process.cwd(), dir);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 传给底层引擎子进程的环境变量(含输出目录覆盖) */
|
|
26
|
+
function buildChildEnv() {
|
|
27
|
+
const env = { ...process.env };
|
|
28
|
+
if (!env.PLAYWRIGHT_MCP_OUTPUT_DIR) {
|
|
29
|
+
env.PLAYWRIGHT_MCP_OUTPUT_DIR = resolveOutputDir();
|
|
30
|
+
}
|
|
31
|
+
return env;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 将参数原样交给底层浏览器自动化引擎,返回子进程退出码。
|
|
36
|
+
* 使用 node 直接执行入口脚本,避免依赖 npx、Shell 与 PATH,在 Windows / macOS 上一致。
|
|
37
|
+
*/
|
|
38
|
+
function runPlaywrightCliPassthrough(args) {
|
|
39
|
+
const result = spawnSync(process.execPath, [playwrightCliEntry(), ...args], {
|
|
40
|
+
stdio: 'inherit',
|
|
41
|
+
env: buildChildEnv(),
|
|
42
|
+
windowsHide: true,
|
|
43
|
+
});
|
|
44
|
+
if (result.error) {
|
|
45
|
+
throw result.error;
|
|
46
|
+
}
|
|
47
|
+
if (result.signal) {
|
|
48
|
+
console.error(`[${PACKAGE_JSON.name}] 子进程被终止: ${result.signal}`);
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
return result.status === null || result.status === undefined ? 1 : result.status;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function parseHelixFlags(tokens, startIndex) {
|
|
55
|
+
const options = {
|
|
56
|
+
headless: false,
|
|
57
|
+
cdpUrl: process.env.HELIX_CDP_URL || 'http://127.0.0.1:9222',
|
|
58
|
+
profile:
|
|
59
|
+
process.env.HELIX_PROFILE_DIR || path.join(process.cwd(), '.helixlife-profile'),
|
|
60
|
+
};
|
|
61
|
+
for (let i = startIndex; i < tokens.length; i += 1) {
|
|
62
|
+
const t = tokens[i];
|
|
63
|
+
if (t === '--headless') {
|
|
64
|
+
options.headless = true;
|
|
65
|
+
} else if (t === '--profile') {
|
|
66
|
+
const next = tokens[i + 1];
|
|
67
|
+
if (next === undefined) {
|
|
68
|
+
console.error(`[${PACKAGE_JSON.name}] --profile requires a value`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
options.profile = next;
|
|
72
|
+
i += 1;
|
|
73
|
+
} else if (t.startsWith('--profile=')) {
|
|
74
|
+
options.profile = t.slice('--profile='.length);
|
|
75
|
+
} else if (t === '--cdp-url') {
|
|
76
|
+
const next = tokens[i + 1];
|
|
77
|
+
if (next === undefined) {
|
|
78
|
+
console.error(`[${PACKAGE_JSON.name}] --cdp-url requires a value`);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
options.cdpUrl = next;
|
|
82
|
+
i += 1;
|
|
83
|
+
} else {
|
|
84
|
+
console.error(`[${PACKAGE_JSON.name}] unknown option for helix command: ${t}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return options;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function printHelp() {
|
|
92
|
+
const n = PACKAGE_JSON.name;
|
|
93
|
+
console.log(`${n} v${PACKAGE_JSON.version}`);
|
|
94
|
+
console.log('');
|
|
95
|
+
console.log('默认将所有参数透传给底层浏览器自动化引擎(与直接调用相同):');
|
|
96
|
+
console.log(` ${n} list`);
|
|
97
|
+
console.log(` ${n} open --headed`);
|
|
98
|
+
console.log(` ${n} snapshot`);
|
|
99
|
+
console.log('');
|
|
100
|
+
console.log('Helix 站点快捷子命令:');
|
|
101
|
+
console.log(
|
|
102
|
+
` ${n} helix home [--headless] [--profile <dir>] 单次 open 到 HELIX_BASE_URL(有头),再输出页面标题`
|
|
103
|
+
);
|
|
104
|
+
console.log(
|
|
105
|
+
` ${n} helix doctor [--cdp-url <url>] [--profile <dir>] 先 list,再执行 helix home`
|
|
106
|
+
);
|
|
107
|
+
console.log('');
|
|
108
|
+
console.log('本地开发可使用:');
|
|
109
|
+
console.log(' node helix-cli.js <同上参数>');
|
|
110
|
+
console.log('');
|
|
111
|
+
console.log(
|
|
112
|
+
'环境变量:HELIX_BASE_URL、HELIX_CDP_URL、HELIX_PROFILE_DIR(默认 profile 目录)、'
|
|
113
|
+
);
|
|
114
|
+
console.log(
|
|
115
|
+
` HELIX_OUTPUT_DIR / PLAYWRIGHT_MCP_OUTPUT_DIR(默认输出目录 <cwd>/${DEFAULT_OUTPUT_DIR_NAME})`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getBaseUrl() {
|
|
120
|
+
return process.env.HELIX_BASE_URL || 'https://vip.helixlife.cn/';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function helixHome(options) {
|
|
124
|
+
const baseUrl = getBaseUrl();
|
|
125
|
+
// 单次 `open <url>`:与手动打开一致,避免先 about:blank 再二次 `goto` 的跨进程导航竞态(偶发样式/布局不全)。
|
|
126
|
+
const openArgs = ['open', baseUrl];
|
|
127
|
+
if (options.profile) {
|
|
128
|
+
openArgs.push(`--profile=${options.profile}`);
|
|
129
|
+
}
|
|
130
|
+
if (!options.headless) {
|
|
131
|
+
openArgs.push('--headed');
|
|
132
|
+
}
|
|
133
|
+
const code = runPlaywrightCliPassthrough(openArgs);
|
|
134
|
+
if (code !== 0) {
|
|
135
|
+
return code;
|
|
136
|
+
}
|
|
137
|
+
console.error(`[${PACKAGE_JSON.name}] opened: ${baseUrl}`);
|
|
138
|
+
return runPlaywrightCliPassthrough(['eval', 'document.title']);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function helixDoctor(options) {
|
|
142
|
+
const baseUrl = getBaseUrl();
|
|
143
|
+
console.error(`[${PACKAGE_JSON.name} doctor] HELIX_BASE_URL: ${baseUrl}`);
|
|
144
|
+
console.error(`[${PACKAGE_JSON.name} doctor] HELIX_CDP_URL (hint): ${options.cdpUrl}`);
|
|
145
|
+
console.error(`[${PACKAGE_JSON.name} doctor] HELIX_PROFILE_DIR: ${options.profile}`);
|
|
146
|
+
console.error(`[${PACKAGE_JSON.name} doctor] helixlife-v5-cli list`);
|
|
147
|
+
const code = runPlaywrightCliPassthrough(['list']);
|
|
148
|
+
if (code !== 0) {
|
|
149
|
+
return code;
|
|
150
|
+
}
|
|
151
|
+
console.error(`[${PACKAGE_JSON.name} doctor] helix home`);
|
|
152
|
+
return helixHome(options);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @param {string[]} argv process.argv
|
|
157
|
+
* @returns {Promise<number>} exit code
|
|
158
|
+
*/
|
|
159
|
+
async function main(argv = process.argv) {
|
|
160
|
+
const tokens = argv.slice(2);
|
|
161
|
+
|
|
162
|
+
if (tokens[0] === '--helix-help' || tokens[0] === 'helix-help') {
|
|
163
|
+
printHelp();
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 勿透传到底层引擎:否则 --version 会变成 @playwright/cli 的版本(如 0.1.x),易与本体版本混淆。
|
|
168
|
+
if (tokens[0] === '--version' || tokens[0] === '-v') {
|
|
169
|
+
console.log(PACKAGE_JSON.version);
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (tokens.length === 0) {
|
|
174
|
+
console.error(`${PACKAGE_JSON.name}: 缺少子命令。透传示例:list、open、snapshot。`);
|
|
175
|
+
console.error(`Helix 快捷:helix home | helix doctor。详细说明:${PACKAGE_JSON.name} --helix-help`);
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (tokens[0] === 'helix') {
|
|
180
|
+
const sub = tokens[1];
|
|
181
|
+
const rest = parseHelixFlags(tokens, 2);
|
|
182
|
+
if (rest === null) {
|
|
183
|
+
return 2;
|
|
184
|
+
}
|
|
185
|
+
if (sub === 'home') {
|
|
186
|
+
return helixHome(rest);
|
|
187
|
+
}
|
|
188
|
+
if (sub === 'doctor') {
|
|
189
|
+
return helixDoctor(rest);
|
|
190
|
+
}
|
|
191
|
+
if (sub === 'help' || sub === '--help' || sub === '-h' || sub === undefined) {
|
|
192
|
+
printHelp();
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
console.error(
|
|
196
|
+
`[${PACKAGE_JSON.name}] unknown helix subcommand: ${sub}. See --helix-help`
|
|
197
|
+
);
|
|
198
|
+
return 2;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return runPlaywrightCliPassthrough(tokens);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main.printHelp = printHelp;
|
|
205
|
+
main.runPlaywrightCliPassthrough = runPlaywrightCliPassthrough;
|
|
206
|
+
main.playwrightCliEntry = playwrightCliEntry;
|
|
207
|
+
|
|
208
|
+
if (require.main === module) {
|
|
209
|
+
main(process.argv).then(
|
|
210
|
+
(code) => process.exit(code),
|
|
211
|
+
(err) => {
|
|
212
|
+
console.error(err);
|
|
213
|
+
process.exit(2);
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
module.exports = {
|
|
219
|
+
main,
|
|
220
|
+
resolveOutputDir,
|
|
221
|
+
buildChildEnv,
|
|
222
|
+
DEFAULT_OUTPUT_DIR_NAME,
|
|
223
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "helixlife-v5-cli",
|
|
3
|
+
"version": "1.1.3",
|
|
4
|
+
"description": "Helix(vip.helixlife.cn)精细化浏览器自动化命令行工具。",
|
|
5
|
+
"main": "helix-cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"helixlife-v5-cli": "helix-cli.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"helixlife",
|
|
14
|
+
"cli",
|
|
15
|
+
"playwright",
|
|
16
|
+
"cdp"
|
|
17
|
+
],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"type": "commonjs",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "node ./helix-cli.js helix doctor"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"references",
|
|
26
|
+
"scripts",
|
|
27
|
+
"helix-cli.js",
|
|
28
|
+
"lib",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@playwright/cli": "^0.1.9"
|
|
33
|
+
}
|
|
34
|
+
}
|