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