uniner-cli 1.0.3 → 1.0.6
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/LICENSE +4 -4
- package/README.md +181 -171
- package/bin/uniner-cli-uninstall.js +32 -129
- package/bin/uniner-cli.js +42 -42
- package/lib/SKILL.md.template +373 -371
- package/lib/paths.js +78 -78
- package/package.json +50 -47
- package/scripts/cleanup.js +123 -0
- package/scripts/postinstall-helpers.js +140 -0
- package/scripts/postinstall.js +385 -428
- package/scripts/preuninstall.js +25 -0
package/LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2026 Uniner Tech. All rights reserved.
|
|
2
|
-
|
|
3
|
-
This software is proprietary. Use is subject to the terms of the
|
|
4
|
-
license agreement at https://www.uniner.com/.
|
|
1
|
+
Copyright (c) 2026 Uniner Tech. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software is proprietary. Use is subject to the terms of the
|
|
4
|
+
license agreement at https://www.uniner.com/.
|
package/README.md
CHANGED
|
@@ -1,171 +1,181 @@
|
|
|
1
|
-
# uniner-cli
|
|
2
|
-
|
|
3
|
-
`uniner-cli` 是 [Uniner](https://www.uniner.com/) 的 RPA 自动化命令行工具,提供:
|
|
4
|
-
|
|
5
|
-
- **Web 自动化**(Playwright + CDP)—— 浏览器抓取、表单填写、元素交互
|
|
6
|
-
- **Excel 操作** —— 读写、批量处理、样式
|
|
7
|
-
- **OCR 识别** —— ddddocr(验证码)+ PaddleOCR(通用)
|
|
8
|
-
- **桌面 / SAP 自动化**(仅 Windows)
|
|
9
|
-
- **通用工具** —— 加密、HTTP、邮件、文件、PDF、压缩
|
|
10
|
-
|
|
11
|
-
安装时会自动注册一个 [Claude Code](https://claude.com/claude-code) 技能(`~/.claude/skills/uniner-cli/SKILL.md`),让 Claude 一启动就能识别并调用本 CLI。
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 安装
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install -g uniner-cli
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
安装过程:
|
|
22
|
-
|
|
23
|
-
1. npm 装本壳包(约 50KB)
|
|
24
|
-
2. `postinstall`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
#
|
|
57
|
-
uniner-cli --
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
uniner-cli
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
uniner-cli
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
npm config
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
两种方式互不干扰,但**不要**同时启用——会有两份 SKILL.md / 两份二进制。
|
|
162
|
-
|
|
163
|
-
## 链接
|
|
164
|
-
|
|
165
|
-
- 官网:[https://www.uniner.com/](https://www.uniner.com/)
|
|
166
|
-
- 技术支持:support@uniner.com
|
|
167
|
-
- Claude Code:[https://claude.com/claude-code](https://claude.com/claude-code)
|
|
168
|
-
|
|
169
|
-
##
|
|
170
|
-
|
|
171
|
-
|
|
1
|
+
# uniner-cli
|
|
2
|
+
|
|
3
|
+
`uniner-cli` 是 [Uniner](https://www.uniner.com/) 的 RPA 自动化命令行工具,提供:
|
|
4
|
+
|
|
5
|
+
- **Web 自动化**(Playwright + CDP)—— 浏览器抓取、表单填写、元素交互
|
|
6
|
+
- **Excel 操作** —— 读写、批量处理、样式
|
|
7
|
+
- **OCR 识别** —— ddddocr(验证码)+ PaddleOCR(通用)
|
|
8
|
+
- **桌面 / SAP 自动化**(仅 Windows)
|
|
9
|
+
- **通用工具** —— 加密、HTTP、邮件、文件、PDF、压缩
|
|
10
|
+
|
|
11
|
+
安装时会自动注册一个 [Claude Code](https://claude.com/claude-code) 技能(`~/.claude/skills/uniner-cli/SKILL.md`),让 Claude 一启动就能识别并调用本 CLI。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g uniner-cli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
安装过程:
|
|
22
|
+
|
|
23
|
+
1. npm 装本壳包(约 50KB)
|
|
24
|
+
2. `postinstall` 钩子从 `static.uniner.com` 拉取对应平台的便携包(Windows ~280MB / macOS ~75MB / Linux ~185MB)
|
|
25
|
+
3. 解压到 `~/.uniner-cli/<version>/`
|
|
26
|
+
4. 写入 `~/.claude/skills/uniner-cli/SKILL.md`
|
|
27
|
+
|
|
28
|
+
完成后任意终端:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uniner-cli --help
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 系统要求
|
|
35
|
+
|
|
36
|
+
| 项 | 要求 |
|
|
37
|
+
|---|---|
|
|
38
|
+
| 操作系统 | Windows 10/11 x64 · macOS 12+ ARM64 · Linux x64 |
|
|
39
|
+
| Node.js | >= 14 |
|
|
40
|
+
| 磁盘空间 | ≥ 500 MB(单平台) |
|
|
41
|
+
| 网络 | 能访问 `static.uniner.com` |
|
|
42
|
+
|
|
43
|
+
> 安装脚本会自动检测当前系统并下载对应版本。
|
|
44
|
+
|
|
45
|
+
## 使用示例
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 看一级命令
|
|
49
|
+
uniner-cli --help
|
|
50
|
+
|
|
51
|
+
# 浏览器自动化
|
|
52
|
+
uniner-cli web browser open --url "https://example.com"
|
|
53
|
+
uniner-cli web element click --xpath "//button[@id='submit']"
|
|
54
|
+
uniner-cli web element get-text --xpath "//h1"
|
|
55
|
+
|
|
56
|
+
# Excel
|
|
57
|
+
uniner-cli excel data read --file data.xlsx --cell A1
|
|
58
|
+
uniner-cli excel data write --file out.xlsx --cell B2 --value "hello"
|
|
59
|
+
|
|
60
|
+
# OCR
|
|
61
|
+
uniner-cli tool ocr ddddocr --img captcha.png
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
完整命令树(一级 + 二级 + 三级参数)见安装后 `~/.uniner-cli/<version>/skills/cli/SKILL.md`。
|
|
65
|
+
|
|
66
|
+
## 升级
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm update -g uniner-cli
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
或者重新安装:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install -g uniner-cli@latest
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> 不支持降级。如需回滚,请手动卸载后再装旧版。
|
|
79
|
+
|
|
80
|
+
## 卸载
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm uninstall -g uniner-cli
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
卸载时 `preuninstall` 钩子会自动清理:
|
|
87
|
+
|
|
88
|
+
- `~/.uniner-cli/` 整个运行时目录(含所有版本)
|
|
89
|
+
- `~/.claude/skills/uniner-cli/SKILL.md` 技能文件
|
|
90
|
+
|
|
91
|
+
> `~/.claude/skills/uniner-cli/scenarios/` 下用户积累的场景数据不会被自动删除,需手动清理。
|
|
92
|
+
|
|
93
|
+
如需手动触发清理(如 npm 包已不在但残留仍在):
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
uniner-cli-uninstall
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 环境变量
|
|
100
|
+
|
|
101
|
+
无需手动配置。npm 全局 bin 目录已自动加入 PATH。
|
|
102
|
+
|
|
103
|
+
## 故障排查
|
|
104
|
+
|
|
105
|
+
### `uniner-cli: command not found`
|
|
106
|
+
|
|
107
|
+
确认 npm 全局 bin 在 PATH 上:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# macOS / Linux
|
|
111
|
+
npm config get prefix
|
|
112
|
+
echo $PATH | tr ':' '\n' | grep npm
|
|
113
|
+
|
|
114
|
+
# Windows (PowerShell)
|
|
115
|
+
npm config get prefix
|
|
116
|
+
$env:Path -split ';' | Where-Object { $_ -like '*npm*' }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
如果没有,手动将 npm 全局 bin 目录加入 PATH:
|
|
120
|
+
- macOS / Linux: `$(npm config get prefix)/bin`
|
|
121
|
+
- Windows: `%APPDATA%\npm`
|
|
122
|
+
|
|
123
|
+
### 安装时下载失败 / 超时
|
|
124
|
+
|
|
125
|
+
公司代理或防火墙可能屏蔽 `static.uniner.com`。试试:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm config get proxy
|
|
129
|
+
npm config get https-proxy
|
|
130
|
+
# 设置代理:
|
|
131
|
+
npm config set https-proxy http://your-proxy:port
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
或者直接走原生安装方式(不走 npm):
|
|
135
|
+
|
|
136
|
+
```powershell
|
|
137
|
+
iwr https://static.uniner.com/soft/editor/uninercli/install.ps1 -UseBasicParsing | iex
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### postinstall 卡在解压
|
|
141
|
+
|
|
142
|
+
杀软(360 / 火绒)会扫描 1279 个新文件,慢则 5–10 分钟。让它跑完即可。
|
|
143
|
+
|
|
144
|
+
### 想重装
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm uninstall -g uniner-cli
|
|
148
|
+
npm install -g uniner-cli
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## 备选安装方式(仅 Windows,不走 npm)
|
|
152
|
+
|
|
153
|
+
如果你不想要 Node.js 依赖,可以用 PowerShell 一行安装(基于 Inno Setup 安装器):
|
|
154
|
+
|
|
155
|
+
```powershell
|
|
156
|
+
iwr https://static.uniner.com/soft/editor/uninercli/install.ps1 -UseBasicParsing | iex
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
> macOS / Linux 用户请使用 npm 安装。
|
|
160
|
+
|
|
161
|
+
两种方式互不干扰,但**不要**同时启用——会有两份 SKILL.md / 两份二进制。
|
|
162
|
+
|
|
163
|
+
## 链接
|
|
164
|
+
|
|
165
|
+
- 官网:[https://www.uniner.com/](https://www.uniner.com/)
|
|
166
|
+
- 技术支持:support@uniner.com
|
|
167
|
+
- Claude Code:[https://claude.com/claude-code](https://claude.com/claude-code)
|
|
168
|
+
|
|
169
|
+
## Changelog
|
|
170
|
+
|
|
171
|
+
### 1.0.6 (2026-06-28)
|
|
172
|
+
|
|
173
|
+
- **fix**: 修复 `npm uninstall -g uniner-cli` 未清理 `~/.uniner-cli/` 和 `~/.claude/skills/uniner-cli/SKILL.md` 的问题(新增 `preuninstall` 钩子)
|
|
174
|
+
|
|
175
|
+
### 1.0.5 (2026-06-27)
|
|
176
|
+
|
|
177
|
+
- 首个公开版本,支持 Windows 10/11 (x64)、macOS (Apple Silicon)、Linux (x64)
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
Proprietary. (C) 2026 Uniner Tech.
|
|
@@ -1,145 +1,48 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// uniner-cli-uninstall
|
|
2
|
+
// uniner-cli-uninstall — manual cleanup command.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// Since v1.0.6 we register a `preuninstall` lifecycle hook that does the
|
|
5
|
+
// same cleanup automatically when you run `npm uninstall -g uniner-cli`.
|
|
6
|
+
// This manual command is still available for users who want verbose
|
|
7
|
+
// feedback, or who want to clean up without going through npm
|
|
8
|
+
// (e.g. the npm package is already gone but the runtime files remain).
|
|
7
9
|
//
|
|
8
10
|
// Recommended workflow:
|
|
9
|
-
// uniner-cli
|
|
10
|
-
//
|
|
11
|
+
// npm uninstall -g uniner-cli # hook runs the cleanup automatically
|
|
12
|
+
//
|
|
13
|
+
// If something is still left behind, run this to force a verbose cleanup:
|
|
14
|
+
// npx uniner-cli-uninstall # before uninstalling
|
|
15
|
+
// # or, after npm already removed the package:
|
|
16
|
+
// node ~/.uniner-cli/<version>/bin/uniner-cli-uninstall.js (if the shim is gone)
|
|
11
17
|
//
|
|
12
18
|
// Idempotent: if files are already gone, just no-ops.
|
|
13
19
|
|
|
14
20
|
'use strict';
|
|
15
21
|
|
|
16
|
-
const
|
|
17
|
-
const path = require('path');
|
|
18
|
-
|
|
19
|
-
const {
|
|
20
|
-
pkg,
|
|
21
|
-
rootDir,
|
|
22
|
-
versionDir,
|
|
23
|
-
skillDir,
|
|
24
|
-
skillFile,
|
|
25
|
-
} = require('../lib/paths');
|
|
22
|
+
const { cleanup } = require('../scripts/cleanup');
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log('==========================================');
|
|
26
|
+
console.log(' uniner-cli uninstall cleanup');
|
|
27
|
+
console.log('==========================================');
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
if (fs.existsSync(p)) {
|
|
33
|
-
fs.rmSync(p, { recursive: true, force: true });
|
|
34
|
-
log(`Removed ${p}`);
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
} catch (e) {
|
|
39
|
-
warn(`Could not remove ${p}: ${e.message}`);
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
29
|
+
const result = cleanup({ verbose: true });
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (fs.existsSync(p)) {
|
|
47
|
-
fs.unlinkSync(p);
|
|
48
|
-
log(`Removed ${p}`);
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
} catch (e) {
|
|
53
|
-
warn(`Could not remove ${p}: ${e.message}`);
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function main() {
|
|
59
|
-
console.log('');
|
|
31
|
+
console.log('');
|
|
32
|
+
if (result.scenariosKept > 0) {
|
|
60
33
|
console.log('==========================================');
|
|
61
|
-
console.log(
|
|
34
|
+
console.log(` ${result.scenariosKept} scenario(s) PRESERVED at:`);
|
|
35
|
+
console.log(` ${result.scenariosDir}`);
|
|
36
|
+
console.log(' (your accumulated knowledge — not deleted)');
|
|
37
|
+
console.log(' To delete them too:');
|
|
38
|
+
console.log(` rm -rf "${result.scenariosDir}" # bash`);
|
|
39
|
+
console.log(` Remove-Item "${result.scenariosDir}" -Recurse -Force # PowerShell`);
|
|
62
40
|
console.log('==========================================');
|
|
63
|
-
|
|
64
|
-
const myVersion = pkg.version;
|
|
65
|
-
const myVersionDir = versionDir(myVersion);
|
|
66
|
-
let didSomething = false;
|
|
67
|
-
|
|
68
|
-
// 1. Remove our version's runtime tree.
|
|
69
|
-
if (rmRf(myVersionDir)) didSomething = true;
|
|
70
|
-
|
|
71
|
-
// 2. If ~/.uniner-cli/ has no other versions, drop the whole directory.
|
|
72
|
-
try {
|
|
73
|
-
const root = rootDir();
|
|
74
|
-
if (fs.existsSync(root)) {
|
|
75
|
-
const remaining = fs.readdirSync(root);
|
|
76
|
-
if (remaining.length === 0) {
|
|
77
|
-
fs.rmdirSync(root);
|
|
78
|
-
log(`Removed empty ${root}`);
|
|
79
|
-
didSomething = true;
|
|
80
|
-
} else {
|
|
81
|
-
log(`Other uniner-cli versions still installed: ${remaining.join(', ')}`);
|
|
82
|
-
log(`Keeping ${root}`);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
} catch (e) {
|
|
86
|
-
warn(`Could not inspect ${rootDir()}: ${e.message}`);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// 3. Remove ONLY the SKILL.md file. NEVER rm -rf the whole skill dir,
|
|
90
|
-
// because the user's accumulated scenarios/<name>/ subdirectories
|
|
91
|
-
// live there and represent real work they've sedimented over time.
|
|
92
|
-
// If they really want everything gone they can do it manually; we
|
|
93
|
-
// surface the path below so they know where to look.
|
|
94
|
-
const dir = skillDir();
|
|
95
|
-
if (rmFile(skillFile())) didSomething = true;
|
|
96
|
-
|
|
97
|
-
// 4. Look for scenarios/ — if there are any, keep them and tell the user.
|
|
98
|
-
let scenariosKept = 0;
|
|
99
|
-
const scenariosDir = path.join(dir, 'scenarios');
|
|
100
|
-
if (fs.existsSync(scenariosDir)) {
|
|
101
|
-
try {
|
|
102
|
-
scenariosKept = fs.readdirSync(scenariosDir).filter(n => {
|
|
103
|
-
const meta = path.join(scenariosDir, n, 'meta.json');
|
|
104
|
-
return fs.existsSync(meta);
|
|
105
|
-
}).length;
|
|
106
|
-
} catch (_) {
|
|
107
|
-
// best-effort
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 5. If skill dir is now genuinely empty, remove it. Otherwise keep it.
|
|
112
|
-
try {
|
|
113
|
-
if (fs.existsSync(dir)) {
|
|
114
|
-
const remaining = fs.readdirSync(dir);
|
|
115
|
-
if (remaining.length === 0) {
|
|
116
|
-
fs.rmdirSync(dir);
|
|
117
|
-
log(`Removed empty ${dir}`);
|
|
118
|
-
didSomething = true;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
} catch (_) {
|
|
122
|
-
// best-effort
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (!didSomething) {
|
|
126
|
-
log('Nothing to clean — already removed.');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
console.log('');
|
|
130
|
-
if (scenariosKept > 0) {
|
|
131
|
-
console.log('==========================================');
|
|
132
|
-
console.log(` ${scenariosKept} scenario(s) PRESERVED at:`);
|
|
133
|
-
console.log(` ${scenariosDir}`);
|
|
134
|
-
console.log(' (your accumulated knowledge — not deleted)');
|
|
135
|
-
console.log(' To delete them too:');
|
|
136
|
-
console.log(` rm -rf "${scenariosDir}" # bash`);
|
|
137
|
-
console.log(` Remove-Item "${scenariosDir}" -Recurse -Force # PowerShell`);
|
|
138
|
-
console.log('==========================================');
|
|
139
|
-
}
|
|
140
|
-
console.log('Cleanup done. To complete uninstall, also run:');
|
|
141
|
-
console.log(' npm uninstall -g uniner-cli');
|
|
142
|
-
console.log('');
|
|
143
41
|
}
|
|
144
42
|
|
|
145
|
-
|
|
43
|
+
if (result.didSomething) {
|
|
44
|
+
console.log('Cleanup done.');
|
|
45
|
+
} else {
|
|
46
|
+
console.log('Nothing to clean — already removed.');
|
|
47
|
+
}
|
|
48
|
+
console.log('');
|
package/bin/uniner-cli.js
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Tiny launcher: forward every CLI invocation to the real PyInstaller exe.
|
|
3
|
-
//
|
|
4
|
-
// `npm install -g uniner-cli` registers this file as the global `uniner-cli`
|
|
5
|
-
// command (because of the `bin` entry in package.json). We just spawn the real
|
|
6
|
-
// binary with the same argv/stdio and exit with its status code.
|
|
7
|
-
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
const { spawnSync } = require('child_process');
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const { realBinaryPath, isInstalled, pkg } = require('../lib/paths');
|
|
13
|
-
|
|
14
|
-
const exe = realBinaryPath();
|
|
15
|
-
|
|
16
|
-
if (!isInstalled()) {
|
|
17
|
-
console.error('');
|
|
18
|
-
console.error(`[uniner-cli] Real binary not found at: ${exe}`);
|
|
19
|
-
console.error('[uniner-cli] The npm postinstall step must not have completed.');
|
|
20
|
-
console.error('[uniner-cli] Try one of:');
|
|
21
|
-
console.error('[uniner-cli] npm rebuild -g uniner-cli');
|
|
22
|
-
console.error('[uniner-cli] npm uninstall -g uniner-cli && npm install -g uniner-cli');
|
|
23
|
-
console.error('');
|
|
24
|
-
process.exit(127);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// stdio:'inherit' forwards stdin/stdout/stderr unchanged so interactive prompts
|
|
28
|
-
// and ANSI color survive. shell:false avoids any path-quoting surprises.
|
|
29
|
-
const result = spawnSync(exe, process.argv.slice(2), {
|
|
30
|
-
stdio: 'inherit',
|
|
31
|
-
shell: false,
|
|
32
|
-
windowsHide: false,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if (result.error) {
|
|
36
|
-
console.error(`[uniner-cli] Failed to launch ${exe}: ${result.error.message}`);
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// `status` is null when the child was killed by a signal; in that case let
|
|
41
|
-
// the user see we did not exit cleanly.
|
|
42
|
-
process.exit(result.status === null ? 1 : result.status);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Tiny launcher: forward every CLI invocation to the real PyInstaller exe.
|
|
3
|
+
//
|
|
4
|
+
// `npm install -g uniner-cli` registers this file as the global `uniner-cli`
|
|
5
|
+
// command (because of the `bin` entry in package.json). We just spawn the real
|
|
6
|
+
// binary with the same argv/stdio and exit with its status code.
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const { spawnSync } = require('child_process');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const { realBinaryPath, isInstalled, pkg } = require('../lib/paths');
|
|
13
|
+
|
|
14
|
+
const exe = realBinaryPath();
|
|
15
|
+
|
|
16
|
+
if (!isInstalled()) {
|
|
17
|
+
console.error('');
|
|
18
|
+
console.error(`[uniner-cli] Real binary not found at: ${exe}`);
|
|
19
|
+
console.error('[uniner-cli] The npm postinstall step must not have completed.');
|
|
20
|
+
console.error('[uniner-cli] Try one of:');
|
|
21
|
+
console.error('[uniner-cli] npm rebuild -g uniner-cli');
|
|
22
|
+
console.error('[uniner-cli] npm uninstall -g uniner-cli && npm install -g uniner-cli');
|
|
23
|
+
console.error('');
|
|
24
|
+
process.exit(127);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// stdio:'inherit' forwards stdin/stdout/stderr unchanged so interactive prompts
|
|
28
|
+
// and ANSI color survive. shell:false avoids any path-quoting surprises.
|
|
29
|
+
const result = spawnSync(exe, process.argv.slice(2), {
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
shell: false,
|
|
32
|
+
windowsHide: false,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (result.error) {
|
|
36
|
+
console.error(`[uniner-cli] Failed to launch ${exe}: ${result.error.message}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// `status` is null when the child was killed by a signal; in that case let
|
|
41
|
+
// the user see we did not exit cleanly.
|
|
42
|
+
process.exit(result.status === null ? 1 : result.status);
|