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