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.
Files changed (46) hide show
  1. package/README.md +33 -58
  2. package/helix-cli.js +7 -2
  3. package/lib/.cli.runtime.generated.json +10 -1
  4. package/lib/automation-file-chooser-guard.js +182 -0
  5. package/lib/automation-overlay.init.js +332 -68
  6. package/lib/browser-css-cache-guard.init-page.js +49 -0
  7. package/lib/browser-window-maximize.init-page.js +64 -0
  8. package/lib/cli.js +316 -14
  9. package/lib/helix-reload-bypass-cache.run-code.js +7 -0
  10. package/lib/helix-styles-guard.js +77 -0
  11. package/lib/hide-nested-skills.js +120 -0
  12. package/lib/native-mode-patch.js +288 -0
  13. package/lib/postinstall.js +39 -0
  14. package/lib/profile-lock-guard.js +187 -0
  15. package/lib/runtime-config.js +90 -0
  16. package/lib/token-file.js +189 -0
  17. package/lib/visual-page-wrap.js +311 -0
  18. package/lib/visual.js +255 -86
  19. package/package.json +9 -21
  20. package/scripts/README.md +81 -0
  21. package/scripts/{analysis-detail-upload-file.js → analysis/detail-upload-file.js} +1 -1
  22. package/scripts/{jigsaw-inspect-dom.js → analysis/jigsaw-inspect-dom.js} +15 -15
  23. package/scripts/{jigsaw-verify-enumerate.js → analysis/jigsaw-verify-enumerate.js} +1 -1
  24. package/scripts/common/.gitkeep +0 -0
  25. package/scripts/{chrome-pdf-enumerate-a11y.js → edu/chrome-pdf-enumerate-a11y.js} +1 -1
  26. package/scripts/{chrome-pdf-goto-page.js → edu/chrome-pdf-goto-page.js} +1 -1
  27. package/scripts/{chrome-pdf-zoom-out.js → edu/chrome-pdf-zoom-out.js} +1 -1
  28. package/scripts/{hover-partial-chapter-match.js → edu/hover-partial-chapter-match.js} +1 -1
  29. package/scripts/maintainer/strip-extra-skills.ps1 +27 -0
  30. package/scripts/workbench/jova/outline-edit-root-title.js +33 -0
  31. package/scripts/workbench/jova/outline-keyword-edit.js +122 -0
  32. package/scripts/workbench/jova/outline-ops.js +344 -0
  33. package/scripts/workbench/jova/outline-probe-hover.js +56 -0
  34. package/scripts/workbench/jova/proofread-ai-rewrite-section.js +94 -0
  35. package/scripts/workbench/jova/proofread-ops.js +261 -0
  36. package/scripts/workbench/jova/writing-ops.js +376 -0
  37. package/scripts/workbench/jova/writing-proofread-abstract.js +103 -0
  38. package/lib/cli.runtime.config.json +0 -5
  39. package/references/vip.helixlife.cn.site-ops.md +0 -1469
  40. package/scripts/workbench-jova-outline-regenerate-node.js +0 -173
  41. package/scripts/workbench-jova-proofread-section.js +0 -262
  42. package/scripts/workbench-jova-writing-tool.js +0 -308
  43. /package/scripts/{analysis-detail-upload-both.js → analysis/detail-upload-both.js} +0 -0
  44. /package/scripts/{jigsaw-verify-bio-tools.js → analysis/jigsaw-verify-bio-tools.js} +0 -0
  45. /package/scripts/{jigsaw-verify-drag-tool.js → analysis/jigsaw-verify-drag-tool.js} +0 -0
  46. /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
- 全局安装 **`helixlife-v5-cli`** 后即可直接操作网页。该工具默认会把命令行参数原样透传到底层浏览器自动化引擎,同时附带 **Helix** 站点一键流程 `helix home` / `helix doctor`。
3
+ Helix(vip.helixlife.cn)精细化浏览器自动化 CLI npm 包运行时。
4
4
 
5
- ## 安装(仅 npm 全局安装)
5
+ 业务规则与 Agent Skill 文档在独立 **Skill 仓库**;本仓库仅包含可全局安装的 CLI。
6
6
 
7
- ```bash
8
- npm install -g helixlife-v5-cli
9
- ```
7
+ ## 安装
10
8
 
11
- > 不再支持 / 不建议项目级本地安装与 `npx helixlife-v5-cli`,以避免污染用户工程的 `node_modules` 与 lockfile
12
-
13
- 全局安装后直接:
9
+ **前置条件**:Node.js 18、npm
14
10
 
15
11
  ```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
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
- `PATH` 不含 `npm bin -g` 时(nvm / Volta / 自定义 prefix 常见),临时兜底:
16
+ 从源码目录安装(开发/本地验证):
36
17
 
37
18
  ```bash
38
- node "$(npm root -g)/helixlife-v5-cli/helix-cli.js" --version
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
- 仓库克隆后,**仅在开发本仓库时**使用 `node helix-cli.js`:
26
+ ## 快速开始
44
27
 
45
28
  ```bash
46
- npm install
47
- node helix-cli.js --help
48
- node helix-cli.js list
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
- ## Helix 快捷命令
35
+ 完整命令参考见 Skill 仓库 `references/cli-reference.md`;Helix 站点强约束见 Skill 仓库 `SKILL.md`。
52
36
 
53
- 封装了「打开会话 → `goto` 到 HELIX_BASE_URL → `eval` 标题」,对应原 skill 文档里分步执行的启动流程:
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
- ```bash
65
- helixlife-v5-cli --helix-help
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
- - `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` 生效;也可直接设置后者覆盖)
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
- 详见 Cursor skill **helixlife-v5-cli**(`.cursor/skills/helixlife-v5-cli/SKILL.md`,通常不随 npm 包发布);站点流程以包内 **`references/vip.helixlife.cn.site-ops.md`** 为准。
55
+ ## Skill 仓库的关系
84
56
 
85
- ## 依赖
57
+ | 组件 | 位置 |
58
+ | --- | --- |
59
+ | CLI 运行时 | 本仓库 → `npm install -g helixlife-v5-cli` |
60
+ | Skill + 业务文档 | 独立 Skill 仓库 → `SKILL.md` + `references/` |
86
61
 
87
- 运行时依赖:`@playwright/cli` ^0.1.9(要求 Node 18)。
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
- console.error(e);
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
+ };