easytouch-windows 2.0.3 → 2.0.5
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 +36 -74
- package/SKILL.md +16 -36
- package/bin/{arm64/et.exe → et_arm64.exe} +0 -0
- package/bin/{x64/et.exe → et_x64.exe} +0 -0
- package/init.js +218 -22
- package/package.json +4 -2
- package/bin/et.js +0 -60
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
目前支持以下操作系统的 x64 和 ARM64 两种 CPU 架构:
|
|
8
8
|
|
|
9
9
|
- [x] Windows
|
|
10
|
-
|
|
11
10
|
- [x] Linux
|
|
12
11
|
- [x] MACOS(目前缺少设备验证功能)
|
|
13
12
|
|
|
@@ -35,34 +34,40 @@
|
|
|
35
34
|
|
|
36
35
|
### 安装
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
推荐安装带初始化 helper 的聚合包;如果你只想安装当前系统,也可以直接安装对应平台包:
|
|
39
38
|
|
|
40
39
|
```bash
|
|
41
|
-
#
|
|
40
|
+
# 推荐:聚合包,执行 easytouch init 生成 et
|
|
42
41
|
npm i -g @whuanle/easytouch
|
|
43
42
|
|
|
44
|
-
#
|
|
43
|
+
# 只安装当前系统的平台包
|
|
45
44
|
npm i -g easytouch-windows
|
|
46
|
-
|
|
47
|
-
# Linux
|
|
48
45
|
npm i -g easytouch-linux
|
|
49
|
-
|
|
50
|
-
# macOS
|
|
51
46
|
npm i -g easytouch-macos
|
|
52
47
|
```
|
|
53
48
|
|
|
49
|
+
`@whuanle/easytouch` 会在包内直接附带 Windows、Linux、macOS 的 x64 和 arm64 原生二进制。执行 `easytouch init` 后,会按当前主机平台和 CPU 架构复制出真正可直接调用的 `et` 文件。
|
|
54
50
|
|
|
51
|
+
初始化命令:
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
```bash
|
|
54
|
+
# 聚合包
|
|
55
|
+
easytouch init
|
|
57
56
|
|
|
58
|
-
|
|
57
|
+
# 只安装当前系统的平台包时
|
|
58
|
+
easytouch-windows init
|
|
59
|
+
easytouch-linux init
|
|
60
|
+
easytouch-macos init
|
|
61
|
+
```
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
`init` 默认会把原生文件写回安装包目录,并同步刷新 `et` 命令入口。
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
### 使用示例
|
|
65
68
|
|
|
69
|
+
以下示例假设你已经通过 `init` 生成好了原生 `et`:
|
|
70
|
+
|
|
66
71
|
截取屏幕。
|
|
67
72
|
|
|
68
73
|
```
|
|
@@ -107,99 +112,56 @@ npx skills add https://github.com/whuanle/EasyTouch
|
|
|
107
112
|
|
|
108
113
|
|
|
109
114
|
|
|
110
|
-
|
|
115
|
+
**全局安装后(推荐)**
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
如果你已经全局安装了聚合包,可以直接使用 helper 命令生成原生 `et`:
|
|
113
118
|
|
|
114
119
|
```bash
|
|
115
|
-
npm i @whuanle/easytouch
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
如果安装的是 `@whuanle/easytouch`,这一步会先自动安装当前系统对应的平台包,再复制出原生 `et` 文件。
|
|
120
|
-
|
|
121
|
-
如果你已经在项目里安装了依赖,也可以直接执行 `node ./node_modules/@whuanle/easytouch/init.js`,但这只适用于本地安装目录。
|
|
122
|
-
|
|
123
|
-
执行后会生成:
|
|
124
|
-
|
|
125
|
-
- Windows:`./node_modules/@whuanle/easytouch/et.exe`
|
|
126
|
-
- Linux / macOS:`./node_modules/@whuanle/easytouch/et`
|
|
127
|
-
|
|
128
|
-
然后在 MCP 配置里直接指向这个原生文件:
|
|
129
|
-
|
|
130
|
-
```json
|
|
131
|
-
{
|
|
132
|
-
"mcpServers": {
|
|
133
|
-
"easytouch": {
|
|
134
|
-
"command": "<你的项目路径>/node_modules/@whuanle/easytouch/et",
|
|
135
|
-
"args": ["mcp-stdio"]
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
120
|
+
npm i -g @whuanle/easytouch
|
|
121
|
+
easytouch init
|
|
139
122
|
```
|
|
140
123
|
|
|
141
|
-
|
|
124
|
+
默认会把 `et` 生成到全局安装目录下的包目录里,并同步刷新全局 `et` 命令。
|
|
142
125
|
|
|
143
|
-
|
|
126
|
+
默认路径通常是:
|
|
144
127
|
|
|
145
|
-
|
|
128
|
+
- Windows:`<npm root -g>/@whuanle/easytouch/et.exe`
|
|
129
|
+
- Linux / macOS:`<npm root -g>/@whuanle/easytouch/et`
|
|
146
130
|
|
|
147
|
-
|
|
148
|
-
npm i -g @whuanle/easytouch
|
|
149
|
-
et init
|
|
150
|
-
```
|
|
131
|
+
全局命令入口通常是:
|
|
151
132
|
|
|
152
|
-
|
|
133
|
+
- Windows:`<npm prefix -g>/et.cmd`
|
|
134
|
+
- Linux / macOS:`<npm prefix -g>/bin/et`
|
|
153
135
|
|
|
154
|
-
|
|
155
|
-
{
|
|
156
|
-
"mcpServers": {
|
|
157
|
-
"easytouch": {
|
|
158
|
-
"command": "et",
|
|
159
|
-
"args": ["mcp-stdio"]
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
如果你只是想手工执行脚本而不走 `et init`,需要先找到全局安装目录,例如:
|
|
136
|
+
如果需要查看你自己机器上的真实路径,可以执行:
|
|
166
137
|
|
|
167
138
|
```bash
|
|
168
139
|
npm root -g
|
|
140
|
+
npm prefix -g
|
|
169
141
|
```
|
|
170
142
|
|
|
171
|
-
|
|
143
|
+
也可以显式指定输出位置:
|
|
172
144
|
|
|
173
145
|
```bash
|
|
174
|
-
|
|
146
|
+
easytouch init --output <你想放置 et 的路径>
|
|
175
147
|
```
|
|
176
148
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- Windows:把 `command` 改成 `C:\\Users\\<你自己的用户名>\\AppData\\Roaming\\npm\\et.cmd`
|
|
180
|
-
- Linux / macOS:先执行 `npm prefix -g`,然后把 `command` 改成 `<prefix>/bin/et`
|
|
149
|
+
如果你安装的是平台包,初始化命令分别是 `easytouch-windows init`、`easytouch-linux init`、`easytouch-macos init`。
|
|
181
150
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
如果你不想先执行 `init.js`,也可以临时通过 `npx` 启动。这里同样建议统一使用 `@whuanle/easytouch`,不要再按平台分别写包名。首次运行可能会多花一点时间,因为会自动安装当前平台包。
|
|
185
|
-
|
|
186
|
-
- Windows:`command` 推荐写 `npx.cmd`
|
|
187
|
-
- Linux / macOS:`command` 写 `npx`
|
|
151
|
+
生成完原生文件后,MCP 直接调用这个文件即可:
|
|
188
152
|
|
|
189
153
|
```json
|
|
190
154
|
{
|
|
191
155
|
"mcpServers": {
|
|
192
156
|
"easytouch": {
|
|
193
|
-
"command": "
|
|
194
|
-
"args": ["
|
|
157
|
+
"command": "et",
|
|
158
|
+
"args": ["mcp-stdio"]
|
|
195
159
|
}
|
|
196
160
|
}
|
|
197
161
|
}
|
|
198
162
|
```
|
|
199
163
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
Linux / macOS 如果使用这段备用配置,把 `command` 改回 `npx` 即可。
|
|
164
|
+
如果宿主程序不走 PATH,再改成 `npm prefix -g` 或 `npm root -g` 对应的实际路径。
|
|
203
165
|
|
|
204
166
|
### 语义元素定位
|
|
205
167
|
|
package/SKILL.md
CHANGED
|
@@ -364,21 +364,23 @@ et mcp-stdio --output json
|
|
|
364
364
|
|
|
365
365
|
### 配置示例
|
|
366
366
|
|
|
367
|
-
|
|
367
|
+
优先在全局安装后执行 `init`,把真实原生 `et` 程序复制出来,再让 MCP 直接调用这个文件:
|
|
368
368
|
|
|
369
369
|
```bash
|
|
370
|
-
npm i @whuanle/easytouch
|
|
371
|
-
|
|
370
|
+
npm i -g @whuanle/easytouch
|
|
371
|
+
easytouch init
|
|
372
372
|
```
|
|
373
373
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
如果依赖已经装在当前项目里,也可以直接执行 `node ./node_modules/@whuanle/easytouch/init.js`,但这只适用于本地安装目录。
|
|
374
|
+
这一步会直接从包内复制当前主机对应的原生 `et` 文件。
|
|
377
375
|
|
|
378
376
|
生成后的默认路径:
|
|
379
377
|
|
|
380
|
-
- Windows
|
|
381
|
-
- Linux / macOS
|
|
378
|
+
- Windows:`<npm root -g>/@whuanle/easytouch/et.exe`
|
|
379
|
+
- Linux / macOS:`<npm root -g>/@whuanle/easytouch/et`
|
|
380
|
+
|
|
381
|
+
同时会刷新全局 `et` 命令。
|
|
382
|
+
|
|
383
|
+
也可以通过 `--output` 指定输出位置。
|
|
382
384
|
|
|
383
385
|
推荐配置:
|
|
384
386
|
|
|
@@ -386,7 +388,7 @@ npx @whuanle/easytouch init
|
|
|
386
388
|
{
|
|
387
389
|
"mcpServers": {
|
|
388
390
|
"easytouch": {
|
|
389
|
-
"command": "
|
|
391
|
+
"command": "et",
|
|
390
392
|
"args": ["mcp-stdio"]
|
|
391
393
|
}
|
|
392
394
|
}
|
|
@@ -395,39 +397,17 @@ npx @whuanle/easytouch init
|
|
|
395
397
|
|
|
396
398
|
Windows 请把文件名写成 `et.exe`。Linux / macOS 写 `et`。
|
|
397
399
|
|
|
398
|
-
|
|
400
|
+
默认会把 `et` 生成到全局安装目录下的包目录里,并同步刷新全局 `et` 命令。也可以显式指定输出路径:
|
|
399
401
|
|
|
400
402
|
```bash
|
|
401
|
-
|
|
402
|
-
et init
|
|
403
|
+
easytouch init --output <你想放置 et 的路径>
|
|
403
404
|
```
|
|
404
405
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
如果你已经全局安装并且宿主能正确处理 PATH,也可以继续直接调用全局 `et`。首次运行时,如果平台包缺失,启动器也会自动安装当前平台包。
|
|
408
|
-
|
|
409
|
-
如果宿主程序不能从 PATH 找到命令,旧方式仍可用:
|
|
410
|
-
|
|
411
|
-
- Windows:把 `command` 改成 `C:\Users\<你自己的用户名>\AppData\Roaming\npm\et.cmd`
|
|
412
|
-
- Linux / macOS:先执行 `npm prefix -g`,再把 `command` 改成 `<prefix>/bin/et`
|
|
413
|
-
|
|
414
|
-
如果不想执行 `init.js`,也可以临时通过 `npx` 启动。首次运行可能会稍慢,因为会自动安装当前平台包:
|
|
406
|
+
如果你直接安装的是平台包,初始化命令分别是 `easytouch-windows init`、`easytouch-linux init`、`easytouch-macos init`。
|
|
415
407
|
|
|
416
|
-
|
|
417
|
-
- Linux / macOS:`command` 写 `npx`
|
|
418
|
-
|
|
419
|
-
```json
|
|
420
|
-
{
|
|
421
|
-
"mcpServers": {
|
|
422
|
-
"easytouch": {
|
|
423
|
-
"command": "npx.cmd",
|
|
424
|
-
"args": ["-y", "@whuanle/easytouch", "mcp-stdio"]
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
```
|
|
408
|
+
如果需要查看实际路径,可以执行 `npm root -g` 和 `npm prefix -g`。
|
|
429
409
|
|
|
430
|
-
|
|
410
|
+
如果宿主能正确处理 PATH,MCP 直接写 `command: "et"` 即可;否则改成 `npm prefix -g` 或 `npm root -g` 对应的实际路径。
|
|
431
411
|
|
|
432
412
|
如果 Windows 宿主报 `LOCAL_PROCESS_ERROR`,优先改用 `init.js` 生成的 `et.exe`;如果仍走 npm 命令,再检查这里是不是还写成了 `npx`。
|
|
433
413
|
|
|
Binary file
|
|
Binary file
|
package/init.js
CHANGED
|
@@ -3,19 +3,23 @@
|
|
|
3
3
|
const fs = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const helperCommandName = "easytouch-windows";
|
|
7
|
+
const generatedCommandName = "et";
|
|
8
|
+
|
|
9
|
+
const supportedArchitectures = new Set(["x64", "arm64"]);
|
|
10
10
|
|
|
11
11
|
function printUsage() {
|
|
12
12
|
process.stdout.write(
|
|
13
13
|
[
|
|
14
14
|
"Usage:",
|
|
15
|
-
"
|
|
15
|
+
" easytouch-windows init [--output <path>] [--force]",
|
|
16
|
+
" npx easytouch-windows init [--output <path>] [--force]",
|
|
17
|
+
" node init.js [init] [--output <path>] [--force]",
|
|
16
18
|
"",
|
|
17
19
|
"Behavior:",
|
|
18
|
-
" Copies the current Windows binary
|
|
20
|
+
" Copies the current Windows binary out of this package as 'et.exe' by default.",
|
|
21
|
+
" By default, writes into this package directory.",
|
|
22
|
+
" When installed through npm, also refreshes the 'et' command if possible.",
|
|
19
23
|
" Use --output to write to a different file or directory.",
|
|
20
24
|
].join("\n") + "\n"
|
|
21
25
|
);
|
|
@@ -26,7 +30,16 @@ function fail(message) {
|
|
|
26
30
|
process.exit(1);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
function
|
|
33
|
+
function normalizeArgs(argv) {
|
|
34
|
+
if (argv[0] === "init") {
|
|
35
|
+
return argv.slice(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return argv;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseArgs(rawArgv) {
|
|
42
|
+
const argv = normalizeArgs(rawArgv);
|
|
30
43
|
let output = null;
|
|
31
44
|
let force = false;
|
|
32
45
|
|
|
@@ -54,11 +67,11 @@ function parseArgs(argv) {
|
|
|
54
67
|
return { output, force };
|
|
55
68
|
}
|
|
56
69
|
|
|
57
|
-
function
|
|
70
|
+
function getAvailableBinaries() {
|
|
58
71
|
try {
|
|
59
72
|
return fs
|
|
60
73
|
.readdirSync(path.join(__dirname, "bin"), { withFileTypes: true })
|
|
61
|
-
.filter((entry) => entry.
|
|
74
|
+
.filter((entry) => entry.isFile() && entry.name.startsWith("et_"))
|
|
62
75
|
.map((entry) => entry.name)
|
|
63
76
|
.sort();
|
|
64
77
|
} catch {
|
|
@@ -66,28 +79,36 @@ function getAvailableArchitectures() {
|
|
|
66
79
|
}
|
|
67
80
|
}
|
|
68
81
|
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
fail(`Unsupported architecture '${process.arch}' on platform '${process.platform}'.`);
|
|
82
|
+
function binaryNameForArch(arch = process.arch) {
|
|
83
|
+
if (!supportedArchitectures.has(arch)) {
|
|
84
|
+
fail(`Unsupported architecture '${arch}' on platform '${process.platform}'.`);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
return `et_${arch}.exe`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resolveBinaryPath() {
|
|
91
|
+
const fileName = binaryNameForArch();
|
|
92
|
+
const sourcePath = path.join(__dirname, "bin", fileName);
|
|
76
93
|
if (!fs.existsSync(sourcePath)) {
|
|
77
|
-
const
|
|
78
|
-
const availableMessage =
|
|
79
|
-
? ` Available
|
|
94
|
+
const availableBinaries = getAvailableBinaries();
|
|
95
|
+
const availableMessage = availableBinaries.length
|
|
96
|
+
? ` Available binaries: ${availableBinaries.join(", ")}.`
|
|
80
97
|
: "";
|
|
81
|
-
fail(`The package is missing '
|
|
98
|
+
fail(`The package is missing '${fileName}' for architecture '${process.arch}'.${availableMessage}`);
|
|
82
99
|
}
|
|
83
100
|
|
|
84
101
|
return sourcePath;
|
|
85
102
|
}
|
|
86
103
|
|
|
104
|
+
function defaultOutputPath() {
|
|
105
|
+
return path.join(__dirname, "et.exe");
|
|
106
|
+
}
|
|
107
|
+
|
|
87
108
|
function resolveOutputPath(requestedOutput) {
|
|
88
109
|
let outputPath = requestedOutput
|
|
89
110
|
? path.resolve(process.cwd(), requestedOutput)
|
|
90
|
-
:
|
|
111
|
+
: defaultOutputPath();
|
|
91
112
|
|
|
92
113
|
if (fs.existsSync(outputPath)) {
|
|
93
114
|
const stat = fs.statSync(outputPath);
|
|
@@ -103,6 +124,22 @@ function resolveOutputPath(requestedOutput) {
|
|
|
103
124
|
return outputPath;
|
|
104
125
|
}
|
|
105
126
|
|
|
127
|
+
function filesAreIdentical(leftPath, rightPath) {
|
|
128
|
+
try {
|
|
129
|
+
const leftStat = fs.statSync(leftPath);
|
|
130
|
+
const rightStat = fs.statSync(rightPath);
|
|
131
|
+
if (leftStat.size !== rightStat.size) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const leftContent = fs.readFileSync(leftPath);
|
|
136
|
+
const rightContent = fs.readFileSync(rightPath);
|
|
137
|
+
return leftContent.equals(rightContent);
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
106
143
|
function initializeBinary(sourcePath, destinationPath, force) {
|
|
107
144
|
const resolvedSource = path.resolve(sourcePath);
|
|
108
145
|
const resolvedDestination = path.resolve(destinationPath);
|
|
@@ -111,8 +148,15 @@ function initializeBinary(sourcePath, destinationPath, force) {
|
|
|
111
148
|
return;
|
|
112
149
|
}
|
|
113
150
|
|
|
114
|
-
if (fs.existsSync(resolvedDestination)
|
|
115
|
-
|
|
151
|
+
if (fs.existsSync(resolvedDestination)) {
|
|
152
|
+
if (filesAreIdentical(resolvedSource, resolvedDestination)) {
|
|
153
|
+
process.stdout.write(`et init: binary already available at ${resolvedDestination}\n`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (!force) {
|
|
158
|
+
fail(`Destination '${resolvedDestination}' already exists. Use --force to overwrite it.`);
|
|
159
|
+
}
|
|
116
160
|
}
|
|
117
161
|
|
|
118
162
|
fs.mkdirSync(path.dirname(resolvedDestination), { recursive: true });
|
|
@@ -120,5 +164,157 @@ function initializeBinary(sourcePath, destinationPath, force) {
|
|
|
120
164
|
process.stdout.write(`et init: copied ${resolvedSource} -> ${resolvedDestination}\n`);
|
|
121
165
|
}
|
|
122
166
|
|
|
167
|
+
function findNodeModulesRoot(startDir) {
|
|
168
|
+
let current = path.resolve(startDir);
|
|
169
|
+
|
|
170
|
+
while (true) {
|
|
171
|
+
if (path.basename(current) === "node_modules") {
|
|
172
|
+
return current;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const parent = path.dirname(current);
|
|
176
|
+
if (parent === current) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
current = parent;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function commandFileCandidates(commandName) {
|
|
184
|
+
return [`${commandName}.cmd`, `${commandName}.ps1`, commandName];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function uniquePaths(paths) {
|
|
188
|
+
return [...new Set(paths.filter(Boolean).map((entry) => path.resolve(entry)))];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function resolveCommandBinDir(startDir, commandName) {
|
|
192
|
+
const nodeModulesRoot = findNodeModulesRoot(startDir);
|
|
193
|
+
if (!nodeModulesRoot) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const installRoot = path.dirname(nodeModulesRoot);
|
|
198
|
+
const candidates = uniquePaths([path.join(nodeModulesRoot, ".bin"), installRoot]);
|
|
199
|
+
|
|
200
|
+
for (const candidate of candidates) {
|
|
201
|
+
for (const fileName of commandFileCandidates(commandName)) {
|
|
202
|
+
if (fs.existsSync(path.join(candidate, fileName))) {
|
|
203
|
+
return candidate;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const candidate of candidates) {
|
|
209
|
+
if (fs.existsSync(candidate)) {
|
|
210
|
+
return candidate;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function managedCommandMarker() {
|
|
218
|
+
return "EasyTouch generated by init";
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function readTextFile(filePath) {
|
|
222
|
+
try {
|
|
223
|
+
return fs.readFileSync(filePath, "utf8");
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function assertManagedCommandTarget(filePath, force) {
|
|
230
|
+
if (!fs.existsSync(filePath)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const content = readTextFile(filePath);
|
|
235
|
+
if (content && content.includes(managedCommandMarker())) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (!force) {
|
|
240
|
+
fail(`Command '${filePath}' already exists. Use --force to overwrite it.`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function writeManagedCommand(filePath, content, force, executable) {
|
|
245
|
+
assertManagedCommandTarget(filePath, force);
|
|
246
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
247
|
+
fs.writeFileSync(filePath, content, "utf8");
|
|
248
|
+
if (executable) {
|
|
249
|
+
fs.chmodSync(filePath, 0o755);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function escapeForSingleQuotedPowerShell(value) {
|
|
254
|
+
return value.replace(/'/g, "''");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function relativeShellPath(fromDir, toFile) {
|
|
258
|
+
return path.relative(fromDir, toFile).split(path.sep).join("/");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function installGeneratedCommand(targetBinaryPath, force) {
|
|
262
|
+
const binDir = resolveCommandBinDir(__dirname, helperCommandName);
|
|
263
|
+
if (!binDir) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const resolvedTarget = path.resolve(targetBinaryPath);
|
|
268
|
+
const relativeWindowsPath = path.relative(binDir, resolvedTarget);
|
|
269
|
+
const relativePosixPath = relativeShellPath(binDir, resolvedTarget);
|
|
270
|
+
|
|
271
|
+
writeManagedCommand(
|
|
272
|
+
path.join(binDir, generatedCommandName),
|
|
273
|
+
[
|
|
274
|
+
"#!/bin/sh",
|
|
275
|
+
`# ${managedCommandMarker()}`,
|
|
276
|
+
'basedir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)',
|
|
277
|
+
`exec "$basedir/${relativePosixPath}" "$@"`,
|
|
278
|
+
"",
|
|
279
|
+
].join("\n"),
|
|
280
|
+
force,
|
|
281
|
+
true
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
writeManagedCommand(
|
|
285
|
+
path.join(binDir, `${generatedCommandName}.cmd`),
|
|
286
|
+
[
|
|
287
|
+
"@ECHO off",
|
|
288
|
+
`:: ${managedCommandMarker()}`,
|
|
289
|
+
"SETLOCAL",
|
|
290
|
+
`SET \"_ET_TARGET=%~dp0${relativeWindowsPath}\"`,
|
|
291
|
+
'"%_ET_TARGET%" %*',
|
|
292
|
+
"",
|
|
293
|
+
].join("\r\n"),
|
|
294
|
+
force,
|
|
295
|
+
false
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
writeManagedCommand(
|
|
299
|
+
path.join(binDir, `${generatedCommandName}.ps1`),
|
|
300
|
+
[
|
|
301
|
+
`# ${managedCommandMarker()}`,
|
|
302
|
+
`$target = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '${escapeForSingleQuotedPowerShell(relativeWindowsPath)}'))`,
|
|
303
|
+
"& $target @args",
|
|
304
|
+
"exit $LASTEXITCODE",
|
|
305
|
+
"",
|
|
306
|
+
].join("\r\n"),
|
|
307
|
+
force,
|
|
308
|
+
false
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
process.stdout.write(`et init: updated command '${generatedCommandName}' in ${binDir}\n`);
|
|
312
|
+
}
|
|
313
|
+
|
|
123
314
|
const options = parseArgs(process.argv.slice(2));
|
|
124
|
-
|
|
315
|
+
const outputPath = resolveOutputPath(options.output);
|
|
316
|
+
initializeBinary(resolveBinaryPath(), outputPath, options.force);
|
|
317
|
+
|
|
318
|
+
if (path.resolve(outputPath) === path.resolve(defaultOutputPath())) {
|
|
319
|
+
installGeneratedCommand(outputPath, options.force);
|
|
320
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "easytouch-windows",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Windows binary distribution for EasyTouch with x64 and arm64 binaries.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
},
|
|
14
14
|
"homepage": "https://github.com/whuanle/EasyTouch#readme",
|
|
15
15
|
"bin": {
|
|
16
|
-
"
|
|
16
|
+
"easytouch-windows": "init.js",
|
|
17
|
+
"et_x64": "bin/et_x64.exe",
|
|
18
|
+
"et_arm64": "bin/et_arm64.exe"
|
|
17
19
|
},
|
|
18
20
|
"os": [
|
|
19
21
|
"win32"
|
package/bin/et.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const cp = require("node:child_process");
|
|
4
|
-
const fs = require("node:fs");
|
|
5
|
-
const path = require("node:path");
|
|
6
|
-
|
|
7
|
-
const archDirectoryByNodeArch = {
|
|
8
|
-
x64: "x64",
|
|
9
|
-
arm64: "arm64",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const packageName = require(path.join(__dirname, "..", "package.json")).name;
|
|
13
|
-
|
|
14
|
-
function fail(message) {
|
|
15
|
-
process.stderr.write(`et: ${message}\n`);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function getAvailableArchitectures() {
|
|
20
|
-
try {
|
|
21
|
-
return fs
|
|
22
|
-
.readdirSync(__dirname, { withFileTypes: true })
|
|
23
|
-
.filter((entry) => entry.isDirectory())
|
|
24
|
-
.map((entry) => entry.name)
|
|
25
|
-
.sort();
|
|
26
|
-
} catch {
|
|
27
|
-
return [];
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function resolveBinaryPath() {
|
|
32
|
-
const archDirectory = archDirectoryByNodeArch[process.arch];
|
|
33
|
-
if (!archDirectory) {
|
|
34
|
-
fail(`Unsupported architecture '${process.arch}' on platform '${process.platform}'.`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const binaryName = process.platform === "win32" ? "et.exe" : "et";
|
|
38
|
-
const binaryPath = path.join(__dirname, archDirectory, binaryName);
|
|
39
|
-
if (!fs.existsSync(binaryPath)) {
|
|
40
|
-
const availableArchitectures = getAvailableArchitectures();
|
|
41
|
-
const availableMessage = availableArchitectures.length
|
|
42
|
-
? ` Available architectures: ${availableArchitectures.join(", ")}.`
|
|
43
|
-
: "";
|
|
44
|
-
fail(
|
|
45
|
-
`The platform package '${packageName}' is missing '${binaryName}' for architecture '${archDirectory}'.${availableMessage}`
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return binaryPath;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const result = cp.spawnSync(resolveBinaryPath(), process.argv.slice(2), {
|
|
53
|
-
stdio: "inherit",
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (result.error) {
|
|
57
|
-
fail(result.error.message);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
process.exit(result.status ?? 1);
|