helixlife-v5-cli 1.2.7 → 1.2.9
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 +33 -58
- package/helix-cli.js +7 -2
- package/lib/.cli.runtime.generated.json +10 -1
- package/lib/automation-file-chooser-guard.js +182 -0
- package/lib/automation-overlay.init.js +332 -68
- package/lib/browser-css-cache-guard.init-page.js +49 -0
- package/lib/browser-window-maximize.init-page.js +64 -0
- package/lib/cli.js +316 -14
- package/lib/helix-reload-bypass-cache.run-code.js +7 -0
- package/lib/helix-styles-guard.js +77 -0
- package/lib/hide-nested-skills.js +120 -0
- package/lib/native-mode-patch.js +288 -0
- package/lib/postinstall.js +39 -0
- package/lib/profile-lock-guard.js +187 -0
- package/lib/runtime-config.js +90 -0
- package/lib/token-file.js +189 -0
- package/lib/visual-page-wrap.js +311 -0
- package/lib/visual.js +255 -86
- package/package.json +9 -21
- package/scripts/README.md +81 -0
- package/scripts/{analysis-detail-upload-file.js → analysis/detail-upload-file.js} +1 -1
- package/scripts/{jigsaw-inspect-dom.js → analysis/jigsaw-inspect-dom.js} +15 -15
- package/scripts/{jigsaw-verify-enumerate.js → analysis/jigsaw-verify-enumerate.js} +1 -1
- package/scripts/common/.gitkeep +0 -0
- package/scripts/{chrome-pdf-enumerate-a11y.js → edu/chrome-pdf-enumerate-a11y.js} +1 -1
- package/scripts/{chrome-pdf-goto-page.js → edu/chrome-pdf-goto-page.js} +1 -1
- package/scripts/{chrome-pdf-zoom-out.js → edu/chrome-pdf-zoom-out.js} +1 -1
- package/scripts/{hover-partial-chapter-match.js → edu/hover-partial-chapter-match.js} +1 -1
- package/scripts/maintainer/strip-extra-skills.ps1 +27 -0
- package/scripts/workbench/jova/outline-edit-root-title.js +33 -0
- package/scripts/workbench/jova/outline-keyword-edit.js +122 -0
- package/scripts/workbench/jova/outline-ops.js +344 -0
- package/scripts/workbench/jova/outline-probe-hover.js +56 -0
- package/scripts/workbench/jova/proofread-ai-rewrite-section.js +94 -0
- package/scripts/workbench/jova/proofread-ops.js +261 -0
- package/scripts/workbench/jova/writing-ops.js +376 -0
- package/scripts/workbench/jova/writing-proofread-abstract.js +103 -0
- package/lib/cli.runtime.config.json +0 -5
- package/references/vip.helixlife.cn.site-ops.md +0 -1469
- package/scripts/workbench-jova-outline-regenerate-node.js +0 -173
- package/scripts/workbench-jova-proofread-section.js +0 -262
- package/scripts/workbench-jova-writing-tool.js +0 -308
- /package/scripts/{analysis-detail-upload-both.js → analysis/detail-upload-both.js} +0 -0
- /package/scripts/{jigsaw-verify-bio-tools.js → analysis/jigsaw-verify-bio-tools.js} +0 -0
- /package/scripts/{jigsaw-verify-drag-tool.js → analysis/jigsaw-verify-drag-tool.js} +0 -0
- /package/scripts/{jigsaw-verify-reference-lines.js → analysis/jigsaw-verify-reference-lines.js} +0 -0
package/README.md
CHANGED
|
@@ -1,87 +1,62 @@
|
|
|
1
1
|
# helixlife-v5-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Helix(vip.helixlife.cn)精细化浏览器自动化 CLI — npm 包运行时。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
业务规则与 Agent Skill 文档在独立 **Skill 仓库**;本仓库仅包含可全局安装的 CLI。
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
npm install -g helixlife-v5-cli
|
|
9
|
-
```
|
|
7
|
+
## 安装
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
全局安装后直接:
|
|
9
|
+
**前置条件**:Node.js ≥ 18、npm。
|
|
14
10
|
|
|
15
11
|
```bash
|
|
16
|
-
helixlife-v5-cli
|
|
17
|
-
helixlife-v5-cli list
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## 首次执行前的自检(建议)
|
|
21
|
-
|
|
22
|
-
每次新会话第一次使用前,按下面 3 步确认环境就绪(与 Cursor skill 中代理执行的逻辑一致):
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# 1. 是否已安装;未安装时执行 npm install -g helixlife-v5-cli
|
|
12
|
+
npm install -g helixlife-v5-cli
|
|
26
13
|
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
14
|
```
|
|
34
15
|
|
|
35
|
-
|
|
16
|
+
从源码目录安装(开发/本地验证):
|
|
36
17
|
|
|
37
18
|
```bash
|
|
38
|
-
|
|
19
|
+
cd helixlife-v5-cli
|
|
20
|
+
npm install -g .
|
|
21
|
+
helixlife-v5-cli --version
|
|
39
22
|
```
|
|
40
23
|
|
|
41
|
-
|
|
24
|
+
首次执行任意浏览器命令时,CLI 会自动校验并安装 `@playwright/cli` 运行时依赖(需联网)。设 `HELIX_AUTO_INSTALL=0` 可关闭自动安装。
|
|
42
25
|
|
|
43
|
-
|
|
26
|
+
## 快速开始
|
|
44
27
|
|
|
45
28
|
```bash
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
29
|
+
helixlife-v5-cli list
|
|
30
|
+
helixlife-v5-cli open https://vip.helixlife.cn/ --profile=.helixlife-profile --headed
|
|
31
|
+
helixlife-v5-cli snapshot
|
|
32
|
+
helixlife-v5-cli click "getByRole('button', { name: '登录' })"
|
|
49
33
|
```
|
|
50
34
|
|
|
51
|
-
|
|
35
|
+
完整命令参考见 Skill 仓库 `references/cli-reference.md`;Helix 站点强约束见 Skill 仓库 `SKILL.md`。
|
|
52
36
|
|
|
53
|
-
|
|
37
|
+
## 目录结构
|
|
54
38
|
|
|
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
39
|
```
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
40
|
+
helixlife-v5-cli/
|
|
41
|
+
├── helix-cli.js # CLI 入口(npm bin)
|
|
42
|
+
├── package.json
|
|
43
|
+
├── lib/ # 运行时逻辑
|
|
44
|
+
├── node_modules/ # 依赖(安装后生成,git 忽略)
|
|
45
|
+
└── scripts/
|
|
46
|
+
└── README.md # run-code 脚本总索引
|
|
66
47
|
```
|
|
67
48
|
|
|
68
|
-
##
|
|
69
|
-
|
|
70
|
-
- **Windows / macOS**:通过当前 Node 直接引用包内引擎脚本启动子进程,不依赖 Shell、`npx` 或 PATH,便于 CI 与 Agent 调用。
|
|
71
|
-
- **stdin/stdout/stderr**:子进程继承 `stdio: 'inherit'`,交互行为与直接调用引擎一致。
|
|
72
|
-
- **退出码**:透传底层引擎退出码;包装器在无参数、`helix` 子命令错误时返回非零码。
|
|
73
|
-
|
|
74
|
-
### 环境变量
|
|
49
|
+
## 维护者说明
|
|
75
50
|
|
|
76
|
-
- `
|
|
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` 生效;也可直接设置后者覆盖)
|
|
51
|
+
更新 `@playwright/cli` 依赖时,修改 `package.json` 中的版本,在 `helixlife-v5-cli` 目录执行 `npm i`,再运行 `scripts/maintainer/strip-extra-skills.ps1` 剔除 `node_modules` 内多余的 `SKILL.md`,确认 `helixlife-v5-cli --version` 正常。
|
|
80
52
|
|
|
81
|
-
|
|
53
|
+
发布前确认 `package.json` 的 `bin` 字段指向 `helix-cli.js`,且 `files` 包含 `helix-cli.js`、`lib/`、`scripts/`。
|
|
82
54
|
|
|
83
|
-
|
|
55
|
+
## 与 Skill 仓库的关系
|
|
84
56
|
|
|
85
|
-
|
|
57
|
+
| 组件 | 位置 |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| CLI 运行时 | 本仓库 → `npm install -g helixlife-v5-cli` |
|
|
60
|
+
| Skill + 业务文档 | 独立 Skill 仓库 → `SKILL.md` + `references/` |
|
|
86
61
|
|
|
87
|
-
|
|
62
|
+
Agent 通过 Skill 读文档、通过本 CLI 跑命令,二者以全局 npm 安装衔接。
|
package/helix-cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* helixlife-v5-cli 入口:将参数透传给底层浏览器自动化引擎。
|
|
4
4
|
*/
|
|
@@ -10,7 +10,12 @@ if (require.main === module) {
|
|
|
10
10
|
main()
|
|
11
11
|
.then((code) => process.exit(typeof code === 'number' ? code : 0))
|
|
12
12
|
.catch((e) => {
|
|
13
|
-
|
|
13
|
+
if (process.env.HELIX_DEBUG) {
|
|
14
|
+
console.error(e);
|
|
15
|
+
} else {
|
|
16
|
+
console.error(`[helixlife-v5-cli] 执行失败: ${e && e.message ? e.message : e}`);
|
|
17
|
+
console.error('[helixlife-v5-cli] 设置环境变量 HELIX_DEBUG=1 可查看完整堆栈。');
|
|
18
|
+
}
|
|
14
19
|
process.exit(2);
|
|
15
20
|
});
|
|
16
21
|
}
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"browser": {
|
|
3
|
+
"launchOptions": {
|
|
4
|
+
"args": [
|
|
5
|
+
"--start-maximized"
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"initPage": [
|
|
9
|
+
"C:/Users/zouyh/Desktop/WorkSpace/jieluoxuan/cli/helix-cli/helixlife-v5-cli/lib/browser-window-maximize.init-page.js",
|
|
10
|
+
"C:/Users/zouyh/Desktop/WorkSpace/jieluoxuan/cli/helix-cli/helixlife-v5-cli/lib/browser-css-cache-guard.init-page.js"
|
|
11
|
+
],
|
|
3
12
|
"initScript": [
|
|
4
|
-
"C:/Users/zouyh/Desktop/WorkSpace/jieluoxuan/cli/helix-cli/lib/automation-overlay.init.js"
|
|
13
|
+
"C:/Users/zouyh/Desktop/WorkSpace/jieluoxuan/cli/helix-cli/helixlife-v5-cli/lib/automation-overlay.init.js"
|
|
5
14
|
]
|
|
6
15
|
}
|
|
7
16
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* automation-file-chooser-guard
|
|
5
|
+
*
|
|
6
|
+
* 场景隔离(方案 1):
|
|
7
|
+
* - 默认(HELIX_NATIVE_FILE_CHOOSER=1):Tab 不常驻订阅 filechooser → 用户手动点上传可弹系统选文件框。
|
|
8
|
+
* - run-code / upload / drop 自动化链路:在执行期间临时 page.on('filechooser') → 启用 CDP 拦截,
|
|
9
|
+
* 避免点击上传控件时弹出系统原生文件选择框;脚本内 waitForEvent('filechooser') / setInputFiles 照常工作。
|
|
10
|
+
*
|
|
11
|
+
* 实现要点:
|
|
12
|
+
* - 守卫监听器仅记录未处理的 chooser,在 finally 中尝试 setFiles([]) 关闭遗留弹层。
|
|
13
|
+
* - 与 waitForEvent('filechooser') 兼容:守卫不主动 setFiles,由脚本消费;finally 仅清理残留。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** 注入到 async page => { ... } 体内的守卫代码(在 try 内、用户逻辑之前)。 */
|
|
17
|
+
const GUARD_SETUP = `
|
|
18
|
+
let __helixActiveFc = null;
|
|
19
|
+
const __helixFcGuard = (chooser) => {
|
|
20
|
+
__helixActiveFc = chooser;
|
|
21
|
+
};
|
|
22
|
+
page.on('filechooser', __helixFcGuard);
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
/** 与 GUARD_SETUP 配对的 finally 清理片段。 */
|
|
26
|
+
const GUARD_TEARDOWN = `
|
|
27
|
+
page.off('filechooser', __helixFcGuard);
|
|
28
|
+
if (__helixActiveFc) {
|
|
29
|
+
try {
|
|
30
|
+
await __helixActiveFc.setFiles([]);
|
|
31
|
+
} catch (_) {
|
|
32
|
+
/* chooser 可能已被脚本 setFiles 消费 */
|
|
33
|
+
}
|
|
34
|
+
__helixActiveFc = null;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
/** 剥掉源码开头的空白与行注释、块注释(不影响函数体内的注释)。 */
|
|
39
|
+
function stripLeadingComments(source) {
|
|
40
|
+
let s = String(source).trimStart();
|
|
41
|
+
for (;;) {
|
|
42
|
+
if (s.startsWith('//')) {
|
|
43
|
+
const nl = s.indexOf('\n');
|
|
44
|
+
s = nl === -1 ? '' : s.slice(nl + 1).trimStart();
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (s.startsWith('/*')) {
|
|
48
|
+
const end = s.indexOf('*/');
|
|
49
|
+
if (end === -1) break;
|
|
50
|
+
s = s.slice(end + 2).trimStart();
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
return s;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 判断是否为 `async page =>` / `async (page) =>` 箭头函数(允许 leading 注释)。 */
|
|
59
|
+
function isRunCodeArrowFunction(source) {
|
|
60
|
+
const head = stripLeadingComments(source);
|
|
61
|
+
return /^async\s+page\s*=>/.test(head) || /^async\s*\(\s*page\s*\)\s*=>/.test(head);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 将用户 run-code 源(`async page => { ... }` 或裸函数体)包上一层自动化上传守卫。
|
|
66
|
+
* @param {string} userSource
|
|
67
|
+
* @returns {string} 完整 `async page => { ... }` 源码字符串
|
|
68
|
+
*/
|
|
69
|
+
function wrapUserRunCodeWithUploadGuard(userSource) {
|
|
70
|
+
const trimmed = String(userSource || '').trim().replace(/;+\s*$/, '');
|
|
71
|
+
if (!trimmed) {
|
|
72
|
+
throw Object.assign(new Error('run-code 脚本为空'), { code: 'E_BAD_ARG' });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const isArrow = isRunCodeArrowFunction(trimmed);
|
|
76
|
+
|
|
77
|
+
const userFnLiteral = isArrow ? trimmed : `async page => { ${trimmed} }`;
|
|
78
|
+
|
|
79
|
+
return `async page => {${GUARD_SETUP}
|
|
80
|
+
try {
|
|
81
|
+
const __userFn = ${userFnLiteral};
|
|
82
|
+
return await __userFn(page);
|
|
83
|
+
} finally {${GUARD_TEARDOWN}
|
|
84
|
+
}
|
|
85
|
+
}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 对 run-code CLI tokens 注入上传守卫(不依赖 visual 包装)。
|
|
90
|
+
* @param {string[]} tokens
|
|
91
|
+
* @returns {string[]}
|
|
92
|
+
*/
|
|
93
|
+
function applyRunCodeAutomationGuard(tokens) {
|
|
94
|
+
const { parseRunCodePayload } = require('./visual-page-wrap');
|
|
95
|
+
const { code, passthroughFlags } = parseRunCodePayload(tokens);
|
|
96
|
+
const guarded = wrapUserRunCodeWithUploadGuard(code);
|
|
97
|
+
return ['run-code', guarded, ...passthroughFlags];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** 需启用上传守卫的自动化子命令(会触发文件选择或 setInputFiles 的 CLI 通道)。 */
|
|
101
|
+
const AUTOMATION_UPLOAD_COMMANDS = new Set(['run-code', 'upload', 'drop']);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {string[]} tokens 已剥离 visual flags 的 argv
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
function isAutomationUploadCommand(tokens) {
|
|
108
|
+
let i = 0;
|
|
109
|
+
if (tokens[i] === '--raw') i += 1;
|
|
110
|
+
if (tokens[i] === '--json') i += 1;
|
|
111
|
+
if (tokens[i] && tokens[i].startsWith('-s=')) i += 1;
|
|
112
|
+
const cmd = tokens[i];
|
|
113
|
+
return AUTOMATION_UPLOAD_COMMANDS.has(cmd);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* upload / drop:用极简 run-code 注册/注销守卫(跨多次 CLI 调用,挂在 page 对象上)。
|
|
118
|
+
* @param {'enable' | 'disable'} mode
|
|
119
|
+
* @returns {string} run-code 源码
|
|
120
|
+
*/
|
|
121
|
+
function buildUploadGuardOnlyRunCode(mode) {
|
|
122
|
+
if (mode === 'enable') {
|
|
123
|
+
return `async page => {
|
|
124
|
+
if (!page.__helixUploadGuardInstalled) {
|
|
125
|
+
page.__helixActiveFc = null;
|
|
126
|
+
page.__helixFcGuard = (chooser) => {
|
|
127
|
+
page.__helixActiveFc = chooser;
|
|
128
|
+
};
|
|
129
|
+
page.on('filechooser', page.__helixFcGuard);
|
|
130
|
+
page.__helixUploadGuardInstalled = true;
|
|
131
|
+
}
|
|
132
|
+
}`;
|
|
133
|
+
}
|
|
134
|
+
return `async page => {
|
|
135
|
+
if (page.__helixFcGuard) {
|
|
136
|
+
page.off('filechooser', page.__helixFcGuard);
|
|
137
|
+
page.__helixFcGuard = null;
|
|
138
|
+
page.__helixUploadGuardInstalled = false;
|
|
139
|
+
}
|
|
140
|
+
if (page.__helixActiveFc) {
|
|
141
|
+
try {
|
|
142
|
+
await page.__helixActiveFc.setFiles([]);
|
|
143
|
+
} catch (_) {}
|
|
144
|
+
page.__helixActiveFc = null;
|
|
145
|
+
}
|
|
146
|
+
}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 对 upload / drop 命令:先 enable 守卫 → 执行原命令 → disable 守卫。
|
|
151
|
+
* @param {string[]} tokens
|
|
152
|
+
* @param {(args: string[], opts?: object) => number | { code: number, stdout?: string, stderr?: string }} runPassthrough
|
|
153
|
+
* @returns {number}
|
|
154
|
+
*/
|
|
155
|
+
function runUploadCommandWithGuard(tokens, runPassthrough) {
|
|
156
|
+
const enableCode = buildUploadGuardOnlyRunCode('enable');
|
|
157
|
+
const disableCode = buildUploadGuardOnlyRunCode('disable');
|
|
158
|
+
|
|
159
|
+
const pre = runPassthrough(['run-code', enableCode], { capture: true });
|
|
160
|
+
const preCode = typeof pre === 'object' ? pre.code : pre;
|
|
161
|
+
if (preCode !== 0) {
|
|
162
|
+
return preCode;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const main = runPassthrough(tokens);
|
|
166
|
+
const mainCode = typeof main === 'object' ? main.code : main;
|
|
167
|
+
|
|
168
|
+
const post = runPassthrough(['run-code', disableCode], { capture: true });
|
|
169
|
+
const postCode = typeof post === 'object' ? post.code : post;
|
|
170
|
+
|
|
171
|
+
return mainCode !== 0 ? mainCode : postCode;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
GUARD_SETUP,
|
|
176
|
+
GUARD_TEARDOWN,
|
|
177
|
+
wrapUserRunCodeWithUploadGuard,
|
|
178
|
+
applyRunCodeAutomationGuard,
|
|
179
|
+
isAutomationUploadCommand,
|
|
180
|
+
AUTOMATION_UPLOAD_COMMANDS,
|
|
181
|
+
runUploadCommandWithGuard,
|
|
182
|
+
};
|