uniner-cli 1.0.24 → 1.0.26

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 CHANGED
@@ -8,7 +8,7 @@
8
8
  - **桌面 / SAP 自动化** —— 支持桌面软件的自动化(仅 Windows)
9
9
  - **通用工具** —— 加密、HTTP、邮件、文件、PDF、压缩、FTP
10
10
 
11
- 安装时会自动注册一个 [Claude Code](https://claude.com/claude-code) 技能(`~/.claude/skills/uniner-cli/SKILL.md`),让 Claude 一启动就能识别并调用本 CLI。
11
+ 在项目目录运行 `uniner-cli setup-for-agent` 可把 skill 文档部署到该项目 `.agents/skills/`,Claude Code / Codex / pi 等 Agent 即可识别并调用本 CLI。
12
12
 
13
13
  ---
14
14
 
@@ -112,10 +112,9 @@ uniner-cli uninstall --local
112
112
 
113
113
  清理内容:
114
114
 
115
- - `~/.uniner-cli/` 整个运行时目录(含所有版本)
116
- - `~/.claude/skills/uniner-cli/SKILL.md` 技能文件
115
+ - `~/.uniner-cli/` 整个运行时目录(含所有版本、文档与场景库 `scenarios/`)
117
116
 
118
- > `~/.claude/skills/uniner-cli/scenarios/` 下用户积累的场景数据不会被删除,需手动清理。
117
+ > 项目里的 `.agents/skills/`(`setup-for-agent` 部署)与 `.claude/skills/` 桥接在用户项目内,卸载不会触碰,不需要时请自行删除。
119
118
 
120
119
  **备选方式**(如果 npm 包已经先被卸载,`uniner-cli` 不在 PATH 上):
121
120
 
@@ -6,8 +6,7 @@
6
6
  // lifecycle hook for globally installed packages. This means
7
7
  // `npm uninstall -g uniner-cli` removes the npm package but leaves
8
8
  // residual files behind:
9
- // - ~/.uniner-cli/ (runtime binaries, skills, docs)
10
- // - ~/.claude/skills/uniner-cli/SKILL.md (Claude Code skill file)
9
+ // - ~/.uniner-cli/ (runtime binaries, skills, docs, scenarios)
11
10
  //
12
11
  // To solve this, postinstall writes a self-contained cleanup script to
13
12
  // ~/.uniner-cli/cleanup.js. That file survives `npm uninstall` and can
@@ -36,7 +35,7 @@ const standalonePath = path.join(os.homedir(), '.uniner-cli', 'cleanup.js');
36
35
 
37
36
  if (fs.existsSync(standalonePath)) {
38
37
  // Delegate to the standalone script. It handles everything: removing
39
- // ~/.uniner-cli/, removing SKILL.md, preserving scenarios/.
38
+ // the ~/.uniner-cli/ tree (binaries, docs, scenarios).
40
39
  // Use spawnSync to avoid any require() path issues.
41
40
  const { spawnSync } = require('child_process');
42
41
  const result = spawnSync(process.execPath, [standalonePath], {
@@ -65,17 +64,6 @@ console.log('');
65
64
  const result = cleanup({ verbose: true });
66
65
 
67
66
  console.log('');
68
- if (result.scenariosKept > 0) {
69
- console.log('==========================================');
70
- console.log(` ${result.scenariosKept} scenario(s) PRESERVED at:`);
71
- console.log(` ${result.scenariosDir}`);
72
- console.log(' (your accumulated knowledge — not deleted)');
73
- console.log(' To delete them too:');
74
- console.log(` rm -rf "${result.scenariosDir}" # bash`);
75
- console.log(` Remove-Item "${result.scenariosDir}" -Recurse -Force # PowerShell`);
76
- console.log('==========================================');
77
- }
78
-
79
67
  if (result.didSomething) {
80
68
  console.log('Cleanup done.');
81
69
  } else {
package/bin/uniner-cli.js CHANGED
@@ -21,7 +21,7 @@ function printUninstallHelp() {
21
21
  console.log('');
22
22
  console.log('Usage: uniner-cli uninstall [--global|--local]');
23
23
  console.log('');
24
- console.log('Cleans ~/.uniner-cli and the installed Claude Code skill,');
24
+ console.log('Cleans ~/.uniner-cli (binaries, docs, scenarios),');
25
25
  console.log('then removes this npm package from the detected install scope.');
26
26
  console.log('');
27
27
  }
@@ -196,10 +196,6 @@ function runFullUninstall() {
196
196
  const status = result.status === null ? 1 : result.status;
197
197
  if (status === 0) {
198
198
  console.log('');
199
- if (cleanupResult.scenariosKept > 0) {
200
- console.log(`[uniner-cli] Preserved ${cleanupResult.scenariosKept} scenario(s) at:`);
201
- console.log(`[uniner-cli] ${cleanupResult.scenariosDir}`);
202
- }
203
199
  console.log('[uniner-cli] Uninstall complete.');
204
200
  console.log('');
205
201
  }
package/lib/paths.js CHANGED
@@ -44,19 +44,6 @@ function realBinaryPath(version) {
44
44
  return path.join(versionDir(ver), 'bin', 'uniner-cli', 'uniner-cli');
45
45
  }
46
46
 
47
- function skillDir() {
48
- // Same place Claude Code looks for user-global skills.
49
- return path.join(homeDir(), '.claude', 'skills', 'uniner-cli');
50
- }
51
-
52
- function skillFile() {
53
- return path.join(skillDir(), 'SKILL.md');
54
- }
55
-
56
- function templatePath() {
57
- return path.join(__dirname, 'SKILL.md.template');
58
- }
59
-
60
47
  function isInstalled(version) {
61
48
  try {
62
49
  return fs.statSync(realBinaryPath(version)).isFile();
@@ -71,8 +58,5 @@ module.exports = {
71
58
  rootDir,
72
59
  versionDir,
73
60
  realBinaryPath,
74
- skillDir,
75
- skillFile,
76
- templatePath,
77
61
  isInstalled,
78
62
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "uniner-cli",
3
- "version": "1.0.24",
4
- "description": "uniner-cli — RPA automation CLI: browser automation (Playwright/CDP), Excel, OCR, desktop/SAP, crypto/HTTP/email tools. Auto-installs a Claude Code skill on install.",
3
+ "version": "1.0.26",
4
+ "description": "uniner-cli — RPA automation CLI: browser automation (Playwright/CDP), Excel, OCR, desktop/SAP, crypto/HTTP/email tools. Deploy Agent skills to your project via `uniner-cli setup-for-agent`.",
5
5
  "keywords": [
6
6
  "rpa",
7
7
  "automation",
@@ -9,7 +9,10 @@
9
9
  "playwright",
10
10
  "excel",
11
11
  "ocr",
12
+ "agent-skills",
12
13
  "claude-code",
14
+ "codex",
15
+ "pi",
13
16
  "ai-agent"
14
17
  ],
15
18
  "homepage": "https://www.uniner.com/",
@@ -1,123 +1,68 @@
1
- // Shared cleanup logic for uninstall.
2
- // Extracted so both the npm lifecycle hook (preuninstall.js) and the
3
- // manual command (bin/uniner-cli-uninstall.js) can share it.
4
-
5
- 'use strict';
6
-
7
- const fs = require('fs');
8
- const path = require('path');
9
-
10
- const {
11
- rootDir,
12
- skillDir,
13
- skillFile,
14
- } = require('../lib/paths');
15
-
16
- function rmRf(p) {
17
- try {
18
- if (fs.existsSync(p)) {
19
- fs.rmSync(p, { recursive: true, force: true });
20
- return true;
21
- }
22
- return false;
23
- } catch (_) {
24
- return false;
25
- }
26
- }
27
-
28
- function rmFile(p) {
29
- try {
30
- if (fs.existsSync(p)) {
31
- fs.unlinkSync(p);
32
- return true;
33
- }
34
- return false;
35
- } catch (_) {
36
- return false;
37
- }
38
- }
39
-
40
- /**
41
- * Run the full uninstall cleanup.
42
- *
43
- * @param {object} [opts]
44
- * @param {boolean} [opts.verbose=false] Print progress lines to stdout.
45
- * The preuninstall hook should pass false
46
- * because npm swallows lifecycle output
47
- * unless --verbose is used; the manual
48
- * command passes true so the user sees
49
- * what happened.
50
- * @returns {{ didSomething: boolean, scenariosKept: number, scenariosDir: string|null }}
51
- */
52
- function cleanup(opts) {
53
- const verbose = (opts && opts.verbose) || false;
54
-
55
- function log(msg) {
56
- if (verbose) {
57
- console.log(`[uniner-cli] ${msg}`);
58
- }
59
- }
60
-
61
- const root = rootDir();
62
- let didSomething = false;
63
-
64
- // 1. Remove the entire ~/.uniner-cli/ tree (all versions).
65
- // The version-specific layout exists so the installer can support
66
- // multi-version coexistence, but on uninstall the user wants a full
67
- // removal — orphaned old version dirs would just waste disk space.
68
- if (rmRf(root)) {
69
- log(`Removed ${root}`);
70
- didSomething = true;
71
- }
72
-
73
- // 2. Remove ONLY the SKILL.md file. NEVER rm -rf the whole skill dir,
74
- // because the user's accumulated scenarios/<name>/ subdirectories
75
- // live there and represent real work they've sedimented over time.
76
- const dir = skillDir();
77
- if (rmFile(skillFile())) {
78
- log(`Removed ${skillFile()}`);
79
- didSomething = true;
80
- }
81
-
82
- // 3. Count scenarios/ subdirs that contain a meta.json.
83
- let scenariosKept = 0;
84
- const scenariosPath = path.join(dir, 'scenarios');
85
- if (fs.existsSync(scenariosPath)) {
86
- try {
87
- scenariosKept = fs.readdirSync(scenariosPath).filter(n => {
88
- const meta = path.join(scenariosPath, n, 'meta.json');
89
- return fs.existsSync(meta);
90
- }).length;
91
- } catch (_) {
92
- // best-effort
93
- }
94
- }
95
-
96
- // 4. If skill dir is now genuinely empty, remove it. Otherwise keep it.
97
- try {
98
- if (fs.existsSync(dir)) {
99
- const remaining = fs.readdirSync(dir);
100
- if (remaining.length === 0) {
101
- fs.rmdirSync(dir);
102
- log(`Removed empty ${dir}`);
103
- didSomething = true;
104
- } else if (verbose) {
105
- log(`Keeping ${dir} (${scenariosKept} scenario(s) preserved)`);
106
- }
107
- }
108
- } catch (_) {
109
- // best-effort
110
- }
111
-
112
- if (!didSomething) {
113
- log('Nothing to clean — already removed.');
114
- }
115
-
116
- return {
117
- didSomething,
118
- scenariosKept,
119
- scenariosDir: scenariosKept > 0 ? scenariosPath : null,
120
- };
121
- }
122
-
123
- module.exports = { cleanup };
1
+ // Shared cleanup logic for uninstall.
2
+ // Extracted so both the npm lifecycle hook (preuninstall.js) and the
3
+ // manual command (bin/uniner-cli-uninstall.js) can share it.
4
+ //
5
+ // Full removal: ~/.uniner-cli/ holds binaries, docs AND the scenarios
6
+ // library (moved out of ~/.claude/ in 1.0.25), so deleting the whole tree
7
+ // is the correct uninstall semantics. Project-level .agents/skills/ and
8
+ // .claude/ bridges created by `uniner-cli setup-for-agent` live inside
9
+ // user projects and are intentionally NOT touched here.
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+
15
+ const {
16
+ rootDir,
17
+ } = require('../lib/paths');
18
+
19
+ function rmRf(p) {
20
+ try {
21
+ if (fs.existsSync(p)) {
22
+ fs.rmSync(p, { recursive: true, force: true });
23
+ return true;
24
+ }
25
+ return false;
26
+ } catch (_) {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Run the full uninstall cleanup.
33
+ *
34
+ * @param {object} [opts]
35
+ * @param {boolean} [opts.verbose=false] Print progress lines to stdout.
36
+ * The preuninstall hook should pass false
37
+ * because npm swallows lifecycle output
38
+ * unless --verbose is used; the manual
39
+ * command passes true so the user sees
40
+ * what happened.
41
+ * @returns {{ didSomething: boolean }}
42
+ */
43
+ function cleanup(opts) {
44
+ const verbose = (opts && opts.verbose) || false;
45
+
46
+ function log(msg) {
47
+ if (verbose) {
48
+ console.log(`[uniner-cli] ${msg}`);
49
+ }
50
+ }
51
+
52
+ const root = rootDir();
53
+
54
+ // Remove the entire ~/.uniner-cli/ tree (all versions, scenarios, docs).
55
+ // The version-specific layout exists so the installer can support
56
+ // multi-version coexistence, but on uninstall the user wants a full
57
+ // removal — orphaned old version dirs would just waste disk space.
58
+ const didSomething = rmRf(root);
59
+ if (didSomething) {
60
+ log(`Removed ${root}`);
61
+ } else {
62
+ log('Nothing to clean — already removed.');
63
+ }
64
+
65
+ return { didSomething };
66
+ }
67
+
68
+ module.exports = { cleanup };
@@ -9,9 +9,7 @@
9
9
  // - Up to 3 retries with exponential backoff.
10
10
  // 4. Verify SHA-256.
11
11
  // 5. Extract via PowerShell's Expand-Archive (no native dep).
12
- // 6. Render SKILL.md.template with {{INSTALL_PATH}} / {{VERSION}} substituted
13
- // and write to ~/.claude/skills/uniner-cli/SKILL.md.
14
- // 7. Print "ready" and how to validate.
12
+ // 6. Print "ready", how to enable the Agent skill (setup-for-agent), validate.
15
13
  //
16
14
  // Failures throw -> non-zero exit -> npm marks install as failed (strict mode).
17
15
  // User can then `npm install -g uniner-cli` again to retry cleanly.
@@ -32,9 +30,6 @@ const {
32
30
  versionDir,
33
31
  realBinaryPath,
34
32
  isInstalled,
35
- skillDir,
36
- skillFile,
37
- templatePath,
38
33
  } = require('../lib/paths');
39
34
 
40
35
  const {
@@ -196,48 +191,6 @@ function sha256OfFile(file) {
196
191
  });
197
192
  }
198
193
 
199
- // ----------------------------------------------------------------------
200
- // SKILL.md template render
201
- // ----------------------------------------------------------------------
202
-
203
- function writeSkillMd(installPath, version) {
204
- const tplPath = templatePath();
205
- if (!fs.existsSync(tplPath)) {
206
- log(` [WARN] SKILL.md.template missing at ${tplPath}; skipping skill install.`);
207
- return;
208
- }
209
- let content = fs.readFileSync(tplPath, 'utf8');
210
- // PowerShell-side StringChangeEx is case-insensitive; here we keep it strict
211
- // because our template uses exactly these tokens.
212
- content = content.split('{{INSTALL_PATH}}').join(installPath);
213
- content = content.split('{{VERSION}}').join(version);
214
-
215
- const dir = skillDir();
216
- fs.mkdirSync(dir, { recursive: true });
217
-
218
- // IMPORTANT: only writeFileSync the single SKILL.md file. Do NOT touch
219
- // anything else under ~/.claude/skills/uniner-cli/. In particular, the
220
- // scenarios/ subdirectory is the user's accumulated knowledge — if we
221
- // ever rmdir or rmSync this directory we'd wipe their library on every
222
- // upgrade. Single-file write is the contract.
223
- fs.writeFileSync(skillFile(), content, 'utf8');
224
- log(` SKILL.md written to ${skillFile()}`);
225
-
226
- // Tell the user we kept their scenarios (if any).
227
- const scenariosDir = path.join(dir, 'scenarios');
228
- if (fs.existsSync(scenariosDir)) {
229
- try {
230
- const count = fs.readdirSync(scenariosDir).filter(n => {
231
- const meta = path.join(scenariosDir, n, 'meta.json');
232
- return fs.existsSync(meta);
233
- }).length;
234
- log(` [INFO] Preserved ${count} scenario(s) in ${scenariosDir}`);
235
- } catch (_) {
236
- // best-effort; never fail postinstall over a logging stat
237
- }
238
- }
239
- }
240
-
241
194
  // ----------------------------------------------------------------------
242
195
  // Write standalone cleanup script to ~/.uniner-cli/cleanup.js
243
196
  //
@@ -264,9 +217,8 @@ function writeCleanupScript() {
264
217
  // --------------------
265
218
  // npm 7+ (including npm 11) does NOT reliably execute the \`preuninstall\`
266
219
  // lifecycle hook for globally installed packages. Running
267
- // \`npm uninstall -g uniner-cli\` removes the npm package but leaves:
268
- // - ~/.uniner-cli/ (runtime binaries, skills, docs)
269
- // - ~/.claude/skills/uniner-cli/SKILL.md (Claude Code skill file)
220
+ // \`npm uninstall -g uniner-cli\` removes the npm package but leaves
221
+ // ~/.uniner-cli/ (runtime binaries, skills, docs, scenarios).
270
222
  //
271
223
  // This script was written to this location by the postinstall hook. Because
272
224
  // it lives OUTSIDE the npm package directory, it survives \`npm uninstall\`
@@ -276,24 +228,14 @@ function writeCleanupScript() {
276
228
  //
277
229
  // Idempotent: running it twice is harmless; the second run just reports
278
230
  // "nothing to clean".
279
- //
280
- // WHAT IT PRESERVES
281
- // -----------------
282
- // The \`scenarios/\` subdirectory under
283
- // ~/.claude/skills/uniner-cli/scenarios/
284
- // contains user-accumulated knowledge (saved workflows). Those are NEVER
285
- // deleted by this script — only the single SKILL.md file is removed.
286
231
 
287
232
  'use strict';
288
233
 
289
- const fs = require('fs');
234
+ const fs = require('fs');
290
235
  const path = require('path');
291
- const os = require('os');
236
+ const os = require('os');
292
237
 
293
- const rootDir = path.join(os.homedir(), '.uniner-cli');
294
- const claudeSkillRoot = path.join(os.homedir(), '.claude', 'skills', 'uniner-cli');
295
- const skillFilePath = path.join(claudeSkillRoot, 'SKILL.md');
296
- const scenariosDir = path.join(claudeSkillRoot, 'scenarios');
238
+ const rootDir = path.join(os.homedir(), '.uniner-cli');
297
239
 
298
240
  function rmRf(p) {
299
241
  try {
@@ -305,73 +247,14 @@ function rmRf(p) {
305
247
  } catch (_) { return false; }
306
248
  }
307
249
 
308
- function rmFile(p) {
309
- try {
310
- if (fs.existsSync(p)) {
311
- fs.unlinkSync(p);
312
- return true;
313
- }
314
- return false;
315
- } catch (_) { return false; }
316
- }
317
-
318
250
  console.log('');
319
251
  console.log('==========================================');
320
252
  console.log(' uniner-cli uninstall cleanup');
321
253
  console.log('==========================================');
322
254
 
323
- let didSomething = false;
324
-
325
- // 1. Remove entire ~/.uniner-cli/ tree (all versions + this script itself).
326
255
  if (rmRf(rootDir)) {
327
256
  console.log('[uniner-cli] Removed ' + rootDir);
328
- didSomething = true;
329
- }
330
-
331
- // 2. Remove ONLY the SKILL.md file. NEVER rm -rf the whole skill dir —
332
- // scenarios/ lives there and is the user's accumulated knowledge.
333
- if (rmFile(skillFilePath)) {
334
- console.log('[uniner-cli] Removed ' + skillFilePath);
335
- didSomething = true;
336
- }
337
-
338
- // 3. Count preserved scenarios.
339
- let scenariosKept = 0;
340
- if (fs.existsSync(scenariosDir)) {
341
- try {
342
- scenariosKept = fs.readdirSync(scenariosDir).filter(function(n) {
343
- return fs.existsSync(path.join(scenariosDir, n, 'meta.json'));
344
- }).length;
345
- } catch (_) {}
346
- }
347
-
348
- // 4. If the skill dir is now empty, remove it. Otherwise keep it.
349
- try {
350
- if (fs.existsSync(claudeSkillRoot)) {
351
- var remaining = fs.readdirSync(claudeSkillRoot);
352
- if (remaining.length === 0) {
353
- fs.rmdirSync(claudeSkillRoot);
354
- console.log('[uniner-cli] Removed empty ' + claudeSkillRoot);
355
- didSomething = true;
356
- } else if (scenariosKept > 0) {
357
- console.log('[uniner-cli] Keeping ' + claudeSkillRoot +
358
- ' (' + scenariosKept + ' scenario(s) preserved)');
359
- }
360
- }
361
- } catch (_) {}
362
-
363
- console.log('');
364
- if (scenariosKept > 0) {
365
- console.log(' ' + scenariosKept + ' scenario(s) PRESERVED at:');
366
- console.log(' ' + scenariosDir);
367
- console.log(' (your accumulated knowledge — not deleted)');
368
- console.log(' To delete them too:');
369
- console.log(' rm -rf "' + scenariosDir + '" # bash');
370
- console.log(' Remove-Item "' + scenariosDir + '" -Recurse -Force # PowerShell');
371
257
  console.log('');
372
- }
373
-
374
- if (didSomething) {
375
258
  console.log('Cleanup done.');
376
259
  } else {
377
260
  console.log('Nothing to clean — already removed.');
@@ -418,9 +301,6 @@ async function main() {
418
301
  log(`Already installed at ${realBinaryPath(version)}; skipping download.`);
419
302
  log('To force a clean reinstall:');
420
303
  log(' npm uninstall -g uniner-cli && npm install -g uniner-cli');
421
- // Still re-render the skill — it's tiny and lets users edit the template
422
- // and re-run rebuild to refresh.
423
- writeSkillMd(versionDir(version), version);
424
304
  // Refresh the cleanup helper too — it's idempotent and ensures the
425
305
  // script is always present after any postinstall run.
426
306
  writeCleanupScript();
@@ -498,13 +378,6 @@ async function main() {
498
378
  try { fs.rmdirSync(tmpDir); } catch (_) {}
499
379
  log(' Removed temporary files.');
500
380
 
501
- step('Installing Claude Code skill');
502
- writeSkillMd(targetDir, version);
503
-
504
- // Write the standalone cleanup helper to ~/.uniner-cli/cleanup.js.
505
- // This survives `npm uninstall` (the npm package dir is gone but this
506
- // file persists), giving users a reliable way to clean up residual
507
- // files even when the `preuninstall` hook doesn't fire.
508
381
  step('Writing uninstall cleanup helper');
509
382
  writeCleanupScript();
510
383
 
@@ -514,7 +387,10 @@ async function main() {
514
387
  console.log('==========================================');
515
388
  console.log('');
516
389
  console.log(` Location: ${targetDir}`);
517
- console.log(` Skill: ${skillFile()}`);
390
+ console.log('');
391
+ console.log(' Enable the Agent skill (Claude Code / Codex / pi):');
392
+ console.log(' cd <your-project-dir>');
393
+ console.log(' uniner-cli setup-for-agent');
518
394
  console.log('');
519
395
  console.log(' To uninstall:');
520
396
  console.log(' uniner-cli uninstall # auto-detects global vs local install');
@@ -2,10 +2,10 @@
2
2
  // preuninstall.js — npm lifecycle hook, runs automatically on
3
3
  // `npm uninstall -g uniner-cli`.
4
4
  //
5
- // Cleans up the runtime tree (~/.uniner-cli/<version>/) and the skill file
6
- // (~/.claude/skills/uniner-cli/SKILL.md). Preserves user scenarios under
7
- // ~/.claude/skills/uniner-cli/scenarios/ — those represent accumulated
8
- // knowledge and are never deleted automatically.
5
+ // Cleans up the runtime tree (~/.uniner-cli/ — binaries, docs, scenarios;
6
+ // full removal). Project-level .agents/skills/ and .claude/ bridges created
7
+ // by `uniner-cli setup-for-agent` live inside user projects and are not
8
+ // touched.
9
9
  //
10
10
  // IMPORTANT: This script MUST exit 0 in all cases, otherwise it would
11
11
  // abort the user's `npm uninstall`. Errors are swallowed silently.
@@ -1,404 +0,0 @@
1
- ---
2
- name: uniner-cli
3
- description: RPA 自动化 CLI 工具,提供浏览器自动化、Excel 操作、OCR 识别、魔像云流程开发与发布(流程发布/上传流程/魔微子)、加密/HTTP/邮件等通用工具能力。Use when user mentions RPA、自动化、抓取网页、抓数据、填表、读 Excel、写 Excel、识别验证码、OCR、流程发布、上传流程、业务流程、魔像云、魔微子、uniner-cli、爬虫、表单自动化.
4
- ---
5
-
6
- # uniner-cli Skill
7
-
8
- > 由 UninerCLI Installer v{{VERSION}} 自动安装到 `~/.claude/skills/uniner-cli/`
9
- > 安装位置(绝对路径):`{{INSTALL_PATH}}`
10
-
11
- ## 🛑 任务结束硬性规则(最重要,先读这一节)
12
-
13
- **每次用 uniner-cli 完成一个任务后,在你要说"任务完成"或写汇总表之前,必须先执行下面这段:**
14
-
15
- 如果这次任务**满足任一条件**:
16
-
17
- - 调用了 ≥ 3 个 uniner-cli 命令
18
- - 涉及 web / OCR 任何一个模块(desktop 暂不开放,不在其列)
19
- - 涉及魔像云流程开发(init/validate/publish 多步链路,沉淀后可 0 token 重放)
20
- - 用户提到过"以后还会跑" / "每天都要" / "做成自动化"
21
- - 任务里踩过坑(XPath 不稳、参数名错、Vue 组件、超时调整等)
22
-
23
- → **必须主动问用户**:"这个流程涉及多步,要不要保存为可重放场景?以后 `uniner-cli skill run <name> --param ...` 就能 0 token 重放,可以放定时任务/RPA 平台里跑。要保存吗?"
24
-
25
- 不要等用户开口,**主动**问。这是最有价值的一步——你刚刚花了上千 token 调通的流程,沉淀下来下次就能秒跑。
26
-
27
- 如果用户说"好" / "可以" / "保存吧":按下面"场景沉淀(4 步流程)"章节执行。
28
-
29
- 如果用户说"不用":直接结束,不再追问。
30
-
31
- **违反这条规则 = 任务未完成。** 不管你跑得多顺,没问就是没问完整。
32
-
33
- ---
34
-
35
- ## 何时使用本 Skill
36
-
37
- 用户提到以下任一关键词或任务,**优先调用 uniner-cli**:
38
-
39
- - **网页类**:打开网页、抓取数据、点击/填表/登录、获取标题/表格、OCR 验证码
40
- - **Excel 类**:创建/读写 Excel、单元格操作、批量数据导出
41
- - **工具类**:MD5/AES/RSA 加密、HTTP 请求、收发邮件、文件/PDF/ZIP 操作
42
-
43
- ## 调用前提(已自动配置)
44
-
45
- - ✅ `uniner-cli` 命令已加入 PATH(安装时自动配置,新终端立即生效)
46
- - ✅ 环境变量 `UNINER_CLI_HOME = {{INSTALL_PATH}}`
47
- - ✅ Web 模块自动通过 CDP 连接本机 Chrome / Edge —— **不要手动启动 Chrome**
48
- - ⚠️ desktop 模块**暂不开放**(测试覆盖少);桌面/SAP 场景告知用户后改用 web 或 tool system execute
49
-
50
- ## 命令结构(一级 + 二级)
51
-
52
- ```
53
- uniner-cli <模块> <子模块> <命令> [选项]
54
-
55
- excel Excel 文件操作
56
- ├── file 创建 / 打开 / 关闭 / 保存 / 复制 / 删除 / info / list
57
- ├── sheet 工作表 创建 / 删除 / 重命名 / 复制 / info / list
58
- ├── data 读 / 写 / 区域读 / 区域写 / 查找 / 清除
59
- └── style 字体 / 边框 / 填充 / 对齐 / 数字格式
60
-
61
- web 浏览器自动化(Playwright + CDP)
62
- ├── browser open / close / reconnect
63
- ├── navigation goto / back / forward / refresh / wait
64
- ├── element click / input / get-text / get-value / select / check / hover / screenshot
65
- ├── iframe enter / exit / list
66
- ├── page list / switch / close
67
- └── action screenshot / pdf / execute-js / get-cookie / set-cookie
68
-
69
- tool 通用工具
70
- ├── list 创建 / append / remove / find / sort / unique
71
- ├── dict 创建 / get / set / remove / keys / values
72
- ├── time now / format / parse / calc / diff
73
- ├── string format / replace / split / join / trim / upper / lower
74
- ├── data json-parse / json-stringify / csv-parse / csv-write
75
- ├── file read / write / copy / move / delete / list / info / exists
76
- ├── system execute / kill-app / env / clipboard [Windows]
77
- ├── ui alert / confirm / prompt [Windows]
78
- ├── crypto md5 / sha256 / aes-encrypt / aes-decrypt / rsa-* / base64-*
79
- ├── math add / sub / mul / div / round / abs / min / max
80
- ├── pdf read / merge / split / extract-text
81
- ├── ocr ddddocr(验证码) / paddleocr(通用) / image-to-text
82
- ├── zip create / extract / list
83
- ├── ftp connect / upload / download / list / delete
84
- ├── email send / receive / list
85
- └── http get / post / put / delete
86
-
87
- desktop 桌面自动化 [暂不开放] —— 测试覆盖少,当前版本未开放。
88
- AI 检测到任务需要桌面/SAP 操作时:停下告知用户"desktop 模块
89
- 暂不开放",改用 web(浏览器场景)或 tool system execute
90
- (执行外部程序),不要尝试调用 desktop 命令。
91
-
92
- cloud 魔像云操作(编辑器账号登录态;见"魔像云流程"章节)
93
- ├── login 登录(多组织时交互列出、**必须用户本人输入序号选定**)
94
- ├── logout 登出
95
- ├── status 查看登录状态(含当前组织)
96
- └── flow 流程管理
97
- ├── init 生成流程脚手架(无参 = 当前目录五件套、流程名=目录名;--dir <路径> 指定目录自动创建;init <流程名> 已废弃)
98
- ├── validate 基础检查流程目录(publish 时自动执行,也可单独运行)
99
- ├── test 本地真跑流程(不强制,建议发布前手动跑)
100
- ├── list-local 列本地流程、发布状态与绑定组织
101
- ├── list 列云端流程(标注来源 CLI / 编辑器)
102
- ├── publish 发布流程目录(自动 validate + 版本号用户确认 + 外发确认)
103
- ├── download 下载云端流程回流当前目录(覆盖前确认,默认 N)
104
- └── pack 打包当前目录流程为 .7z(无参数,输出上级目录)
105
-
106
- config 配置管理
107
- ├── set 设置配置项(仅 editor.path:魔微子安装目录)
108
- ├── list 查看当前配置
109
- └── clear 清除配置
110
- ```
111
-
112
- ## 全局选项(写在子命令前)
113
-
114
- ```bash
115
- uniner-cli [全局选项] <模块> <子命令> <命令> [命令选项]
116
-
117
- --timeout, -t <毫秒> 超时(默认 3000ms)
118
- --exc <0|1> 异常处理:0=抛错退出,1=静默返回错误 JSON
119
- --verbose, -v 详细日志
120
- --quiet, -q 静默
121
- --json/--plain 输出格式(默认 --json)
122
- --config, -c <PATH> 配置文件路径
123
- ```
124
-
125
- ## 输出格式(始终是 JSON)
126
-
127
- ```json
128
- // 成功
129
- {"success": true, "command": "web browser open", "data": {"session_id": "abc123"}, "timestamp": "..."}
130
-
131
- // 失败
132
- {"success": false, "error": "元素未找到: xpath=...", "error_type": "ElementNotFoundError"}
133
- ```
134
-
135
- 调用方应 `json.loads(stdout)` 解析,**不要 grep/regex**。
136
-
137
- ## 场景 → 命令映射(高频)
138
-
139
- | 用户意图 | 命令模板 |
140
- |---|---|
141
- | 打开网页 | `web browser open --url <URL>` |
142
- | 等待元素出现 | `web navigation wait --type element --xpath <X>` |
143
- | 点击 | `web element click --xpath <X>` |
144
- | 输入文本 | `web element input --xpath <X> --value <V>` |
145
- | 获取文本 | `web element get-text --xpath <X>` |
146
- | 获取表格 / 复杂提取 | `web action execute-js --code "..."` |
147
- | 元素截图 | `web element screenshot --xpath <X> --output <PATH>` |
148
- | 进 iframe | `web iframe enter --xpath <X>`(或 XPath JSON 写法) |
149
- | 创建 Excel | `excel file create --file <PATH>` |
150
- | 读单元格 | `excel data read --file <PATH> --cell A1` |
151
- | 写单元格 | `excel data write --file <PATH> --cell A1 --value <V>` |
152
- | 区域写入 | `excel data write-range --file <PATH> --cell A1 --values <2D-JSON>` |
153
- | OCR 验证码 | `tool ocr ddddocr --img <PATH>` |
154
- | OCR 通用 | `tool ocr paddleocr --img <PATH>` |
155
- | MD5 / SHA256 | `tool crypto md5 --text <S>` |
156
- | HTTP GET | `tool http get --url <URL>` |
157
- | 发送邮件 | `tool email send --to <addr> --subject <S> --body <B>` |
158
- | 结束进程 | `tool system kill-app --name chrome.exe` |
159
- | SAP / 桌面操作 | **不可用**:desktop 暂不开放,停下告知用户并改用 web / tool system execute | "登录 SAP 系统" |
160
- | 流程发布 / 上传流程 | **先 Read** [{{INSTALL_PATH}}/skills/cloud-flow-publish.md]({{INSTALL_PATH}}/skills/cloud-flow-publish.md),再走 `cloud login → cloud flow init → validate → publish`;加输入参数看该文档「添加输入参数的标准动作」(字段表 + 可复制示例,validate V6/V7 静态拦错) | "把这个流程发到魔像云" |
161
- | 下载云端流程 | `cloud flow download [流程名]`(覆盖前确认默认 N;AI 不得代答) | "把云端流程拉下来改" |
162
-
163
- ## 命令选择优先级
164
-
165
- 1. **网页操作只用 `web`**(即使是桌面里嵌的 web)
166
- 2. **`web action execute-js` 是万能 fallback**:Vue/React 组件、动态元素、复杂表格提取,普通 element 命令做不到时直接 JS
167
- 3. **iframe 嵌套**用 XPath JSON 格式:`{"ElementName":"...","XPaths":[["iframe_xpath","element_xpath"],["backup_xpath"]]}`
168
- 4. **session 自动管理**:第一次 `web browser open` 后,后续 web 命令默认用最新 session,可省 `--session`;报 `Session not found`(如 CLI 进程退出过)时先 `web browser reconnect` 恢复,不要重开浏览器
169
- 5. **业务产物写当前目录**:Excel/JSON/中间文件等业务产物一律用**相对路径**写当前工作目录(或流程目录),**禁止写 `{{INSTALL_PATH}}/workdir/`**——workdir 是 CLI 安装目录里的临时工作区(路径含版本号,升级即失效),内容也不随流程发布进包
170
-
171
- ## 关键注意事项(6 条 TOC,详情看链接)
172
-
173
- 1. **Chrome CDP 自动管理** — 别手动 kill Chrome;客户机必须装 Chrome 或 Edge
174
- 2. **session 持久化** — `$TEMP/.uniner-cli/sessions.json`(Windows: `%TEMP%\.uniner-cli\sessions.json`);CLI 退出后用 `web browser reconnect` 恢复
175
- 3. **Vue/Element UI 组件** — 日期选择器、级联下拉等,用 `execute-js` 直接操作组件模型,不要硬 input
176
- 4. **desktop 暂不开放** — 测试覆盖少,当前版本未开放;任何 desktop 子命令都提示暂不开放,改用 `web action execute-js` 或 `tool system execute`
177
- 5. **Linux 平台** — Playwright 默认 HTTPS-only,访问 HTTP 站点需改 wrapper 加 `--disable-features=HttpsOnlyMode`,或用 Firefox `--browser-type 1`
178
- 6. **Windows GBK 编码** — CLI 内部已强制 UTF-8 输出;调用方读 stdout 也要 UTF-8
179
-
180
- ## 详细文档(按需 Read)
181
-
182
- | 主题 | 路径 |
183
- |---|---|
184
- | **魔像云流程开发与发布工作流**(login→init→validate→publish,含版本号确认 + 添加入参标准动作) | [{{INSTALL_PATH}}/skills/cloud-flow-publish.md]({{INSTALL_PATH}}/skills/cloud-flow-publish.md) |
185
- | 命令树全集 + 三级参数 | [{{INSTALL_PATH}}/skills/cli/SKILL.md]({{INSTALL_PATH}}/skills/cli/SKILL.md) |
186
- | 完整命令结构(120+ 命令) | [{{INSTALL_PATH}}/skills/cli/command-tree.md]({{INSTALL_PATH}}/skills/cli/command-tree.md) |
187
- | execute-js 使用指南 | [{{INSTALL_PATH}}/skills/cli/execute-js-guide.md]({{INSTALL_PATH}}/skills/cli/execute-js-guide.md) |
188
- | 典型场景示例 | [{{INSTALL_PATH}}/skills/cli/examples.md]({{INSTALL_PATH}}/skills/cli/examples.md) |
189
- | Windows 配置指南 | [{{INSTALL_PATH}}/skills/windows-setup-guide.md]({{INSTALL_PATH}}/skills/windows-setup-guide.md) |
190
- | macOS 配置指南 | [{{INSTALL_PATH}}/skills/macos-setup-guide.md]({{INSTALL_PATH}}/skills/macos-setup-guide.md) |
191
- | Linux 配置指南 | [{{INSTALL_PATH}}/skills/linux-setup-guide.md]({{INSTALL_PATH}}/skills/linux-setup-guide.md) |
192
- | 实战案例(TMS 抓单) | [{{INSTALL_PATH}}/skills/tms-workflow-test-report.md]({{INSTALL_PATH}}/skills/tms-workflow-test-report.md) |
193
- | Helper 脚本(Chrome 启动 / Vue 日期 / 表格提取) | [{{INSTALL_PATH}}/skills/cli/helpers/]({{INSTALL_PATH}}/skills/cli/helpers/) |
194
- | 用户手册 | [{{INSTALL_PATH}}/docs/]({{INSTALL_PATH}}/docs/) |
195
- | 工作目录(CLI 内部临时缓存:截图/下载;**业务产物不要写这里**,写当前目录) | [{{INSTALL_PATH}}/workdir/]({{INSTALL_PATH}}/workdir/) |
196
-
197
- ## 触发词路由
198
-
199
- 用户提到【流程发布】【上传流程】【业务流程】【魔像云】【魔微子】等关键词时,
200
- **先 Read** [{{INSTALL_PATH}}/skills/cloud-flow-publish.md]({{INSTALL_PATH}}/skills/cloud-flow-publish.md)
201
- 再操作——流程生产走 CLI 命令链(login 选组织 → init → validate → (建议手动
202
- test)→ publish,publish 时提示云端最新版本号、版本号由用户确认),有独立的
203
- 环境前提与 AI 行为红线。
204
-
205
- ## 典型完整流程(参考)
206
-
207
- **网页 → OCR → 提取 → Excel**:
208
-
209
- ```bash
210
- # 1. 打开浏览器
211
- uniner-cli web browser open --url "https://example.com/login"
212
-
213
- # 2. 输账号密码 + 截验证码 + OCR(截图 temp 文件放当前目录,勿写 workdir)
214
- uniner-cli web element input --xpath "//input[@id='user']" --value "u01"
215
- uniner-cli web element input --xpath "//input[@id='pwd']" --value "p01"
216
- uniner-cli web element screenshot --xpath "//img[@id='captcha']" --output "cap.png"
217
- uniner-cli tool ocr ddddocr --img "cap.png"
218
- # 从返回 JSON 的 data.result 取识别文本
219
-
220
- # 3. 提交登录
221
- uniner-cli web element input --xpath "//input[@id='captcha']" --value "<识别结果>"
222
- uniner-cli web element click --xpath "//button[@type='submit']"
223
-
224
- # 4. 等表格 + 提取
225
- uniner-cli web navigation wait --type element --xpath "//table"
226
- uniner-cli web action execute-js --code "return Array.from(document.querySelectorAll('table tr')).map(r => Array.from(r.cells).map(c => c.textContent.trim()))"
227
-
228
- # 5. 写 Excel(产物写当前目录)
229
- uniner-cli excel file create --file "out.xlsx"
230
- uniner-cli excel data write-range --file "out.xlsx" --cell A1 --values "<上一步的 JSON 二维数组>"
231
-
232
- # 6. 关浏览器
233
- uniner-cli web browser close
234
- ```
235
-
236
- ## 场景沉淀(4 步流程)
237
-
238
- > 这是顶部"任务结束硬性规则"提到的"4 步流程"。用户同意保存场景后,按这里走。
239
-
240
- 每个场景一个目录,三件套:`meta.json` + `steps.json` + `SKILL.md`,存到:
241
-
242
- ```
243
- ~/.claude/skills/uniner-cli/scenarios/<kebab-case-name>/
244
- ```
245
-
246
- (Windows: `%USERPROFILE%\.claude\skills\uniner-cli\scenarios\<kebab-case-name>\`)
247
-
248
- **4 步标准流程**:
249
-
250
- ```bash
251
- # Step 1: 生成骨架(自动创建目录和模板文件)
252
- uniner-cli skill init <name>
253
-
254
- # Step 2: 用 Write 工具改 meta.json / steps.json / SKILL.md(见下方 schema)
255
-
256
- # Step 3: 校验
257
- uniner-cli skill validate <name>
258
-
259
- # Step 4: 试跑一次
260
- uniner-cli skill run <name> --param ...
261
- ```
262
-
263
- ### meta.json schema
264
-
265
- ```json
266
- {
267
- "name": "<kebab-case-name>",
268
- "version": "1.0",
269
- "description": "一句话描述(会被用来匹配触发关键词)",
270
- "params": {
271
- "URL": { "type": "string", "required": true, "description": "登录页地址" },
272
- "USERNAME": { "type": "string", "required": true, "description": "账号" },
273
- "PASSWORD": { "type": "string", "required": true, "sensitive": true, "description": "密码" },
274
- "DATE_FROM": { "type": "string", "required": false, "default": "today", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" }
275
- }
276
- }
277
- ```
278
-
279
- ### steps.json schema
280
-
281
- ```json
282
- {
283
- "version": "1.0",
284
- "name": "<同 meta.name>",
285
- "steps": [
286
- {
287
- "id": "open-browser",
288
- "cmd": "web browser open",
289
- "args": { "url": "${URL}" },
290
- "timeout_ms": 10000
291
- },
292
- {
293
- "id": "ocr-captcha",
294
- "cmd": "tool ocr ddddocr",
295
- "args": { "img": "out/captcha.png" },
296
- "store": "ocrResult",
297
- "comment": "整个 stdout JSON 存到 ocrResult,下面用 ${ocrResult.data.result} 取"
298
- },
299
- {
300
- "id": "input-captcha",
301
- "cmd": "web element input",
302
- "args": { "xpath": "//input[@placeholder='验证码']", "value": "${ocrResult.data.result}" }
303
- },
304
- {
305
- "id": "close-browser",
306
- "cmd": "web browser close",
307
- "always_run": true,
308
- "comment": "无论前面成功与否都跑(清理用)"
309
- }
310
- ]
311
- }
312
- ```
313
-
314
- ### 每个 step 支持的字段
315
-
316
- | 字段 | 必需 | 说明 |
317
- |---|---|---|
318
- | `id` | ✅ | 步骤 ID(唯一) |
319
- | `cmd` | ✅ | uniner-cli 子命令,**不带 `uniner-cli` 前缀**,如 `web element input` |
320
- | `args` | ✅ | dict 形式参数,对应 CLI 的 `--key value` |
321
- | `store` | ❌ | 把 stdout JSON 整个存到变量名 |
322
- | `if` | ❌ | 条件表达式(见下) |
323
- | `on_fail` | ❌ | `abort`(默认)/ `continue` / `goto:<step_id>` |
324
- | `retry` | ❌ | 失败重试次数(不含首次) |
325
- | `retry_delay_ms` | ❌ | 重试间隔(默认 1000) |
326
- | `timeout_ms` | ❌ | 单步超时(覆盖全局) |
327
- | `always_run` | ❌ | 即使前面失败也跑(清理用) |
328
- | `comment` | ❌ | 给人/AI 看的注释 |
329
-
330
- ### 变量替换语法
331
-
332
- | 写法 | 含义 |
333
- |---|---|
334
- | `${URL}` | 用户传入的 `--param URL=xxx` |
335
- | `${ocrResult.data.result}` | 上一步 store 的 dict 字段(点号路径) |
336
- | `${tableData.0.1}` | list 索引(0-based) |
337
- | `${env:USERPROFILE}` | 环境变量 |
338
-
339
- ### 条件表达式语法(白名单)
340
-
341
- | 写法 | 含义 |
342
- |---|---|
343
- | `${var}` | 真值判断 |
344
- | `!${var}` | 取反 |
345
- | `${var} == 'yes'` | 字符串/数值相等 |
346
- | `${var} != 'no'` | 不等 |
347
- | `${var} > 5` | 数值比较(>=, <=, < 同理) |
348
- | `${var}.includes('login')` | 包含 |
349
- | `${var}.startsWith('https')` / `endsWith('.xlsx')` | 前/后缀 |
350
- | `!${var}.includes('admin')` | 取反组合 |
351
-
352
- **故意不支持** `&&` / `||` / `()`。要复合逻辑用多步 + `if` + `on_fail` 拆开。
353
-
354
- ### 失败时的客户体验
355
-
356
- ```
357
- $ uniner-cli skill run tms-export --param URL=...
358
- {"success": false, "failed_step": "click-search", ...}
359
-
360
- ────────── How to debug ──────────
361
- Step 'click-search' failed: timeout
362
- Open Claude Code, paste this:
363
-
364
- I ran 'uniner-cli skill run' and step 'click-search' failed.
365
- Error: timeout
366
- Please look at the scenario at:
367
- ~/.claude/skills/uniner-cli/scenarios/tms-export
368
- Especially steps.json. Help me figure out why and suggest a fix.
369
- ──────────────────────────────────
370
- ```
371
-
372
- 客户复制这段给你 → 你 Read steps.json 调试 → 改完 `Write` 回去 → 让客户重跑。
373
-
374
- ### 给 Claude 的对话范式(用户同意保存后)
375
-
376
- 按这个顺序,**不要跳步**:
377
-
378
- 1. 想一个 kebab-case 名字(如 `tms-export-waybills` / `sap-monthly-report`)
379
- 2. Bash 跑 `uniner-cli skill init <name>`(生成骨架)
380
- 3. Write 写**真正的**:
381
- - `meta.json`:含动态参数 schema(URL / USERNAME / PASSWORD / DATE_FROM 等)
382
- - `steps.json`:把刚才跑过的命令序列**完整**写进去,含已知坑的避坑配置(retry / on_fail / always_run)
383
- - `SKILL.md`:经验文档(已知坑、用法说明)
384
- 4. Bash 跑 `uniner-cli skill validate <name>` 确认格式无误
385
- 5. 让用户 `uniner-cli skill run <name> --param ...` 自己跑一次确认 0 token 重放可行
386
-
387
- **关键**:steps.json 不是命令清单的注释——是**真正可执行的工作流**。要把 `${URL}` `${USERNAME}` `${ocrResult.data.result}` 这种变量绑好,让 `skill run` 真能跑通。
388
-
389
- ### 已沉淀的场景库
390
-
391
- ```bash
392
- uniner-cli skill list # 看所有
393
- uniner-cli skill show <name> # 看某个的元数据 + 步骤摘要
394
- uniner-cli skill show <name> --steps # 看完整步骤
395
- uniner-cli skill where # 看场景库根目录(可手动浏览)
396
- uniner-cli skill delete <name> --yes # 删除(默认 dry-run)
397
- ```
398
-
399
- ## 不确定时
400
-
401
- - `uniner-cli --help` 看一级
402
- - `uniner-cli web --help` 看二级
403
- - `uniner-cli web element click --help` 看具体参数
404
- - 仍不确定 → Read [{{INSTALL_PATH}}/skills/cli/SKILL.md]({{INSTALL_PATH}}/skills/cli/SKILL.md)