helixlife-v5-cli 1.2.8 → 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/lib/visual.js CHANGED
@@ -1,66 +1,185 @@
1
- 'use strict';
1
+ "use strict";
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
5
 
6
- const INIT_SCRIPT_PATH = path.join(__dirname, 'automation-overlay.init.js');
7
- const GENERATED_CONFIG_PATH = path.join(__dirname, '.cli.runtime.generated.json');
6
+ const { wrapRunCodeTokens } = require("./visual-page-wrap");
7
+ const { applyNativeBrowserOverrides } = require("./runtime-config");
8
+ const { runUploadCommandWithGuard } = require("./automation-file-chooser-guard");
9
+
10
+ const INIT_SCRIPT_PATH = path.join(__dirname, "automation-overlay.init.js");
11
+ const INIT_PAGE_MAXIMIZE_PATH = path.join(
12
+ __dirname,
13
+ "browser-window-maximize.init-page.js",
14
+ );
15
+ const INIT_PAGE_CSS_GUARD_PATH = path.join(
16
+ __dirname,
17
+ "browser-css-cache-guard.init-page.js",
18
+ );
19
+ const GENERATED_CONFIG_PATH = path.join(
20
+ __dirname,
21
+ ".cli.runtime.generated.json",
22
+ );
8
23
 
9
24
  /** 默认开启页面内视觉反馈;HELIX_VISUAL=0 可全局关闭 */
10
- const DEFAULT_VISUAL_ENABLED = process.env.HELIX_VISUAL !== '0';
25
+ const DEFAULT_VISUAL_ENABLED = process.env.HELIX_VISUAL !== "0";
26
+ /** 反馈层常驻不消失;HELIX_VISUAL_PERSIST=1 或 --visual-persist */
27
+ const DEFAULT_VISUAL_PERSIST = process.env.HELIX_VISUAL_PERSIST === "1";
11
28
 
12
29
  const VISUAL_COMMANDS = {
13
- click: { pending: '正在点击', success: '已点击', pendingTone: 'pending', successTone: 'success' },
14
- dblclick: { pending: '正在双击', success: '已双击', pendingTone: 'pending', successTone: 'success' },
15
- fill: { pending: '正在填写', success: '已填写', pendingTone: 'pending', successTone: 'success' },
16
- hover: { pending: '正在悬停', success: '已悬停', pendingTone: 'info', successTone: 'info' },
17
- check: { pending: '正在勾选', success: '已勾选', pendingTone: 'pending', successTone: 'success' },
18
- uncheck: { pending: '正在取消勾选', success: '已取消勾选', pendingTone: 'pending', successTone: 'info' },
19
- select: { pending: '正在选择', success: '已选择', pendingTone: 'pending', successTone: 'success' },
20
- drag: { pending: '正在拖拽', success: '已拖拽', pendingTone: 'pending', successTone: 'success' },
21
- drop: { pending: '正在放置', success: '已放置', pendingTone: 'pending', successTone: 'success' },
22
- upload: { hudOnly: true, pending: '正在上传', success: '已上传', pendingTone: 'pending', successTone: 'success' },
23
- press: { hudOnly: true, pending: '按键', success: '按键完成', pendingTone: 'info', successTone: 'success' },
24
- type: { hudOnly: true, pending: '正在输入', success: '输入完成', pendingTone: 'pending', successTone: 'success' },
30
+ click: {
31
+ pending: "正在点击",
32
+ success: "已点击",
33
+ pendingTone: "pending",
34
+ successTone: "success",
35
+ },
36
+ dblclick: {
37
+ pending: "正在双击",
38
+ success: "已双击",
39
+ pendingTone: "pending",
40
+ successTone: "success",
41
+ },
42
+ fill: {
43
+ pending: "正在填写",
44
+ success: "已填写",
45
+ pendingTone: "pending",
46
+ successTone: "success",
47
+ },
48
+ hover: {
49
+ pending: "正在悬停",
50
+ success: "已悬停",
51
+ pendingTone: "info",
52
+ successTone: "info",
53
+ },
54
+ check: {
55
+ pending: "正在勾选",
56
+ success: "已勾选",
57
+ pendingTone: "pending",
58
+ successTone: "success",
59
+ },
60
+ uncheck: {
61
+ pending: "正在取消勾选",
62
+ success: "已取消勾选",
63
+ pendingTone: "pending",
64
+ successTone: "info",
65
+ },
66
+ select: {
67
+ pending: "正在选择",
68
+ success: "已选择",
69
+ pendingTone: "pending",
70
+ successTone: "success",
71
+ },
72
+ drag: {
73
+ pending: "正在拖拽",
74
+ success: "已拖拽",
75
+ pendingTone: "pending",
76
+ successTone: "success",
77
+ },
78
+ drop: {
79
+ pending: "正在放置",
80
+ success: "已放置",
81
+ pendingTone: "pending",
82
+ successTone: "success",
83
+ },
84
+ upload: {
85
+ hudOnly: true,
86
+ pending: "正在上传",
87
+ success: "已上传",
88
+ pendingTone: "pending",
89
+ successTone: "success",
90
+ },
91
+ press: {
92
+ hudOnly: true,
93
+ pending: "按键",
94
+ success: "按键完成",
95
+ pendingTone: "info",
96
+ successTone: "success",
97
+ },
98
+ type: {
99
+ hudOnly: true,
100
+ pending: "正在输入",
101
+ success: "输入完成",
102
+ pendingTone: "pending",
103
+ successTone: "success",
104
+ },
105
+ "run-code": {
106
+ hudOnly: true,
107
+ pending: "脚本执行中",
108
+ success: "脚本完成",
109
+ pendingTone: "pending",
110
+ successTone: "success",
111
+ },
25
112
  };
