jsir 2.4.5 → 2.4.6

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/cmd/oaa.js CHANGED
@@ -30,9 +30,8 @@ const _libDataDir = getLibDataDir()
30
30
  const _fileWatcherMap = {}
31
31
  const _types = setting.fileType
32
32
  const console = createConsole();
33
- const room = require('../deps/room');
34
- const server = require('../deps/server')
35
- const {reqNode} = require("../deps/room");
33
+ const Room = require('../deps/room');
34
+ const Server = require('../deps/server')
36
35
 
37
36
  let lastFilterArg = '';
38
37
  let _cmdMapFile = setting.name + 'CmdMap.json'
@@ -675,9 +674,9 @@ async function joinServer(uniqueName) {
675
674
  let pair = parseUniqueName(uniqueName)
676
675
  for (let key of Object.keys(exportLib)) {
677
676
  let val = exportLib[key];
678
- server.setRoute('post', `/${pair[0]}/${trimJsirFileName(pair[1])}/${key}`,async (req, res) => {
677
+ Server.setRoute('post', `/${pair[0]}/${trimJsirFileName(pair[1])}/${key}`,async (req, res) => {
679
678
  res.result = await val(...req);
680
- }, server.jsonHandle)
679
+ })
681
680
  }
682
681
  console.msg(uniqueName, 'has become a service.');
683
682
  }
