Initial merged script-task into the agent.
Ylian Saint-Hilaire committed
Aug 18, 2022 at 16:19 UTC
3aca17ea6d35a650b2a225067f9978de6b62ee12
3 files changed
+438
-10
agents/meshcore.js
+6
-8
@@ -708,6 +708,7 @@ db = require('SimpleDataStore').Shared();
708
sha = require('SHA256Stream');
709
mesh = require('MeshAgent');
710
childProcess = require('child_process');
711
+try { scriptTask = require('script-task').CreateScriptTask(mesh); } catch (ex) { }
712
713
if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
714
// Check if this computer supports a desktop
@@ -798,10 +799,6 @@ function getIpLocationDataEx(func) {
799
catch (ex) { return false; }
800
}
801
801
-// Setup script task. Allows running scripts at scheduled intervals
802
-var scriptTask = null;
803
-try { scriptTask = require('scripttask'); } catch (ex) { }
804
-
802
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
803
function clearGatewayMac(str) {
804
if (typeof str != 'string') return null;
@@ -1559,6 +1556,10 @@ function handleServerCommand(data) {
1556
try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (ex) { throw ex; }
1557
break;
1558
}
1559
+ case 'task': {
1560
+ if (scriptTask) { scriptTask.consoleAction(data, data.rights, data.sessionid, false); }
1561
+ break;
1562
+ }
1563
case 'coredump':
1564
// Set the current agent coredump situation.s
1565
if (data.value === true) {
@@ -4566,10 +4567,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
4567
}
4568
case 'task': {
4569
if (!scriptTask) { response = "Tasks are not supported on this agent"; }
4569
- else {
4570
- if (args['_'][0]) { args.cmd = args['_'][0].toLowerCase(); }
4571
- response = scriptTask.processCommand(args, rights, sessionid);
4572
- }
4570
+ else { response = scriptTask.consoleAction(args, rights, sessionid, true); }
4571
break;
4572
}
4573
case 'plugin': {
agents/modules_meshcore/script-task.js
new
+416
@@ -0,0 +1,416 @@
1
+/**
2
+* @description MeshCentral Script-Task
3
+* @author Ryan Blenis
4
+* @copyright
5
+* @license Apache-2.0
6
+*/
7
+
8
+'use strict';
9
+function CreateScriptTask(parent) {
10
+ var obj = {};
11
+ var db = require('SimpleDataStore').Shared();
12
+ var pendingDownload = [];
13
+ var debugFlag = false;
14
+ var runningJobs = [];
15
+ var runningJobPIDs = {};
16
+
17
+ function dbg(str) {
18
+ if (debugFlag !== true) return;
19
+ var fs = require('fs');
20
+ var logStream = fs.createWriteStream('scripttask.txt', { 'flags': 'a' });
21
+ // use {'flags': 'a'} to append and {'flags': 'w'} to erase and write a new file
22
+ logStream.write('\n' + new Date().toLocaleString() + ': ' + str);
23
+ logStream.end('\n');
24
+ }
25
+
26
+ function removeFromArray(arr, from, to) {
27
+ var rest = arr.slice((to || from) + 1 || arr.length);
28
+ arr.length = from < 0 ? arr.length + from : from;
29
+ return arr.push.apply(arr, rest);
30
+ };
31
+
32
+ obj.consoleAction = function(args, rights, sessionid, interactive) {
33
+ //sendConsoleText('task: ' + JSON.stringify(args), sessionid); // Debug
34
+
35
+ /*
36
+ if (typeof args['_'] == 'undefined') {
37
+ args['_'] = [];
38
+ args['_'][1] = args.pluginaction; // TODO
39
+ args['_'][2] = null;
40
+ args['_'][3] = null;
41
+ args['_'][4] = null;
42
+ }
43
+ */
44
+
45
+ var fnname = args['_'][0];
46
+ if (fnname == null) { return "Valid task commands are: trigger, cache, clear, clearCache, debug, list"; }
47
+
48
+ switch (fnname.toLowerCase()) {
49
+ case 'trigger': {
50
+ var jObj = {
51
+ jobId: args.jobId,
52
+ scriptId: args.scriptId,
53
+ replaceVars: args.replaceVars,
54
+ scriptHash: args.scriptHash,
55
+ dispatchTime: args.dispatchTime
56
+ };
57
+ //dbg('jObj args is ' + JSON.stringify(jObj));
58
+ var sObj = getScriptFromCache(jObj.scriptId);
59
+ //dbg('sobj = ' + JSON.stringify(sObj) + ', shash = ' + jObj.scriptHash);
60
+ if ((sObj == null) || (sObj.contentHash != jObj.scriptHash)) {
61
+ // get from the server, then run
62
+ //dbg('Getting and caching script '+ jObj.scriptId);
63
+ parent.SendCommand({ action: 'script-task', subaction: 'getScript', scriptId: jObj.scriptId, sessionid: sessionid, tag: 'console' });
64
+ pendingDownload.push(jObj);
65
+ } else {
66
+ // ready to run
67
+ runScript(sObj, jObj, sessionid);
68
+ }
69
+ break;
70
+ }
71
+ case 'cache': {
72
+ var sObj = args.script;
73
+ cacheScript(sObj);
74
+ var setRun = [];
75
+ if (pendingDownload.length) {
76
+ pendingDownload.forEach(function (pd, k) {
77
+ if ((pd.scriptId == sObj._id) && (pd.scriptHash == sObj.contentHash)) {
78
+ if (setRun.indexOf(pd) === -1) { runScript(sObj, pd, sessionid); setRun.push(pd); }
79
+ removeFromArray(pendingDownload, k);
80
+ }
81
+ });
82
+ }
83
+ break;
84
+ }
85
+ case 'clear': {
86
+ clearCache();
87
+ parent.SendCommand({ action: 'script-task', subaction: 'clearAllPendingTasks', sessionid: sessionid, tag: 'console' });
88
+ return "Cache cleared. All pending tasks cleared.";
89
+ }
90
+ case 'clearcache': {
91
+ clearCache();
92
+ return "The script cache has been cleared";
93
+ }
94
+ case 'debug': {
95
+ debugFlag = (debugFlag) ? false : true;
96
+ var str = (debugFlag) ? 'on' : 'off';
97
+ return 'Debugging is now ' + str;
98
+ }
99
+ case 'list': {
100
+ var ret = '';
101
+ if (pendingDownload.length == 0) return "No tasks pending script download";
102
+ pendingDownload.forEach(function (pd, k) { ret += 'Task ' + k + ': ' + 'TaskID: ' + pd.jobId + ' ScriptID: ' + pd.scriptId + '\r\n'; });
103
+ return ret;
104
+ }
105
+ default: {
106
+ dbg('Unknown action: ' + fnname + ' with data ' + JSON.stringify(args));
107
+ break;
108
+ }
109
+ }
110
+ }
111
+
112
+ function finalizeJob(job, retVal, errVal, sessionid) {
113
+ if (errVal != null && errVal.stack != null) errVal = errVal.stack;
114
+ removeFromArray(runningJobs, runningJobs.indexOf(job.jobId));
115
+ if (typeof runningJobPIDs[job.jobId] != 'undefined') delete runningJobPIDs[job.jobId];
116
+ parent.SendCommand({
117
+ action: 'script-task',
118
+ subaction: 'taskComplete',
119
+ jobId: job.jobId,
120
+ scriptId: job.scriptId,
121
+ retVal: retVal,
122
+ errVal: errVal,
123
+ dispatchTime: job.dispatchTime, // include original run time (long running tasks could have tried a re-send)
124
+ sessionid: sessionid,
125
+ tag: 'console'
126
+ });
127
+ }
128
+
129
+ //@TODO Test powershell on *nix devices with and without powershell installed
130
+ function runPowerShell(sObj, jObj, sessionid) {
131
+ if (process.platform != 'win32') return runPowerShellNonWin(sObj, jObj);
132
+ const fs = require('fs');
133
+ var rand = Math.random().toString(32).replace('0.', '');
134
+
135
+ var oName = 'st' + rand + '.txt';
136
+ var pName = 'st' + rand + '.ps1';
137
+ var pwshout = '', pwsherr = '', cancontinue = false;
138
+ try {
139
+ fs.writeFileSync(pName, sObj.content);
140
+ var outstr = '', errstr = '';
141
+ var child = require('child_process').execFile(process.env['windir'] + '\\system32\\WindowsPowerShell\\v1.0\\powershell.exe', ['-NoLogo']);
142
+ child.stderr.on('data', function (chunk) { errstr += chunk; });
143
+ child.stdout.on('data', function (chunk) { });
144
+ runningJobPIDs[jObj.jobId] = child.pid;
145
+ child.stdin.write('.\\' + pName + ' | Out-File ' + oName + ' -Encoding UTF8\r\n');
146
+ child.on('exit', function (procRetVal, procRetSignal) {
147
+ dbg('Exiting with ' + procRetVal + ', Signal: ' + procRetSignal);
148
+ if (errstr != '') {
149
+ finalizeJob(jObj, null, errstr, sessionid);
150
+ try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
151
+ return;
152
+ }
153
+ if (procRetVal == 1) {
154
+ finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
155
+ try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
156
+ return;
157
+ }
158
+ try { outstr = fs.readFileSync(oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
159
+ if (outstr) {
160
+ //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
161
+ try { outstr = outstr.trim(); } catch (ex) { }
162
+ } else {
163
+ outstr = (procRetVal) ? 'Failure' : 'Success';
164
+ }
165
+ dbg('Output is: ' + outstr);
166
+ finalizeJob(jObj, outstr, null, sessionid);
167
+ try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { }
168
+ });
169
+ child.stdin.write('exit\r\n');
170
+ //child.waitExit(); // this was causing the event loop to stall on long-running scripts, switched to '.on exit'
171
+
172
+ } catch (ex) {
173
+ dbg('Error block was (PowerShell): ' + ex);
174
+ finalizeJob(jObj, null, ex, sessionid);
175
+ }
176
+ }
177
+
178
+ function runPowerShellNonWin(sObj, jObj, sessionid) {
179
+ const fs = require('fs');
180
+ var rand = Math.random().toString(32).replace('0.', '');
181
+
182
+ var path = '';
183
+ var pathTests = ['/usr/local/mesh', '/tmp', '/usr/local/mesh_services/meshagent', '/var/tmp'];
184
+ pathTests.forEach(function (p) { if (path == '' && fs.existsSync(p)) { path = p; } });
185
+ dbg('Path chosen is: ' + path);
186
+ path = path + '/';
187
+
188
+ var oName = 'st' + rand + '.txt';
189
+ var pName = 'st' + rand + '.ps1';
190
+ var pwshout = '', pwsherr = '', cancontinue = false;
191
+ try {
192
+ var childp = require('child_process').execFile('/bin/sh', ['sh']);
193
+ childp.stderr.on('data', function (chunk) { pwsherr += chunk; });
194
+ childp.stdout.on('data', function (chunk) { pwshout += chunk; });
195
+ childp.stdin.write('which pwsh' + '\n');
196
+ childp.stdin.write('exit\n');
197
+ childp.waitExit();
198
+ } catch (ex) { finalizeJob(jObj, null, "Couldn't determine pwsh in env: " + ex, sessionid); }
199
+ if (pwsherr != '') { finalizeJob(jObj, null, "PowerShell env determination error: " + pwsherr, sessionid); return; }
200
+ if (pwshout.trim() != '') { cancontinue = true; }
201
+ if (cancontinue === false) { finalizeJob(jObj, null, "PowerShell is not installed", sessionid); return; }
202
+ try {
203
+ fs.writeFileSync(path + pName, '#!' + pwshout + '\n' + sObj.content.split('\r\n').join('\n').split('\r').join('\n'));
204
+ var outstr = '', errstr = '';
205
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
206
+ child.stderr.on('data', function (chunk) { errstr += chunk; });
207
+ child.stdout.on('data', function (chunk) { });
208
+ runningJobPIDs[jObj.jobId] = child.pid;
209
+
210
+ child.stdin.write('cd ' + path + '\n');
211
+ child.stdin.write('chmod a+x ' + pName + '\n');
212
+ child.stdin.write('./' + pName + ' > ' + oName + '\n');
213
+ child.on('exit', function (procRetVal, procRetSignal) {
214
+ if (errstr != '') {
215
+ finalizeJob(jObj, null, errstr, sessionid);
216
+ try {
217
+ fs.unlinkSync(path + oName);
218
+ fs.unlinkSync(path + pName);
219
+ } catch (ex) { dbg('Could not unlink files, error was: ' + ex + ' for path ' + path); }
220
+ return;
221
+ }
222
+ if (procRetVal == 1) {
223
+ finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
224
+ try {
225
+ fs.unlinkSync(path + oName);
226
+ fs.unlinkSync(path + pName);
227
+ } catch (ex) { dbg('Could not unlink files1, error was: ' + ex + ' for path ' + path); }
228
+ return;
229
+ }
230
+ try { outstr = fs.readFileSync(path + oName, 'utf8').toString(); } catch (es) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
231
+ if (outstr) {
232
+ //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
233
+ try { outstr = outstr.trim(); } catch (ex) { }
234
+ } else {
235
+ outstr = (procRetVal) ? 'Failure' : 'Success';
236
+ }
237
+ dbg('Output is: ' + outstr);
238
+ finalizeJob(jObj, outstr, null, sessionid);
239
+ try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files2, error was: ' + ex + ' for path ' + path); }
240
+ });
241
+ child.stdin.write('exit\n');
242
+ } catch (ex) {
243
+ dbg('Error block was (PowerShellNonWin): ' + ex);
244
+ finalizeJob(jObj, null, ex, sessionid);
245
+ }
246
+ }
247
+
248
+ function runBat(sObj, jObj, sessionid) {
249
+ if (process.platform != 'win32') { finalizeJob(jObj, null, "Platform not supported.", sessionid); return; }
250
+ const fs = require('fs');
251
+ var rand = Math.random().toString(32).replace('0.', '');
252
+ var oName = 'st' + rand + '.txt';
253
+ var pName = 'st' + rand + '.bat';
254
+ try {
255
+ fs.writeFileSync(pName, sObj.content);
256
+ var outstr = '', errstr = '';
257
+ var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe');
258
+ child.stderr.on('data', function (chunk) { errstr += chunk; });
259
+ child.stdout.on('data', function (chunk) { });
260
+ runningJobPIDs[jObj.jobId] = child.pid;
261
+ child.stdin.write(pName + ' > ' + oName + '\r\n');
262
+ child.stdin.write('exit\r\n');
263
+
264
+ child.on('exit', function (procRetVal, procRetSignal) {
265
+ if (errstr != '') {
266
+ try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
267
+ finalizeJob(jObj, null, errstr, sessionid);
268
+ return;
269
+ }
270
+ if (procRetVal == 1) {
271
+ try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
272
+ finalizeJob(jObj, null, 'Process terminated unexpectedly.', sessionid);
273
+ return;
274
+ }
275
+ try { outstr = fs.readFileSync(oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
276
+ if (outstr) {
277
+ //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
278
+ try { outstr = outstr.trim(); } catch (ex) { }
279
+ } else {
280
+ outstr = (procRetVal) ? 'Failure' : 'Success';
281
+ }
282
+ dbg('Output is: ' + outstr);
283
+ try { fs.unlinkSync(oName); fs.unlinkSync(pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex); }
284
+ finalizeJob(jObj, outstr, null, sessionid);
285
+ });
286
+ } catch (ex) {
287
+ dbg('Error block was (BAT): ' + ex);
288
+ finalizeJob(jObj, null, ex, sessionid);
289
+ }
290
+ }
291
+
292
+ function runBash(sObj, jObj, sessionid) {
293
+ if (process.platform == 'win32') { finalizeJob(jObj, null, "Platform not supported.", sessionid); return; }
294
+ //dbg('proc is ' + JSON.stringify(process));
295
+ const fs = require('fs');
296
+ var path = '';
297
+ var pathTests = ['/usr/local/mesh', '/tmp', '/usr/local/mesh_services/meshagent', '/var/tmp'];
298
+ pathTests.forEach(function (p) {
299
+ if (path == '' && fs.existsSync(p)) { path = p; }
300
+ });
301
+ dbg('Path chosen is: ' + path);
302
+ path = path + '/';
303
+ //var child = require('child_process');
304
+ //child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
305
+
306
+ var rand = Math.random().toString(32).replace('0.', '');
307
+ var oName = 'st' + rand + '.txt';
308
+ var pName = 'st' + rand + '.sh';
309
+ try {
310
+ fs.writeFileSync(path + pName, sObj.content);
311
+ var outstr = '', errstr = '';
312
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
313
+ child.stderr.on('data', function (chunk) { errstr += chunk; });
314
+ child.stdout.on('data', function (chunk) { });
315
+ runningJobPIDs[jObj.jobId] = child.pid;
316
+ child.stdin.write('cd ' + path + '\n');
317
+ child.stdin.write('chmod a+x ' + pName + '\n');
318
+ child.stdin.write('./' + pName + ' > ' + oName + '\n');
319
+ child.stdin.write('exit\n');
320
+
321
+ child.on('exit', function (procRetVal, procRetSignal) {
322
+ if (errstr != '') {
323
+ try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files, error was: ' + ex + ' for path ' + path); }
324
+ finalizeJob(jObj, null, errstr, sessionid);
325
+ return;
326
+ }
327
+ if (procRetVal == 1) {
328
+ try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files1, error was: ' + ex + ' for path ' + path); }
329
+ finalizeJob(jObj, null, "Process terminated unexpectedly.", sessionid);
330
+ return;
331
+ }
332
+ try { outstr = fs.readFileSync(path + oName, 'utf8').toString(); } catch (ex) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
333
+ if (outstr) {
334
+ //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
335
+ try { outstr = outstr.trim(); } catch (ex) { }
336
+ } else {
337
+ outstr = (procRetVal) ? 'Failure' : 'Success';
338
+ }
339
+ dbg('Output is: ' + outstr);
340
+ try { fs.unlinkSync(path + oName); fs.unlinkSync(path + pName); } catch (ex) { dbg('Could not unlink files2, error was: ' + ex + ' for path ' + path); }
341
+ finalizeJob(jObj, outstr, null, sessionid);
342
+ });
343
+ } catch (ex) {
344
+ dbg('Error block was (bash): ' + ex);
345
+ finalizeJob(jObj, null, ex, sessionid);
346
+ }
347
+ }
348
+
349
+ function jobIsRunning(jObj) {
350
+ if (runningJobs.indexOf(jObj.jobId) === -1) return false;
351
+ return true;
352
+ }
353
+
354
+ function runScript(sObj, jObj, sessionid) {
355
+ // get current processes and clean running jobs if they are no longer running (computer fell asleep, user caused process to stop, etc.)
356
+ if (process.platform != 'linux' && runningJobs.length) { // linux throws errors here in the meshagent for some reason
357
+ require('process-manager').getProcesses(function (plist) {
358
+ dbg('Got process list');
359
+ dbg('There are currently ' + runningJobs.length + ' running jobs.');
360
+ if (runningJobs.length) {
361
+ runningJobs.forEach(function (jobId, idx) {
362
+ dbg('Checking for running job: ' + jobId + ' with PID ' + runningJobPIDs[jobId]);
363
+ if (typeof plist[runningJobPIDs[jobId]] == 'undefined' || typeof plist[runningJobPIDs[jobId]].cmd != 'string') {
364
+ dbg('Found job with no process. Removing running status.');
365
+ delete runningJobPIDs[jobId];
366
+ removeFromArray(runningJobs, runningJobs.indexOf(idx));
367
+ //dbg('RunningJobs: ' + JSON.stringify(runningJobs));
368
+ //dbg('RunningJobsPIDs: ' + JSON.stringify(runningJobPIDs));
369
+ }
370
+ });
371
+ }
372
+ });
373
+ }
374
+ if (jobIsRunning(jObj)) { dbg('Job already running job id [' + jObj.jobId + ']. Skipping.'); return; }
375
+ if (jObj.replaceVars != null) {
376
+ Object.getOwnPropertyNames(jObj.replaceVars).forEach(function (key) {
377
+ var val = jObj.replaceVars[key];
378
+ sObj.content = sObj.content.replace(new RegExp('#' + key + '#', 'g'), val);
379
+ dbg('replacing var ' + key + ' with ' + val);
380
+ });
381
+ sObj.content = sObj.content.replace(new RegExp('#(.*?)#', 'g'), 'VAR_NOT_FOUND');
382
+ }
383
+ runningJobs.push(jObj.jobId);
384
+ dbg('Running Script ' + sObj._id);
385
+ switch (sObj.filetype) {
386
+ case 'ps1': runPowerShell(sObj, jObj, sessionid); break;
387
+ case 'bat': runBat(sObj, jObj, sessionid); break;
388
+ case 'bash': runBash(sObj, jObj, sessionid); break;
389
+ default: dbg('Unknown filetype: ' + sObj.filetype); break;
390
+ }
391
+ }
392
+
393
+ function getScriptFromCache(id) {
394
+ var script = db.Get('scriptTask_script_' + id);
395
+ if (script == '' || script == null) return null;
396
+ try { script = JSON.parse(script); } catch (ex) { return null; }
397
+ return script;
398
+ }
399
+
400
+ function cacheScript(sObj) {
401
+ db.Put('scriptTask_script_' + sObj._id, sObj);
402
+ }
403
+
404
+ function clearCache() {
405
+ db.Keys.forEach(function (k) { if (k.indexOf('scriptTask_script_') === 0) { db.Put(k, null); db.Delete(k); } });
406
+ }
407
+
408
+ function sendConsoleText(text, sessionid) {
409
+ if (typeof text == 'object') { text = JSON.stringify(text); }
410
+ parent.SendCommand({ action: 'msg', type: 'console', value: 'XXX: ' + text, sessionid: sessionid });
411
+ }
412
+
413
+ return obj;
414
+}
415
+
416
+module.exports = { CreateScriptTask: CreateScriptTask };
\ No newline at end of file
meshagent.js
+16
-2
@@ -1738,8 +1738,22 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1738
try { obj.send(JSON.stringify({ action: 'amtconfig', user: '**MeshAgentApfTunnel**', pass: cookie })); } catch (ex) { }
1739
break;
1740
}
1741
- case 'scriptTask': {
1742
- // TODO
1741
+ case 'script-task': {
1742
+ // These command are for running regular batch jobs on the remote device
1743
+ switch (command.subaction) {
1744
+ case 'getScript': {
1745
+ console.log('getScript');
1746
+ break;
1747
+ }
1748
+ case 'clearAllPendingTasks': {
1749
+ console.log('clearAllPendingTasks');
1750
+ break;
1751
+ }
1752
+ case 'taskComplete': {
1753
+ console.log('taskComplete');
1754
+ break;
1755
+ }
1756
+ }
1757
break;
1758
}
1759
default: {