26
113
 
27
- const SESSION_START_COMMANDS = new Set(['open', 'attach']);
114
+ const SESSION_START_COMMANDS = new Set(["open", "attach"]);
28
115
 
29
116
  function readInitScriptSource() {
30
- return fs.readFileSync(INIT_SCRIPT_PATH, 'utf8');
117
+ return fs.readFileSync(INIT_SCRIPT_PATH, "utf8");
31
118
  }
32
119
 
33
- function resolveGeneratedConfigPath() {
34
- const payload = {
35
- browser: {
36
- initScript: [INIT_SCRIPT_PATH.replace(/\\/g, '/')],
37
- },
38
- };
39
- fs.writeFileSync(GENERATED_CONFIG_PATH, JSON.stringify(payload, null, 2), 'utf8');
120
+ function isHeadedOpen(tokens) {
121
+ return tokens.some((t) => t === "--headed" || t.startsWith("--headed="));
122
+ }
123
+
124
+ function buildRuntimeBrowserConfig(visualEnabled, tokens) {
125
+ let browser = {};
126
+ const cmd = resolveCommandName(tokens);
127
+ if (cmd === "open" && isHeadedOpen(tokens)) {
128
+ browser.launchOptions = { args: ["--start-maximized"] };
129
+ browser.initPage = [
130
+ INIT_PAGE_MAXIMIZE_PATH.replace(/\\/g, "/"),
131
+ INIT_PAGE_CSS_GUARD_PATH.replace(/\\/g, "/"),
132
+ ];
133
+ }
134
+ if (visualEnabled) {
135
+ browser.initScript = [INIT_SCRIPT_PATH.replace(/\\/g, "/")];
136
+ }
137
+ // 合并原生交互模式 overrides(acceptDownloads / chromiumSandbox / downloadsPath)。
138
+ // applyNativeBrowserOverrides 不会覆盖已显式设值的字段。
139
+ browser = applyNativeBrowserOverrides(browser);
140
+ return browser;
141
+ }
142
+
143
+ function resolveGeneratedConfigPath(
144
+ visualEnabled = DEFAULT_VISUAL_ENABLED,
145
+ tokens = [],
146
+ ) {
147
+ const browser = buildRuntimeBrowserConfig(visualEnabled, tokens);
148
+ const payload = Object.keys(browser).length > 0 ? { browser } : {};
149
+ fs.writeFileSync(
150
+ GENERATED_CONFIG_PATH,
151
+ JSON.stringify(payload, null, 2),
152
+ "utf8",
153
+ );
40
154
  return GENERATED_CONFIG_PATH;
41
155
  }
42
156
 
43
157
  /**
44
158
  * 从 argv 中提取并剥离 --visual / --no-visual / --visual-ms。
45
- * @returns {{ tokens: string[], visual: { enabled: boolean, durationMs: number | null } }}
159
+ * @returns {{ tokens: string[], visual: { enabled: boolean, durationMs: number | null, persist?: boolean } }}
46
160
  */
47
161
  function parseVisualFlags(tokens) {
48
162
  const visual = {
49
163
  enabled: DEFAULT_VISUAL_ENABLED,
50
164
  durationMs: null,
51
165
  };
166
+ if (DEFAULT_VISUAL_PERSIST) visual.persist = true;
52
167
  const out = [];
53
168
  for (let i = 0; i < tokens.length; i += 1) {
54
169
  const t = tokens[i];
55
- if (t === '--visual') {
170
+ if (t === "--visual") {
56
171
  visual.enabled = true;
57
- } else if (t === '--no-visual') {
172
+ } else if (t === "--no-visual") {
58
173
  visual.enabled = false;
59
- } else if (t === '--visual-ms') {
174
+ } else if (t === "--visual-persist") {
175
+ visual.persist = true;
176
+ } else if (t === "--no-visual-persist") {
177
+ visual.persist = false;
178
+ } else if (t === "--visual-ms") {
60
179
  visual.durationMs = tokens[i + 1];
61
180
  i += 1;
62
- } else if (t.startsWith('--visual-ms=')) {
63
- visual.durationMs = t.slice('--visual-ms='.length);
181
+ } else if (t.startsWith("--visual-ms=")) {
182
+ visual.durationMs = t.slice("--visual-ms=".length);
64
183
  } else {
65
184
  out.push(t);
66
185
  }
@@ -68,7 +187,12 @@ function parseVisualFlags(tokens) {
68
187
  if (visual.durationMs != null) {
69
188
  const n = Number(visual.durationMs);
70
189
  if (!Number.isFinite(n) || n <= 0) {
71
- throw Object.assign(new Error(`--visual-ms 需要正数,收到 ${JSON.stringify(visual.durationMs)}`), { code: 'E_BAD_ARG' });
190
+ throw Object.assign(
191
+ new Error(
192
+ `--visual-ms 需要正数,收到 ${JSON.stringify(visual.durationMs)}`,
193
+ ),
194
+ { code: "E_BAD_ARG" },
195
+ );
72
196
  }
73
197
  visual.durationMs = Math.round(n);
74
198
  }
@@ -76,36 +200,35 @@ function parseVisualFlags(tokens) {
76
200
  }
77
201
 
78
202
  function hasConfigFlag(tokens) {
79
- return tokens.some((t) => t === '--config' || t.startsWith('--config='));
203
+ return tokens.some((t) => t === "--config" || t.startsWith("--config="));
80
204
  }
81
205
 
82
206
  /**
83
- * open / attach 时自动注入 initScript 配置,使新页面预装视觉层。
207
+ * open / attach 时注入运行时配置:有头 open 最大化窗口;visual 开启时预装视觉层 initScript
84
208
  */
85
209
  function injectVisualConfig(tokens, visual) {
86
- if (!visual.enabled) return tokens;
87
210
  const cmd = resolveCommandName(tokens);
88
211
  if (!SESSION_START_COMMANDS.has(cmd) || hasConfigFlag(tokens)) {
89
212
  return tokens;
90
213
  }
91
- const configPath = resolveGeneratedConfigPath();
214
+ const configPath = resolveGeneratedConfigPath(visual.enabled, tokens);
92
215
  return [...tokens, `--config=${configPath}`];
93
216
  }
94
217
 
95
218
  function resolveCommandName(tokens) {
96
219
  let i = 0;
97
- if (tokens[i] === '--raw') i += 1;
98
- if (tokens[i] === '--json') i += 1;
99
- if (tokens[i] && tokens[i].startsWith('-s=')) i += 1;
100
- return tokens[i] || '';
220
+ if (tokens[i] === "--raw") i += 1;
221
+ if (tokens[i] === "--json") i += 1;
222
+ if (tokens[i] && tokens[i].startsWith("-s=")) i += 1;
223
+ return tokens[i] || "";
101
224
  }
102
225
 
103
226
  function isElementRef(token) {
104
- if (!token || token.startsWith('-')) return false;
227
+ if (!token || token.startsWith("-")) return false;
105
228
  if (/^e\d+$/.test(token)) return true;
106
229
  if (token.startsWith('"') || token.startsWith("'")) return true;
107
- if (token.startsWith('#') || token.startsWith('.')) return true;
108
- if (token.startsWith('getBy')) return true;
230
+ if (token.startsWith("#") || token.startsWith(".")) return true;
231
+ if (token.startsWith("getBy")) return true;
109
232
  return false;
110
233
  }
111
234
 
@@ -119,29 +242,33 @@ function extractElementTarget(tokens) {
119
242
  if (def.hudOnly) return null;
120
243
 
121
244
  let i = 0;
122
- if (tokens[i] === '--raw') i += 1;
123
- if (tokens[i] === '--json') i += 1;
124
- if (tokens[i] && tokens[i].startsWith('-s=')) i += 1;
245
+ if (tokens[i] === "--raw") i += 1;
246
+ if (tokens[i] === "--json") i += 1;
247
+ if (tokens[i] && tokens[i].startsWith("-s=")) i += 1;
125
248
  i += 1; // skip command name
126
249
 
127
250
  const positional = [];
128
251
  for (; i < tokens.length; i += 1) {
129
252
  const t = tokens[i];
130
- if (t.startsWith('--')) {
131
- if (t.includes('=')) continue;
253
+ if (t.startsWith("--")) {
254
+ if (t.includes("=")) continue;
132
255
  const next = tokens[i + 1];
133
- if (next !== undefined && !next.startsWith('-')) i += 1;
256
+ if (next !== undefined && !next.startsWith("-")) i += 1;
134
257
  continue;
135
258
  }
136
259
  positional.push(t);
137
260
  }
138
261
 
139
- if (cmd === 'drag' && positional.length >= 1) return positional[0];
140
- if (cmd === 'fill' && positional.length >= 1) return positional[0];
141
- if (cmd === 'select' && positional.length >= 1) return positional[0];
142
- if (cmd === 'drop' && positional.length >= 1) return positional[0];
143
- if (positional.length >= 1 && isElementRef(positional[0])) return positional[0];
144
- if (positional.length >= 2 && isElementRef(positional[positional.length - 1])) {
262
+ if (cmd === "drag" && positional.length >= 1) return positional[0];
263
+ if (cmd === "fill" && positional.length >= 1) return positional[0];
264
+ if (cmd === "select" && positional.length >= 1) return positional[0];
265
+ if (cmd === "drop" && positional.length >= 1) return positional[0];
266
+ if (positional.length >= 1 && isElementRef(positional[0]))
267
+ return positional[0];
268
+ if (
269
+ positional.length >= 2 &&
270
+ isElementRef(positional[positional.length - 1])
271
+ ) {
145
272
  return positional[positional.length - 1];
146
273
  }
147
274
  return null;
@@ -150,7 +277,7 @@ function extractElementTarget(tokens) {
150
277
  function buildFlashEval(target, options) {
151
278
  const optsLit = JSON.stringify(options);
152
279
  return [
153
- 'eval',
280
+ "eval",
154
281
  `el => { const v = window.__helix_visual__; if (!v) return false; return v.flash(el, ${optsLit}); }`,
155
282
  target,
156
283
  ];
@@ -158,24 +285,32 @@ function buildFlashEval(target, options) {
158
285
 
159
286
  function buildHudEval(options) {
160
287
  const optsLit = JSON.stringify(options);
161
- return ['eval', `() => { const v = window.__helix_visual__; if (!v) return false; return v.showHud(${optsLit}); }`];
288
+ return [
289
+ "eval",
290
+ `() => { const v = window.__helix_visual__; if (!v) return false; return v.showHud(${optsLit}); }`,
291
+ ];
162
292
  }
163
293
 
164
294
  function buildSetOptionsEval(visual) {
165
- const payload = JSON.stringify({
295
+ const payload = {
166
296
  enabled: visual.enabled,
167
297
  durationMs: visual.durationMs,
168
- });
169
- return ['eval', `() => { const v = window.__helix_visual__; if (!v) return null; return v.setVisualOptions(${payload}); }`];
298
+ };
299
+ if (typeof visual.persist === "boolean") payload.persist = visual.persist;
300
+ const payloadLit = JSON.stringify(payload);
301
+ return [
302
+ "eval",
303
+ `() => { const v = window.__helix_visual__; if (!v) return null; return v.setVisualOptions(${payloadLit}); }`,
304
+ ];
170
305
  }
171
306
 
172
307
  function buildInstallEval() {
173
308
  const src = readInitScriptSource();
174
- return ['eval', src];
309
+ return ["eval", src];
175
310
  }
176
311
 
177
312
  function buildBridgeCheckEval() {
178
- return ['eval', '--raw', 'Boolean(window.__helix_visual__)'];
313
+ return ["eval", "--raw", "Boolean(window.__helix_visual__)"];
179
314
  }
180
315
 
181
316
  function shouldWrapWithVisual(tokens, visual) {
@@ -193,56 +328,90 @@ function shouldWrapWithVisual(tokens, visual) {
193
328
  async function runWithVisualFeedback(tokens, visual, runPassthrough) {
194
329
  const cmd = resolveCommandName(tokens);
195
330
  const def = VISUAL_COMMANDS[cmd];
196
- const target = extractElementTarget(tokens);
197
331
 
198
332
  const runCapture = (args) => runPassthrough(args, { capture: true });
199
333
 
200
334
  const check = runCapture(buildBridgeCheckEval());
201
- if (String(check.stdout || '').trim() !== 'true') {
335
+ if (String(check.stdout || "").trim() !== "true") {
202
336
  runCapture(buildInstallEval());
203
337
  }
338
+
339
+ if (cmd === "run-code") {
340
+ runCapture(
341
+ buildSetOptionsEval({
342
+ enabled: visual.enabled,
343
+ durationMs: visual.durationMs,
344
+ }),
345
+ );
346
+ let wrappedTokens;
347
+ try {
348
+ wrappedTokens = wrapRunCodeTokens(tokens, readInitScriptSource());
349
+ } catch (e) {
350
+ console.error(`[helixlife-v5-cli] run-code 视觉包装失败: ${e.message}`);
351
+ return runPassthrough(tokens);
352
+ }
353
+ return runPassthrough(wrappedTokens);
354
+ }
355
+
356
+ const target = extractElementTarget(tokens);
204
357
  runCapture(buildSetOptionsEval(visual));
205
358
 
206
- const hudBase = { action: cmd, target: target || '', status: 'pending' };
359
+ const hudBase = { action: cmd, target: target || "", status: "pending" };
207
360
  runCapture(buildHudEval({ ...hudBase, detail: def.pending }));
208
361
 
209
362
  if (target) {
210
- runCapture(buildFlashEval(target, {
211
- tone: def.pendingTone || 'pending',
212
- label: def.pending,
213
- durationMs: visual.durationMs,
214
- }));
363
+ runCapture(
364
+ buildFlashEval(target, {
365
+ tone: def.pendingTone || "pending",
366
+ label: def.pending,
367
+ durationMs: visual.durationMs,
368
+ }),
369
+ );
215
370
  }
216
371
 
217
- const code = runPassthrough(tokens);
372
+ const nativeFileChooser =
373
+ process.env.HELIX_NATIVE_FILE_CHOOSER === undefined ||
374
+ process.env.HELIX_NATIVE_FILE_CHOOSER === "1";
375
+
376
+ const code =
377
+ nativeFileChooser && (cmd === "upload" || cmd === "drop")
378
+ ? runUploadCommandWithGuard(tokens, runPassthrough)
379
+ : runPassthrough(tokens);
218
380
 
219
- const successTone = code === 0 ? (def.successTone || 'success') : 'danger';
220
- const successLabel = code === 0 ? def.success : '操作失败';
381
+ const successTone = code === 0 ? def.successTone || "success" : "danger";
382
+ const successLabel = code === 0 ? def.success : "错误";
221
383
  runCapture(buildSetOptionsEval(visual));
222
384
  if (target) {
223
- runCapture(buildFlashEval(target, {
224
- tone: successTone,
225
- label: successLabel,
226
- durationMs: visual.durationMs,
227
- }));
385
+ runCapture(
386
+ buildFlashEval(target, {
387
+ tone: successTone,
388
+ label: successLabel,
389
+ durationMs: visual.durationMs,
390
+ }),
391
+ );
228
392
  }
229
- runCapture(buildHudEval({
230
- action: cmd,
231
- target: target || '',
232
- status: code === 0 ? 'success' : 'danger',
233
- detail: successLabel,
234
- }));
393
+ runCapture(
394
+ buildHudEval({
395
+ action: cmd,
396
+ target: target || "",
397
+ status: code === 0 ? "success" : "danger",
398
+ detail: successLabel,
399
+ }),
400
+ );
235
401
 
236
402
  return code;
237
403
  }
238
404
 
239
405
  module.exports = {
240
406
  INIT_SCRIPT_PATH,
407
+ INIT_PAGE_MAXIMIZE_PATH,
241
408
  VISUAL_COMMANDS,
242
409
  DEFAULT_VISUAL_ENABLED,
410
+ DEFAULT_VISUAL_PERSIST,
243
411
  parseVisualFlags,
244
412
  injectVisualConfig,
245
413
  shouldWrapWithVisual,
246
414
  runWithVisualFeedback,
415
+ resolveCommandName,
247
416
  resolveGeneratedConfigPath,
248
417
  };
package/package.json CHANGED
@@ -1,34 +1,22 @@
1
1
  {
2
2
  "name": "helixlife-v5-cli",
3
- "version": "1.2.8",
4
- "description": "Helixvip.helixlife.cn)精细化浏览器自动化命令行工具。",
5
- "main": "helix-cli.js",
3
+ "version": "1.2.9",
4
+ "description": "Helix site (vip.helixlife.cn) browser automation CLI",
6
5
  "bin": {
7
6
  "helixlife-v5-cli": "helix-cli.js"
8
7
  },
8
+ "files": [
9
+ "helix-cli.js",
10
+ "lib/",
11
+ "scripts/"
12
+ ],
9
13
  "engines": {
10
14
  "node": ">=18"
11
15
  },
12
- "keywords": [
13
- "helixlife",
14
- "cli",
15
- "playwright",
16
- "cdp"
17
- ],
18
- "author": "",
19
- "license": "ISC",
20
- "type": "commonjs",
21
16
  "scripts": {
22
- "start": "node ./helix-cli.js helix doctor"
17
+ "postinstall": "node lib/postinstall.js"
23
18
  },
24
- "files": [
25
- "references",
26
- "scripts",
27
- "helix-cli.js",
28
- "lib",
29
- "README.md"
30
- ],
31
19
  "dependencies": {
32
- "@playwright/cli": "^0.1.9"
20
+ "@playwright/cli": "0.1.14"
33
21
  }
34
22
  }
@@ -0,0 +1,81 @@
1
+ # Helix 可复用 `run-code` 脚本目录
2
+
3
+ > 均通过 `helixlife-v5-cli run-code --filename="<CLI_PKG_ROOT>/scripts/<模块>/<脚本>"` 调用。
4
+ > `<CLI_PKG_ROOT>` 为全局安装的 npm 包根目录(见 Skill 仓库 `SKILL.md` 路径解析)。
5
+
6
+ **Agent 选用规则**:勿扫描本目录猜脚本。先 `Read` Skill 仓库 `references/vip.helixlife.cn.site-ops.md` → 定向读目标模块 `references/*.md` → 按模块文档内的脚本表调用;本文件仅为总索引。
7
+
8
+ ## 目录结构
9
+
10
+ | 目录 | 对应 references 模块 | 说明 |
11
+ | --- | --- | --- |
12
+ | [`common/`](./common/) | `vip.helixlife.cn.common.md` | 跨模块通用脚本(当前为空,预留) |
13
+ | [`edu/`](./edu/) | `vip.helixlife.cn.edu.md` | 学习中心 · 课件 PDF、搜索卡片 hover |
14
+ | [`analysis/`](./analysis/) | `analysis/browser.md` | 数据分析 · 浏览器通道:工具详情上传、拼图工具(后台 CLI 见 `references/analysis/backend-cli.md`) |
15
+ | [`workbench/jova/`](./workbench/jova/) | `workbench/jova/MODULE.md` | 综述工作台全流程 |
16
+
17
+ ## 脚本清单
18
+
19
+ ### `common/`
20
+
21
+ (暂无)
22
+
23
+ ### `edu/`
24
+
25
+ | 脚本 | 作用 |
26
+ | --- | --- |
27
+ | `chrome-pdf-goto-page.js` | PDF 页码跳转(改 `target`) |
28
+ | `chrome-pdf-zoom-out.js` | PDF 缩小 |
29
+ | `chrome-pdf-enumerate-a11y.js` | 枚举 PDF 阅读器 `aria-label`(排障) |
30
+ | `hover-partial-chapter-match.js` | 搜索结果「部分章节匹配」hover |
31
+
32
+ ### `analysis/`(仅浏览器通道 · 后台 CLI 勿用)
33
+
34
+ > 细则见 `references/analysis/browser.md`。后台 `xt` 任务见 `references/analysis/backend-cli.md`,**禁止**调用本目录脚本。
35
+
36
+ | 脚本 | 作用 |
37
+ | --- | --- |
38
+ | `detail-upload-file.js` | 工具详情 · 单参数上传(filechooser) |
39
+ | `detail-upload-both.js` | 工具详情 · 多参数顺序上传(setInputFiles) |
40
+ | `jigsaw-verify-drag-tool.js` | 拼图 · 拖图并验收下载解禁 |
41
+ | `jigsaw-verify-enumerate.js` | 拼图 · ABC 字段枚举 |
42
+ | `jigsaw-verify-reference-lines.js` | 拼图 · 参考线操作 |
43
+ | `jigsaw-verify-bio-tools.js` | 拼图 · 生信工具枚举 |
44
+ | `jigsaw-inspect-dom.js` | 拼图 · DOM 排障 |
45
+
46
+ ### `workbench/jova/`
47
+
48
+ | 脚本 | 作用 |
49
+ | --- | --- |
50
+ | `outline-ops.js` | 提纲树标准操作(含节点重新生成,CONFIG) |
51
+ | `outline-edit-root-title.js` | 编辑根标题 |
52
+ | `outline-keyword-edit.js` | 关键词编辑 |
53
+ | `outline-probe-hover.js` | 节点 hover 探测(排障) |
54
+ | `proofread-ops.js` | 校对页标准操作(含单段确认校对,CONFIG) |
55
+ | `proofread-ai-rewrite-section.js` | 单段 AI 改写 / 段落重写面板 |
56
+ | `writing-ops.js` | 精修页标准操作(含右侧功能面板,CONFIG) |
57
+ | `writing-proofread-abstract.js` | 摘要段校对/精修 |
58
+
59
+ 完整说明见 [`references/workbench/jova/MODULE.md`](../references/workbench/jova/MODULE.md) 末尾表格。
60
+
61
+ #### 计划提供(`workbench/jova/` · 暂未落地)
62
+
63
+ | 计划脚本 | 作用 | 当前临时替代 |
64
+ | --- | --- | --- |
65
+ | `outline-regenerate-node.js` | 节点级「重新生成」 | `outline-ops.js` CONFIG |
66
+ | `proofread-section.js` | 单段确认校对 | `proofread-ops.js` CONFIG |
67
+ | `writing-tool.js` | 右侧功能面板单脚本 | `writing-ops.js` CONFIG |
68
+
69
+ 磁盘暂无上述文件,**勿调用**;落地后移入上表「已提供」并删本小节。
70
+
71
+ ## 维护者专用(Agent 勿用)
72
+
73
+ | 路径 | 说明 |
74
+ | --- | --- |
75
+ | `maintainer/strip-extra-skills.ps1` | 打包时剔除 `node_modules` 内多余 `SKILL.md`,非站点自动化 |
76
+
77
+ ## 扩展约定
78
+
79
+ - 新增脚本放入对应模块目录;跨模块复用放 `common/`。
80
+ - AI 工作台其他子工作台(sci / nsfc / clinical)后续脚本放 `workbench/<slug>/`。
81
+ - 文件名去掉模块前缀(目录已表达归属),例如 `outline-ops.js` 而非 `workbench-jova-outline-ops.js`。
@@ -6,7 +6,7 @@
6
6
  * 3) 弹窗内 button「确认」
7
7
  *
8
8
  * 修改 filePath / paramLabel 后执行:
9
- * helixlife-v5-cli run-code --filename=scripts/analysis-detail-upload-file.js
9
+ * helixlife-v5-cli run-code --filename=scripts/analysis/detail-upload-file.js
10
10
  */
11
11
  async page => {
12
12
  const filePath = 'C:/Users/zouyh/Desktop/helix-test-upload.csv';
@@ -1,15 +1,15 @@
1
- async page => {
2
- return page.evaluate(() => {
3
- const root = document.querySelector('.jigsaw');
4
- if (!root) return null;
5
- const walk = (el, depth = 0) => {
6
- if (depth > 4) return null;
7
- return {
8
- tag: el.tagName,
9
- cls: (el.className || '').toString().slice(0, 80),
10
- ch: [...el.children].slice(0, 8).map(c => walk(c, depth + 1)),
11
- };
12
- };
13
- return [...root.children].map(c => walk(c, 0));
14
- });
15
- }
1
+ async page => {
2
+ return page.evaluate(() => {
3
+ const root = document.querySelector('.jigsaw');
4
+ if (!root) return null;
5
+ const walk = (el, depth = 0) => {
6
+ if (depth > 4) return null;
7
+ return {
8
+ tag: el.tagName,
9
+ cls: (el.className || '').toString().slice(0, 80),
10
+ ch: [...el.children].slice(0, 8).map(c => walk(c, depth + 1)),
11
+ };
12
+ };
13
+ return [...root.children].map(c => walk(c, 0));
14
+ });
15
+ }
@@ -1,4 +1,4 @@
1
- /** 拼图工具页 · 非破坏性枚举。helixlife-v5-cli run-code --filename=scripts/jigsaw-verify-enumerate.js */
1
+ /** 拼图工具页 · 非破坏性枚举。helixlife-v5-cli run-code --filename=scripts/analysis/jigsaw-verify-enumerate.js */
2
2
  async page => {
3
3
  const main = page.locator('main');
4
4
  const out = {
File without changes
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * 枚举当前 PDF 阅读器帧内带 aria-label 的节点(排障 / 对照 site-ops 控件表)。
3
- * 运行:helixlife-v5-cli run-code --filename=scripts/chrome-pdf-enumerate-a11y.js
3
+ * 运行:helixlife-v5-cli run-code --filename=scripts/edu/chrome-pdf-enumerate-a11y.js
4
4
  */
5
5
  async page => {
6
6
  const PDF_VIEWER_PREFIX = 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai';