jsir 2.3.1 → 2.3.3
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 +41 -65
- package/deps/evalCode.js +4 -2
- package/deps/room.js +232 -0
- package/deps/setting.js +7 -1
- package/deps/util.js +138 -40
- package/package.json +3 -2
package/cmd/oaa.js
CHANGED
|
@@ -11,7 +11,7 @@ const {
|
|
|
11
11
|
createConsole, setTips, delTips,
|
|
12
12
|
getEditor, errorStr, getConfigDir,
|
|
13
13
|
getFullPath, parseUniqueName, toUniqueName, isJsirFileName, toJsirFileName,
|
|
14
|
-
|
|
14
|
+
getAlias, wrapperJsirText, eia, getKeyTips, getValTips
|
|
15
15
|
} = $lib;
|
|
16
16
|
const _args = process.argv.slice(2).map(trim);
|
|
17
17
|
const evalCode = require('../deps/evalCode')
|
|
@@ -28,6 +28,7 @@ const _libDataDir = getLibDataDir()
|
|
|
28
28
|
const _fileWatcherMap = {}
|
|
29
29
|
const _types = setting.fileType
|
|
30
30
|
const console = createConsole();
|
|
31
|
+
const room = require('../deps/room');
|
|
31
32
|
|
|
32
33
|
let lastFilterArg = '';
|
|
33
34
|
let _cmdMapFile = setting.name + 'CmdMap.json'
|
|
@@ -138,7 +139,7 @@ function getFileOpenExe(fileName) {
|
|
|
138
139
|
|
|
139
140
|
function checkWorkspaces() {
|
|
140
141
|
let localWorkspace = _libDataDir + '/local';
|
|
141
|
-
|
|
142
|
+
setting.workspaceMap = {
|
|
142
143
|
'local': localWorkspace
|
|
143
144
|
}
|
|
144
145
|
mkdir(localWorkspace)
|
|
@@ -149,15 +150,15 @@ function checkWorkspaces() {
|
|
|
149
150
|
continue
|
|
150
151
|
}
|
|
151
152
|
if (fs.existsSync(workspace)) {
|
|
152
|
-
|
|
153
|
+
setting.workspaceMap[space] = workspace;
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
|
-
arrayDataFile(_workspaceConfigFile, () => Object.values(
|
|
156
|
-
initWorkspace()
|
|
156
|
+
arrayDataFile(_workspaceConfigFile, () => Object.values(setting.workspaceMap))
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
async function start() {
|
|
160
160
|
checkWorkspaces()
|
|
161
|
+
initWorkspace()
|
|
161
162
|
|
|
162
163
|
lastFilterArg = getConfig("lastFilterArg");
|
|
163
164
|
_cmdMap = objDataFile(_cmdMapFile);
|
|
@@ -288,7 +289,7 @@ async function nextLine(callback, promptStr, hidden) {
|
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
function defaultPromptStr(showKey = false) {
|
|
291
|
-
let tips = [getTipStr(showKey),
|
|
292
|
+
let tips = [getTipStr(showKey), setting.defaultSpace].filter(i => i).join(':') + `> `;
|
|
292
293
|
_isTipsDoneShowKey = showKey;
|
|
293
294
|
return tips;
|
|
294
295
|
}
|
|
@@ -345,28 +346,7 @@ function initRl(callback, promptStr, hidden) {
|
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
function getTipStr(showKey = false) {
|
|
348
|
-
let
|
|
349
|
-
for (let key of Object.keys(global.$tips)) {
|
|
350
|
-
let val = global.$tips[key].map(i => {
|
|
351
|
-
let item = trim(i)
|
|
352
|
-
if (item.indexOf(',') !== -1) {
|
|
353
|
-
item = `[${item}]`;
|
|
354
|
-
}
|
|
355
|
-
return item;
|
|
356
|
-
}).join("|");
|
|
357
|
-
key = trim(key)
|
|
358
|
-
|
|
359
|
-
let item
|
|
360
|
-
if (!showKey && vl(val)) {
|
|
361
|
-
item = val;
|
|
362
|
-
} else if (vl(key)) {
|
|
363
|
-
item = key
|
|
364
|
-
}
|
|
365
|
-
if (item) {
|
|
366
|
-
items.push(item)
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
let tips = items.join(',')
|
|
349
|
+
let tips = showKey ? getKeyTips().join(","):getValTips().join(",")
|
|
370
350
|
if (!showKey && !_isTipsDoneShowKey && tips.length > 64) {
|
|
371
351
|
return '...'
|
|
372
352
|
}
|
|
@@ -380,6 +360,10 @@ function closeRl() {
|
|
|
380
360
|
}
|
|
381
361
|
|
|
382
362
|
function _nextLine(callback, promptStr, hidden, resolve, end, isText) {
|
|
363
|
+
if (!setting.enableNextLine) {
|
|
364
|
+
console.$log(warnStr("[warn]"), "NextLine Disabled");
|
|
365
|
+
return
|
|
366
|
+
}
|
|
383
367
|
end = trim(end)
|
|
384
368
|
if (!_haveWrapperInput) {
|
|
385
369
|
return
|
|
@@ -872,14 +856,14 @@ function help(filterFn) {
|
|
|
872
856
|
}
|
|
873
857
|
|
|
874
858
|
function delTipsByIndex(idxs) {
|
|
875
|
-
let keys = Object.keys(
|
|
859
|
+
let keys = Object.keys(setting.tips)
|
|
876
860
|
let indexKeyMap = {}
|
|
877
861
|
for(let i = 1;i<=keys.length;i++) {
|
|
878
862
|
indexKeyMap[i] = keys[i - 1]
|
|
879
863
|
}
|
|
880
864
|
let params = []
|
|
881
865
|
for (const index of idxs) {
|
|
882
|
-
if (
|
|
866
|
+
if (setting.tips.hasOwnProperty(index)) {
|
|
883
867
|
params.push(index)
|
|
884
868
|
} else if (indexKeyMap[Number(index)]) {
|
|
885
869
|
params.push(indexKeyMap[Number(index)])
|
|
@@ -895,7 +879,7 @@ function delTipsByIndex(idxs) {
|
|
|
895
879
|
}
|
|
896
880
|
|
|
897
881
|
function getPackageVersion(space, name) {
|
|
898
|
-
let moduleDir =
|
|
882
|
+
let moduleDir = setting.workspaceMap[space] + '/node_modules'
|
|
899
883
|
let dir = moduleDir + "/" + name;
|
|
900
884
|
if (fs.existsSync(dir) && fs.existsSync(dir + '/package.json')) {
|
|
901
885
|
return require(dir + '/package.json').version;
|
|
@@ -908,12 +892,12 @@ function copyToSpace(uniqueName) {
|
|
|
908
892
|
let path = getFullPath(uniqueName)
|
|
909
893
|
let text = String(fs.readFileSync(path))
|
|
910
894
|
let fileName = parseUniqueName(uniqueName)[1];
|
|
911
|
-
let targetPath =
|
|
895
|
+
let targetPath = setting.workspaceMap[setting.defaultSpace] + '/' + fileName;
|
|
912
896
|
if (fs.existsSync(targetPath)) {
|
|
913
|
-
console.warn(`${fileName} already exist in ${
|
|
897
|
+
console.warn(`${fileName} already exist in ${setting.defaultSpace}`)
|
|
914
898
|
} else {
|
|
915
899
|
fs.writeFileSync(targetPath, text)
|
|
916
|
-
console.info(`get ${uniqueName} to ${
|
|
900
|
+
console.info(`get ${uniqueName} to ${setting.defaultSpace}`)
|
|
917
901
|
}
|
|
918
902
|
}
|
|
919
903
|
|
|
@@ -1076,7 +1060,7 @@ const keywordDef = {
|
|
|
1076
1060
|
} else {
|
|
1077
1061
|
let pair = parseUniqueName(uniqueName)
|
|
1078
1062
|
let newName =trim(args.slice(1).join(" "))
|
|
1079
|
-
rename(
|
|
1063
|
+
rename(setting.workspaceMap[pair[0]], pair[1], toJsirFileName(newName), true)
|
|
1080
1064
|
}
|
|
1081
1065
|
},
|
|
1082
1066
|
args: {
|
|
@@ -1165,8 +1149,8 @@ const keywordDef = {
|
|
|
1165
1149
|
return;
|
|
1166
1150
|
}
|
|
1167
1151
|
|
|
1168
|
-
let aFiles = fs.readdirSync(
|
|
1169
|
-
let bFiles = fs.readdirSync(
|
|
1152
|
+
let aFiles = fs.readdirSync(setting.workspaceMap[aSpace]).filter(isJsirFileName)
|
|
1153
|
+
let bFiles = fs.readdirSync(setting.workspaceMap[bSpace]).filter(isJsirFileName)
|
|
1170
1154
|
|
|
1171
1155
|
let result = compareMode[mode](aSpace, bSpace, aFiles, bFiles);
|
|
1172
1156
|
resetCmdMap(arrayToCmdMap(result))
|
|
@@ -1208,8 +1192,8 @@ const keywordDef = {
|
|
|
1208
1192
|
Object.keys(packages).forEach(key => {
|
|
1209
1193
|
let info = packages[key];
|
|
1210
1194
|
info.version = getPackageVersion(info.space, info.name)
|
|
1211
|
-
if (
|
|
1212
|
-
info.loaded =
|
|
1195
|
+
if (setting.packages.hasOwnProperty(info.name)) {
|
|
1196
|
+
info.loaded = setting.packages[info.name]
|
|
1213
1197
|
}
|
|
1214
1198
|
if (info.loaded && info.loaded !== info.space) {
|
|
1215
1199
|
let version = getPackageVersion(info.loaded, info.name)
|
|
@@ -1252,14 +1236,14 @@ const keywordDef = {
|
|
|
1252
1236
|
let newWorkspace = args.join(' ')
|
|
1253
1237
|
let isSwitch = false
|
|
1254
1238
|
if (newWorkspace) {
|
|
1255
|
-
if (
|
|
1239
|
+
if (setting.workspaceMap[newWorkspace]) {
|
|
1256
1240
|
isSwitch = true;
|
|
1257
1241
|
initWorkspace(newWorkspace)
|
|
1258
1242
|
} else if (newWorkspace.startsWith('-')) {
|
|
1259
1243
|
let name = newWorkspace.replace(/^-\s*/, '');
|
|
1260
1244
|
arrayDataFile(_workspaceConfigFile, arr => arr.filter(path => name !== getSpaceFromDir(path)))
|
|
1261
1245
|
} else if (newWorkspace.startsWith("/")) {
|
|
1262
|
-
let workspaces = Object.values(
|
|
1246
|
+
let workspaces = Object.values(setting.workspaceMap)
|
|
1263
1247
|
workspaces.push(newWorkspace)
|
|
1264
1248
|
arrayDataFile(_workspaceConfigFile, () => workspaces)
|
|
1265
1249
|
} else {
|
|
@@ -1267,10 +1251,11 @@ const keywordDef = {
|
|
|
1267
1251
|
}
|
|
1268
1252
|
}
|
|
1269
1253
|
checkWorkspaces()
|
|
1270
|
-
|
|
1254
|
+
initWorkspace(setting.defaultSpace)
|
|
1255
|
+
let workspaces = Object.values(setting.workspaceMap)
|
|
1271
1256
|
let items = workspaces.map((path, index) => {
|
|
1272
1257
|
return {
|
|
1273
|
-
name: (getSpaceFromDir(path) ===
|
|
1258
|
+
name: (getSpaceFromDir(path) === setting.defaultSpace ? '*':' ') + getSpaceFromDir(path),
|
|
1274
1259
|
path
|
|
1275
1260
|
}
|
|
1276
1261
|
})
|
|
@@ -1344,6 +1329,7 @@ const keywordDef = {
|
|
|
1344
1329
|
quit: {
|
|
1345
1330
|
comment: 'Exit',
|
|
1346
1331
|
exeFn: (args) => {
|
|
1332
|
+
room.offRoom();
|
|
1347
1333
|
delTips();
|
|
1348
1334
|
console.log(infoStr("Bye!"));
|
|
1349
1335
|
_noAppendNextLine = true;
|
|
@@ -1356,6 +1342,8 @@ const keywordDef = {
|
|
|
1356
1342
|
_noAppendNextLine = false
|
|
1357
1343
|
resetCmdMap()
|
|
1358
1344
|
console.log(warnStr(`(${setting.name} ${packageJson.version}) You can start with .help, use * to expand context.`))
|
|
1345
|
+
room.onRoom()
|
|
1346
|
+
nextLine()
|
|
1359
1347
|
},
|
|
1360
1348
|
short: 'p'
|
|
1361
1349
|
},
|
|
@@ -1370,7 +1358,7 @@ const keywordDef = {
|
|
|
1370
1358
|
if (args.length > 1) {
|
|
1371
1359
|
_args = args.slice(1)
|
|
1372
1360
|
}
|
|
1373
|
-
await eia(`cd "${
|
|
1361
|
+
await eia(`cd "${setting.workspaceMap[setting.defaultSpace]}";${cmd}`, _args, true)
|
|
1374
1362
|
},
|
|
1375
1363
|
short: 'E'
|
|
1376
1364
|
},
|
|
@@ -1389,12 +1377,12 @@ const keywordDef = {
|
|
|
1389
1377
|
}
|
|
1390
1378
|
}
|
|
1391
1379
|
let suffix = 'log'
|
|
1392
|
-
let cmdStr = 'less -R'
|
|
1380
|
+
let cmdStr = 'less -R +G'
|
|
1393
1381
|
if (type.indexOf("e") !== -1) {
|
|
1394
1382
|
suffix = 'error'
|
|
1395
1383
|
}
|
|
1396
1384
|
if (type.indexOf("t") !== -1) {
|
|
1397
|
-
cmdStr = '
|
|
1385
|
+
cmdStr = 'less -R +F'
|
|
1398
1386
|
}
|
|
1399
1387
|
path = path + '.' + suffix;
|
|
1400
1388
|
if (!fs.existsSync(path)) {
|
|
@@ -1623,13 +1611,13 @@ function getJsirFileSuffix(name) {
|
|
|
1623
1611
|
function initWorkspace(space) {
|
|
1624
1612
|
let current = getConfig("workspace");
|
|
1625
1613
|
let workspace = space || current || 'local'
|
|
1626
|
-
if (
|
|
1614
|
+
if (!setting.workspaceMap[workspace]) {
|
|
1627
1615
|
workspace = 'local'
|
|
1628
1616
|
}
|
|
1629
|
-
if (workspace !== current) {
|
|
1617
|
+
if (workspace !== current && _noAppendNextLine) {
|
|
1630
1618
|
setConfig("workspace", workspace)
|
|
1631
1619
|
}
|
|
1632
|
-
|
|
1620
|
+
setting.defaultSpace = workspace;
|
|
1633
1621
|
}
|
|
1634
1622
|
|
|
1635
1623
|
function filterCmdAndList(arg) {
|
|
@@ -1655,10 +1643,10 @@ function filterCmd(arg){
|
|
|
1655
1643
|
let spaceName
|
|
1656
1644
|
if (arg.indexOf('/') !== -1) {
|
|
1657
1645
|
let index = arg.indexOf('/');
|
|
1658
|
-
spaceName = trim(arg.substring(0, index)) ||
|
|
1646
|
+
spaceName = trim(arg.substring(0, index)) || setting.defaultSpace
|
|
1659
1647
|
arg = trim(arg.substring(index + 1))
|
|
1660
1648
|
}
|
|
1661
|
-
for (let workspace of Object.values(
|
|
1649
|
+
for (let workspace of Object.values(setting.workspaceMap)) {
|
|
1662
1650
|
let spaceTmp = getSpaceFromDir(workspace);
|
|
1663
1651
|
if (!spaceName || spaceName === spaceTmp) {
|
|
1664
1652
|
cmds.push(..._filterCmd(workspace, arg));
|
|
@@ -2063,7 +2051,7 @@ async function evalText($text = '', $cmdName = '', $args = []) {
|
|
|
2063
2051
|
let pair = parseUniqueName($cmdName)
|
|
2064
2052
|
currSpace = pair[0]
|
|
2065
2053
|
} else {
|
|
2066
|
-
currSpace =
|
|
2054
|
+
currSpace = setting.defaultSpace
|
|
2067
2055
|
}
|
|
2068
2056
|
let $requires = async (...matchItems) => {
|
|
2069
2057
|
let result = []
|
|
@@ -2080,22 +2068,13 @@ async function evalText($text = '', $cmdName = '', $args = []) {
|
|
|
2080
2068
|
$require, $requires,
|
|
2081
2069
|
nextLine, nextText,
|
|
2082
2070
|
wrapperInput, filterCmd,
|
|
2083
|
-
currSpace,
|
|
2071
|
+
currSpace, setting.defaultSpace, setting.workspaceMap,
|
|
2084
2072
|
$homeDir, $lib, _cmdMap);
|
|
2085
2073
|
}
|
|
2086
2074
|
|
|
2087
|
-
function clearFileLock() {
|
|
2088
|
-
let fileLockMap = getOr(global, `$fileLock`, {})
|
|
2089
|
-
for (let file of Object.keys(fileLockMap)) {
|
|
2090
|
-
fp.rmdir(file)
|
|
2091
|
-
delete fileLockMap[file]
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
2075
|
function sigExit() {
|
|
2096
2076
|
if (_noAppendNextLine) {
|
|
2097
2077
|
delTips();
|
|
2098
|
-
clearFileLock();
|
|
2099
2078
|
process.exit(0);
|
|
2100
2079
|
} else {
|
|
2101
2080
|
nextLine();
|
|
@@ -2104,15 +2083,12 @@ function sigExit() {
|
|
|
2104
2083
|
|
|
2105
2084
|
process.on('uncaughtException',function(err){
|
|
2106
2085
|
console.$error('uncaughtException', err)
|
|
2107
|
-
_noAppendNextLine || nextLine()
|
|
2108
2086
|
})
|
|
2109
2087
|
process.on('unhandledRejection',function(err){
|
|
2110
2088
|
console.$error('unhandledRejection', err)
|
|
2111
|
-
_noAppendNextLine || nextLine()
|
|
2112
2089
|
})
|
|
2113
2090
|
process.on('rejectionHandled',function(err){
|
|
2114
2091
|
console.$error('rejectionHandled', err)
|
|
2115
|
-
_noAppendNextLine || nextLine()
|
|
2116
2092
|
})
|
|
2117
2093
|
process.on('SIGINT', sigExit);
|
|
2118
2094
|
process.on('SIGTERM', sigExit);
|
package/deps/evalCode.js
CHANGED
|
@@ -3,7 +3,7 @@ module.exports = async ($text = '', $cmdName = '', $args = [],
|
|
|
3
3
|
$require, $requires,
|
|
4
4
|
$nextLine, $nextText,
|
|
5
5
|
$enter, $filterCmd,
|
|
6
|
-
$currentSpace,
|
|
6
|
+
$currentSpace, $defaultSpace, $workspaceMap,
|
|
7
7
|
$homeDir, $lib,
|
|
8
8
|
$cmdMap) => {
|
|
9
9
|
const $defArgs = () => $args;
|
|
@@ -26,13 +26,15 @@ module.exports = async ($text = '', $cmdName = '', $args = [],
|
|
|
26
26
|
const $errorTag = $lib.errorTag;
|
|
27
27
|
|
|
28
28
|
const $context = {
|
|
29
|
+
$defArgs,
|
|
29
30
|
$data, $config, $file,
|
|
30
31
|
$require, $requires, $import,
|
|
31
32
|
$text, $cmdName, $args,
|
|
32
33
|
$nextLine, $nextText,
|
|
33
34
|
$setTips, $delTips,
|
|
34
35
|
$enter, $filterCmd,
|
|
35
|
-
$currentSpace, $
|
|
36
|
+
$currentSpace, $defaultSpace, $workspaceMap,
|
|
37
|
+
$homeDir, $lib, $cmdMap
|
|
36
38
|
}
|
|
37
39
|
let console = $lib.createConsole($cmdName);
|
|
38
40
|
return await eval(`(async ()=>{${$text};
|
package/deps/room.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const {fileJson, fileLock, vl, createConsole, getKeyTips, getValTips} = require('./util');
|
|
3
|
+
const roomDataFile = "jsirRoom.json"
|
|
4
|
+
const console = createConsole();
|
|
5
|
+
const ping = require("ping");
|
|
6
|
+
const net = require("net");
|
|
7
|
+
const setting = require('../deps/setting')
|
|
8
|
+
|
|
9
|
+
function isPidAlive(pid) {
|
|
10
|
+
try {
|
|
11
|
+
process.kill(Number(pid), 0); // 信号 0 不会实际发送,但会检查进程是否存在
|
|
12
|
+
return true; // PID 存在
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.code === 'ESRCH') {
|
|
15
|
+
return false; // 进程不存在
|
|
16
|
+
}
|
|
17
|
+
console.$error(`check isPidAlive ${pid} failed`, err)
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function onRoom() {
|
|
23
|
+
return
|
|
24
|
+
if (!setting.roomTid[0]) {
|
|
25
|
+
_onRoom();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _onRoom() {
|
|
30
|
+
setting.roomTid[0] = setTimeout(async () => {
|
|
31
|
+
try {
|
|
32
|
+
await _initRoom();
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.$error('initRoom', e)
|
|
35
|
+
}
|
|
36
|
+
if (setting.roomTid[0]) {
|
|
37
|
+
_onRoom();
|
|
38
|
+
}
|
|
39
|
+
}, 1000)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function offRoom() {
|
|
43
|
+
return;
|
|
44
|
+
if (setting.roomTid[0]) {
|
|
45
|
+
clearTimeout(setting.roomTid[0])
|
|
46
|
+
setting.roomTid[0] = null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function getTailScaleNodes() {
|
|
51
|
+
console.$log("getNodes")
|
|
52
|
+
let ips = getLocalIPs().ipv4;
|
|
53
|
+
ips = ips.filter(i => i.startsWith("100.")); // tailScale ip前缀
|
|
54
|
+
let nodes = []
|
|
55
|
+
for (let ip of ips) {
|
|
56
|
+
let netIps = await scanLocalNetwork(ip);
|
|
57
|
+
netIps = netIps.filter(i => i !== ip)
|
|
58
|
+
nodes.push(...netIps)
|
|
59
|
+
}
|
|
60
|
+
return await sshEnables(nodes);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function initRoomInfo() {
|
|
64
|
+
await fileLock(roomDataFile + "getNodes", async () => {
|
|
65
|
+
let nodes = await getTailScaleNodes();
|
|
66
|
+
await fileJson(roomDataFile, async room => {
|
|
67
|
+
// 设置roomName
|
|
68
|
+
let name = os.hostname();
|
|
69
|
+
if (!vl(room.name) || room.name !== name) {
|
|
70
|
+
room.name = name;
|
|
71
|
+
console.$log("set roomName", name)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
room.nodes = nodes;
|
|
75
|
+
room.lastUpdateTime = Date.now()
|
|
76
|
+
console.$log("init room", room.name)
|
|
77
|
+
})
|
|
78
|
+
}, false)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function _initRoom() {
|
|
82
|
+
let roomUpdateTouchTime = false;
|
|
83
|
+
await fileJson(roomDataFile, async room => {
|
|
84
|
+
await initRoomJsir(room)
|
|
85
|
+
if (!vl(room.lastUpdateTime) || (Date.now() - room.lastUpdateTime) > 9000) {
|
|
86
|
+
roomUpdateTouchTime = true;
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
if (roomUpdateTouchTime) {
|
|
91
|
+
initRoomInfo();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function getEventLoopDelay() {
|
|
96
|
+
const start = process.hrtime.bigint();
|
|
97
|
+
const delay = 1; // 用于测量的短暂延迟(单位:毫秒)
|
|
98
|
+
const timeoutPromise = new Promise(resolve => setTimeout(resolve, delay));
|
|
99
|
+
return await timeoutPromise.then(() => {
|
|
100
|
+
const end = process.hrtime.bigint();
|
|
101
|
+
return Number(Math.max(Number(end - start) / 1e4 - (delay * 1e2), 0).toFixed(0)); // 避免负值
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function initRoomJsir(room) {
|
|
106
|
+
room.jsir = room.jsir || {};
|
|
107
|
+
|
|
108
|
+
for (let pid of [...Object.keys(room.jsir)]) {
|
|
109
|
+
if (!isPidAlive(pid)) {
|
|
110
|
+
delete room.jsir[pid]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
room.jsir[process.pid] = {
|
|
114
|
+
pid: process.pid,
|
|
115
|
+
space: setting.defaultSpace,
|
|
116
|
+
tips: getKeyTips().reduce((obj, key, index) => {
|
|
117
|
+
obj[key] = getValTips()[index];
|
|
118
|
+
return obj;
|
|
119
|
+
}, {}),
|
|
120
|
+
busy: await getEventLoopDelay(),
|
|
121
|
+
back: isRunningInBackground(),
|
|
122
|
+
lastUpdateTime: Date.now()
|
|
123
|
+
}
|
|
124
|
+
console.$log("init jsir", process.pid)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isRunningInBackground() {
|
|
128
|
+
return !(process.stdout.isTTY && process.stdin.isTTY);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 获取本机所有内网 IP
|
|
133
|
+
* @returns {{ ipv4: string[], ipv6: string[] }}
|
|
134
|
+
*/
|
|
135
|
+
function getLocalIPs() {
|
|
136
|
+
const networkInterfaces = os.networkInterfaces();
|
|
137
|
+
|
|
138
|
+
const result = {
|
|
139
|
+
ipv4: [],
|
|
140
|
+
ipv6: []
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
for (const interfaceName of Object.keys(networkInterfaces)) {
|
|
144
|
+
for (const netInfo of networkInterfaces[interfaceName]) {
|
|
145
|
+
const { family, address, internal } = netInfo;
|
|
146
|
+
|
|
147
|
+
// 只关心非回环地址
|
|
148
|
+
if (!internal) {
|
|
149
|
+
if (family === 'IPv4') {
|
|
150
|
+
result.ipv4.push(address);
|
|
151
|
+
} else if (family === 'IPv6') {
|
|
152
|
+
result.ipv6.push(address);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function sshEnables(ips) {
|
|
162
|
+
// 检查开放 22 端口的 IP
|
|
163
|
+
const sshEnabledIPs = [];
|
|
164
|
+
const portCheckPromises = ips.map((ip) =>
|
|
165
|
+
checkPortOpen(ip, 22).then((isOpen) => {
|
|
166
|
+
if (isOpen) {
|
|
167
|
+
sshEnabledIPs.push(ip);
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
await Promise.all(portCheckPromises);
|
|
173
|
+
return sshEnabledIPs;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 检查指定 IP 的指定端口是否开放
|
|
178
|
+
* @param {string} ip - 目标 IP 地址
|
|
179
|
+
* @param {number} port - 目标端口
|
|
180
|
+
* @returns {Promise<boolean>} - 是否开放
|
|
181
|
+
*/
|
|
182
|
+
function checkPortOpen(ip, port) {
|
|
183
|
+
return new Promise((resolve) => {
|
|
184
|
+
const socket = new net.Socket();
|
|
185
|
+
socket.setTimeout(1000); // 设置超时时间
|
|
186
|
+
|
|
187
|
+
socket
|
|
188
|
+
.connect(port, ip, () => {
|
|
189
|
+
socket.destroy(); // 成功连接后关闭 socket
|
|
190
|
+
resolve(true);
|
|
191
|
+
})
|
|
192
|
+
.on("error", () => {
|
|
193
|
+
socket.destroy(); // 遇到错误时关闭 socket
|
|
194
|
+
resolve(false);
|
|
195
|
+
})
|
|
196
|
+
.on("timeout", () => {
|
|
197
|
+
socket.destroy(); // 超时时关闭 socket
|
|
198
|
+
resolve(false);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 扫描局域网中的可访问IP
|
|
205
|
+
* @param {string} localIP - 本机内网IP地址
|
|
206
|
+
* @returns {Promise<string[]>} - 可访问的IP地址列表
|
|
207
|
+
*/
|
|
208
|
+
async function scanLocalNetwork(localIP) {
|
|
209
|
+
const subnet = localIP.substring(0, localIP.lastIndexOf('.') + 1); // 例如 "192.168.1."
|
|
210
|
+
const reachableIPs = [];
|
|
211
|
+
|
|
212
|
+
// 扫描 1 到 254 的 IP
|
|
213
|
+
const promises = [];
|
|
214
|
+
for (let i = 1; i <= 254; i++) {
|
|
215
|
+
const ip = `${subnet}${i}`;
|
|
216
|
+
promises.push(
|
|
217
|
+
ping.promise.probe(ip, { timeout: 1 }).then((res) => {
|
|
218
|
+
if (res.alive) {
|
|
219
|
+
reachableIPs.push(ip);
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
await Promise.all(promises);
|
|
226
|
+
return reachableIPs;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
module.exports = {
|
|
230
|
+
onRoom,
|
|
231
|
+
offRoom
|
|
232
|
+
}
|
package/deps/setting.js
CHANGED
package/deps/util.js
CHANGED
|
@@ -16,11 +16,7 @@ const _types = setting.fileType
|
|
|
16
16
|
const _typeKeys = Object.keys(_types)
|
|
17
17
|
|
|
18
18
|
global.$newInput = false
|
|
19
|
-
global.$workspaceMap = {}
|
|
20
|
-
global.$defaultSpace = 'local'
|
|
21
19
|
global.$newError = false
|
|
22
|
-
global.$tips = {}
|
|
23
|
-
global.$packages = {}
|
|
24
20
|
|
|
25
21
|
let libDataDir;
|
|
26
22
|
let lockDir;
|
|
@@ -256,7 +252,7 @@ function getFullPath(name) {
|
|
|
256
252
|
}
|
|
257
253
|
let uniqueName = toUniqueName(name)
|
|
258
254
|
let pair = parseUniqueName(uniqueName)
|
|
259
|
-
let space =
|
|
255
|
+
let space = setting.workspaceMap[pair[0]]
|
|
260
256
|
if (!space) {
|
|
261
257
|
return ''
|
|
262
258
|
}
|
|
@@ -282,10 +278,10 @@ function toUniqueName(name, space) {
|
|
|
282
278
|
} else if (name.indexOf('/') !== -1) {
|
|
283
279
|
uniqueName = name;
|
|
284
280
|
} else {
|
|
285
|
-
uniqueName = (space ||
|
|
281
|
+
uniqueName = (space || setting.defaultSpace) + '/' + name;
|
|
286
282
|
}
|
|
287
283
|
let pair = parseUniqueName(uniqueName);
|
|
288
|
-
return (pair[0] || space ||
|
|
284
|
+
return (pair[0] || space || setting.defaultSpace) + '/' + toJsirFileName(pair[1])
|
|
289
285
|
}
|
|
290
286
|
|
|
291
287
|
function toJsirFileName(name) {
|
|
@@ -700,7 +696,7 @@ function iarrayDataFile(fileName, fn, fmt) {
|
|
|
700
696
|
}
|
|
701
697
|
function getInitName(fileName) {
|
|
702
698
|
fileName = trim(fileName)
|
|
703
|
-
let homeDir =
|
|
699
|
+
let homeDir = setting.workspaceMap[setting.defaultSpace]
|
|
704
700
|
if (!fileName.startsWith("/")) {
|
|
705
701
|
fileName = setting.initKey + ' ' + fileName.replace(new RegExp(`^${setting.initKey}\\s+`), '')
|
|
706
702
|
if (!/\..+/.test(fileName)) {
|
|
@@ -762,14 +758,14 @@ async function fileExist(path) {
|
|
|
762
758
|
}
|
|
763
759
|
}
|
|
764
760
|
|
|
765
|
-
async function fileJson(key, fn, fmt = true) {
|
|
761
|
+
async function fileJson(key, fn, fmt = true, safeMs = 49000) {
|
|
766
762
|
`
|
|
767
763
|
多进程安全文件读写
|
|
768
764
|
`
|
|
769
765
|
let dataDir = getLibDataDir() + "/data"
|
|
770
766
|
let fileName = trim(key)
|
|
771
767
|
let path = dataDir + "/" + fileName;
|
|
772
|
-
let homeDir =
|
|
768
|
+
let homeDir = setting.workspaceMap[setting.defaultSpace]
|
|
773
769
|
let isInit = fileName.startsWith(setting.initKey);
|
|
774
770
|
if (isInit) {
|
|
775
771
|
path = homeDir + '/' + toJsirFileName(fileName);
|
|
@@ -793,12 +789,12 @@ async function fileJson(key, fn, fmt = true) {
|
|
|
793
789
|
result = val
|
|
794
790
|
}
|
|
795
791
|
await fp.writeFile(path, prefixStr + JSON.stringify(result, null, fmt ? 2:null))
|
|
796
|
-
});
|
|
792
|
+
}, true, safeMs);
|
|
797
793
|
return result;
|
|
798
794
|
}
|
|
799
795
|
|
|
800
796
|
function setModulePaths(space) {
|
|
801
|
-
let moduleDir =
|
|
797
|
+
let moduleDir = setting.workspaceMap[space] + '/node_modules'
|
|
802
798
|
if (module.paths.indexOf(moduleDir) === -1) {
|
|
803
799
|
module.paths.splice(0, module.paths.length)
|
|
804
800
|
module.paths.push(moduleDir)
|
|
@@ -808,13 +804,13 @@ function setModulePaths(space) {
|
|
|
808
804
|
}
|
|
809
805
|
|
|
810
806
|
function enrichPackages(moduleName, space) {
|
|
811
|
-
if (!
|
|
812
|
-
|
|
807
|
+
if (!setting.packages.hasOwnProperty(moduleName)) {
|
|
808
|
+
setting.packages[moduleName] = space
|
|
813
809
|
}
|
|
814
810
|
}
|
|
815
811
|
|
|
816
812
|
function requireG(moduleName, space){
|
|
817
|
-
space = space ||
|
|
813
|
+
space = space || setting.defaultSpace;
|
|
818
814
|
let moduleDir = setModulePaths(space);
|
|
819
815
|
if (module.paths.indexOf(moduleDir) === -1) {
|
|
820
816
|
module.paths.splice(0, module.paths.length)
|
|
@@ -836,7 +832,7 @@ function requireG(moduleName, space){
|
|
|
836
832
|
}
|
|
837
833
|
|
|
838
834
|
async function importG(moduleName, space) {
|
|
839
|
-
space = space ||
|
|
835
|
+
space = space || setting.defaultSpace;
|
|
840
836
|
let moduleDir = setModulePaths(space);
|
|
841
837
|
try {
|
|
842
838
|
let result = await import(moduleName);
|
|
@@ -1041,7 +1037,7 @@ function getLockDir() {
|
|
|
1041
1037
|
return lockDir;
|
|
1042
1038
|
}
|
|
1043
1039
|
|
|
1044
|
-
function
|
|
1040
|
+
function getLockKeyDir(key) {
|
|
1045
1041
|
if (!key) {
|
|
1046
1042
|
throw "invalid args"
|
|
1047
1043
|
}
|
|
@@ -1050,36 +1046,110 @@ function getLockFile(key) {
|
|
|
1050
1046
|
return lockDir + "/" + key;
|
|
1051
1047
|
}
|
|
1052
1048
|
|
|
1053
|
-
|
|
1049
|
+
/**
|
|
1050
|
+
* 从目录中读取过期时间戳
|
|
1051
|
+
* @param {string} lockDir - 锁目录路径
|
|
1052
|
+
* @returns {Promise<number | null>} 如果目录下存在过期时间戳文件,则返回其数值;否则返回 null
|
|
1053
|
+
*/
|
|
1054
|
+
async function readExpireTimestamp(lockDir) {
|
|
1055
|
+
try {
|
|
1056
|
+
const files = await fp.readdir(lockDir);
|
|
1057
|
+
// 假设我们只会创建一个文件,其文件名包含过期时间,如 `expire-1672531200000`
|
|
1058
|
+
const expireFile = files.find(name => name.startsWith('expire-'));
|
|
1059
|
+
if (!expireFile) {
|
|
1060
|
+
return null;
|
|
1061
|
+
}
|
|
1062
|
+
const timestamp = parseInt(expireFile.replace('expire-', ''), 10);
|
|
1063
|
+
return isNaN(timestamp) ? null : timestamp;
|
|
1064
|
+
} catch (err) {
|
|
1065
|
+
// 读取目录出错(可能不存在?)直接返回 null
|
|
1066
|
+
return null;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
async function _fileLock(key, fn, expireMs = 49000) {
|
|
1054
1071
|
`
|
|
1055
1072
|
文件锁,返回true/false,
|
|
1056
1073
|
如果没锁住则不执行fn
|
|
1057
1074
|
`
|
|
1058
|
-
if (!key ||
|
|
1059
|
-
throw new Error('invalid
|
|
1075
|
+
if (!key || typeof fn !== 'function') {
|
|
1076
|
+
throw new Error('invalid arguments');
|
|
1060
1077
|
}
|
|
1061
|
-
|
|
1078
|
+
|
|
1079
|
+
let lockKeyDir = getLockKeyDir(key)
|
|
1080
|
+
const now = Date.now();
|
|
1081
|
+
const expireAt = expireMs > 0 ? now + expireMs : 0;
|
|
1082
|
+
|
|
1083
|
+
// 1. 尝试判断锁目录是否已存在
|
|
1084
|
+
let lockExists = false;
|
|
1062
1085
|
try {
|
|
1063
|
-
await fp.
|
|
1064
|
-
|
|
1086
|
+
await fp.access(lockKeyDir); // 若存在则不抛错
|
|
1087
|
+
lockExists = true;
|
|
1088
|
+
} catch (_) {
|
|
1089
|
+
// 不存在时会抛ENOENT错误,说明还没有锁
|
|
1090
|
+
lockExists = false;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
if (lockExists) {
|
|
1094
|
+
// 目录存在时,读取文件名中的过期时间戳
|
|
1095
|
+
const storedExpireAt = await readExpireTimestamp(lockKeyDir);
|
|
1096
|
+
if (storedExpireAt === null) {
|
|
1097
|
+
// 理论上不会出现没有“expire-xxxx”文件的情况,除非中途被手动改动
|
|
1098
|
+
// 可以选择强制删除目录,或者直接返回 false
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
if (storedExpireAt === 0) {
|
|
1102
|
+
// 说明当时设置的是永不过期
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
if (storedExpireAt > now) {
|
|
1106
|
+
// 说明锁尚未过期
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
// 如果过期了,则清理原目录,以便重新加锁
|
|
1110
|
+
try {
|
|
1111
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1112
|
+
} catch (err) {
|
|
1113
|
+
// 删除失败,可能是权限问题;这里直接返回 false 或者抛错
|
|
1114
|
+
return false;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// 2. 创建锁目录
|
|
1119
|
+
try {
|
|
1120
|
+
await fp.mkdir(lockKeyDir, { recursive: false });
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
// 如果 mkdir 依旧失败,说明在这段时间里又被别人抢先创建了
|
|
1123
|
+
return false;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// 3. 在锁目录下创建一个文件,文件名带“过期时间戳”
|
|
1127
|
+
// expireAt === 0 表示永不过期
|
|
1128
|
+
const expireFilename = `expire-${expireAt}`;
|
|
1129
|
+
const expireFilePath = path.join(lockKeyDir, expireFilename);
|
|
1130
|
+
try {
|
|
1131
|
+
await fp.writeFile(expireFilePath, '');
|
|
1132
|
+
} catch (err) {
|
|
1133
|
+
// 无法写文件就释放锁并抛错
|
|
1134
|
+
try {
|
|
1135
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1136
|
+
} catch (_) {}
|
|
1065
1137
|
return false;
|
|
1066
1138
|
}
|
|
1067
|
-
|
|
1068
|
-
|
|
1139
|
+
|
|
1140
|
+
// 4. 成功加锁后执行 fn
|
|
1069
1141
|
try {
|
|
1070
1142
|
await fn();
|
|
1071
1143
|
return true;
|
|
1072
|
-
} catch (e) {
|
|
1073
|
-
throw e;
|
|
1074
1144
|
} finally {
|
|
1145
|
+
// 5. 执行完毕后,手动释放锁目录(也可选择保留到过期自动失效,但一般都建议主动释放)
|
|
1075
1146
|
try {
|
|
1076
|
-
await fp.
|
|
1077
|
-
|
|
1078
|
-
} catch (_){}
|
|
1147
|
+
await fp.rm(lockKeyDir, { recursive: true, force: true });
|
|
1148
|
+
} catch (_) {}
|
|
1079
1149
|
}
|
|
1080
1150
|
}
|
|
1081
1151
|
|
|
1082
|
-
async function fileLock(key, fn, wait = true) {
|
|
1152
|
+
async function fileLock(key, fn, wait = true, expireMs = 49000) {
|
|
1083
1153
|
`
|
|
1084
1154
|
文件锁, 默认一直等待,直到加锁成功执行fn
|
|
1085
1155
|
wait = false, 加锁失败则不执行fn
|
|
@@ -1087,13 +1157,13 @@ async function fileLock(key, fn, wait = true) {
|
|
|
1087
1157
|
`
|
|
1088
1158
|
if (wait) {
|
|
1089
1159
|
while (true) {
|
|
1090
|
-
if (await _fileLock(key, fn)) {
|
|
1160
|
+
if (await _fileLock(key, fn, expireMs)) {
|
|
1091
1161
|
break;
|
|
1092
1162
|
}
|
|
1093
|
-
await sleep(
|
|
1163
|
+
await sleep(9);
|
|
1094
1164
|
}
|
|
1095
1165
|
} else {
|
|
1096
|
-
await _fileLock(key, fn);
|
|
1166
|
+
await _fileLock(key, fn, expireMs);
|
|
1097
1167
|
}
|
|
1098
1168
|
}
|
|
1099
1169
|
|
|
@@ -1116,19 +1186,19 @@ function setTips(key, value, onRm) {
|
|
|
1116
1186
|
if (!vl(key) || key.indexOf(",") !== -1) {
|
|
1117
1187
|
throw "invalid tip key";
|
|
1118
1188
|
}
|
|
1119
|
-
getOr(
|
|
1189
|
+
getOr(setting.tips, key, []).push(value);
|
|
1120
1190
|
if (onRm) {
|
|
1121
1191
|
getOr(_tipsOnRm, key, []).push(onRm)
|
|
1122
1192
|
}
|
|
1123
1193
|
}
|
|
1124
1194
|
|
|
1125
1195
|
function delTips(...keys) {
|
|
1126
|
-
for (let key of Object.keys(
|
|
1196
|
+
for (let key of Object.keys(setting.tips)) {
|
|
1127
1197
|
if (keys.length === 0) {
|
|
1128
|
-
delete
|
|
1198
|
+
delete setting.tips[key]
|
|
1129
1199
|
tipsOnRmCallback(key)
|
|
1130
1200
|
} else if (keys.indexOf(key) !== -1) {
|
|
1131
|
-
delete
|
|
1201
|
+
delete setting.tips[key]
|
|
1132
1202
|
tipsOnRmCallback(key)
|
|
1133
1203
|
}
|
|
1134
1204
|
}
|
|
@@ -1186,7 +1256,7 @@ function fileCleaner(path, maxChars) {
|
|
|
1186
1256
|
`
|
|
1187
1257
|
cleanFiles[path] = Date.now() + (1000 * 60)
|
|
1188
1258
|
let flag = "sys"
|
|
1189
|
-
if (
|
|
1259
|
+
if (!setting.tips.hasOwnProperty(flag)) {
|
|
1190
1260
|
timer(flag, 1000 * 9, () => {
|
|
1191
1261
|
for (let aPath of Object.keys(cleanFiles)) {
|
|
1192
1262
|
if (cleanFiles[aPath] < Date.now()) {
|
|
@@ -1399,14 +1469,17 @@ async function eia(cmd, args = [], shell = false) {
|
|
|
1399
1469
|
`
|
|
1400
1470
|
当前进程不会卡住,输入输出由cmd进程持有
|
|
1401
1471
|
`
|
|
1472
|
+
setting.enableNextLine = false;
|
|
1402
1473
|
let child = spawn(cmd, args, {stdio:"inherit", shell});
|
|
1403
1474
|
return new Promise((resolve, reject) => {
|
|
1404
1475
|
// 监听子进程的关闭事件
|
|
1405
1476
|
child.on('close', (code) => {
|
|
1477
|
+
setting.enableNextLine = true;
|
|
1406
1478
|
resolve(code);
|
|
1407
1479
|
});
|
|
1408
1480
|
// 可选:监听子进程的错误事件
|
|
1409
1481
|
child.on('error', (err) => {
|
|
1482
|
+
setting.enableNextLine = true;
|
|
1410
1483
|
reject(err)
|
|
1411
1484
|
});
|
|
1412
1485
|
})
|
|
@@ -1864,6 +1937,29 @@ end tell
|
|
|
1864
1937
|
}
|
|
1865
1938
|
}
|
|
1866
1939
|
|
|
1940
|
+
function getKeyTips() {
|
|
1941
|
+
let items = [];
|
|
1942
|
+
for (let key of Object.keys(setting.tips)) {
|
|
1943
|
+
items.push(trim(key))
|
|
1944
|
+
}
|
|
1945
|
+
return items
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
function getValTips() {
|
|
1949
|
+
let items = [];
|
|
1950
|
+
for (let key of Object.keys(setting.tips)) {
|
|
1951
|
+
let val = setting.tips[key].map(i => {
|
|
1952
|
+
let item = trim(i)
|
|
1953
|
+
if (item.indexOf(',') !== -1) {
|
|
1954
|
+
item = `[${item}]`;
|
|
1955
|
+
}
|
|
1956
|
+
return item;
|
|
1957
|
+
}).join("|");
|
|
1958
|
+
items.push(val)
|
|
1959
|
+
}
|
|
1960
|
+
return items
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1867
1963
|
module.exports = {
|
|
1868
1964
|
wrapperJsirText,
|
|
1869
1965
|
run,
|
|
@@ -1964,5 +2060,7 @@ module.exports = {
|
|
|
1964
2060
|
createDirs,
|
|
1965
2061
|
getTempDir,
|
|
1966
2062
|
terminalRun,
|
|
1967
|
-
eia
|
|
2063
|
+
eia,
|
|
2064
|
+
getKeyTips,
|
|
2065
|
+
getValTips
|
|
1968
2066
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsir",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "JavaScript Script Management Tool",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"chokidar": "^3.5.2",
|
|
23
23
|
"console.table": "^0.10.0",
|
|
24
24
|
"dayjs": "^1.10.4",
|
|
25
|
-
"pad": "^3.2.0"
|
|
25
|
+
"pad": "^3.2.0",
|
|
26
|
+
"ping": "^0.4.4"
|
|
26
27
|
}
|
|
27
28
|
}
|