@@ -1187,11 +1186,13 @@ const keywordDef = {
1187
1186
  setting: {
1188
1187
  comment: 'Global settings',
1189
1188
  exeFn: async (args) => {
1190
- let uniqueName = _cmdMap[args[0]];
1191
- if (uniqueName) {
1192
- let configDir = `${getLibDataDir()}/config`;
1193
- mkdir(configDir)
1194
- await eia(getEditor(), [`${configDir}/${md5(uniqueName)}.json`])
1189
+ if (args[0]) {
1190
+ if (_cmdMap[args[0]]){
1191
+ let uniqueName = _cmdMap[args[0]];
1192
+ await eia(getEditor(), [`${getConfigDir()}/${md5(uniqueName)}.json`])
1193
+ } else {
1194
+ console.warn('invalid args')
1195
+ }
1195
1196
  } else {
1196
1197
  await eia(getEditor(), [`${getLibDataDir()}/config.json`])
1197
1198
  }
@@ -1428,7 +1429,7 @@ const keywordDef = {
1428
1429
  quit: {
1429
1430
  comment: 'Exit',
1430
1431
  exeFn: (args) => {
1431
- room.offRoom();
1432
+ Room.offRoom();
1432
1433
  delTips();
1433
1434
  console.log(infoStr("Bye!"));
1434
1435
  _noAppendNextLine = true;
@@ -1490,7 +1491,11 @@ const keywordDef = {
1490
1491
  console.warn('log file not found')
1491
1492
  return;
1492
1493
  }
1493
- await eia(cmdStr, [`"${path}"`], true)
1494
+ if (isRunningInBackground()) {
1495
+ console.log(await e(`tail -n 500 "${path}"`))
1496
+ } else {
1497
+ await eia(cmdStr, [`"${path}"`], true)
1498
+ }
1494
1499
  },
1495
1500
  short: 'l'
1496
1501
  },
@@ -1509,7 +1514,7 @@ const keywordDef = {
1509
1514
  room: {
1510
1515
  comment: 'manage room ([node])',
1511
1516
  exeFn: async (args) => {
1512
- await room.syncSetting();
1517
+ await Room.syncSetting();
1513
1518
  if (args.length > 0) {
1514
1519
  let have = false;
1515
1520
  let tasks = []
@@ -1525,7 +1530,7 @@ const keywordDef = {
1525
1530
  tasks.push(async (input) => {
1526
1531
  let jsir = room.jsirs[pid];
1527
1532
  console.log(infoStr(`[${pid}]`))
1528
- let resp = await reqNode(room.selfNode, "post", "/enter", jsir.port, JSON.stringify({input}))
1533
+ let resp = await Room.reqNode(room.selfNode, "post", "/enter", jsir.port, JSON.stringify({input}))
1529
1534
  console.log(JSON.parse(resp).output)
1530
1535
  })
1531
1536
  }
@@ -2162,20 +2167,7 @@ async function _requireSource(currSpace, cmdMatchStr, force = false) {
2162
2167
  let serviceObj = {};
2163
2168
  for (let fn of setting.services[serviceKey]) {
2164
2169
  serviceObj[fn] = async (...args) => {
2165
- let key = `${serviceKey}/${fn}`;
2166
- let targets = setting.serviceFns[key] || []
2167
- targets = targets
2168
- .filter(i => i.active)
2169
- .filter(i => !room.isDisableConnect(i.node, i.port));
2170
- let target = null;
2171
- if (targets.length > 0) {
2172
- target = room.busyPick(targets);
2173
- } else {
2174
- throw `service function ${key} not found`;
2175
- }
2176
- let resp = await room.reqNode(target.node, 'post',
2177
- `/${pair[0]}/${encodeURIComponent(trimJsirFileName(pair[1]))}/${fn}`, target.port, JSON.stringify(args))
2178
- return JSON.parse(resp).result;
2170
+ return await Room.reqFn(`${serviceKey}/${fn}`, args);
2179
2171
  }
2180
2172
  }
2181
2173
  return serviceObj
@@ -2271,7 +2263,7 @@ process.on('beforeExit', function () {
2271
2263
  delTips();
2272
2264
  } else {
2273
2265
  nextLine();
2274
- room.onRoom(wrapperInput)
2266
+ Room.onRoom(wrapperInput)
2275
2267
  }
2276
2268
  });
2277
2269
 
package/deps/room.js CHANGED
@@ -1,20 +1,22 @@
1
1
  const os = require('os');
2
2
  const {fileJson, fileLock, vl, createConsole, getKeyTips, getValTips,
3
3
  getRoomsDir, e, regEach, isRunningInBackground,
4
- batchAsync, debugStr, trim, getOr, errorTag, warnStr, getConfig
4
+ batchAsync, debugStr, trim, getOr, errorTag, warnStr, getConfig, getConfigDir,
5
+ fileExist
5
6
  } = require('./util');
6
7
  const {setRoute, createSign} = require('../deps/server')
7
8
  const roomDataFile = "jsirRoom.json"
8
9
  const roomsDirLockKey = "RW_" + getRoomsDir();
9
10
  const syncRoomsLockKey = "SyncRooms_" + getRoomsDir();
11
+ const syncConfigsLockKey = "SyncConfigs_" + getConfigDir();
10
12
  const updateRoomInfoLockKey = "UPDATE_" + roomDataFile
11
13
  const console = createConsole();
12
- const net = require("net");
13
14
  const setting = require('../deps/setting')
14
15
  const fp = require('fs').promises
15
16
  const http = require('http');
16
- let tailscalePath = os.platform() === 'darwin' ?
17
+ const tailscalePath = os.platform() === 'darwin' ?
17
18
  '/Applications/Tailscale.app/Contents/MacOS/Tailscale':'tailscale';
19
+ const configMain = getConfig("configMain");
18
20
 
19
21
  function debug(...args) {
20
22
  if (global.$DEBUG) {
@@ -35,6 +37,19 @@ function isPidAlive(pid) {
35
37
  }
36
38
  }
37
39
 
40
+ async function localConfigs() {
41
+ let configDir = getConfigDir();
42
+ let configFiles = await fp.readdir(configDir)
43
+ let fns = configFiles
44
+ .map(i => () => fp.readFile(`${configDir}/${i}`));
45
+ let buffers = await batchAsync(fns, 33);
46
+ let configs = {}
47
+ for (let i = 0; i < configFiles.length; i++) {
48
+ configs[configFiles[i]] = String(buffers[i]);
49
+ }
50
+ return configs;
51
+ }
52
+
38
53
  function onRoom(wrapperInput) {
39
54
  if (!setting.roomTid[0]) {
40
55
  try {
@@ -42,12 +57,12 @@ function onRoom(wrapperInput) {
42
57
  setRoute("get", "/", (req, res) => setting.selfRoom)
43
58
  setRoute("post", "/enter", async (req, res) => {
44
59
  if (!getConfig("enableRemote", true)) {
45
- res.output = warnStr("Disable Remote!")
60
+ res.output = warnStr("Disable Remote")
46
61
  return;
47
62
  }
48
63
  if (vl(req.input)) {
49
64
  if (!isRunningInBackground() && ['.nsir', '.N'].indexOf(req.input.trim()) === -1) {
50
- res.output = warnStr("Disable Remote!")
65
+ res.output = warnStr("Disable Remote")
51
66
  return;
52
67
  }
53
68
  setting.enterOutputs = []
@@ -57,6 +72,11 @@ function onRoom(wrapperInput) {
57
72
  res.output = outputs.join('');
58
73
  }
59
74
  })
75
+ if (configMain) {
76
+ setRoute("post", "/" + setting.configMainFnKey, async (req, res) => {
77
+ res.result = await localConfigs();
78
+ })
79
+ }
60
80
  } catch (e) {
61
81
  debug("initRoute failed", e)
62
82
  }
@@ -252,11 +272,44 @@ async function initRoom() {
252
272
  if (roomUpdateTouchTime) {
253
273
  await fileLock(updateRoomInfoLockKey, updateRoomInfo, false)
254
274
  fileLock(syncRoomsLockKey, syncRooms, false);
275
+ if (!configMain && setting.serviceFns.hasOwnProperty(setting.configMainFnKey)) {
276
+ fileLock(syncConfigsLockKey, syncConfigs, false);
277
+ }
255
278
  }
256
279
 
257
280
  await syncSetting();
258
281
  }
259
282
 
283
+ async function syncConfigs() {
284
+ let configs;
285
+ try {
286
+ configs = await reqFn(setting.configMainFnKey);
287
+ } catch (e) {
288
+ debug("syncConfigs failed", e)
289
+ return;
290
+ }
291
+ let configDir = getConfigDir();
292
+ let fns = [];
293
+ for (let key of Object.keys(configs)) {
294
+ let path = configDir + "/" + key;
295
+ let text = configs[key];
296
+ fns.push(async () => {
297
+ if (await fileExist(path)) {
298
+ let currText = String(await fp.readFile(path));
299
+ if (currText !== text) {
300
+ await fp.writeFile(path, text)
301
+ debug(`sync ${key} success`);
302
+ }
303
+ } else {
304
+ await fp.writeFile(path, text)
305
+ debug(`sync ${key} success`);
306
+ }
307
+ });
308
+ }
309
+ await batchAsync(fns, 33)
310
+ debug('syncConfigs done')
311
+ }
312
+
260
313
  async function cleanRoom(room) {
261
314
  // 清理进程
262
315
  for (let pid of Object.keys(room.jsirs || {})) {
@@ -297,7 +350,11 @@ async function initRoomJsir(room) {
297
350
  for (let key of Object.keys(setting.routes)) {
298
351
  let ss = key.split("/").map(trim).filter(i => i);
299
352
  if (ss.length > 3) {
353
+ // 脚本空间service
300
354
  getOr(services, ss[1] + '/' + ss[2], []).push(ss[3])
355
+ } else if (ss.length > 2) {
356
+ // jsir service
357
+ getOr(services, ss[1], []).push(ss[2])
301
358
  }
302
359
  }
303
360
  // 初始化jsir
@@ -374,53 +431,57 @@ async function reqNode(node, method, url, port, body) {
374
431
  'sign': createSign()
375
432
  }
376
433
  };
377
- debug('reqRoom', JSON.stringify(opt))
378
- return await new Promise((resolve, reject) => {
379
- try {
380
- let timeoutMs = setting.reqNodeTimeouts[url] || setting.reqNodeDefaultTimeout;
381
- let timeout = null
382
-
383
- const req = http.request(opt, (res) => {
384
- let data = '';
385
- // 数据块接收
386
- res.on('data', (chunk) => {
387
- data += chunk;
434
+ let time = Date.now();
435
+ try {
436
+ return await new Promise((resolve, reject) => {
437
+ try {
438
+ let timeoutMs = setting.reqNodeTimeouts[url] || setting.reqNodeDefaultTimeout;
439
+ let timeout = null
440
+
441
+ const req = http.request(opt, (res) => {
442
+ let data = '';
443
+ // 数据块接收
444
+ res.on('data', (chunk) => {
445
+ data += chunk;
446
+ });
447
+ // 响应结束
448
+ res.on('end', () => {
449
+ clearTimeout(timeout)
450
+ // 检查响应状态码
451
+ if (res.statusCode !== 200) {
452
+ reject(errorTag(new Error(data), decodeURIComponent(url)));
453
+ return;
454
+ }
455
+ resolve(data)
456
+ });
388
457
  });
389
- // 响应结束
390
- res.on('end', () => {
391
- clearTimeout(timeout)
392
- // 检查响应状态码
393
- if (res.statusCode !== 200) {
394
- reject(errorTag(new Error(data), decodeURIComponent(url)));
395
- return;
458
+
459
+ // 设置超时逻辑
460
+ timeout = setTimeout(() => {
461
+ req.destroy(); // 中断请求
462
+ }, timeoutMs); // 10秒超时
463
+
464
+ // 错误处理
465
+ req.on('error', (e) => {
466
+ clearTimeout(timeout); // 清除超时定时器
467
+ if (room) {
468
+ setting.disableConnect[node + ":" + port] = Date.now() + setting.disableConnectLimit
396
469
  }
397
- resolve(data)
470
+ reject(e)
398
471
  });
399
- });
400
472
 
401
- // 设置超时逻辑
402
- timeout = setTimeout(() => {
403
- req.destroy(); // 中断请求
404
- }, timeoutMs); // 10秒超时
405
-
406
- // 错误处理
407
- req.on('error', (e) => {
408
- clearTimeout(timeout); // 清除超时定时器
409
- if (room) {
410
- setting.disableConnect[node + ":" + port] = Date.now() + setting.disableConnectLimit
473
+ if (body) {
474
+ req.write(body)
411
475
  }
476
+ // 结束请求
477
+ req.end();
478
+ } catch (e) {
412
479
  reject(e)
413
- });
414
-
415
- if (body) {
416
- req.write(body)
417
480
  }
418
- // 结束请求
419
- req.end();
420
- } catch (e) {
421
- reject(e)
422
- }
423
- })
481
+ });
482
+ } finally {
483
+ debug(`reqRoom cost ${Date.now() - time}ms`, JSON.stringify(opt), )
484
+ }
424
485
  }
425
486
 
426
487
  function busyPick(busyItems) {
@@ -441,11 +502,27 @@ function busyPick(busyItems) {
441
502
  }
442
503
  }
443
504
 
505
+ async function reqFn(fnKey, args = []) {
506
+ let targets = setting.serviceFns[fnKey] || []
507
+ targets = targets
508
+ .filter(i => i.active)
509
+ .filter(i => !isDisableConnect(i.node, i.port));
510
+ let target = null;
511
+ if (targets.length > 0) {
512
+ target = busyPick(targets);
513
+ } else {
514
+ throw `service function ${fnKey} not found`;
515
+ }
516
+ let resp = await reqNode(target.node, 'post',
517
+ `/${encodeURI(fnKey)}`, target.port, JSON.stringify(args || []))
518
+ return JSON.parse(resp).result;
519
+ }
520
+
444
521
  module.exports = {
445
522
  onRoom,
446
523
  offRoom,
447
524
  syncSetting,
448
525
  reqNode,
449
- isDisableConnect,
450
- busyPick
526
+ reqFn,
527
+ localConfigs
451
528
  }
package/deps/server.js CHANGED
@@ -36,7 +36,7 @@ function createSign() {
36
36
  async function createServer(port) {
37
37
  const server = http.createServer(async (req, res) => {
38
38
  const method = req.method.toLowerCase();
39
- const url = decodeURIComponent(req.url);
39
+ const url = decodeURI(req.url);
40
40
 
41
41
  // 获取客户端的 IP 地址
42
42
  const clientIp = req.socket.remoteAddress || req.connection.remoteAddress;
package/deps/setting.js CHANGED
@@ -39,5 +39,5 @@ module.exports = {
39
39
  roomTimer: 1000,
40
40
  serverSignExpire: 6000,
41
41
  roomHeartbeatExpire: 9000,
42
- consoleMap: {}
42
+ configMainFnKey: "config/main"
43
43
  }
package/deps/util.js CHANGED
@@ -256,7 +256,7 @@ function createConsole(uniqueName) {
256
256
  if ('debug' === key && !global.$DEBUG) {
257
257
  return;
258
258
  }
259
- if (uniqueName && (quite || isRunningInBackground())) {
259
+ if (uniqueName && quite) {
260
260
  if ((key === 'table' || key === 'nable') && typeof args[0] === "object") {
261
261
  args = ['', ...args]
262
262
  }
@@ -272,9 +272,6 @@ function createConsole(uniqueName) {
272
272
  }
273
273
  }
274
274
  }
275
- if (uniqueName) {
276
- setting.consoleMap[uniqueName] = result;
277
- }
278
275
  return result;
279
276
  }
280
277
 
@@ -1017,9 +1014,6 @@ function _getConfig(key, defaultVal, uniqueName) {
1017
1014
  if (key === undefined) {
1018
1015
  return config
1019
1016
  }
1020
- if (uniqueName && setting.consoleMap[uniqueName]) {
1021
- setting.consoleMap[uniqueName].msg(`require ${uniqueName} config "${key}"`)
1022
- }
1023
1017
  let writeFlag = false
1024
1018
  if (!(key in config)) {
1025
1019
  writeFlag = true
@@ -1175,14 +1169,7 @@ async function _fileLock(key, fn, expireMs = 49000) {
1175
1169
  const expireAt = expireMs > 0 ? now + expireMs : 0;
1176
1170
 
1177
1171
  // 1. 尝试判断锁目录是否已存在
1178
- let lockExists = false;
1179
- try {
1180
- await fp.access(lockKeyDir); // 若存在则不抛错
1181
- lockExists = true;
1182
- } catch (_) {
1183
- // 不存在时会抛ENOENT错误,说明还没有锁
1184
- lockExists = false;
1185
- }
1172
+ let lockExists = await fileExist(lockKeyDir);
1186
1173
 
1187
1174
  if (lockExists) {
1188
1175
  // 目录存在时,读取文件名中的过期时间戳
@@ -1569,6 +1556,9 @@ async function eia(cmd, args = [], shell = false) {
1569
1556
  `
1570
1557
  当前进程不会卡住,输入输出由cmd进程持有
1571
1558
  `
1559
+ if (isRunningInBackground()) {
1560
+ throw 'Unsupported Operation';
1561
+ }
1572
1562
  setting.enableNextLine = false;
1573
1563
  let child = spawn(cmd, args, {stdio:"inherit", shell});
1574
1564
  return new Promise((resolve, reject) => {
@@ -1895,7 +1885,7 @@ function md5(message) {
1895
1885
  return crypto.createHash('md5').update(message).digest('hex');
1896
1886
  }
1897
1887
 
1898
- async function batchAsync(fns = [], asyncNum = 1, limitMs = 49000) {
1888
+ async function batchAsync(fns = [], asyncNum = 9, limitMs = 0) {
1899
1889
  if (fns.length <= 0 || asyncNum < 1) {
1900
1890
  return []
1901
1891
  }
@@ -1905,7 +1895,12 @@ async function batchAsync(fns = [], asyncNum = 1, limitMs = 49000) {
1905
1895
  for(let i = 0; i< fns.length; i++) {
1906
1896
  let pro = new Promise(async (resolve, reject) => {
1907
1897
  try {
1908
- let resp = await timeLimit([fns[i]()], limitMs);
1898
+ let resp;
1899
+ if (limitMs) {
1900
+ resp = await timeLimit([fns[i]()], limitMs);
1901
+ } else {
1902
+ resp = [await fns[i]()];
1903
+ }
1909
1904
  doneMap[i] = pro;
1910
1905
  resolve(resp[0])
1911
1906
  } catch (e) {
@@ -2082,6 +2077,8 @@ function interceptStdStreams() {
2082
2077
  process.stdout.write = (chunk, ...args) => {
2083
2078
  if(setting.enterOutputs) {
2084
2079
  setting.enterOutputs.push(chunk.toString());
2080
+ } else if (isRunningInBackground()) {
2081
+ console.$log(process.pid, "STDOUT", "\n" + chunk.toString().trimEnd());
2085
2082
  } else {
2086
2083
  originalStdoutWrite(chunk, ...args); // 保留原始行为
2087
2084
  }
@@ -2091,6 +2088,8 @@ function interceptStdStreams() {
2091
2088
  process.stderr.write = (chunk, ...args) => {
2092
2089
  if(setting.enterOutputs) {
2093
2090
  setting.enterOutputs.push(chunk.toString());
2091
+ } else if (isRunningInBackground()) {
2092
+ console.$error(process.pid, errorStr("STDERR"), "\n" + chunk.toString().trimEnd());
2094
2093
  } else {
2095
2094
  originalStderrWrite(chunk, ...args); // 保留原始行为
2096
2095
  }
@@ -2206,5 +2205,6 @@ module.exports = {
2206
2205
  isRunningInBackground,
2207
2206
  createDetachedProcess,
2208
2207
  interceptStdStreams,
2209
- draftModify
2208
+ draftModify,
2209
+ fileExist
2210
2210
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsir",
3
- "version": "2.4.5",
3
+ "version": "2.4.6",
4
4
  "description": "JavaScript Script Management Tool",
5
5
  "main": "index.js",
6
6
  "scripts": {