cdp-client-tool 1.1.1 → 2.1.1
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/Client.d.ts +8 -56
- package/dist/common.d.ts +30 -5
- package/dist/core/Handler.decorator.d.ts +71 -0
- package/dist/core/Runner.d.ts +33 -0
- package/dist/core/ScriptWorker.d.ts +17 -0
- package/dist/core/WsHandler.d.ts +32 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/excuteFn.type.d.ts +2 -0
- package/dist/index.cjs +505 -185
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +499 -184
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/presetEventHandlers.d.ts +5 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils.d.ts +1 -0
- package/package.json +3 -2
- package/readme.en.md +31 -13
- package/readme.md +35 -23
package/dist/index.cjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
+
var path = require('node:path');
|
|
5
|
+
var promises = require('node:fs/promises');
|
|
4
6
|
var axios = require('axios');
|
|
5
7
|
var puppeteer = require('puppeteer-core');
|
|
6
|
-
var
|
|
8
|
+
var node_worker_threads = require('node:worker_threads');
|
|
9
|
+
var fs = require('node:fs');
|
|
7
10
|
var socket_ioClient = require('socket.io-client');
|
|
8
|
-
var path = require('node:path');
|
|
9
|
-
var requireFromString = require('require-from-string');
|
|
10
11
|
|
|
11
12
|
function _interopNamespaceDefault(e) {
|
|
12
13
|
var n = Object.create(null);
|
|
@@ -26,7 +27,7 @@ function _interopNamespaceDefault(e) {
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
29
|
-
var
|
|
30
|
+
var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
|
|
30
31
|
|
|
31
32
|
const raw = chalk.default ?? chalk;
|
|
32
33
|
const c = {
|
|
@@ -93,7 +94,13 @@ class Logger {
|
|
|
93
94
|
this.logMessage(LogLevel.DEBUG, message, ...args);
|
|
94
95
|
}
|
|
95
96
|
}
|
|
97
|
+
const logger = new Logger();
|
|
96
98
|
|
|
99
|
+
function sleep(timeout) {
|
|
100
|
+
return new Promise((resolve) => {
|
|
101
|
+
setTimeout(resolve, timeout);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
97
104
|
async function launchBrowser() {
|
|
98
105
|
const debugUrl = 'http://localhost:9222';
|
|
99
106
|
const { data } = await axios.get(`${debugUrl}/json/version`);
|
|
@@ -104,6 +111,30 @@ async function launchBrowser() {
|
|
|
104
111
|
});
|
|
105
112
|
return browser;
|
|
106
113
|
}
|
|
114
|
+
async function ensureDir(dirPath, create = true) {
|
|
115
|
+
try {
|
|
116
|
+
const stats = await promises.stat(dirPath);
|
|
117
|
+
if (!stats.isDirectory()) {
|
|
118
|
+
throw new Error(`${dirPath} 存在但不是目录`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
if (err.code === 'ENOENT') {
|
|
123
|
+
if (create) {
|
|
124
|
+
await promises.mkdir(dirPath, { recursive: true });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
throw new Error(`目录 ${dirPath} 不存在`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function reportResult(result) {
|
|
136
|
+
node_worker_threads.parentPort?.postMessage(result);
|
|
137
|
+
}
|
|
107
138
|
|
|
108
139
|
exports.EVENTS = void 0;
|
|
109
140
|
(function (EVENTS) {
|
|
@@ -115,7 +146,18 @@ exports.EVENTS = void 0;
|
|
|
115
146
|
EVENTS["EXEC_LOCAL_SCRIPT"] = "exec_local_script";
|
|
116
147
|
EVENTS["EXEC_REMOTE_SCRIPT"] = "exec_remote_script";
|
|
117
148
|
EVENTS["SCRIPT_QUEUE"] = "script_queue";
|
|
149
|
+
EVENTS["INTERRUPT_SCRIPT"] = "interrupt_script";
|
|
150
|
+
EVENTS["UNDO_SCRIPT"] = "undo_script";
|
|
151
|
+
EVENTS["REPORT_RESULT"] = "report_result";
|
|
118
152
|
})(exports.EVENTS || (exports.EVENTS = {}));
|
|
153
|
+
var PushJobResult;
|
|
154
|
+
(function (PushJobResult) {
|
|
155
|
+
PushJobResult["SUCCESS"] = "success";
|
|
156
|
+
PushJobResult["SUCCESS_IN_QUEUE"] = "success_in_queue";
|
|
157
|
+
PushJobResult["FAILED"] = "failed";
|
|
158
|
+
PushJobResult["QUEUE_FULL"] = "queue_full";
|
|
159
|
+
PushJobResult["FILE_SAVE_FAILED"] = "file_save_failed";
|
|
160
|
+
})(PushJobResult || (PushJobResult = {}));
|
|
119
161
|
const ALLOWED_DIRS = ['local_scripts', 'screenshots'];
|
|
120
162
|
function resolveAndValidate(basePath) {
|
|
121
163
|
const cwd = process.cwd();
|
|
@@ -128,228 +170,506 @@ function resolveAndValidate(basePath) {
|
|
|
128
170
|
}
|
|
129
171
|
return resolved;
|
|
130
172
|
}
|
|
173
|
+
async function saveScriptFile(filename, script) {
|
|
174
|
+
await ensureDir(path__namespace.resolve(process.cwd(), 'local_scripts'));
|
|
175
|
+
fs__namespace.writeFileSync(path__namespace.resolve(process.cwd(), 'local_scripts', filename), script);
|
|
176
|
+
}
|
|
131
177
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
178
|
+
class ScriptWorker {
|
|
179
|
+
constructor(options) {
|
|
180
|
+
this.options = options;
|
|
181
|
+
}
|
|
182
|
+
terminate() {
|
|
183
|
+
if (this.timeoutTimer) {
|
|
184
|
+
clearTimeout(this.timeoutTimer);
|
|
185
|
+
this.timeoutTimer = undefined;
|
|
186
|
+
}
|
|
187
|
+
this.worker?.terminate();
|
|
188
|
+
}
|
|
189
|
+
run() {
|
|
190
|
+
if (this.options.job.type === 'local') {
|
|
191
|
+
const { filename, params } = this.options.job;
|
|
192
|
+
this.worker = new node_worker_threads.Worker(filename, {
|
|
193
|
+
workerData: {
|
|
194
|
+
params,
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
const { script, params } = this.options.job;
|
|
200
|
+
this.worker = new node_worker_threads.Worker(script, {
|
|
201
|
+
eval: true,
|
|
202
|
+
workerData: {
|
|
203
|
+
params,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return new Promise((resolve, reject) => {
|
|
208
|
+
let settled = false;
|
|
209
|
+
const settle = (fn) => {
|
|
210
|
+
if (settled)
|
|
211
|
+
return;
|
|
212
|
+
settled = true;
|
|
213
|
+
fn();
|
|
214
|
+
};
|
|
215
|
+
this.timeoutTimer = setTimeout(() => {
|
|
216
|
+
this.timeoutTimer = undefined;
|
|
217
|
+
this.worker.terminate();
|
|
218
|
+
settle(() => reject(new Error('Worker timeout')));
|
|
219
|
+
}, this.options.timeout);
|
|
220
|
+
this.worker.on('error', (error) => {
|
|
221
|
+
if (this.timeoutTimer) {
|
|
222
|
+
clearTimeout(this.timeoutTimer);
|
|
223
|
+
this.timeoutTimer = undefined;
|
|
224
|
+
}
|
|
225
|
+
logger.error('Worker error', error);
|
|
226
|
+
settle(() => reject(error));
|
|
227
|
+
});
|
|
228
|
+
this.worker.on('exit', (code) => {
|
|
229
|
+
if (this.timeoutTimer) {
|
|
230
|
+
clearTimeout(this.timeoutTimer);
|
|
231
|
+
this.timeoutTimer = undefined;
|
|
232
|
+
}
|
|
233
|
+
if (code === 0) {
|
|
234
|
+
logger.success('Worker exit', code);
|
|
235
|
+
settle(() => resolve());
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
logger.error('Worker exit', code);
|
|
239
|
+
settle(() => reject(new Error(`Worker exited with code ${code}`)));
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
this.worker.on('message', (message) => {
|
|
243
|
+
logger.info('Worker message', message);
|
|
244
|
+
console.log('看顺序 4', this.options);
|
|
245
|
+
const socket = this.options.sockets.get(this.options.job.gatewayName);
|
|
246
|
+
socket.emit(exports.EVENTS.REPORT_RESULT, {
|
|
247
|
+
payload: {
|
|
248
|
+
jobId: this.options.job.id,
|
|
249
|
+
result: message,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
});
|
|
155
253
|
});
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
},
|
|
159
|
-
async [exports.EVENTS.READ_FILE]({ data, callback }) {
|
|
160
|
-
const { payload } = data;
|
|
161
|
-
const filePath = resolveAndValidate(payload.path);
|
|
162
|
-
const run = this.runCatchFunction(async () => await promises.readFile(filePath));
|
|
163
|
-
const content = await run();
|
|
164
|
-
callback(content ?? null);
|
|
165
|
-
},
|
|
166
|
-
async [exports.EVENTS.RM]({ data, callback }) {
|
|
167
|
-
const { payload } = data;
|
|
168
|
-
const filePath = resolveAndValidate(payload.path);
|
|
169
|
-
const run = this.runCatchFunction(async () => await promises.rm(filePath, { force: true }));
|
|
170
|
-
await run();
|
|
171
|
-
callback({ ok: true });
|
|
172
|
-
},
|
|
173
|
-
async [exports.EVENTS.SCRIPT_QUEUE]({ callback }) {
|
|
174
|
-
const snapshot = this.getScriptQueueSnapshot();
|
|
175
|
-
callback(snapshot);
|
|
176
|
-
},
|
|
177
|
-
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
178
256
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
};
|
|
183
|
-
class Client {
|
|
184
|
-
constructor(options = defaultOptions) {
|
|
257
|
+
class Runner {
|
|
258
|
+
constructor(client, options) {
|
|
259
|
+
this.client = client;
|
|
185
260
|
this.options = options;
|
|
186
|
-
this.
|
|
187
|
-
this.running = false;
|
|
188
|
-
this.scriptQueue = [];
|
|
189
|
-
this.runningJob = null;
|
|
190
|
-
this.queueCapacity = 10;
|
|
261
|
+
this.queue = [];
|
|
191
262
|
this.jobIdSeq = 0;
|
|
192
|
-
this.
|
|
263
|
+
this.queue = [];
|
|
264
|
+
}
|
|
265
|
+
get ctx() {
|
|
266
|
+
return this.client.deps;
|
|
193
267
|
}
|
|
194
268
|
nextJobId() {
|
|
195
269
|
return `job-${Date.now()}-${this.jobIdSeq++}`;
|
|
196
270
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
271
|
+
getQueueLength() {
|
|
272
|
+
return this.queue.length;
|
|
273
|
+
}
|
|
274
|
+
interruptJob(jobId) {
|
|
275
|
+
const idx = this.queue.findIndex((j) => j.id === jobId);
|
|
276
|
+
if (idx >= 0) {
|
|
277
|
+
this.queue.splice(idx, 1);
|
|
278
|
+
return { ok: true };
|
|
279
|
+
}
|
|
280
|
+
if (this.worker) {
|
|
281
|
+
const job = this.worker.options?.job;
|
|
282
|
+
if (job?.id === jobId) {
|
|
283
|
+
this.worker.terminate();
|
|
284
|
+
return { ok: true };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return { ok: false, reason: `job not found: ${jobId}` };
|
|
288
|
+
}
|
|
289
|
+
getScriptQueueSnapshot() {
|
|
290
|
+
const running = this.worker ? this.worker.options?.job : null;
|
|
291
|
+
return {
|
|
292
|
+
running,
|
|
293
|
+
pending: [...this.queue],
|
|
294
|
+
capacity: this.options.capacity,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async execJob(job) {
|
|
298
|
+
if (this.queue.length >= this.options.capacity) {
|
|
299
|
+
return PushJobResult.QUEUE_FULL;
|
|
300
|
+
}
|
|
301
|
+
if (job.type === 'local' && !job.filename) {
|
|
302
|
+
job.filename = this.nextJobId();
|
|
200
303
|
try {
|
|
201
|
-
|
|
304
|
+
await saveScriptFile(job.filename, job.script);
|
|
202
305
|
}
|
|
203
306
|
catch (error) {
|
|
204
|
-
|
|
205
|
-
that.options.onError && that.options.onError(error);
|
|
307
|
+
return PushJobResult.FILE_SAVE_FAILED;
|
|
206
308
|
}
|
|
207
|
-
}
|
|
309
|
+
}
|
|
310
|
+
this.queue.push(job);
|
|
311
|
+
if (!this.worker) {
|
|
312
|
+
const jobToRun = this.queue.shift();
|
|
313
|
+
if (this.last_finished_at && Date.now() - this.last_finished_at < this.options.minInterval) {
|
|
314
|
+
console.log('冷却时间');
|
|
315
|
+
await sleep(this.options.minInterval - (Date.now() - this.last_finished_at));
|
|
316
|
+
}
|
|
317
|
+
console.log('看顺序 3', this.ctx);
|
|
318
|
+
this.worker = new ScriptWorker({
|
|
319
|
+
job: jobToRun,
|
|
320
|
+
timeout: this.options.timeout,
|
|
321
|
+
sockets: this.ctx.ws.sockets
|
|
322
|
+
});
|
|
323
|
+
try {
|
|
324
|
+
const res = await this.worker.run();
|
|
325
|
+
console.log('res 执行完了', res);
|
|
326
|
+
return PushJobResult.SUCCESS;
|
|
327
|
+
}
|
|
328
|
+
catch (e) {
|
|
329
|
+
console.log('res 执行失败', e);
|
|
330
|
+
return PushJobResult.FAILED;
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
this.worker = undefined;
|
|
334
|
+
this.last_finished_at = Date.now();
|
|
335
|
+
console.log('next', this.queue);
|
|
336
|
+
const next = this.queue.shift();
|
|
337
|
+
if (next) {
|
|
338
|
+
console.log('next!', next);
|
|
339
|
+
return this.execJob(next);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
return PushJobResult.SUCCESS_IN_QUEUE;
|
|
345
|
+
}
|
|
208
346
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/******************************************************************************
|
|
350
|
+
Copyright (c) Microsoft Corporation.
|
|
351
|
+
|
|
352
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
353
|
+
purpose with or without fee is hereby granted.
|
|
354
|
+
|
|
355
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
356
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
357
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
358
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
359
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
360
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
361
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
362
|
+
***************************************************************************** */
|
|
363
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
function __decorate(decorators, target, key, desc) {
|
|
367
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
368
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
369
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
370
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function __metadata(metadataKey, metadataValue) {
|
|
374
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
378
|
+
var e = new Error(message);
|
|
379
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const HANDLER_REGISTRY = Symbol("handler:registry");
|
|
383
|
+
function getHandlerRegistry(instance) {
|
|
384
|
+
const ctor = instance.constructor;
|
|
385
|
+
return ctor[HANDLER_REGISTRY] ?? [];
|
|
386
|
+
}
|
|
387
|
+
function Handler(eventName) {
|
|
388
|
+
return function (target, propertyKey, _descriptor) {
|
|
389
|
+
const ctor = typeof target === "function" ? target : target.constructor;
|
|
390
|
+
const registry = ctor[HANDLER_REGISTRY] ?? [];
|
|
391
|
+
registry.push({ event: eventName, key: propertyKey });
|
|
392
|
+
ctor[HANDLER_REGISTRY] = registry;
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
class WsHandler {
|
|
397
|
+
constructor(client, options) {
|
|
398
|
+
this.client = client;
|
|
399
|
+
this.options = options;
|
|
400
|
+
this.sockets = new Map();
|
|
401
|
+
this.init();
|
|
402
|
+
}
|
|
403
|
+
get ctx() {
|
|
404
|
+
return this.client.deps;
|
|
405
|
+
}
|
|
406
|
+
init() {
|
|
407
|
+
this.options.onInit?.(this.ctx);
|
|
212
408
|
for (const gateway of this.options.gateways) {
|
|
213
|
-
const warpHandler = (fn) => {
|
|
214
|
-
return (data, callback) => {
|
|
215
|
-
return fn.call(this, {
|
|
216
|
-
data,
|
|
217
|
-
callback,
|
|
218
|
-
gateway,
|
|
219
|
-
ctx: this.ctx
|
|
220
|
-
});
|
|
221
|
-
};
|
|
222
|
-
};
|
|
223
409
|
const socket = socket_ioClient.io(gateway.uri, {
|
|
224
410
|
...gateway.opts,
|
|
225
|
-
query: {
|
|
411
|
+
query: {
|
|
412
|
+
...gateway.opts?.query,
|
|
413
|
+
deviceName: this.options.deviceName ?? "default",
|
|
414
|
+
},
|
|
226
415
|
});
|
|
227
|
-
socket.on(
|
|
228
|
-
|
|
416
|
+
socket.on("connect", () => {
|
|
417
|
+
logger.success(`[${gateway.name}]: socketio 连接成功`);
|
|
229
418
|
});
|
|
230
|
-
socket.on(
|
|
231
|
-
|
|
419
|
+
socket.on("disconnect", () => {
|
|
420
|
+
logger.warn(`[${gateway.name}]: socketio 断开连接`);
|
|
232
421
|
});
|
|
233
|
-
socket.on(
|
|
234
|
-
|
|
422
|
+
socket.on("connect_error", (err) => {
|
|
423
|
+
logger.error(`[${gateway.name}]: connect_error 错误`, err);
|
|
235
424
|
});
|
|
236
|
-
socket.on(
|
|
237
|
-
|
|
425
|
+
socket.on("error", (err) => {
|
|
426
|
+
logger.error(`[${gateway.name}]: socketio 错误`, err);
|
|
238
427
|
});
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
gateway.onSocketInit && gateway.onSocketInit(socket);
|
|
428
|
+
this.sockets.set(gateway.name, socket);
|
|
429
|
+
this.attachHandlers(socket, gateway);
|
|
430
|
+
gateway.onSocketInit?.(socket);
|
|
243
431
|
}
|
|
244
432
|
}
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
433
|
+
attachHandlers(socket, gateway) {
|
|
434
|
+
const warpHandler = (fn) => (data, callback) => fn.call(this, { data, callback, gateway, ctx: this.ctx });
|
|
435
|
+
for (const { event, key } of getHandlerRegistry(this)) {
|
|
436
|
+
const method = this[key];
|
|
437
|
+
if (typeof method === "function") {
|
|
438
|
+
socket.on(event, warpHandler(method.bind(this)));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
252
441
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
442
|
+
async readDir({ data, callback }) {
|
|
443
|
+
try {
|
|
444
|
+
const dirPath = resolveAndValidate(data.payload.path);
|
|
445
|
+
const list = await promises.readdir(dirPath, { withFileTypes: true });
|
|
446
|
+
callback(list.map((d) => ({
|
|
447
|
+
name: d.name,
|
|
448
|
+
type: d.isDirectory() ? "dir" : "file",
|
|
449
|
+
})));
|
|
450
|
+
}
|
|
451
|
+
catch (e) {
|
|
452
|
+
logger.error("read_dir 失败", e);
|
|
453
|
+
callback([]);
|
|
454
|
+
}
|
|
259
455
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
456
|
+
async readFile({ data, callback }) {
|
|
457
|
+
try {
|
|
458
|
+
const filePath = resolveAndValidate(data.payload.path);
|
|
459
|
+
const content = await promises.readFile(filePath);
|
|
460
|
+
callback(content);
|
|
461
|
+
}
|
|
462
|
+
catch (e) {
|
|
463
|
+
logger.error("read_file 失败", e);
|
|
464
|
+
callback(null);
|
|
465
|
+
}
|
|
266
466
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
await run();
|
|
281
|
-
});
|
|
467
|
+
async writeFile({ data, callback }) {
|
|
468
|
+
try {
|
|
469
|
+
const { filename, content, flags } = data.payload;
|
|
470
|
+
const filePath = resolveAndValidate(filename);
|
|
471
|
+
await ensureDir(path.dirname(filePath));
|
|
472
|
+
const buf = Buffer.isBuffer(content) ? content : typeof content === "string" ? Buffer.from(content, "utf8") : Buffer.from(content);
|
|
473
|
+
await promises.writeFile(filePath, buf, { flag: flags ?? "w" });
|
|
474
|
+
callback({ ok: true });
|
|
475
|
+
}
|
|
476
|
+
catch (e) {
|
|
477
|
+
logger.error("write_file 失败", e);
|
|
478
|
+
callback({ ok: false });
|
|
479
|
+
}
|
|
282
480
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
return this.enqueueJob(job, async () => {
|
|
294
|
-
const script = this.getLocalModule(filename);
|
|
295
|
-
const runCtx = { ...this.ctx, params: params ?? {} };
|
|
296
|
-
const run = this.runCatchFunction(async () => await script(runCtx));
|
|
297
|
-
await run();
|
|
298
|
-
});
|
|
481
|
+
async rm({ data, callback }) {
|
|
482
|
+
try {
|
|
483
|
+
const filePath = resolveAndValidate(data.payload.path);
|
|
484
|
+
await promises.rm(filePath, { force: true });
|
|
485
|
+
callback({ ok: true });
|
|
486
|
+
}
|
|
487
|
+
catch (e) {
|
|
488
|
+
logger.error("rm 失败", e);
|
|
489
|
+
callback({ ok: false });
|
|
490
|
+
}
|
|
299
491
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
492
|
+
async execLocalScript({ data, callback, gateway }) {
|
|
493
|
+
try {
|
|
494
|
+
const { filename, params } = data.payload;
|
|
495
|
+
const base = filename.includes("/") ? filename : path.join("local_scripts", filename);
|
|
496
|
+
const filePath = resolveAndValidate(base);
|
|
497
|
+
const job = {
|
|
498
|
+
id: `job-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
499
|
+
type: "local",
|
|
500
|
+
filename: filePath,
|
|
501
|
+
params,
|
|
502
|
+
gatewayName: gateway.name,
|
|
306
503
|
};
|
|
504
|
+
const result = await this.ctx.runner.execJob(job);
|
|
505
|
+
console.log('handler:', result);
|
|
506
|
+
callback(this.mapExecResult(result, job.id));
|
|
307
507
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
job.startedAt = Date.now();
|
|
312
|
-
this.runningJob = job;
|
|
313
|
-
this.runJob(job);
|
|
314
|
-
return { status: 'executing', jobId: job.id };
|
|
508
|
+
catch (e) {
|
|
509
|
+
logger.error("exec_local_script 失败", e);
|
|
510
|
+
callback({ status: "overflow", reason: e?.message ?? "执行失败" });
|
|
315
511
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
512
|
+
}
|
|
513
|
+
async execRemoteScript({ data, callback, gateway }) {
|
|
514
|
+
try {
|
|
515
|
+
const { raw, params } = data.payload;
|
|
516
|
+
const job = {
|
|
517
|
+
id: `job-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
518
|
+
type: "remote",
|
|
519
|
+
script: raw.toString("utf8"),
|
|
520
|
+
params,
|
|
521
|
+
gatewayName: gateway.name,
|
|
323
522
|
};
|
|
523
|
+
const result = await this.ctx.runner.execJob(job);
|
|
524
|
+
callback(this.mapExecResult(result, job.id));
|
|
525
|
+
}
|
|
526
|
+
catch (e) {
|
|
527
|
+
logger.error("exec_remote_script 失败", e);
|
|
528
|
+
callback({ status: "overflow", reason: e?.message ?? "执行失败" });
|
|
324
529
|
}
|
|
325
530
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
531
|
+
mapExecResult(result, jobId) {
|
|
532
|
+
switch (result) {
|
|
533
|
+
case PushJobResult.SUCCESS:
|
|
534
|
+
return { status: "executing", jobId };
|
|
535
|
+
case PushJobResult.SUCCESS_IN_QUEUE:
|
|
536
|
+
return {
|
|
537
|
+
status: "queued",
|
|
538
|
+
jobId,
|
|
539
|
+
position: this.ctx.runner.getQueueLength(),
|
|
540
|
+
};
|
|
541
|
+
case PushJobResult.QUEUE_FULL:
|
|
542
|
+
return { status: "overflow", reason: "queue is full" };
|
|
543
|
+
case PushJobResult.FAILED:
|
|
544
|
+
return { status: "overflow", reason: "execution failed" };
|
|
545
|
+
case PushJobResult.FILE_SAVE_FAILED:
|
|
546
|
+
return { status: "overflow", reason: "file save failed" };
|
|
547
|
+
default:
|
|
548
|
+
return { status: "overflow", reason: "unknown" };
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async interruptScript({ data, callback }) {
|
|
330
552
|
try {
|
|
331
|
-
|
|
332
|
-
|
|
553
|
+
const { jobId } = data.payload;
|
|
554
|
+
const result = this.ctx.runner.interruptJob(jobId);
|
|
555
|
+
callback(result);
|
|
333
556
|
}
|
|
334
557
|
catch (e) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
next.status = 'running';
|
|
345
|
-
next.startedAt = Date.now();
|
|
346
|
-
this.runningJob = next;
|
|
347
|
-
this.runJob(next);
|
|
348
|
-
}
|
|
558
|
+
logger.error("interrupt_script 失败", e);
|
|
559
|
+
callback({ ok: false, reason: e?.message ?? "取消失败" });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async undoScript({ data, callback }) {
|
|
563
|
+
try {
|
|
564
|
+
const { jobId } = data.payload;
|
|
565
|
+
const result = this.ctx.runner.interruptJob(jobId);
|
|
566
|
+
callback(result);
|
|
349
567
|
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
logger.error("undo_script 失败", e);
|
|
570
|
+
callback({ ok: false, reason: e?.message ?? "撤销失败" });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async scriptQueue({ callback }) {
|
|
574
|
+
try {
|
|
575
|
+
const snapshot = this.ctx.runner.getScriptQueueSnapshot();
|
|
576
|
+
callback(snapshot);
|
|
577
|
+
}
|
|
578
|
+
catch (e) {
|
|
579
|
+
logger.error("script_queue 失败", e);
|
|
580
|
+
callback({ running: null, pending: [], capacity: 10 });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
__decorate([
|
|
585
|
+
Handler(exports.EVENTS.READ_DIR),
|
|
586
|
+
__metadata("design:type", Function),
|
|
587
|
+
__metadata("design:paramtypes", [Object]),
|
|
588
|
+
__metadata("design:returntype", Promise)
|
|
589
|
+
], WsHandler.prototype, "readDir", null);
|
|
590
|
+
__decorate([
|
|
591
|
+
Handler(exports.EVENTS.READ_FILE),
|
|
592
|
+
__metadata("design:type", Function),
|
|
593
|
+
__metadata("design:paramtypes", [Object]),
|
|
594
|
+
__metadata("design:returntype", Promise)
|
|
595
|
+
], WsHandler.prototype, "readFile", null);
|
|
596
|
+
__decorate([
|
|
597
|
+
Handler(exports.EVENTS.WRITE_FILE),
|
|
598
|
+
__metadata("design:type", Function),
|
|
599
|
+
__metadata("design:paramtypes", [Object]),
|
|
600
|
+
__metadata("design:returntype", Promise)
|
|
601
|
+
], WsHandler.prototype, "writeFile", null);
|
|
602
|
+
__decorate([
|
|
603
|
+
Handler(exports.EVENTS.RM),
|
|
604
|
+
__metadata("design:type", Function),
|
|
605
|
+
__metadata("design:paramtypes", [Object]),
|
|
606
|
+
__metadata("design:returntype", Promise)
|
|
607
|
+
], WsHandler.prototype, "rm", null);
|
|
608
|
+
__decorate([
|
|
609
|
+
Handler(exports.EVENTS.EXEC_LOCAL_SCRIPT),
|
|
610
|
+
__metadata("design:type", Function),
|
|
611
|
+
__metadata("design:paramtypes", [Object]),
|
|
612
|
+
__metadata("design:returntype", Promise)
|
|
613
|
+
], WsHandler.prototype, "execLocalScript", null);
|
|
614
|
+
__decorate([
|
|
615
|
+
Handler(exports.EVENTS.EXEC_REMOTE_SCRIPT),
|
|
616
|
+
__metadata("design:type", Function),
|
|
617
|
+
__metadata("design:paramtypes", [Object]),
|
|
618
|
+
__metadata("design:returntype", Promise)
|
|
619
|
+
], WsHandler.prototype, "execRemoteScript", null);
|
|
620
|
+
__decorate([
|
|
621
|
+
Handler(exports.EVENTS.INTERRUPT_SCRIPT),
|
|
622
|
+
__metadata("design:type", Function),
|
|
623
|
+
__metadata("design:paramtypes", [Object]),
|
|
624
|
+
__metadata("design:returntype", Promise)
|
|
625
|
+
], WsHandler.prototype, "interruptScript", null);
|
|
626
|
+
__decorate([
|
|
627
|
+
Handler(exports.EVENTS.UNDO_SCRIPT),
|
|
628
|
+
__metadata("design:type", Function),
|
|
629
|
+
__metadata("design:paramtypes", [Object]),
|
|
630
|
+
__metadata("design:returntype", Promise)
|
|
631
|
+
], WsHandler.prototype, "undoScript", null);
|
|
632
|
+
__decorate([
|
|
633
|
+
Handler(exports.EVENTS.SCRIPT_QUEUE),
|
|
634
|
+
__metadata("design:type", Function),
|
|
635
|
+
__metadata("design:paramtypes", [Object]),
|
|
636
|
+
__metadata("design:returntype", Promise)
|
|
637
|
+
], WsHandler.prototype, "scriptQueue", null);
|
|
638
|
+
|
|
639
|
+
const defaultOptions = {
|
|
640
|
+
capacity: 10,
|
|
641
|
+
timeout: 1000 * 60 * 5,
|
|
642
|
+
minInterval: 1000 * 5};
|
|
643
|
+
class Client {
|
|
644
|
+
get deps() {
|
|
645
|
+
return {
|
|
646
|
+
runner: this.runner,
|
|
647
|
+
ws: this.ws,
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
constructor(options) {
|
|
651
|
+
const runner = new Runner(this, {
|
|
652
|
+
capacity: options.capacity ?? defaultOptions.capacity,
|
|
653
|
+
minInterval: options.minInterval ?? defaultOptions.minInterval,
|
|
654
|
+
timeout: options.timeout ?? defaultOptions.timeout,
|
|
655
|
+
});
|
|
656
|
+
this.runner = runner;
|
|
657
|
+
console.log('看顺序 1,runner 赋值完', this.deps);
|
|
658
|
+
const ws = new WsHandler(this, {
|
|
659
|
+
gateways: options.gateways,
|
|
660
|
+
deviceName: options.deviceName,
|
|
661
|
+
});
|
|
662
|
+
this.ws = ws;
|
|
663
|
+
console.log('看顺序 2,ws 赋值完', this.deps);
|
|
350
664
|
}
|
|
351
665
|
}
|
|
352
666
|
|
|
353
667
|
exports.Client = Client;
|
|
354
|
-
exports.
|
|
668
|
+
exports.Handler = Handler;
|
|
669
|
+
exports.Runner = Runner;
|
|
670
|
+
exports.WsHandler = WsHandler;
|
|
671
|
+
exports.launchBrowser = launchBrowser;
|
|
672
|
+
exports.logger = logger;
|
|
673
|
+
exports.reportResult = reportResult;
|
|
674
|
+
exports.sleep = sleep;
|
|
355
675
|
//# sourceMappingURL=index.cjs.map
|