ms-vite-plugin 1.4.47 → 1.4.49
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/dist/build.js +1 -292
- package/dist/cli.js +6 -1234
- package/dist/mcp/device-config.d.ts +0 -47
- package/dist/mcp/device-config.js +1 -183
- package/dist/mcp/device-log.d.ts +0 -8
- package/dist/mcp/device-log.js +3 -27
- package/dist/mcp/doc-tools.d.ts +0 -7
- package/dist/mcp/doc-tools.js +2 -39
- package/dist/mcp/docs-service.d.ts +0 -13
- package/dist/mcp/docs-service.js +1 -63
- package/dist/mcp/httpapi-docs-service.d.ts +0 -31
- package/dist/mcp/httpapi-docs-service.js +1 -122
- package/dist/mcp/httpapi-tools.d.ts +0 -7
- package/dist/mcp/httpapi-tools.js +4 -222
- package/dist/mcp/image-tools.d.ts +0 -62
- package/dist/mcp/image-tools.js +8 -607
- package/dist/mcp/ocr-tools.d.ts +0 -25
- package/dist/mcp/ocr-tools.js +5 -413
- package/dist/mcp/project.d.ts +0 -14
- package/dist/mcp/project.js +1 -74
- package/dist/mcp/runtime-tools.d.ts +0 -7
- package/dist/mcp/runtime-tools.js +11 -308
- package/dist/mcp/tool-utils.d.ts +0 -40
- package/dist/mcp/tool-utils.js +1 -81
- package/dist/mcp/tools.d.ts +0 -12
- package/dist/mcp/tools.js +1 -31
- package/dist/mcp/types.d.ts +0 -12
- package/dist/mcp/types.js +1 -11
- package/dist/mcp-server.js +1 -85
- package/dist/packager.d.ts +0 -7
- package/dist/packager.js +1 -125
- package/dist/project.d.ts +0 -61
- package/dist/project.js +1 -619
- package/dist/stopGuardPlugin.d.ts +0 -6
- package/dist/stopGuardPlugin.js +1 -165
- package/dist/version.d.ts +0 -10
- package/dist/version.js +1 -63
- package/dist/ws-manager.d.ts +0 -95
- package/dist/ws-manager.js +1 -392
- package/docs/api/image.md +249 -25
- package/docs/apicn/image.md +245 -19
- package/docs/apipython/image.md +238 -18
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -1,1235 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
-
}
|
|
9
|
-
Object.defineProperty(o, k2, desc);
|
|
10
|
-
}) : (function(o, m, k, k2) {
|
|
11
|
-
if (k2 === undefined) k2 = k;
|
|
12
|
-
o[k2] = m[k];
|
|
13
|
-
}));
|
|
14
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
-
}) : function(o, v) {
|
|
17
|
-
o["default"] = v;
|
|
18
|
-
});
|
|
19
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
-
var ownKeys = function(o) {
|
|
21
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
-
var ar = [];
|
|
23
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
-
return ar;
|
|
25
|
-
};
|
|
26
|
-
return ownKeys(o);
|
|
27
|
-
};
|
|
28
|
-
return function (mod) {
|
|
29
|
-
if (mod && mod.__esModule) return mod;
|
|
30
|
-
var result = {};
|
|
31
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
-
__setModuleDefault(result, mod);
|
|
33
|
-
return result;
|
|
34
|
-
};
|
|
35
|
-
})();
|
|
36
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
const commander_1 = require("commander");
|
|
38
|
-
const os = __importStar(require("os"));
|
|
39
|
-
const path = __importStar(require("path"));
|
|
40
|
-
const build_1 = require("./build");
|
|
41
|
-
const fsExtra = __importStar(require("fs-extra"));
|
|
42
|
-
const project_1 = require("./project");
|
|
43
|
-
const packager_1 = require("./packager");
|
|
44
|
-
const ws_manager_1 = require("./ws-manager");
|
|
45
|
-
const version_1 = require("./version");
|
|
46
|
-
const docs_service_1 = require("./mcp/docs-service");
|
|
47
|
-
const httpapi_docs_service_1 = require("./mcp/httpapi-docs-service");
|
|
48
|
-
const tool_utils_1 = require("./mcp/tool-utils");
|
|
49
|
-
const types_1 = require("./mcp/types");
|
|
50
|
-
const ocr_tools_1 = require("./mcp/ocr-tools");
|
|
51
|
-
const image_tools_1 = require("./mcp/image-tools");
|
|
52
|
-
/**
|
|
53
|
-
* WS 状态持久化文件路径(用于跨进程查询 ws-status)
|
|
54
|
-
*/
|
|
55
|
-
const WS_PID_FILE = path.join(os.tmpdir(), "ms-cli-ws-server.json");
|
|
56
|
-
/**
|
|
57
|
-
* 检查工作目录是否为有效的 KuaiJS 项目
|
|
58
|
-
* @param workspacePath 工作目录路径
|
|
59
|
-
* @returns 是否为有效项目
|
|
60
|
-
* @example
|
|
61
|
-
* await isValidKuaiJSProject(process.cwd())
|
|
62
|
-
*/
|
|
63
|
-
async function isValidKuaiJSProject(workspacePath) {
|
|
64
|
-
try {
|
|
65
|
-
const packageJsonPath = path.join(workspacePath, "package.json");
|
|
66
|
-
const scriptsPath = path.join(workspacePath, "scripts");
|
|
67
|
-
const [packageExists, scriptsExists] = await Promise.all([
|
|
68
|
-
fsExtra.pathExists(packageJsonPath),
|
|
69
|
-
fsExtra.pathExists(scriptsPath),
|
|
70
|
-
]);
|
|
71
|
-
return packageExists && scriptsExists;
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* 统一校验项目目录合法性
|
|
79
|
-
* @param workspacePath 待校验的项目目录
|
|
80
|
-
* @returns 校验通过返回 Promise<void>
|
|
81
|
-
* @example
|
|
82
|
-
* await ensureValidKuaiJSProject("/Users/demo/project")
|
|
83
|
-
*/
|
|
84
|
-
async function ensureValidKuaiJSProject(workspacePath) {
|
|
85
|
-
if (await isValidKuaiJSProject(workspacePath)) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
console.error("❌ 错误: 当前目录不是有效的 快点JS 项目");
|
|
89
|
-
console.error("请确保目录包含以下文件:");
|
|
90
|
-
console.error(" - package.json");
|
|
91
|
-
console.error(" - scripts/ 目录");
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* 解析整数参数
|
|
96
|
-
* @param value 用户传入的参数文本
|
|
97
|
-
* @param defaultValue 默认值
|
|
98
|
-
* @param name 参数名称
|
|
99
|
-
* @param min 最小值
|
|
100
|
-
* @param max 最大值
|
|
101
|
-
* @returns 返回校验后的整数
|
|
102
|
-
* @example
|
|
103
|
-
* parseCliInteger("20", 10, "limit", 1, 100)
|
|
104
|
-
*/
|
|
105
|
-
function parseCliInteger(value, defaultValue, name, min, max) {
|
|
106
|
-
const text = (value ?? String(defaultValue)).trim();
|
|
107
|
-
const numberValue = Number.parseInt(text, 10);
|
|
108
|
-
if (!Number.isInteger(numberValue) ||
|
|
109
|
-
numberValue < min ||
|
|
110
|
-
(max !== undefined && numberValue > max)) {
|
|
111
|
-
throw new Error(`无效 ${name}: ${text},允许范围 ${min}${max === undefined ? "+" : `..${max}`}`);
|
|
112
|
-
}
|
|
113
|
-
return numberValue;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* 解析浮点数参数
|
|
117
|
-
* @param value 用户传入的参数文本
|
|
118
|
-
* @param defaultValue 默认值
|
|
119
|
-
* @param name 参数名称
|
|
120
|
-
* @param min 最小值
|
|
121
|
-
* @param max 最大值
|
|
122
|
-
* @returns 返回校验后的数字
|
|
123
|
-
* @example
|
|
124
|
-
* parseCliNumber("0.6", 0.6, "confidence", 0, 1)
|
|
125
|
-
*/
|
|
126
|
-
function parseCliNumber(value, defaultValue, name, min, max) {
|
|
127
|
-
const text = (value ?? String(defaultValue)).trim();
|
|
128
|
-
const numberValue = Number.parseFloat(text);
|
|
129
|
-
if (!Number.isFinite(numberValue) || numberValue < min || numberValue > max) {
|
|
130
|
-
throw new Error(`无效 ${name}: ${text},允许范围 ${min}..${max}`);
|
|
131
|
-
}
|
|
132
|
-
return numberValue;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* 解析枚举参数
|
|
136
|
-
* @param value 用户传入的参数文本
|
|
137
|
-
* @param defaultValue 默认值
|
|
138
|
-
* @param choices 允许的取值
|
|
139
|
-
* @param name 参数名称
|
|
140
|
-
* @returns 返回校验后的枚举值
|
|
141
|
-
* @example
|
|
142
|
-
* parseCliChoice("json", "text", ["text", "json"], "format")
|
|
143
|
-
*/
|
|
144
|
-
function parseCliChoice(value, defaultValue, choices, name) {
|
|
145
|
-
const text = (value ?? defaultValue).trim();
|
|
146
|
-
if (!choices.includes(text)) {
|
|
147
|
-
throw new Error(`无效 ${name}: ${text},可选值: ${choices.join("|")}`);
|
|
148
|
-
}
|
|
149
|
-
return text;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* 解析 HTTP API 请求方法
|
|
153
|
-
* @param value 用户传入的方法
|
|
154
|
-
* @returns 返回 HTTP API 方法
|
|
155
|
-
* @example
|
|
156
|
-
* parseHttpApiMethodOption("GET")
|
|
157
|
-
*/
|
|
158
|
-
function parseHttpApiMethodOption(value) {
|
|
159
|
-
const method = value?.trim().toUpperCase();
|
|
160
|
-
return parseCliChoice(method, "GET", ["GET", "POST"], "method");
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* 解析设备 HTTP 地址
|
|
164
|
-
* @param options 设备命令选项
|
|
165
|
-
* @returns 返回设备 HTTP 目标
|
|
166
|
-
* @example
|
|
167
|
-
* parseDeviceHttpTarget({ ip: "192.168.1.10", port: "9800" })
|
|
168
|
-
*/
|
|
169
|
-
function parseDeviceHttpTarget(options) {
|
|
170
|
-
const ip = options.ip?.trim();
|
|
171
|
-
if (!ip) {
|
|
172
|
-
throw new Error("请通过 --ip 指定设备 IP 地址");
|
|
173
|
-
}
|
|
174
|
-
const port = parseCliInteger(options.port, 9800, "port", 1, 65535);
|
|
175
|
-
return {
|
|
176
|
-
ip,
|
|
177
|
-
port: String(port),
|
|
178
|
-
label: `${ip}:${port}`,
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* 输出文本到终端或文件
|
|
183
|
-
* @param text 待输出文本
|
|
184
|
-
* @param output 用户指定输出路径
|
|
185
|
-
* @param savedLabel 保存成功提示
|
|
186
|
-
* @returns 输出完成后返回
|
|
187
|
-
* @example
|
|
188
|
-
* await writeOrPrintText("ok", "./out.txt", "结果已保存")
|
|
189
|
-
*/
|
|
190
|
-
async function writeOrPrintText(text, output, savedLabel) {
|
|
191
|
-
if (output?.trim()) {
|
|
192
|
-
const outputPath = path.resolve(output.trim());
|
|
193
|
-
await fsExtra.ensureDir(path.dirname(outputPath));
|
|
194
|
-
await fsExtra.writeFile(outputPath, text, "utf8");
|
|
195
|
-
console.log(`✅ ${savedLabel}: ${outputPath}`);
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
console.log(text);
|
|
199
|
-
}
|
|
200
|
-
/**
|
|
201
|
-
* 格式化 CLI 文档路径清单
|
|
202
|
-
* @returns 返回本地文档路径文本
|
|
203
|
-
* @example
|
|
204
|
-
* const text = formatCliDocsPathsText()
|
|
205
|
-
*/
|
|
206
|
-
function formatCliDocsPathsText() {
|
|
207
|
-
const languages = ["js", "js_zh", "python"];
|
|
208
|
-
return [
|
|
209
|
-
"KuaiJS 文档路径:",
|
|
210
|
-
`docsRoot: ${(0, docs_service_1.getDocsRootDir)()}`,
|
|
211
|
-
...languages.map((language) => `${language}: ${(0, docs_service_1.getDocsDirByLanguage)(language)} (${types_1.DOC_LANGUAGE_LABELS[language]})`),
|
|
212
|
-
`httpApi: ${(0, httpapi_docs_service_1.getHttpApiDocPath)()}`,
|
|
213
|
-
"说明: 直接读取上述 markdown 路径。",
|
|
214
|
-
].join("\n");
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* 收集可重复字符串参数
|
|
218
|
-
* @param value 当前参数值
|
|
219
|
-
* @param previous 之前收集的值
|
|
220
|
-
* @returns 返回追加后的数组
|
|
221
|
-
* @example
|
|
222
|
-
* collectStringOption("登录", [])
|
|
223
|
-
*/
|
|
224
|
-
function collectStringOption(value, previous) {
|
|
225
|
-
previous.push(value);
|
|
226
|
-
return previous;
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* 解析字符串数组参数
|
|
230
|
-
* @param value 用户传入文本
|
|
231
|
-
* @param name 参数名
|
|
232
|
-
* @returns 返回字符串数组或 undefined
|
|
233
|
-
* @example
|
|
234
|
-
* parseStringArrayOption("zh-Hans,en-US", "languages")
|
|
235
|
-
*/
|
|
236
|
-
function parseStringArrayOption(value, name) {
|
|
237
|
-
const text = value?.trim();
|
|
238
|
-
if (!text) {
|
|
239
|
-
return undefined;
|
|
240
|
-
}
|
|
241
|
-
if (text.startsWith("[")) {
|
|
242
|
-
const parsed = JSON.parse(text);
|
|
243
|
-
if (!Array.isArray(parsed) ||
|
|
244
|
-
!parsed.every((item) => typeof item === "string" && item.trim())) {
|
|
245
|
-
throw new Error(`${name} 必须是字符串数组。`);
|
|
246
|
-
}
|
|
247
|
-
return parsed.map((item) => item.trim());
|
|
248
|
-
}
|
|
249
|
-
const items = text
|
|
250
|
-
.split(",")
|
|
251
|
-
.map((item) => item.trim())
|
|
252
|
-
.filter((item) => item.length > 0);
|
|
253
|
-
if (items.length === 0) {
|
|
254
|
-
throw new Error(`${name} 不能为空。`);
|
|
255
|
-
}
|
|
256
|
-
return items;
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* 解析 Apple OCR 语言数组
|
|
260
|
-
* @param value 用户传入语言文本
|
|
261
|
-
* @returns 返回语言数组或 undefined
|
|
262
|
-
* @example
|
|
263
|
-
* parseAppleOcrLanguages("zh-Hans,en-US")
|
|
264
|
-
*/
|
|
265
|
-
function parseAppleOcrLanguages(value) {
|
|
266
|
-
const languages = parseStringArrayOption(value, "languages");
|
|
267
|
-
if (!languages) {
|
|
268
|
-
return undefined;
|
|
269
|
-
}
|
|
270
|
-
const allowed = new Set(ocr_tools_1.APPLE_OCR_LANGUAGES);
|
|
271
|
-
const invalid = languages.filter((language) => !allowed.has(language));
|
|
272
|
-
if (invalid.length > 0) {
|
|
273
|
-
throw new Error(`无效 languages: ${invalid.join(", ")},可选值: ${ocr_tools_1.APPLE_OCR_LANGUAGES.join(", ")}`);
|
|
274
|
-
}
|
|
275
|
-
return languages;
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* 解析 JSON 对象参数
|
|
279
|
-
* @param value 用户传入 JSON 文本
|
|
280
|
-
* @param name 参数名
|
|
281
|
-
* @returns 返回对象或 undefined
|
|
282
|
-
* @example
|
|
283
|
-
* parseJsonRecordOption('{"x":1}', "query")
|
|
284
|
-
*/
|
|
285
|
-
function parseJsonRecordOption(value, name) {
|
|
286
|
-
const text = value?.trim();
|
|
287
|
-
if (!text) {
|
|
288
|
-
return undefined;
|
|
289
|
-
}
|
|
290
|
-
const parsed = JSON.parse(text);
|
|
291
|
-
if (!parsed ||
|
|
292
|
-
typeof parsed !== "object" ||
|
|
293
|
-
Array.isArray(parsed) ||
|
|
294
|
-
!Object.values(parsed).every((item) => typeof item === "string" ||
|
|
295
|
-
typeof item === "number" ||
|
|
296
|
-
typeof item === "boolean" ||
|
|
297
|
-
item === null)) {
|
|
298
|
-
throw new Error(`${name} 必须是 JSON 对象,且值只能是 string/number/boolean/null。`);
|
|
299
|
-
}
|
|
300
|
-
return parsed;
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* 解析 JSON 请求体参数
|
|
304
|
-
* @param value 用户传入请求体文本
|
|
305
|
-
* @returns 返回 JSON 值或 undefined
|
|
306
|
-
* @example
|
|
307
|
-
* parseJsonBodyOption('{"x":1}')
|
|
308
|
-
*/
|
|
309
|
-
function parseJsonBodyOption(value) {
|
|
310
|
-
const text = value?.trim();
|
|
311
|
-
if (text === undefined || text.length === 0) {
|
|
312
|
-
return undefined;
|
|
313
|
-
}
|
|
314
|
-
try {
|
|
315
|
-
return JSON.parse(text);
|
|
316
|
-
}
|
|
317
|
-
catch {
|
|
318
|
-
return value;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* 标准化 HTTP API 路径
|
|
323
|
-
* @param apiPath 用户传入路径
|
|
324
|
-
* @returns 返回相对路径
|
|
325
|
-
* @example
|
|
326
|
-
* normalizeCliHttpApiPath("/api/status")
|
|
327
|
-
*/
|
|
328
|
-
function normalizeCliHttpApiPath(apiPath) {
|
|
329
|
-
const normalizedPath = apiPath.trim();
|
|
330
|
-
if (/^https?:\/\//i.test(normalizedPath)) {
|
|
331
|
-
throw new Error("path 只能传 HTTP API 相对路径,不能传完整 URL。");
|
|
332
|
-
}
|
|
333
|
-
if (!normalizedPath.startsWith("/")) {
|
|
334
|
-
throw new Error("path 必须以 / 开头,例如 /api/status。");
|
|
335
|
-
}
|
|
336
|
-
if (normalizedPath.includes("?")) {
|
|
337
|
-
throw new Error("path 不能包含 query string,请使用 --query 传递查询参数。");
|
|
338
|
-
}
|
|
339
|
-
return normalizedPath;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* 追加 HTTP query 参数
|
|
343
|
-
* @param url 目标 URL
|
|
344
|
-
* @param query query 参数
|
|
345
|
-
* @returns 无返回值
|
|
346
|
-
* @example
|
|
347
|
-
* appendCliQueryParams(url, { x: 1 })
|
|
348
|
-
*/
|
|
349
|
-
function appendCliQueryParams(url, query) {
|
|
350
|
-
if (!query) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
for (const [key, value] of Object.entries(query)) {
|
|
354
|
-
if (value === null) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
url.searchParams.set(key, String(value));
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* 创建 HTTP API 请求体
|
|
362
|
-
* @param body 用户传入请求体
|
|
363
|
-
* @returns 返回 fetch 请求体和头
|
|
364
|
-
* @example
|
|
365
|
-
* createCliHttpRequestBody({ x: 1 })
|
|
366
|
-
*/
|
|
367
|
-
function createCliHttpRequestBody(body) {
|
|
368
|
-
if (body === undefined) {
|
|
369
|
-
return {};
|
|
370
|
-
}
|
|
371
|
-
return {
|
|
372
|
-
headers: {
|
|
373
|
-
"Content-Type": "application/json",
|
|
374
|
-
},
|
|
375
|
-
requestBody: JSON.stringify(body),
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* 读取 HTTP API 响应文本
|
|
380
|
-
* @param response fetch 响应
|
|
381
|
-
* @param responseFormat 响应格式
|
|
382
|
-
* @returns 返回格式化文本
|
|
383
|
-
* @example
|
|
384
|
-
* await readCliHttpApiResponseText(response, "json")
|
|
385
|
-
*/
|
|
386
|
-
async function readCliHttpApiResponseText(response, responseFormat) {
|
|
387
|
-
const responseText = await response.text();
|
|
388
|
-
if (responseFormat === "text") {
|
|
389
|
-
return responseText;
|
|
390
|
-
}
|
|
391
|
-
return (0, tool_utils_1.formatRuntimeJsonText)(JSON.parse(responseText));
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* 构建命令处理函数
|
|
395
|
-
* @param options 命令选项
|
|
396
|
-
* @returns 请求完成后退出
|
|
397
|
-
* @example
|
|
398
|
-
* ms build -d
|
|
399
|
-
*/
|
|
400
|
-
async function buildCommand(options) {
|
|
401
|
-
try {
|
|
402
|
-
const workspacePath = process.cwd();
|
|
403
|
-
const isDev = options.dev || false;
|
|
404
|
-
console.log(`🔍 检查项目目录: ${workspacePath}`);
|
|
405
|
-
await ensureValidKuaiJSProject(workspacePath);
|
|
406
|
-
console.log(`🚀 开始构建项目 (${isDev ? "开发模式" : "生产模式"})...`);
|
|
407
|
-
await (0, build_1.buildAll)(isDev, workspacePath);
|
|
408
|
-
console.log("✅ 构建完成!");
|
|
409
|
-
// console.log(
|
|
410
|
-
// `📦 输出文件: ${path.join(workspacePath, "dist", "ms.msbundle")}`
|
|
411
|
-
// );
|
|
412
|
-
}
|
|
413
|
-
catch (error) {
|
|
414
|
-
console.error("❌ 构建失败:", error instanceof Error ? error.message : error);
|
|
415
|
-
process.exit(1);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
/**
|
|
419
|
-
* package 命令处理函数(生产构建 + 加密)
|
|
420
|
-
* @returns 请求完成后退出
|
|
421
|
-
* @example
|
|
422
|
-
* ms package
|
|
423
|
-
*/
|
|
424
|
-
async function packageCommand() {
|
|
425
|
-
try {
|
|
426
|
-
const workspacePath = process.cwd();
|
|
427
|
-
console.log(`🔍 检查项目目录: ${workspacePath}`);
|
|
428
|
-
await ensureValidKuaiJSProject(workspacePath);
|
|
429
|
-
console.log("📦 开始打包生产构建并加密 bundle...");
|
|
430
|
-
const encryptedPath = await (0, packager_1.packageProject)(workspacePath);
|
|
431
|
-
console.log(`✅ 打包完成: ${encryptedPath}`);
|
|
432
|
-
}
|
|
433
|
-
catch (error) {
|
|
434
|
-
console.error("❌ 打包失败:", error instanceof Error ? error.message : error);
|
|
435
|
-
process.exit(1);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
/**
|
|
439
|
-
* run 命令处理函数
|
|
440
|
-
* @param options 命令选项
|
|
441
|
-
* @returns 请求完成后退出
|
|
442
|
-
* @example
|
|
443
|
-
* ms run --ip 192.168.1.100 --port 9800
|
|
444
|
-
*/
|
|
445
|
-
async function runCommand(options) {
|
|
446
|
-
try {
|
|
447
|
-
const workspacePath = process.cwd();
|
|
448
|
-
await ensureValidKuaiJSProject(workspacePath);
|
|
449
|
-
await (0, project_1.runOnDevice)(options);
|
|
450
|
-
}
|
|
451
|
-
catch (error) {
|
|
452
|
-
console.error("❌ 运行失败:", error instanceof Error ? error.message : error);
|
|
453
|
-
process.exit(1);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
/**
|
|
457
|
-
* runUI 命令处理函数
|
|
458
|
-
* @param options 命令选项
|
|
459
|
-
* @returns 请求完成后退出
|
|
460
|
-
* @example
|
|
461
|
-
* ms run-ui --ip 192.168.1.100 --port 9800
|
|
462
|
-
*/
|
|
463
|
-
async function runUICommand(options) {
|
|
464
|
-
try {
|
|
465
|
-
const workspacePath = process.cwd();
|
|
466
|
-
await ensureValidKuaiJSProject(workspacePath);
|
|
467
|
-
await (0, project_1.runUIOnDevice)(options);
|
|
468
|
-
}
|
|
469
|
-
catch (error) {
|
|
470
|
-
console.error("❌ UI 预览失败:", error instanceof Error ? error.message : error);
|
|
471
|
-
process.exit(1);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* stop 命令处理函数
|
|
476
|
-
* @param options 命令选项
|
|
477
|
-
* @returns 请求完成后退出
|
|
478
|
-
* @example
|
|
479
|
-
* ms stop --ip 192.168.1.100 --port 9800
|
|
480
|
-
*/
|
|
481
|
-
async function stopCommand(options) {
|
|
482
|
-
try {
|
|
483
|
-
await (0, project_1.stopOnDevice)(options);
|
|
484
|
-
}
|
|
485
|
-
catch (error) {
|
|
486
|
-
console.error("❌ 停止失败:", error instanceof Error ? error.message : error);
|
|
487
|
-
process.exit(1);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* screenshot 命令处理函数
|
|
492
|
-
* @param options 命令选项
|
|
493
|
-
* @returns 请求完成后退出
|
|
494
|
-
* @example
|
|
495
|
-
* ms screenshot --ip 192.168.1.100 --port 9800 --output ./screen.jpg
|
|
496
|
-
*/
|
|
497
|
-
async function screenshotCommand(options) {
|
|
498
|
-
try {
|
|
499
|
-
const format = options.format ?? "file";
|
|
500
|
-
if (format === "base64") {
|
|
501
|
-
const base64 = await (0, project_1.getScreenshotBase64OnDevice)(options);
|
|
502
|
-
console.log(base64);
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const image = await (0, project_1.getScreenshotOnDevice)(options);
|
|
506
|
-
const outputPath = options.output?.trim()
|
|
507
|
-
? path.resolve(options.output.trim())
|
|
508
|
-
: path.resolve(process.cwd(), "screenshot.jpg");
|
|
509
|
-
await fsExtra.ensureDir(path.dirname(outputPath));
|
|
510
|
-
await fsExtra.writeFile(outputPath, image);
|
|
511
|
-
console.log(`✅ 截图成功: ${outputPath} (${image.length} bytes)`);
|
|
512
|
-
}
|
|
513
|
-
catch (error) {
|
|
514
|
-
console.error("❌ 截图失败:", error instanceof Error ? error.message : error);
|
|
515
|
-
process.exit(1);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
/**
|
|
519
|
-
* source 命令处理函数
|
|
520
|
-
* @param options 命令选项
|
|
521
|
-
* @returns 请求完成后退出
|
|
522
|
-
* @example
|
|
523
|
-
* ms source --ip 192.168.1.100 --port 9800 --max-depth 50 --timeout 120 --mode 1
|
|
524
|
-
*/
|
|
525
|
-
async function sourceCommand(options) {
|
|
526
|
-
try {
|
|
527
|
-
const maxDepthText = (options.maxDepth ?? "50").trim();
|
|
528
|
-
const timeoutText = (options.timeout ?? "120").trim();
|
|
529
|
-
const modeText = (options.mode ?? "1").trim();
|
|
530
|
-
const maxDepth = Number.parseInt(maxDepthText, 10);
|
|
531
|
-
const timeout = Number.parseInt(timeoutText, 10);
|
|
532
|
-
const mode = Number.parseInt(modeText, 10);
|
|
533
|
-
if (!Number.isInteger(maxDepth) || maxDepth < 1) {
|
|
534
|
-
throw new Error(`无效 max-depth: ${maxDepthText}`);
|
|
535
|
-
}
|
|
536
|
-
if (!Number.isInteger(timeout) || timeout < 1) {
|
|
537
|
-
throw new Error(`无效 timeout: ${timeoutText}`);
|
|
538
|
-
}
|
|
539
|
-
if (!Number.isInteger(mode) || (mode !== 1 && mode !== 2 && mode !== 3)) {
|
|
540
|
-
throw new Error(`无效 mode: ${modeText}`);
|
|
541
|
-
}
|
|
542
|
-
const source = await (0, project_1.getSourceOnDevice)(options, maxDepth, timeout, mode);
|
|
543
|
-
if (options.output?.trim()) {
|
|
544
|
-
const outputPath = path.resolve(options.output.trim());
|
|
545
|
-
await fsExtra.ensureDir(path.dirname(outputPath));
|
|
546
|
-
await fsExtra.writeFile(outputPath, source, "utf8");
|
|
547
|
-
console.log(`✅ 节点已保存: ${outputPath}`);
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
console.log(source);
|
|
551
|
-
}
|
|
552
|
-
catch (error) {
|
|
553
|
-
console.error("❌ 获取节点失败:", error instanceof Error ? error.message : error);
|
|
554
|
-
process.exit(1);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* logs 命令处理函数
|
|
559
|
-
* @param options 命令选项
|
|
560
|
-
* @returns 请求完成后退出
|
|
561
|
-
* @example
|
|
562
|
-
* ms logs --ip 192.168.1.100 --port 9800 --limit 200
|
|
563
|
-
*/
|
|
564
|
-
async function logsCommand(options) {
|
|
565
|
-
try {
|
|
566
|
-
const limit = parseCliInteger(options.limit, 200, "limit", 1, 5000);
|
|
567
|
-
const format = parseCliChoice(options.format, "text", ["text", "json"], "format");
|
|
568
|
-
const result = await (0, project_1.getCurrentLogLinesOnDevice)(options, limit);
|
|
569
|
-
const text = format === "json"
|
|
570
|
-
? (0, tool_utils_1.formatRuntimeJsonText)(result)
|
|
571
|
-
: result.lines.length > 0
|
|
572
|
-
? result.lines.join("\n")
|
|
573
|
-
: "无日志";
|
|
574
|
-
await writeOrPrintText(text, options.output, "日志已保存");
|
|
575
|
-
if (format === "text" && result.hasMore && !options.output?.trim()) {
|
|
576
|
-
console.error(`还有更早日志,可增大 --limit,当前最多 ${limit} 行。`);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
catch (error) {
|
|
580
|
-
console.error("❌ 获取日志失败:", error instanceof Error ? error.message : error);
|
|
581
|
-
process.exit(1);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* docs paths 命令处理函数
|
|
586
|
-
* @param options 命令选项
|
|
587
|
-
* @returns 请求完成后退出
|
|
588
|
-
* @example
|
|
589
|
-
* ms docs paths
|
|
590
|
-
*/
|
|
591
|
-
async function docsPathsCommand(options) {
|
|
592
|
-
try {
|
|
593
|
-
await writeOrPrintText(formatCliDocsPathsText(), options.output, "文档路径已保存");
|
|
594
|
-
}
|
|
595
|
-
catch (error) {
|
|
596
|
-
console.error("❌ 输出文档路径失败:", error instanceof Error ? error.message : error);
|
|
597
|
-
process.exit(1);
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* http-call 命令处理函数
|
|
602
|
-
* @param options 命令选项
|
|
603
|
-
* @returns 请求完成后退出
|
|
604
|
-
* @example
|
|
605
|
-
* ms http-call --ip 192.168.1.100 --method GET --path /api/status
|
|
606
|
-
*/
|
|
607
|
-
async function httpCallCommand(options) {
|
|
608
|
-
try {
|
|
609
|
-
const target = parseDeviceHttpTarget(options);
|
|
610
|
-
const method = parseHttpApiMethodOption(options.method);
|
|
611
|
-
const apiPath = normalizeCliHttpApiPath(options.path ?? "");
|
|
612
|
-
const entry = await (0, httpapi_docs_service_1.findHttpApiDocEntry)(apiPath, method);
|
|
613
|
-
if (!entry) {
|
|
614
|
-
throw new Error(`HTTP API 文档中不存在该接口: ${method} ${apiPath}。请先读取 docs paths 返回的 HTTP API 文档路径。`);
|
|
615
|
-
}
|
|
616
|
-
const query = parseJsonRecordOption(options.query, "query");
|
|
617
|
-
const body = parseJsonBodyOption(options.body);
|
|
618
|
-
if (method === "GET" && body !== undefined) {
|
|
619
|
-
throw new Error("GET 接口不能传 body,请使用 --query。");
|
|
620
|
-
}
|
|
621
|
-
const responseFormat = parseCliChoice(options.responseFormat, "json", ["json", "text"], "response-format");
|
|
622
|
-
const timeoutMs = parseCliInteger(options.timeoutMs, 30000, "timeout-ms", 1000, 600000);
|
|
623
|
-
const url = new URL(`http://${target.ip}:${target.port}${apiPath}`);
|
|
624
|
-
appendCliQueryParams(url, query);
|
|
625
|
-
const controller = new AbortController();
|
|
626
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
627
|
-
try {
|
|
628
|
-
const { headers, requestBody } = createCliHttpRequestBody(body);
|
|
629
|
-
const response = await fetch(url.toString(), {
|
|
630
|
-
method,
|
|
631
|
-
signal: controller.signal,
|
|
632
|
-
headers,
|
|
633
|
-
body: requestBody,
|
|
634
|
-
});
|
|
635
|
-
const responseText = await readCliHttpApiResponseText(response, responseFormat);
|
|
636
|
-
const text = [
|
|
637
|
-
`HTTP API 调用${response.ok ? "成功" : "失败"}: ${method} ${apiPath}`,
|
|
638
|
-
`设备: ${target.label}`,
|
|
639
|
-
`文档: ${entry.title} (lines ${entry.startLine}-${entry.endLine})`,
|
|
640
|
-
`状态码: ${response.status}`,
|
|
641
|
-
"",
|
|
642
|
-
responseText,
|
|
643
|
-
].join("\n");
|
|
644
|
-
await writeOrPrintText(text, options.output, "HTTP API 响应已保存");
|
|
645
|
-
if (!response.ok) {
|
|
646
|
-
process.exit(1);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
finally {
|
|
650
|
-
clearTimeout(timeout);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
catch (error) {
|
|
654
|
-
const message = error instanceof Error && error.name === "AbortError"
|
|
655
|
-
? "请求超时"
|
|
656
|
-
: error instanceof Error
|
|
657
|
-
? error.message
|
|
658
|
-
: String(error);
|
|
659
|
-
console.error("❌ HTTP API 调用失败:", message);
|
|
660
|
-
process.exit(1);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* ocr 命令处理函数
|
|
665
|
-
* @param options 命令选项
|
|
666
|
-
* @returns 请求完成后退出
|
|
667
|
-
* @example
|
|
668
|
-
* ms ocr --ip 192.168.1.100 --mode recognize
|
|
669
|
-
*/
|
|
670
|
-
async function ocrCommand(options) {
|
|
671
|
-
try {
|
|
672
|
-
const target = parseDeviceHttpTarget(options);
|
|
673
|
-
const engine = parseCliChoice(options.engine, "appleocr", ["appleocr", "paddleocr"], "engine");
|
|
674
|
-
const mode = parseCliChoice(options.mode, "recognize", ["recognize", "numbers", "findText"], "mode");
|
|
675
|
-
const input = options.input?.trim() || "screen";
|
|
676
|
-
const x = parseCliInteger(options.x, 0, "x", 0);
|
|
677
|
-
const y = parseCliInteger(options.y, 0, "y", 0);
|
|
678
|
-
const ex = parseCliInteger(options.ex, 0, "ex", 0);
|
|
679
|
-
const ey = parseCliInteger(options.ey, 0, "ey", 0);
|
|
680
|
-
const textItems = [
|
|
681
|
-
...(parseStringArrayOption(options.texts, "texts") ?? []),
|
|
682
|
-
...(options.text ?? []),
|
|
683
|
-
];
|
|
684
|
-
const texts = textItems.length > 0 ? textItems : undefined;
|
|
685
|
-
const languages = parseAppleOcrLanguages(options.languages);
|
|
686
|
-
const confidenceThreshold = parseCliNumber(options.confidenceThreshold, 0.6, "confidence-threshold", 0, 1);
|
|
687
|
-
const paddleModel = options.paddleModel === undefined
|
|
688
|
-
? undefined
|
|
689
|
-
: parseCliChoice(options.paddleModel, ocr_tools_1.PADDLE_OCR_MODELS[0], ocr_tools_1.PADDLE_OCR_MODELS, "paddle-model");
|
|
690
|
-
const maxSideLen = options.maxSideLen === undefined
|
|
691
|
-
? undefined
|
|
692
|
-
: parseCliInteger(options.maxSideLen, 0, "max-side-len", 0);
|
|
693
|
-
const useGpu = options.useGpu === true ? true : undefined;
|
|
694
|
-
const timeoutMs = parseCliInteger(options.timeoutMs, 60000, "timeout-ms", 1000, 600000);
|
|
695
|
-
const format = parseCliChoice(options.format, "summary", ["summary", "json"], "format");
|
|
696
|
-
const result = await (0, ocr_tools_1.runOcrRecognitionOnDevice)(target, {
|
|
697
|
-
engine,
|
|
698
|
-
mode,
|
|
699
|
-
input,
|
|
700
|
-
x,
|
|
701
|
-
y,
|
|
702
|
-
ex,
|
|
703
|
-
ey,
|
|
704
|
-
texts,
|
|
705
|
-
languages,
|
|
706
|
-
confidenceThreshold,
|
|
707
|
-
exactMatch: options.exactMatch === true,
|
|
708
|
-
paddleModel,
|
|
709
|
-
maxSideLen,
|
|
710
|
-
useGpu,
|
|
711
|
-
outputPath: format === "summary" ? options.output : undefined,
|
|
712
|
-
timeoutMs,
|
|
713
|
-
});
|
|
714
|
-
const text = format === "json" ? (0, tool_utils_1.formatRuntimeJsonText)(result.body) : result.text;
|
|
715
|
-
if (format === "json") {
|
|
716
|
-
await writeOrPrintText(text, options.output, "OCR 结果已保存");
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
console.log([
|
|
720
|
-
text,
|
|
721
|
-
"",
|
|
722
|
-
`device: ${target.label}`,
|
|
723
|
-
`engine: ${engine}`,
|
|
724
|
-
`mode: ${mode}`,
|
|
725
|
-
`input: ${input}`,
|
|
726
|
-
`region: x=${x}, y=${y}, ex=${ex}, ey=${ey}`,
|
|
727
|
-
...(engine === "paddleocr" && paddleModel
|
|
728
|
-
? [`paddleModel: ${paddleModel}`]
|
|
729
|
-
: []),
|
|
730
|
-
...(engine === "paddleocr" && maxSideLen !== undefined
|
|
731
|
-
? [`maxSideLen: ${maxSideLen}`]
|
|
732
|
-
: []),
|
|
733
|
-
`status: ${result.status}`,
|
|
734
|
-
].join("\n"));
|
|
735
|
-
}
|
|
736
|
-
if (result.body.success === false) {
|
|
737
|
-
process.exit(1);
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
catch (error) {
|
|
741
|
-
console.error("❌ OCR 执行失败:", error instanceof Error ? error.message : error);
|
|
742
|
-
process.exit(1);
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
/**
|
|
746
|
-
* image-crop 命令处理函数
|
|
747
|
-
* @param options 命令选项
|
|
748
|
-
* @returns 请求完成后退出
|
|
749
|
-
* @example
|
|
750
|
-
* ms image-crop --image ./screen.jpg --x 0 --y 0 --width 100 --height 100
|
|
751
|
-
*/
|
|
752
|
-
async function imageCropCommand(options) {
|
|
753
|
-
try {
|
|
754
|
-
const imagePath = options.image?.trim();
|
|
755
|
-
if (!imagePath) {
|
|
756
|
-
throw new Error("请通过 --image 指定输入图片路径。");
|
|
757
|
-
}
|
|
758
|
-
const x = parseCliInteger(options.x, 0, "x", 0);
|
|
759
|
-
const y = parseCliInteger(options.y, 0, "y", 0);
|
|
760
|
-
const width = parseCliInteger(options.width, 1, "width", 1);
|
|
761
|
-
const height = parseCliInteger(options.height, 1, "height", 1);
|
|
762
|
-
const text = await (0, image_tools_1.cropLocalImage)(imagePath, x, y, width, height, options.output);
|
|
763
|
-
console.log(text);
|
|
764
|
-
}
|
|
765
|
-
catch (error) {
|
|
766
|
-
console.error("❌ 图片裁切失败:", error instanceof Error ? error.message : error);
|
|
767
|
-
process.exit(1);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
/**
|
|
771
|
-
* screen-crop 命令处理函数
|
|
772
|
-
* @param options 命令选项
|
|
773
|
-
* @returns 请求完成后退出
|
|
774
|
-
* @example
|
|
775
|
-
* ms screen-crop -i 192.168.1.100 --x 0 --y 0 --width 100 --height 100
|
|
776
|
-
*/
|
|
777
|
-
async function screenCropCommand(options) {
|
|
778
|
-
try {
|
|
779
|
-
const target = parseDeviceHttpTarget(options);
|
|
780
|
-
const x = parseCliInteger(options.x, 0, "x", 0);
|
|
781
|
-
const y = parseCliInteger(options.y, 0, "y", 0);
|
|
782
|
-
const width = parseCliInteger(options.width, 1, "width", 1);
|
|
783
|
-
const height = parseCliInteger(options.height, 1, "height", 1);
|
|
784
|
-
const text = await (0, image_tools_1.cropDeviceScreen)(target, x, y, width, height, options.output);
|
|
785
|
-
console.log(text);
|
|
786
|
-
}
|
|
787
|
-
catch (error) {
|
|
788
|
-
console.error("❌ 屏幕裁图失败:", error instanceof Error ? error.message : error);
|
|
789
|
-
process.exit(1);
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* image-pick-color 命令处理函数
|
|
794
|
-
* @param options 命令选项
|
|
795
|
-
* @returns 请求完成后退出
|
|
796
|
-
* @example
|
|
797
|
-
* ms image-pick-color --image ./screen.jpg --x 10 --y 10
|
|
798
|
-
*/
|
|
799
|
-
async function imagePickColorCommand(options) {
|
|
800
|
-
try {
|
|
801
|
-
const imagePath = options.image?.trim();
|
|
802
|
-
if (!imagePath) {
|
|
803
|
-
throw new Error("请通过 --image 指定输入图片路径。");
|
|
804
|
-
}
|
|
805
|
-
const x = parseCliInteger(options.x, 0, "x", 0);
|
|
806
|
-
const y = parseCliInteger(options.y, 0, "y", 0);
|
|
807
|
-
const radius = parseCliInteger(options.radius, 0, "radius", 0, 20);
|
|
808
|
-
const text = await (0, image_tools_1.pickLocalImageColor)(imagePath, x, y, radius);
|
|
809
|
-
console.log(text);
|
|
810
|
-
}
|
|
811
|
-
catch (error) {
|
|
812
|
-
console.error("❌ 图片取色失败:", error instanceof Error ? error.message : error);
|
|
813
|
-
process.exit(1);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
/**
|
|
817
|
-
* screen-pick-color 命令处理函数
|
|
818
|
-
* @param options 命令选项
|
|
819
|
-
* @returns 请求完成后退出
|
|
820
|
-
* @example
|
|
821
|
-
* ms screen-pick-color -i 192.168.1.100 --x 10 --y 10
|
|
822
|
-
*/
|
|
823
|
-
async function screenPickColorCommand(options) {
|
|
824
|
-
try {
|
|
825
|
-
const target = parseDeviceHttpTarget(options);
|
|
826
|
-
const x = parseCliInteger(options.x, 0, "x", 0);
|
|
827
|
-
const y = parseCliInteger(options.y, 0, "y", 0);
|
|
828
|
-
const radius = parseCliInteger(options.radius, 0, "radius", 0, 20);
|
|
829
|
-
const text = await (0, image_tools_1.pickDeviceScreenColor)(target, x, y, radius);
|
|
830
|
-
console.log(text);
|
|
831
|
-
}
|
|
832
|
-
catch (error) {
|
|
833
|
-
console.error("❌ 屏幕取色失败:", error instanceof Error ? error.message : error);
|
|
834
|
-
process.exit(1);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
/**
|
|
838
|
-
* image-transparent 命令处理函数
|
|
839
|
-
* @param options 命令选项
|
|
840
|
-
* @returns 请求完成后退出
|
|
841
|
-
* @example
|
|
842
|
-
* ms image-transparent --image ./button.png --transparent-color "#ffffff"
|
|
843
|
-
*/
|
|
844
|
-
async function imageTransparentCommand(options) {
|
|
845
|
-
try {
|
|
846
|
-
const imagePath = options.image?.trim();
|
|
847
|
-
if (!imagePath) {
|
|
848
|
-
throw new Error("请通过 --image 指定输入图片路径。");
|
|
849
|
-
}
|
|
850
|
-
const sampleCorner = parseCliChoice(options.sampleCorner, "topLeft", ["topLeft", "topRight", "bottomLeft", "bottomRight"], "sample-corner");
|
|
851
|
-
const tolerance = parseCliInteger(options.tolerance, 24, "tolerance", 0, 441);
|
|
852
|
-
const text = await (0, image_tools_1.makeLocalImageTransparent)(imagePath, options.output, options.transparentColor, sampleCorner, tolerance);
|
|
853
|
-
console.log(text);
|
|
854
|
-
}
|
|
855
|
-
catch (error) {
|
|
856
|
-
console.error("❌ 透明图制作失败:", error instanceof Error ? error.message : error);
|
|
857
|
-
process.exit(1);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* ws-start 命令处理函数
|
|
862
|
-
* @param options 命令选项
|
|
863
|
-
* @returns 常驻直到收到退出信号
|
|
864
|
-
* @example
|
|
865
|
-
* ms ws-start --ws-port 31111
|
|
866
|
-
*/
|
|
867
|
-
async function wsStartCommand(options) {
|
|
868
|
-
try {
|
|
869
|
-
const wsPortText = (options.wsPort ?? "31111").trim();
|
|
870
|
-
const wsPort = Number.parseInt(wsPortText, 10);
|
|
871
|
-
if (!Number.isInteger(wsPort) || wsPort < 1 || wsPort > 65535) {
|
|
872
|
-
throw new Error(`无效 ws 端口: ${wsPortText}`);
|
|
873
|
-
}
|
|
874
|
-
const ws = ws_manager_1.WSManager.get();
|
|
875
|
-
await ws.start(wsPort);
|
|
876
|
-
/**
|
|
877
|
-
* 写入 WS 运行状态到临时文件,供 ws-status 命令跨进程读取
|
|
878
|
-
*/
|
|
879
|
-
const writeStatus = async () => {
|
|
880
|
-
const payload = {
|
|
881
|
-
pid: process.pid,
|
|
882
|
-
port: wsPort,
|
|
883
|
-
address: ws.getAddress(),
|
|
884
|
-
startedAt: Date.now(),
|
|
885
|
-
connected: ws.isClientConnected(),
|
|
886
|
-
clientIp: ws.getConnectedClientIp(),
|
|
887
|
-
};
|
|
888
|
-
await fsExtra.writeJSON(WS_PID_FILE, payload);
|
|
889
|
-
};
|
|
890
|
-
await writeStatus();
|
|
891
|
-
const statusTimer = setInterval(() => {
|
|
892
|
-
writeStatus().catch(() => {
|
|
893
|
-
// ignore periodic write errors
|
|
894
|
-
});
|
|
895
|
-
}, 1000);
|
|
896
|
-
await fsExtra.writeJSON(WS_PID_FILE, {
|
|
897
|
-
pid: process.pid,
|
|
898
|
-
port: wsPort,
|
|
899
|
-
address: ws.getAddress(),
|
|
900
|
-
startedAt: Date.now(),
|
|
901
|
-
connected: ws.isClientConnected(),
|
|
902
|
-
clientIp: ws.getConnectedClientIp(),
|
|
903
|
-
});
|
|
904
|
-
await new Promise((resolve) => {
|
|
905
|
-
const shutdown = async () => {
|
|
906
|
-
process.off("SIGINT", shutdown);
|
|
907
|
-
process.off("SIGTERM", shutdown);
|
|
908
|
-
clearInterval(statusTimer);
|
|
909
|
-
await ws.stop();
|
|
910
|
-
await fsExtra.remove(WS_PID_FILE);
|
|
911
|
-
resolve();
|
|
912
|
-
};
|
|
913
|
-
process.on("SIGINT", shutdown);
|
|
914
|
-
process.on("SIGTERM", shutdown);
|
|
915
|
-
});
|
|
916
|
-
}
|
|
917
|
-
catch (error) {
|
|
918
|
-
console.error("❌ WS 服务启动失败:", error instanceof Error ? error.message : error);
|
|
919
|
-
process.exit(1);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* ws-stop 命令处理函数
|
|
924
|
-
* @returns 请求完成后退出
|
|
925
|
-
* @example
|
|
926
|
-
* ms ws-stop
|
|
927
|
-
*/
|
|
928
|
-
async function wsStopCommand() {
|
|
929
|
-
try {
|
|
930
|
-
if (!(await fsExtra.pathExists(WS_PID_FILE))) {
|
|
931
|
-
console.log("WS 服务未运行");
|
|
932
|
-
return;
|
|
933
|
-
}
|
|
934
|
-
const info = await fsExtra.readJSON(WS_PID_FILE);
|
|
935
|
-
const pid = Number(info.pid);
|
|
936
|
-
if (!Number.isInteger(pid) || pid <= 0) {
|
|
937
|
-
await fsExtra.remove(WS_PID_FILE);
|
|
938
|
-
throw new Error("WS PID 文件损坏");
|
|
939
|
-
}
|
|
940
|
-
try {
|
|
941
|
-
process.kill(pid, "SIGTERM");
|
|
942
|
-
console.log(`✅ 已发送停止信号到 WS 进程: ${pid}`);
|
|
943
|
-
}
|
|
944
|
-
catch {
|
|
945
|
-
console.log("WS 进程不存在,清理 PID 文件");
|
|
946
|
-
}
|
|
947
|
-
await fsExtra.remove(WS_PID_FILE);
|
|
948
|
-
}
|
|
949
|
-
catch (error) {
|
|
950
|
-
console.error("❌ WS 服务停止失败:", error instanceof Error ? error.message : error);
|
|
951
|
-
process.exit(1);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
|
-
* ws-status 命令处理函数
|
|
956
|
-
* @returns 请求完成后退出
|
|
957
|
-
* @example
|
|
958
|
-
* ms ws-status
|
|
959
|
-
*/
|
|
960
|
-
async function wsStatusCommand() {
|
|
961
|
-
if (!(await fsExtra.pathExists(WS_PID_FILE))) {
|
|
962
|
-
console.log(JSON.stringify({
|
|
963
|
-
status: "stopped",
|
|
964
|
-
running: false,
|
|
965
|
-
pid: null,
|
|
966
|
-
address: null,
|
|
967
|
-
connected: false,
|
|
968
|
-
clientIp: null,
|
|
969
|
-
}));
|
|
970
|
-
return;
|
|
971
|
-
}
|
|
972
|
-
const info = await fsExtra.readJSON(WS_PID_FILE);
|
|
973
|
-
const pid = Number(info.pid);
|
|
974
|
-
const address = String(info.address || "unknown");
|
|
975
|
-
const connected = Boolean(info.connected);
|
|
976
|
-
const clientIp = info.clientIp === null || info.clientIp === undefined
|
|
977
|
-
? null
|
|
978
|
-
: String(info.clientIp);
|
|
979
|
-
let running = false;
|
|
980
|
-
try {
|
|
981
|
-
process.kill(pid, 0);
|
|
982
|
-
running = true;
|
|
983
|
-
}
|
|
984
|
-
catch {
|
|
985
|
-
running = false;
|
|
986
|
-
}
|
|
987
|
-
if (!running) {
|
|
988
|
-
await fsExtra.remove(WS_PID_FILE);
|
|
989
|
-
console.log(JSON.stringify({
|
|
990
|
-
status: "stopped",
|
|
991
|
-
running: false,
|
|
992
|
-
pid: null,
|
|
993
|
-
address: null,
|
|
994
|
-
connected: false,
|
|
995
|
-
clientIp: null,
|
|
996
|
-
}));
|
|
997
|
-
return;
|
|
998
|
-
}
|
|
999
|
-
console.log(JSON.stringify({
|
|
1000
|
-
status: "running",
|
|
1001
|
-
running: true,
|
|
1002
|
-
pid,
|
|
1003
|
-
address,
|
|
1004
|
-
connected,
|
|
1005
|
-
clientIp: connected ? clientIp || "unknown" : null,
|
|
1006
|
-
}));
|
|
1007
|
-
}
|
|
1008
|
-
/**
|
|
1009
|
-
* 生成顶层 help 的详细命令参数说明
|
|
1010
|
-
* @returns 返回格式化后的帮助文本
|
|
1011
|
-
* @example
|
|
1012
|
-
* renderDetailedCommandHelp()
|
|
1013
|
-
*/
|
|
1014
|
-
function renderDetailedCommandHelp() {
|
|
1015
|
-
const lines = [];
|
|
1016
|
-
lines.push("");
|
|
1017
|
-
lines.push("详细命令参数:");
|
|
1018
|
-
const commands = commander_1.program.commands.filter((cmd) => cmd.name() !== "help");
|
|
1019
|
-
for (const cmd of commands) {
|
|
1020
|
-
lines.push(` ${cmd.name()}:`);
|
|
1021
|
-
if (cmd.options.length === 0) {
|
|
1022
|
-
lines.push(" (无额外参数)");
|
|
1023
|
-
continue;
|
|
1024
|
-
}
|
|
1025
|
-
for (const opt of cmd.options) {
|
|
1026
|
-
const defaultText = opt.defaultValue !== undefined && opt.defaultValue !== null
|
|
1027
|
-
? ` (默认: ${String(opt.defaultValue)})`
|
|
1028
|
-
: "";
|
|
1029
|
-
const desc = opt.description
|
|
1030
|
-
? ` ${opt.description}${defaultText}`
|
|
1031
|
-
: defaultText;
|
|
1032
|
-
lines.push(` ${opt.flags}${desc}`);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
lines.push("");
|
|
1036
|
-
return lines.join("\n");
|
|
1037
|
-
}
|
|
1038
|
-
// 配置 CLI 程序
|
|
1039
|
-
commander_1.program.name("ms-cli").description("快点JS 构建工具").version(version_1.PACKAGE_VERSION);
|
|
1040
|
-
commander_1.program.addHelpText("after", renderDetailedCommandHelp);
|
|
1041
|
-
// 构建命令
|
|
1042
|
-
commander_1.program
|
|
1043
|
-
.command("build")
|
|
1044
|
-
.description("构建 快点JS 项目")
|
|
1045
|
-
.option("-d, --dev", "开发模式构建 (包含 source map)")
|
|
1046
|
-
.action(buildCommand);
|
|
1047
|
-
// 打包命令
|
|
1048
|
-
commander_1.program
|
|
1049
|
-
.command("package")
|
|
1050
|
-
.description("生产构建并加密 ms.msbundle")
|
|
1051
|
-
.action(packageCommand);
|
|
1052
|
-
// 运行命令
|
|
1053
|
-
commander_1.program
|
|
1054
|
-
.command("run")
|
|
1055
|
-
.description("在设备上运行项目")
|
|
1056
|
-
.option("-i, --ip <ip>", "设备 IP 地址(transport=http 时必填)")
|
|
1057
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1058
|
-
.option("-t, --transport <transport>", "传输方式: http|ws", "http")
|
|
1059
|
-
.option("--ws-port <wsPort>", "WS 服务端口", "31111")
|
|
1060
|
-
.option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
|
|
1061
|
-
.action(runCommand);
|
|
1062
|
-
// UI 预览命令
|
|
1063
|
-
commander_1.program
|
|
1064
|
-
.command("run-ui")
|
|
1065
|
-
.description("在设备上预览 UI")
|
|
1066
|
-
.option("-i, --ip <ip>", "设备 IP 地址(transport=http 时必填)")
|
|
1067
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1068
|
-
.option("-t, --transport <transport>", "传输方式: http|ws", "http")
|
|
1069
|
-
.option("--ws-port <wsPort>", "WS 服务端口", "31111")
|
|
1070
|
-
.option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
|
|
1071
|
-
.action(runUICommand);
|
|
1072
|
-
// 停止命令
|
|
1073
|
-
commander_1.program
|
|
1074
|
-
.command("stop")
|
|
1075
|
-
.description("停止设备上的项目")
|
|
1076
|
-
.option("-i, --ip <ip>", "设备 IP 地址(transport=http 时必填)")
|
|
1077
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1078
|
-
.option("-t, --transport <transport>", "传输方式: http|ws", "http")
|
|
1079
|
-
.option("--ws-port <wsPort>", "WS 服务端口", "31111")
|
|
1080
|
-
.option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
|
|
1081
|
-
.action(stopCommand);
|
|
1082
|
-
// 截图命令
|
|
1083
|
-
commander_1.program
|
|
1084
|
-
.command("screenshot")
|
|
1085
|
-
.description("获取设备截图(JPG)")
|
|
1086
|
-
.option("-i, --ip <ip>", "设备 IP 地址(transport=http 时必填)")
|
|
1087
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1088
|
-
.option("-t, --transport <transport>", "传输方式: http|ws", "http")
|
|
1089
|
-
.option("--ws-port <wsPort>", "WS 服务端口", "31111")
|
|
1090
|
-
.option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
|
|
1091
|
-
.option("--format <format>", "返回格式: file|base64", "file")
|
|
1092
|
-
.option("--output <output>", "截图输出文件路径(format=file 时生效)")
|
|
1093
|
-
.action(screenshotCommand);
|
|
1094
|
-
// 节点命令
|
|
1095
|
-
commander_1.program
|
|
1096
|
-
.command("source")
|
|
1097
|
-
.description("获取设备当前页面节点 XML")
|
|
1098
|
-
.option("-i, --ip <ip>", "设备 IP 地址(transport=http 时必填)")
|
|
1099
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1100
|
-
.option("-t, --transport <transport>", "传输方式: http|ws", "http")
|
|
1101
|
-
.option("--ws-port <wsPort>", "WS 服务端口", "31111")
|
|
1102
|
-
.option("--ws-wait-ms <wsWaitMs>", "WS 等待设备连接时间(毫秒)", "30000")
|
|
1103
|
-
.option("--max-depth <maxDepth>", "节点最大深度", "50")
|
|
1104
|
-
.option("--timeout <timeout>", "节点抓取超时秒数", "120")
|
|
1105
|
-
.option("--mode <mode>", "节点抓取模式: 1=模式1, 2=模式2, 3=模式3", "1")
|
|
1106
|
-
.option("--output <output>", "节点 XML 输出文件路径(可选)")
|
|
1107
|
-
.action(sourceCommand);
|
|
1108
|
-
// 日志命令
|
|
1109
|
-
commander_1.program
|
|
1110
|
-
.command("logs")
|
|
1111
|
-
.description("获取设备当前日志最新行")
|
|
1112
|
-
.option("-i, --ip <ip>", "设备 IP 地址")
|
|
1113
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1114
|
-
.option("--limit <limit>", "读取最新日志行数,范围 1..5000", "200")
|
|
1115
|
-
.option("--format <format>", "返回格式: text|json", "text")
|
|
1116
|
-
.option("--output <output>", "日志输出文件路径(可选)")
|
|
1117
|
-
.action(logsCommand);
|
|
1118
|
-
// OCR 命令
|
|
1119
|
-
commander_1.program
|
|
1120
|
-
.command("ocr")
|
|
1121
|
-
.description("在设备上执行 OCR 识别")
|
|
1122
|
-
.option("-i, --ip <ip>", "设备 IP 地址")
|
|
1123
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1124
|
-
.option("--engine <engine>", "OCR 引擎: appleocr|paddleocr", "appleocr")
|
|
1125
|
-
.option("--mode <mode>", "OCR 模式: recognize|numbers|findText", "recognize")
|
|
1126
|
-
.option("--input <input>", '输入源,默认 "screen"', "screen")
|
|
1127
|
-
.option("--x <x>", "区域左上角 x", "0")
|
|
1128
|
-
.option("--y <y>", "区域左上角 y", "0")
|
|
1129
|
-
.option("--ex <ex>", "区域右下角 x;全屏可传 0", "0")
|
|
1130
|
-
.option("--ey <ey>", "区域右下角 y;全屏可传 0", "0")
|
|
1131
|
-
.option("--text <text>", "findText 文本,可重复", collectStringOption, [])
|
|
1132
|
-
.option("--texts <texts>", "findText 文本数组,支持 JSON 数组或逗号分隔")
|
|
1133
|
-
.option("--languages <languages>", "Apple OCR 语言,支持 JSON 数组或逗号分隔")
|
|
1134
|
-
.option("--exact-match", "findText 要求完整匹配文本")
|
|
1135
|
-
.option("--confidence-threshold <confidenceThreshold>", "PaddleOCR 置信度阈值", "0.6")
|
|
1136
|
-
.option("--paddle-model <paddleModel>", "仅 paddleocr:切换模型 ppocr-v6-tiny|ppocr-v6-small|ppocr-v5")
|
|
1137
|
-
.option("--max-side-len <maxSideLen>", "仅 paddleocr:检测最大边长,0 回落默认 768")
|
|
1138
|
-
.option("--use-gpu", "仅 paddleocr:切换模型时启用 GPU")
|
|
1139
|
-
.option("--timeout-ms <timeoutMs>", "请求超时时间(毫秒)", "60000")
|
|
1140
|
-
.option("--format <format>", "返回格式: summary|json", "summary")
|
|
1141
|
-
.option("--output <output>", "OCR 输出文件路径(可选)")
|
|
1142
|
-
.action(ocrCommand);
|
|
1143
|
-
// 本地图片裁切命令
|
|
1144
|
-
commander_1.program
|
|
1145
|
-
.command("image-crop")
|
|
1146
|
-
.description("裁切本地图片并输出 PNG")
|
|
1147
|
-
.requiredOption("--image <image>", "输入图片路径")
|
|
1148
|
-
.option("--x <x>", "裁切起点 x", "0")
|
|
1149
|
-
.option("--y <y>", "裁切起点 y", "0")
|
|
1150
|
-
.requiredOption("--width <width>", "裁切宽度")
|
|
1151
|
-
.requiredOption("--height <height>", "裁切高度")
|
|
1152
|
-
.option("--output <output>", "输出 PNG 路径(可选)")
|
|
1153
|
-
.action(imageCropCommand);
|
|
1154
|
-
// 设备截图裁切命令
|
|
1155
|
-
commander_1.program
|
|
1156
|
-
.command("screen-crop")
|
|
1157
|
-
.description("从设备截图中裁切 PNG")
|
|
1158
|
-
.option("-i, --ip <ip>", "设备 IP 地址")
|
|
1159
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1160
|
-
.option("--x <x>", "裁切起点 x", "0")
|
|
1161
|
-
.option("--y <y>", "裁切起点 y", "0")
|
|
1162
|
-
.requiredOption("--width <width>", "裁切宽度")
|
|
1163
|
-
.requiredOption("--height <height>", "裁切高度")
|
|
1164
|
-
.option("--output <output>", "输出 PNG 路径(可选)")
|
|
1165
|
-
.action(screenCropCommand);
|
|
1166
|
-
// 本地图片取色命令
|
|
1167
|
-
commander_1.program
|
|
1168
|
-
.command("image-pick-color")
|
|
1169
|
-
.description("读取本地图片指定坐标颜色")
|
|
1170
|
-
.requiredOption("--image <image>", "输入图片路径")
|
|
1171
|
-
.option("--x <x>", "目标坐标 x", "0")
|
|
1172
|
-
.option("--y <y>", "目标坐标 y", "0")
|
|
1173
|
-
.option("--radius <radius>", "采样半径,范围 0..20", "0")
|
|
1174
|
-
.action(imagePickColorCommand);
|
|
1175
|
-
// 设备截图取色命令
|
|
1176
|
-
commander_1.program
|
|
1177
|
-
.command("screen-pick-color")
|
|
1178
|
-
.description("读取设备截图指定坐标颜色")
|
|
1179
|
-
.option("-i, --ip <ip>", "设备 IP 地址")
|
|
1180
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1181
|
-
.option("--x <x>", "目标坐标 x", "0")
|
|
1182
|
-
.option("--y <y>", "目标坐标 y", "0")
|
|
1183
|
-
.option("--radius <radius>", "采样半径,范围 0..20", "0")
|
|
1184
|
-
.action(screenPickColorCommand);
|
|
1185
|
-
// 本地图片透明化命令
|
|
1186
|
-
commander_1.program
|
|
1187
|
-
.command("image-transparent")
|
|
1188
|
-
.description("将图片中接近指定颜色或角落背景色的像素设为透明")
|
|
1189
|
-
.requiredOption("--image <image>", "输入图片路径")
|
|
1190
|
-
.option("--output <output>", "输出 PNG 路径(可选)")
|
|
1191
|
-
.option("--transparent-color <transparentColor>", "透明化颜色,格式 #RRGGBB")
|
|
1192
|
-
.option("--sample-corner <sampleCorner>", "未传 transparent-color 时采样角落: topLeft|topRight|bottomLeft|bottomRight", "topLeft")
|
|
1193
|
-
.option("--tolerance <tolerance>", "RGB 欧氏距离容差,范围 0..441", "24")
|
|
1194
|
-
.action(imageTransparentCommand);
|
|
1195
|
-
// API 文档命令
|
|
1196
|
-
const docsCommand = commander_1.program
|
|
1197
|
-
.command("docs")
|
|
1198
|
-
.description("显示 KuaiJS 本地文档路径");
|
|
1199
|
-
docsCommand
|
|
1200
|
-
.command("paths")
|
|
1201
|
-
.description("显示 KuaiJS 本地文档路径")
|
|
1202
|
-
.option("--output <output>", "输出文件路径(可选)")
|
|
1203
|
-
.action(docsPathsCommand);
|
|
1204
|
-
// HTTP API 调用命令
|
|
1205
|
-
commander_1.program
|
|
1206
|
-
.command("http-call")
|
|
1207
|
-
.description("按 HTTP API 文档声明调用设备接口")
|
|
1208
|
-
.option("-i, --ip <ip>", "设备 IP 地址")
|
|
1209
|
-
.option("--port <port>", "设备端口", "9800")
|
|
1210
|
-
.requiredOption("--method <method>", "HTTP 方法: GET|POST")
|
|
1211
|
-
.requiredOption("--path <path>", "HTTP API 相对路径,例如 /api/status")
|
|
1212
|
-
.option("--query <query>", "JSON 对象 query 参数")
|
|
1213
|
-
.option("--body <body>", "JSON 请求体;普通字符串会作为 JSON 字符串提交")
|
|
1214
|
-
.option("--response-format <responseFormat>", "响应格式: json|text", "json")
|
|
1215
|
-
.option("--timeout-ms <timeoutMs>", "请求超时时间(毫秒)", "30000")
|
|
1216
|
-
.option("--output <output>", "响应输出文件路径(可选)")
|
|
1217
|
-
.action(httpCallCommand);
|
|
1218
|
-
// WS 服务启动命令
|
|
1219
|
-
commander_1.program
|
|
1220
|
-
.command("ws-start")
|
|
1221
|
-
.description("启动 CLI 内置 WS 服务")
|
|
1222
|
-
.option("--ws-port <wsPort>", "WS 服务端口", "31111")
|
|
1223
|
-
.action(wsStartCommand);
|
|
1224
|
-
// WS 服务停止命令
|
|
1225
|
-
commander_1.program
|
|
1226
|
-
.command("ws-stop")
|
|
1227
|
-
.description("停止 CLI 内置 WS 服务")
|
|
1228
|
-
.action(wsStopCommand);
|
|
1229
|
-
// WS 服务状态命令
|
|
1230
|
-
commander_1.program
|
|
1231
|
-
.command("ws-status")
|
|
1232
|
-
.description("查看 CLI 内置 WS 服务状态")
|
|
1233
|
-
.action(wsStatusCommand);
|
|
1234
|
-
// 解析命令行参数
|
|
1235
|
-
commander_1.program.parse();
|
|
2
|
+
"use strict";var R=exports&&exports.__createBinding||(Object.create?(function(e,t,r,o){o===void 0&&(o=r);var n=Object.getOwnPropertyDescriptor(t,r);(!n||("get"in n?!t.__esModule:n.writable||n.configurable))&&(n={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,o,n)}):(function(e,t,r,o){o===void 0&&(o=r),e[o]=t[r]})),J=exports&&exports.__setModuleDefault||(Object.create?(function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}):function(e,t){e.default=t}),N=exports&&exports.__importStar||(function(){var e=function(t){return e=Object.getOwnPropertyNames||function(r){var o=[];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(o[o.length]=n);return o},e(t)};return function(t){if(t&&t.__esModule)return t;var r={};if(t!=null)for(var o=e(t),n=0;n<o.length;n++)o[n]!=="default"&&R(r,t,o[n]);return J(r,t),r}})();Object.defineProperty(exports,"__esModule",{value:!0});const c=require("commander"),M=N(require("os")),m=N(require("path")),G=require("./build"),p=N(require("fs-extra")),h=require("./project"),W=require("./packager"),H=require("./ws-manager"),k=require("./version"),A=require("./mcp/docs-service"),L=require("./mcp/httpapi-docs-service"),j=require("./mcp/tool-utils"),B=require("./mcp/types"),x=require("./mcp/ocr-tools"),P=require("./mcp/image-tools"),d=m.join(M.tmpdir(),"ms-cli-ws-server.json");async function U(e){try{const t=m.join(e,"package.json"),r=m.join(e,"scripts"),[o,n]=await Promise.all([p.pathExists(t),p.pathExists(r)]);return o&&n}catch{return!1}}async function C(e){await U(e)||(console.error("\u274C \u9519\u8BEF: \u5F53\u524D\u76EE\u5F55\u4E0D\u662F\u6709\u6548\u7684 \u5FEB\u70B9JS \u9879\u76EE"),console.error("\u8BF7\u786E\u4FDD\u76EE\u5F55\u5305\u542B\u4EE5\u4E0B\u6587\u4EF6:"),console.error(" - package.json"),console.error(" - scripts/ \u76EE\u5F55"),process.exit(1))}function a(e,t,r,o,n){const i=(e??String(t)).trim(),s=Number.parseInt(i,10);if(!Number.isInteger(s)||s<o||n!==void 0&&s>n)throw new Error(`\u65E0\u6548 ${r}: ${i}\uFF0C\u5141\u8BB8\u8303\u56F4 ${o}${n===void 0?"+":`..${n}`}`);return s}function F(e,t,r,o,n){const i=(e??String(t)).trim(),s=Number.parseFloat(i);if(!Number.isFinite(s)||s<o||s>n)throw new Error(`\u65E0\u6548 ${r}: ${i}\uFF0C\u5141\u8BB8\u8303\u56F4 ${o}..${n}`);return s}function g(e,t,r,o){const n=(e??t).trim();if(!r.includes(n))throw new Error(`\u65E0\u6548 ${o}: ${n}\uFF0C\u53EF\u9009\u503C: ${r.join("|")}`);return n}function V(e){const t=e?.trim().toUpperCase();return g(t,"GET",["GET","POST"],"method")}function E(e){const t=e.ip?.trim();if(!t)throw new Error("\u8BF7\u901A\u8FC7 --ip \u6307\u5B9A\u8BBE\u5907 IP \u5730\u5740");const r=a(e.port,9800,"port",1,65535);return{ip:t,port:String(r),label:`${t}:${r}`}}async function $(e,t,r){if(t?.trim()){const o=m.resolve(t.trim());await p.ensureDir(m.dirname(o)),await p.writeFile(o,e,"utf8"),console.log(`\u2705 ${r}: ${o}`);return}console.log(e)}function z(){const e=["js","js_zh","python"];return["KuaiJS \u6587\u6863\u8DEF\u5F84:",`docsRoot: ${(0,A.getDocsRootDir)()}`,...e.map(t=>`${t}: ${(0,A.getDocsDirByLanguage)(t)} (${B.DOC_LANGUAGE_LABELS[t]})`),`httpApi: ${(0,L.getHttpApiDocPath)()}`,"\u8BF4\u660E: \u76F4\u63A5\u8BFB\u53D6\u4E0A\u8FF0 markdown \u8DEF\u5F84\u3002"].join(`
|
|
3
|
+
`)}function K(e,t){return t.push(e),t}function q(e,t){const r=e?.trim();if(!r)return;if(r.startsWith("[")){const n=JSON.parse(r);if(!Array.isArray(n)||!n.every(i=>typeof i=="string"&&i.trim()))throw new Error(`${t} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\u6570\u7EC4\u3002`);return n.map(i=>i.trim())}const o=r.split(",").map(n=>n.trim()).filter(n=>n.length>0);if(o.length===0)throw new Error(`${t} \u4E0D\u80FD\u4E3A\u7A7A\u3002`);return o}function X(e){const t=q(e,"languages");if(!t)return;const r=new Set(x.APPLE_OCR_LANGUAGES),o=t.filter(n=>!r.has(n));if(o.length>0)throw new Error(`\u65E0\u6548 languages: ${o.join(", ")}\uFF0C\u53EF\u9009\u503C: ${x.APPLE_OCR_LANGUAGES.join(", ")}`);return t}function Q(e,t){const r=e?.trim();if(!r)return;const o=JSON.parse(r);if(!o||typeof o!="object"||Array.isArray(o)||!Object.values(o).every(n=>typeof n=="string"||typeof n=="number"||typeof n=="boolean"||n===null))throw new Error(`${t} \u5FC5\u987B\u662F JSON \u5BF9\u8C61\uFF0C\u4E14\u503C\u53EA\u80FD\u662F string/number/boolean/null\u3002`);return o}function Y(e){const t=e?.trim();if(!(t===void 0||t.length===0))try{return JSON.parse(t)}catch{return e}}function Z(e){const t=e.trim();if(/^https?:\/\//i.test(t))throw new Error("path \u53EA\u80FD\u4F20 HTTP API \u76F8\u5BF9\u8DEF\u5F84\uFF0C\u4E0D\u80FD\u4F20\u5B8C\u6574 URL\u3002");if(!t.startsWith("/"))throw new Error("path \u5FC5\u987B\u4EE5 / \u5F00\u5934\uFF0C\u4F8B\u5982 /api/status\u3002");if(t.includes("?"))throw new Error("path \u4E0D\u80FD\u5305\u542B query string\uFF0C\u8BF7\u4F7F\u7528 --query \u4F20\u9012\u67E5\u8BE2\u53C2\u6570\u3002");return t}function tt(e,t){if(t)for(const[r,o]of Object.entries(t))o!==null&&e.searchParams.set(r,String(o))}function et(e){return e===void 0?{}:{headers:{"Content-Type":"application/json"},requestBody:JSON.stringify(e)}}async function ot(e,t){const r=await e.text();return t==="text"?r:(0,j.formatRuntimeJsonText)(JSON.parse(r))}async function rt(e){try{const t=process.cwd(),r=e.dev||!1;console.log(`\u{1F50D} \u68C0\u67E5\u9879\u76EE\u76EE\u5F55: ${t}`),await C(t),console.log(`\u{1F680} \u5F00\u59CB\u6784\u5EFA\u9879\u76EE (${r?"\u5F00\u53D1\u6A21\u5F0F":"\u751F\u4EA7\u6A21\u5F0F"})...`),await(0,G.buildAll)(r,t),console.log("\u2705 \u6784\u5EFA\u5B8C\u6210!")}catch(t){console.error("\u274C \u6784\u5EFA\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function nt(){try{const e=process.cwd();console.log(`\u{1F50D} \u68C0\u67E5\u9879\u76EE\u76EE\u5F55: ${e}`),await C(e),console.log("\u{1F4E6} \u5F00\u59CB\u6253\u5305\u751F\u4EA7\u6784\u5EFA\u5E76\u52A0\u5BC6 bundle...");const t=await(0,W.packageProject)(e);console.log(`\u2705 \u6253\u5305\u5B8C\u6210: ${t}`)}catch(e){console.error("\u274C \u6253\u5305\u5931\u8D25:",e instanceof Error?e.message:e),process.exit(1)}}async function it(e){try{const t=process.cwd();await C(t),await(0,h.runOnDevice)(e)}catch(t){console.error("\u274C \u8FD0\u884C\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function st(e){try{const t=process.cwd();await C(t),await(0,h.runUIOnDevice)(e)}catch(t){console.error("\u274C UI \u9884\u89C8\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function at(e){try{await(0,h.stopOnDevice)(e)}catch(t){console.error("\u274C \u505C\u6B62\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function ct(e){try{if((e.format??"file")==="base64"){const n=await(0,h.getScreenshotBase64OnDevice)(e);console.log(n);return}const r=await(0,h.getScreenshotOnDevice)(e),o=e.output?.trim()?m.resolve(e.output.trim()):m.resolve(process.cwd(),"screenshot.jpg");await p.ensureDir(m.dirname(o)),await p.writeFile(o,r),console.log(`\u2705 \u622A\u56FE\u6210\u529F: ${o} (${r.length} bytes)`)}catch(t){console.error("\u274C \u622A\u56FE\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function pt(e){try{const t=(e.maxDepth??"50").trim(),r=(e.timeout??"120").trim(),o=(e.mode??"1").trim(),n=Number.parseInt(t,10),i=Number.parseInt(r,10),s=Number.parseInt(o,10);if(!Number.isInteger(n)||n<1)throw new Error(`\u65E0\u6548 max-depth: ${t}`);if(!Number.isInteger(i)||i<1)throw new Error(`\u65E0\u6548 timeout: ${r}`);if(!Number.isInteger(s)||s!==1&&s!==2&&s!==3)throw new Error(`\u65E0\u6548 mode: ${o}`);const u=await(0,h.getSourceOnDevice)(e,n,i,s);if(e.output?.trim()){const l=m.resolve(e.output.trim());await p.ensureDir(m.dirname(l)),await p.writeFile(l,u,"utf8"),console.log(`\u2705 \u8282\u70B9\u5DF2\u4FDD\u5B58: ${l}`);return}console.log(u)}catch(t){console.error("\u274C \u83B7\u53D6\u8282\u70B9\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function ut(e){try{const t=a(e.limit,200,"limit",1,5e3),r=g(e.format,"text",["text","json"],"format"),o=await(0,h.getCurrentLogLinesOnDevice)(e,t),n=r==="json"?(0,j.formatRuntimeJsonText)(o):o.lines.length>0?o.lines.join(`
|
|
4
|
+
`):"\u65E0\u65E5\u5FD7";await $(n,e.output,"\u65E5\u5FD7\u5DF2\u4FDD\u5B58"),r==="text"&&o.hasMore&&!e.output?.trim()&&console.error(`\u8FD8\u6709\u66F4\u65E9\u65E5\u5FD7\uFF0C\u53EF\u589E\u5927 --limit\uFF0C\u5F53\u524D\u6700\u591A ${t} \u884C\u3002`)}catch(t){console.error("\u274C \u83B7\u53D6\u65E5\u5FD7\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function mt(e){try{await $(z(),e.output,"\u6587\u6863\u8DEF\u5F84\u5DF2\u4FDD\u5B58")}catch(t){console.error("\u274C \u8F93\u51FA\u6587\u6863\u8DEF\u5F84\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function dt(e){try{const t=E(e),r=V(e.method),o=Z(e.path??""),n=await(0,L.findHttpApiDocEntry)(o,r);if(!n)throw new Error(`HTTP API \u6587\u6863\u4E2D\u4E0D\u5B58\u5728\u8BE5\u63A5\u53E3: ${r} ${o}\u3002\u8BF7\u5148\u8BFB\u53D6 docs paths \u8FD4\u56DE\u7684 HTTP API \u6587\u6863\u8DEF\u5F84\u3002`);const i=Q(e.query,"query"),s=Y(e.body);if(r==="GET"&&s!==void 0)throw new Error("GET \u63A5\u53E3\u4E0D\u80FD\u4F20 body\uFF0C\u8BF7\u4F7F\u7528 --query\u3002");const u=g(e.responseFormat,"json",["json","text"],"response-format"),l=a(e.timeoutMs,3e4,"timeout-ms",1e3,6e5),w=new URL(`http://${t.ip}:${t.port}${o}`);tt(w,i);const S=new AbortController,I=setTimeout(()=>S.abort(),l);try{const{headers:b,requestBody:y}=et(s),f=await fetch(w.toString(),{method:r,signal:S.signal,headers:b,body:y}),T=await ot(f,u),_=[`HTTP API \u8C03\u7528${f.ok?"\u6210\u529F":"\u5931\u8D25"}: ${r} ${o}`,`\u8BBE\u5907: ${t.label}`,`\u6587\u6863: ${n.title} (lines ${n.startLine}-${n.endLine})`,`\u72B6\u6001\u7801: ${f.status}`,"",T].join(`
|
|
5
|
+
`);await $(_,e.output,"HTTP API \u54CD\u5E94\u5DF2\u4FDD\u5B58"),f.ok||process.exit(1)}finally{clearTimeout(I)}}catch(t){const r=t instanceof Error&&t.name==="AbortError"?"\u8BF7\u6C42\u8D85\u65F6":t instanceof Error?t.message:String(t);console.error("\u274C HTTP API \u8C03\u7528\u5931\u8D25:",r),process.exit(1)}}async function lt(e){try{const t=E(e),r=g(e.engine,"appleocr",["appleocr","paddleocr"],"engine"),o=g(e.mode,"recognize",["recognize","numbers","findText"],"mode"),n=e.input?.trim()||"screen",i=a(e.x,0,"x",0),s=a(e.y,0,"y",0),u=a(e.ex,0,"ex",0),l=a(e.ey,0,"ey",0),w=[...q(e.texts,"texts")??[],...e.text??[]],S=w.length>0?w:void 0,I=X(e.languages),b=F(e.confidenceThreshold,.6,"confidence-threshold",0,1),y=e.paddleModel===void 0?void 0:g(e.paddleModel,x.PADDLE_OCR_MODELS[0],x.PADDLE_OCR_MODELS,"paddle-model"),f=e.maxSideLen===void 0?void 0:a(e.maxSideLen,0,"max-side-len",0),T=e.useGpu===!0?!0:void 0,_=a(e.timeoutMs,6e4,"timeout-ms",1e3,6e5),v=g(e.format,"summary",["summary","json"],"format"),O=await(0,x.runOcrRecognitionOnDevice)(t,{engine:r,mode:o,input:n,x:i,y:s,ex:u,ey:l,texts:S,languages:I,confidenceThreshold:b,exactMatch:e.exactMatch===!0,paddleModel:y,maxSideLen:f,useGpu:T,outputPath:v==="summary"?e.output:void 0,timeoutMs:_}),D=v==="json"?(0,j.formatRuntimeJsonText)(O.body):O.text;v==="json"?await $(D,e.output,"OCR \u7ED3\u679C\u5DF2\u4FDD\u5B58"):console.log([D,"",`device: ${t.label}`,`engine: ${r}`,`mode: ${o}`,`input: ${n}`,`region: x=${i}, y=${s}, ex=${u}, ey=${l}`,...r==="paddleocr"&&y?[`paddleModel: ${y}`]:[],...r==="paddleocr"&&f!==void 0?[`maxSideLen: ${f}`]:[],`status: ${O.status}`].join(`
|
|
6
|
+
`)),O.body.success===!1&&process.exit(1)}catch(t){console.error("\u274C OCR \u6267\u884C\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function ft(e){try{const t=e.image?.trim();if(!t)throw new Error("\u8BF7\u901A\u8FC7 --image \u6307\u5B9A\u8F93\u5165\u56FE\u7247\u8DEF\u5F84\u3002");const r=a(e.x,0,"x",0),o=a(e.y,0,"y",0),n=a(e.width,1,"width",1),i=a(e.height,1,"height",1),s=await(0,P.cropLocalImage)(t,r,o,n,i,e.output);console.log(s)}catch(t){console.error("\u274C \u56FE\u7247\u88C1\u5207\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function gt(e){try{const t=E(e),r=a(e.x,0,"x",0),o=a(e.y,0,"y",0),n=a(e.width,1,"width",1),i=a(e.height,1,"height",1),s=await(0,P.cropDeviceScreen)(t,r,o,n,i,e.output);console.log(s)}catch(t){console.error("\u274C \u5C4F\u5E55\u88C1\u56FE\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function ht(e){try{const t=e.image?.trim();if(!t)throw new Error("\u8BF7\u901A\u8FC7 --image \u6307\u5B9A\u8F93\u5165\u56FE\u7247\u8DEF\u5F84\u3002");const r=a(e.x,0,"x",0),o=a(e.y,0,"y",0),n=a(e.radius,0,"radius",0,20),i=await(0,P.pickLocalImageColor)(t,r,o,n);console.log(i)}catch(t){console.error("\u274C \u56FE\u7247\u53D6\u8272\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function wt(e){try{const t=E(e),r=a(e.x,0,"x",0),o=a(e.y,0,"y",0),n=a(e.radius,0,"radius",0,20),i=await(0,P.pickDeviceScreenColor)(t,r,o,n);console.log(i)}catch(t){console.error("\u274C \u5C4F\u5E55\u53D6\u8272\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function yt(e){try{const t=e.image?.trim();if(!t)throw new Error("\u8BF7\u901A\u8FC7 --image \u6307\u5B9A\u8F93\u5165\u56FE\u7247\u8DEF\u5F84\u3002");const r=g(e.sampleCorner,"topLeft",["topLeft","topRight","bottomLeft","bottomRight"],"sample-corner"),o=a(e.tolerance,24,"tolerance",0,441),n=await(0,P.makeLocalImageTransparent)(t,e.output,e.transparentColor,r,o);console.log(n)}catch(t){console.error("\u274C \u900F\u660E\u56FE\u5236\u4F5C\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function xt(e){try{const t=(e.wsPort??"31111").trim(),r=Number.parseInt(t,10);if(!Number.isInteger(r)||r<1||r>65535)throw new Error(`\u65E0\u6548 ws \u7AEF\u53E3: ${t}`);const o=H.WSManager.get();await o.start(r);const n=async()=>{const s={pid:process.pid,port:r,address:o.getAddress(),startedAt:Date.now(),connected:o.isClientConnected(),clientIp:o.getConnectedClientIp()};await p.writeJSON(d,s)};await n();const i=setInterval(()=>{n().catch(()=>{})},1e3);await p.writeJSON(d,{pid:process.pid,port:r,address:o.getAddress(),startedAt:Date.now(),connected:o.isClientConnected(),clientIp:o.getConnectedClientIp()}),await new Promise(s=>{const u=async()=>{process.off("SIGINT",u),process.off("SIGTERM",u),clearInterval(i),await o.stop(),await p.remove(d),s()};process.on("SIGINT",u),process.on("SIGTERM",u)})}catch(t){console.error("\u274C WS \u670D\u52A1\u542F\u52A8\u5931\u8D25:",t instanceof Error?t.message:t),process.exit(1)}}async function Pt(){try{if(!await p.pathExists(d)){console.log("WS \u670D\u52A1\u672A\u8FD0\u884C");return}const e=await p.readJSON(d),t=Number(e.pid);if(!Number.isInteger(t)||t<=0)throw await p.remove(d),new Error("WS PID \u6587\u4EF6\u635F\u574F");try{process.kill(t,"SIGTERM"),console.log(`\u2705 \u5DF2\u53D1\u9001\u505C\u6B62\u4FE1\u53F7\u5230 WS \u8FDB\u7A0B: ${t}`)}catch{console.log("WS \u8FDB\u7A0B\u4E0D\u5B58\u5728\uFF0C\u6E05\u7406 PID \u6587\u4EF6")}await p.remove(d)}catch(e){console.error("\u274C WS \u670D\u52A1\u505C\u6B62\u5931\u8D25:",e instanceof Error?e.message:e),process.exit(1)}}async function St(){if(!await p.pathExists(d)){console.log(JSON.stringify({status:"stopped",running:!1,pid:null,address:null,connected:!1,clientIp:null}));return}const e=await p.readJSON(d),t=Number(e.pid),r=String(e.address||"unknown"),o=!!e.connected,n=e.clientIp===null||e.clientIp===void 0?null:String(e.clientIp);let i=!1;try{process.kill(t,0),i=!0}catch{i=!1}if(!i){await p.remove(d),console.log(JSON.stringify({status:"stopped",running:!1,pid:null,address:null,connected:!1,clientIp:null}));return}console.log(JSON.stringify({status:"running",running:!0,pid:t,address:r,connected:o,clientIp:o?n||"unknown":null}))}function Ot(){const e=[];e.push(""),e.push("\u8BE6\u7EC6\u547D\u4EE4\u53C2\u6570:");const t=c.program.commands.filter(r=>r.name()!=="help");for(const r of t){if(e.push(` ${r.name()}:`),r.options.length===0){e.push(" (\u65E0\u989D\u5916\u53C2\u6570)");continue}for(const o of r.options){const n=o.defaultValue!==void 0&&o.defaultValue!==null?` (\u9ED8\u8BA4: ${String(o.defaultValue)})`:"",i=o.description?` ${o.description}${n}`:n;e.push(` ${o.flags}${i}`)}}return e.push(""),e.join(`
|
|
7
|
+
`)}c.program.name("ms-cli").description("\u5FEB\u70B9JS \u6784\u5EFA\u5DE5\u5177").version(k.PACKAGE_VERSION),c.program.addHelpText("after",Ot),c.program.command("build").description("\u6784\u5EFA \u5FEB\u70B9JS \u9879\u76EE").option("-d, --dev","\u5F00\u53D1\u6A21\u5F0F\u6784\u5EFA (\u5305\u542B source map)").action(rt),c.program.command("package").description("\u751F\u4EA7\u6784\u5EFA\u5E76\u52A0\u5BC6 ms.msbundle").action(nt),c.program.command("run").description("\u5728\u8BBE\u5907\u4E0A\u8FD0\u884C\u9879\u76EE").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740\uFF08transport=http \u65F6\u5FC5\u586B\uFF09").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("-t, --transport <transport>","\u4F20\u8F93\u65B9\u5F0F: http|ws","http").option("--ws-port <wsPort>","WS \u670D\u52A1\u7AEF\u53E3","31111").option("--ws-wait-ms <wsWaitMs>","WS \u7B49\u5F85\u8BBE\u5907\u8FDE\u63A5\u65F6\u95F4(\u6BEB\u79D2)","30000").action(it),c.program.command("run-ui").description("\u5728\u8BBE\u5907\u4E0A\u9884\u89C8 UI").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740\uFF08transport=http \u65F6\u5FC5\u586B\uFF09").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("-t, --transport <transport>","\u4F20\u8F93\u65B9\u5F0F: http|ws","http").option("--ws-port <wsPort>","WS \u670D\u52A1\u7AEF\u53E3","31111").option("--ws-wait-ms <wsWaitMs>","WS \u7B49\u5F85\u8BBE\u5907\u8FDE\u63A5\u65F6\u95F4(\u6BEB\u79D2)","30000").action(st),c.program.command("stop").description("\u505C\u6B62\u8BBE\u5907\u4E0A\u7684\u9879\u76EE").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740\uFF08transport=http \u65F6\u5FC5\u586B\uFF09").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("-t, --transport <transport>","\u4F20\u8F93\u65B9\u5F0F: http|ws","http").option("--ws-port <wsPort>","WS \u670D\u52A1\u7AEF\u53E3","31111").option("--ws-wait-ms <wsWaitMs>","WS \u7B49\u5F85\u8BBE\u5907\u8FDE\u63A5\u65F6\u95F4(\u6BEB\u79D2)","30000").action(at),c.program.command("screenshot").description("\u83B7\u53D6\u8BBE\u5907\u622A\u56FE\uFF08JPG\uFF09").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740\uFF08transport=http \u65F6\u5FC5\u586B\uFF09").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("-t, --transport <transport>","\u4F20\u8F93\u65B9\u5F0F: http|ws","http").option("--ws-port <wsPort>","WS \u670D\u52A1\u7AEF\u53E3","31111").option("--ws-wait-ms <wsWaitMs>","WS \u7B49\u5F85\u8BBE\u5907\u8FDE\u63A5\u65F6\u95F4(\u6BEB\u79D2)","30000").option("--format <format>","\u8FD4\u56DE\u683C\u5F0F: file|base64","file").option("--output <output>","\u622A\u56FE\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08format=file \u65F6\u751F\u6548\uFF09").action(ct),c.program.command("source").description("\u83B7\u53D6\u8BBE\u5907\u5F53\u524D\u9875\u9762\u8282\u70B9 XML").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740\uFF08transport=http \u65F6\u5FC5\u586B\uFF09").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("-t, --transport <transport>","\u4F20\u8F93\u65B9\u5F0F: http|ws","http").option("--ws-port <wsPort>","WS \u670D\u52A1\u7AEF\u53E3","31111").option("--ws-wait-ms <wsWaitMs>","WS \u7B49\u5F85\u8BBE\u5907\u8FDE\u63A5\u65F6\u95F4(\u6BEB\u79D2)","30000").option("--max-depth <maxDepth>","\u8282\u70B9\u6700\u5927\u6DF1\u5EA6","50").option("--timeout <timeout>","\u8282\u70B9\u6293\u53D6\u8D85\u65F6\u79D2\u6570","120").option("--mode <mode>","\u8282\u70B9\u6293\u53D6\u6A21\u5F0F: 1=\u6A21\u5F0F1, 2=\u6A21\u5F0F2, 3=\u6A21\u5F0F3","1").option("--output <output>","\u8282\u70B9 XML \u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(pt),c.program.command("logs").description("\u83B7\u53D6\u8BBE\u5907\u5F53\u524D\u65E5\u5FD7\u6700\u65B0\u884C").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("--limit <limit>","\u8BFB\u53D6\u6700\u65B0\u65E5\u5FD7\u884C\u6570\uFF0C\u8303\u56F4 1..5000","200").option("--format <format>","\u8FD4\u56DE\u683C\u5F0F: text|json","text").option("--output <output>","\u65E5\u5FD7\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(ut),c.program.command("ocr").description("\u5728\u8BBE\u5907\u4E0A\u6267\u884C OCR \u8BC6\u522B").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("--engine <engine>","OCR \u5F15\u64CE: appleocr|paddleocr","appleocr").option("--mode <mode>","OCR \u6A21\u5F0F: recognize|numbers|findText","recognize").option("--input <input>",'\u8F93\u5165\u6E90\uFF0C\u9ED8\u8BA4 "screen"',"screen").option("--x <x>","\u533A\u57DF\u5DE6\u4E0A\u89D2 x","0").option("--y <y>","\u533A\u57DF\u5DE6\u4E0A\u89D2 y","0").option("--ex <ex>","\u533A\u57DF\u53F3\u4E0B\u89D2 x\uFF1B\u5168\u5C4F\u53EF\u4F20 0","0").option("--ey <ey>","\u533A\u57DF\u53F3\u4E0B\u89D2 y\uFF1B\u5168\u5C4F\u53EF\u4F20 0","0").option("--text <text>","findText \u6587\u672C\uFF0C\u53EF\u91CD\u590D",K,[]).option("--texts <texts>","findText \u6587\u672C\u6570\u7EC4\uFF0C\u652F\u6301 JSON \u6570\u7EC4\u6216\u9017\u53F7\u5206\u9694").option("--languages <languages>","Apple OCR \u8BED\u8A00\uFF0C\u652F\u6301 JSON \u6570\u7EC4\u6216\u9017\u53F7\u5206\u9694").option("--exact-match","findText \u8981\u6C42\u5B8C\u6574\u5339\u914D\u6587\u672C").option("--confidence-threshold <confidenceThreshold>","PaddleOCR \u7F6E\u4FE1\u5EA6\u9608\u503C","0.6").option("--paddle-model <paddleModel>","\u4EC5 paddleocr\uFF1A\u5207\u6362\u6A21\u578B ppocr-v6-tiny|ppocr-v6-small|ppocr-v5").option("--max-side-len <maxSideLen>","\u4EC5 paddleocr\uFF1A\u68C0\u6D4B\u6700\u5927\u8FB9\u957F\uFF0C0 \u56DE\u843D\u9ED8\u8BA4 768").option("--use-gpu","\u4EC5 paddleocr\uFF1A\u5207\u6362\u6A21\u578B\u65F6\u542F\u7528 GPU").option("--timeout-ms <timeoutMs>","\u8BF7\u6C42\u8D85\u65F6\u65F6\u95F4(\u6BEB\u79D2)","60000").option("--format <format>","\u8FD4\u56DE\u683C\u5F0F: summary|json","summary").option("--output <output>","OCR \u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(lt),c.program.command("image-crop").description("\u88C1\u5207\u672C\u5730\u56FE\u7247\u5E76\u8F93\u51FA PNG").requiredOption("--image <image>","\u8F93\u5165\u56FE\u7247\u8DEF\u5F84").option("--x <x>","\u88C1\u5207\u8D77\u70B9 x","0").option("--y <y>","\u88C1\u5207\u8D77\u70B9 y","0").requiredOption("--width <width>","\u88C1\u5207\u5BBD\u5EA6").requiredOption("--height <height>","\u88C1\u5207\u9AD8\u5EA6").option("--output <output>","\u8F93\u51FA PNG \u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(ft),c.program.command("screen-crop").description("\u4ECE\u8BBE\u5907\u622A\u56FE\u4E2D\u88C1\u5207 PNG").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("--x <x>","\u88C1\u5207\u8D77\u70B9 x","0").option("--y <y>","\u88C1\u5207\u8D77\u70B9 y","0").requiredOption("--width <width>","\u88C1\u5207\u5BBD\u5EA6").requiredOption("--height <height>","\u88C1\u5207\u9AD8\u5EA6").option("--output <output>","\u8F93\u51FA PNG \u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(gt),c.program.command("image-pick-color").description("\u8BFB\u53D6\u672C\u5730\u56FE\u7247\u6307\u5B9A\u5750\u6807\u989C\u8272").requiredOption("--image <image>","\u8F93\u5165\u56FE\u7247\u8DEF\u5F84").option("--x <x>","\u76EE\u6807\u5750\u6807 x","0").option("--y <y>","\u76EE\u6807\u5750\u6807 y","0").option("--radius <radius>","\u91C7\u6837\u534A\u5F84\uFF0C\u8303\u56F4 0..20","0").action(ht),c.program.command("screen-pick-color").description("\u8BFB\u53D6\u8BBE\u5907\u622A\u56FE\u6307\u5B9A\u5750\u6807\u989C\u8272").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").option("--x <x>","\u76EE\u6807\u5750\u6807 x","0").option("--y <y>","\u76EE\u6807\u5750\u6807 y","0").option("--radius <radius>","\u91C7\u6837\u534A\u5F84\uFF0C\u8303\u56F4 0..20","0").action(wt),c.program.command("image-transparent").description("\u5C06\u56FE\u7247\u4E2D\u63A5\u8FD1\u6307\u5B9A\u989C\u8272\u6216\u89D2\u843D\u80CC\u666F\u8272\u7684\u50CF\u7D20\u8BBE\u4E3A\u900F\u660E").requiredOption("--image <image>","\u8F93\u5165\u56FE\u7247\u8DEF\u5F84").option("--output <output>","\u8F93\u51FA PNG \u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").option("--transparent-color <transparentColor>","\u900F\u660E\u5316\u989C\u8272\uFF0C\u683C\u5F0F #RRGGBB").option("--sample-corner <sampleCorner>","\u672A\u4F20 transparent-color \u65F6\u91C7\u6837\u89D2\u843D: topLeft|topRight|bottomLeft|bottomRight","topLeft").option("--tolerance <tolerance>","RGB \u6B27\u6C0F\u8DDD\u79BB\u5BB9\u5DEE\uFF0C\u8303\u56F4 0..441","24").action(yt);const Ct=c.program.command("docs").description("\u663E\u793A KuaiJS \u672C\u5730\u6587\u6863\u8DEF\u5F84");Ct.command("paths").description("\u663E\u793A KuaiJS \u672C\u5730\u6587\u6863\u8DEF\u5F84").option("--output <output>","\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(mt),c.program.command("http-call").description("\u6309 HTTP API \u6587\u6863\u58F0\u660E\u8C03\u7528\u8BBE\u5907\u63A5\u53E3").option("-i, --ip <ip>","\u8BBE\u5907 IP \u5730\u5740").option("--port <port>","\u8BBE\u5907\u7AEF\u53E3","9800").requiredOption("--method <method>","HTTP \u65B9\u6CD5: GET|POST").requiredOption("--path <path>","HTTP API \u76F8\u5BF9\u8DEF\u5F84\uFF0C\u4F8B\u5982 /api/status").option("--query <query>","JSON \u5BF9\u8C61 query \u53C2\u6570").option("--body <body>","JSON \u8BF7\u6C42\u4F53\uFF1B\u666E\u901A\u5B57\u7B26\u4E32\u4F1A\u4F5C\u4E3A JSON \u5B57\u7B26\u4E32\u63D0\u4EA4").option("--response-format <responseFormat>","\u54CD\u5E94\u683C\u5F0F: json|text","json").option("--timeout-ms <timeoutMs>","\u8BF7\u6C42\u8D85\u65F6\u65F6\u95F4(\u6BEB\u79D2)","30000").option("--output <output>","\u54CD\u5E94\u8F93\u51FA\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u9009\uFF09").action(dt),c.program.command("ws-start").description("\u542F\u52A8 CLI \u5185\u7F6E WS \u670D\u52A1").option("--ws-port <wsPort>","WS \u670D\u52A1\u7AEF\u53E3","31111").action(xt),c.program.command("ws-stop").description("\u505C\u6B62 CLI \u5185\u7F6E WS \u670D\u52A1").action(Pt),c.program.command("ws-status").description("\u67E5\u770B CLI \u5185\u7F6E WS \u670D\u52A1\u72B6\u6001").action(St),c.program.parse();
|