mm_cmd 0.0.0-stage → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -3
- package/com.json +5 -0
- package/config.tpl.json +48 -0
- package/index.js +17 -0
- package/job/config.json +66 -0
- package/job/index.js +241 -0
- package/job/test.js +188 -0
- package/lib/drive.js +330 -0
- package/lib/manager.js +418 -0
- package/msg.json +48 -0
- package/package.json +24 -4
- package/script.tpl.js +12 -0
- package/test.js +18 -0
package/lib/manager.js
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
const Manager = require('mm_corejs').Manager;
|
|
2
|
+
const Drive = require('./drive');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 内置会话缓存类(替代原框架注入的全局缓存服务,进程内 Map + 过期时间)
|
|
6
|
+
* @class
|
|
7
|
+
*/
|
|
8
|
+
class LocalCache {
|
|
9
|
+
/**
|
|
10
|
+
* 构造函数
|
|
11
|
+
* @class
|
|
12
|
+
*/
|
|
13
|
+
constructor() {
|
|
14
|
+
/**
|
|
15
|
+
* 缓存存储(key => { value, expire })
|
|
16
|
+
* @type {Map}
|
|
17
|
+
*/
|
|
18
|
+
this.store = new Map();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 读取缓存(过期自动删除)
|
|
23
|
+
* @param {string} key 缓存键
|
|
24
|
+
* @returns {*} 缓存值,不存在返回 undefined
|
|
25
|
+
*/
|
|
26
|
+
get(key) {
|
|
27
|
+
var item = this.store.get(key);
|
|
28
|
+
if (!item) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
if (item.expire && item.expire < Date.now()) {
|
|
32
|
+
this.store.delete(key);
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return item.value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 写入缓存
|
|
40
|
+
* @param {string} key 缓存键
|
|
41
|
+
* @param {*} value 缓存值
|
|
42
|
+
* @param {number} timeout 有效期秒数,0 或不传为不过期
|
|
43
|
+
* @returns {void}
|
|
44
|
+
*/
|
|
45
|
+
set(key, value, timeout = 0) {
|
|
46
|
+
this.store.set(key, {
|
|
47
|
+
value,
|
|
48
|
+
expire: timeout > 0 ? Date.now() + timeout * 1000 : 0,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 删除缓存
|
|
54
|
+
* @param {string} key 缓存键
|
|
55
|
+
* @returns {boolean} 是否删除成功
|
|
56
|
+
*/
|
|
57
|
+
del(key) {
|
|
58
|
+
return this.store.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 指令管理器类(迁移自 mm_os com/cmd,基于 mm_corejs 内置管理器模式)
|
|
64
|
+
* @augments {Manager}
|
|
65
|
+
* @class
|
|
66
|
+
*/
|
|
67
|
+
class Cmd extends Manager {
|
|
68
|
+
/**
|
|
69
|
+
* 配置参数(方案B:无目录扫描机制键,drive 由 add() 手动注册)
|
|
70
|
+
* @type {object}
|
|
71
|
+
*/
|
|
72
|
+
static config = {
|
|
73
|
+
/**
|
|
74
|
+
* 名称
|
|
75
|
+
* @type {string}
|
|
76
|
+
*/
|
|
77
|
+
name: '',
|
|
78
|
+
/**
|
|
79
|
+
* 标题
|
|
80
|
+
* @type {string}
|
|
81
|
+
*/
|
|
82
|
+
title: '指令管理',
|
|
83
|
+
/**
|
|
84
|
+
* 描述
|
|
85
|
+
* @type {string}
|
|
86
|
+
*/
|
|
87
|
+
description: '指令框架管理器,负责指令注册、匹配与执行',
|
|
88
|
+
/**
|
|
89
|
+
* 会话超时(秒)
|
|
90
|
+
* @type {number}
|
|
91
|
+
*/
|
|
92
|
+
session_timeout: 600,
|
|
93
|
+
/**
|
|
94
|
+
* 会话缓存实例(可选注入,需含 get/set/del;未配置时使用内置进程内缓存)
|
|
95
|
+
* @type {object|null}
|
|
96
|
+
*/
|
|
97
|
+
cache: null,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 构造函数
|
|
102
|
+
* @param {object} config 配置参数
|
|
103
|
+
* @param {object} parent 父级模块
|
|
104
|
+
*/
|
|
105
|
+
constructor(config, parent) {
|
|
106
|
+
super({ ...Cmd.config, ...config }, parent);
|
|
107
|
+
this.list_before = [];
|
|
108
|
+
this.list_main = [];
|
|
109
|
+
this.list_after = [];
|
|
110
|
+
// 插件注册表(方案B:手动注册,供 usePlugin 登记)
|
|
111
|
+
this.plugins = {};
|
|
112
|
+
// 内置会话缓存(config.cache 未注入时使用)
|
|
113
|
+
this._local_cache = new LocalCache();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Cmd驱动类
|
|
119
|
+
* @type {Drive}
|
|
120
|
+
*/
|
|
121
|
+
Cmd.prototype.Drive = Drive;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 注册一个指令驱动(方案B:手动注册,不扫描目录)
|
|
125
|
+
* @param {object} config 驱动配置(必须包含 name)
|
|
126
|
+
* @returns {object} 驱动实例
|
|
127
|
+
*/
|
|
128
|
+
Cmd.prototype.add = function (config) {
|
|
129
|
+
var drive = new Drive({ ...config }, this);
|
|
130
|
+
this.set(drive.config.name, drive);
|
|
131
|
+
this._fullList();
|
|
132
|
+
return drive;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 注销一个指令驱动(删除注册表项并重建指令列表)
|
|
137
|
+
* @param {string} name 驱动名称
|
|
138
|
+
* @returns {Promise<boolean>} 是否删除成功
|
|
139
|
+
*/
|
|
140
|
+
Cmd.prototype.del = async function (name) {
|
|
141
|
+
var result = await Manager.prototype.del.call(this, name);
|
|
142
|
+
this._fullList();
|
|
143
|
+
return result;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 重载一个指令驱动(重新加载脚本并重建指令列表)
|
|
148
|
+
* @param {string} name 驱动名称
|
|
149
|
+
* @returns {boolean} 是否执行成功
|
|
150
|
+
*/
|
|
151
|
+
Cmd.prototype.reload = function (name) {
|
|
152
|
+
var drive = this.get(name);
|
|
153
|
+
if (!drive) {
|
|
154
|
+
this.log('warn', '驱动不存在,无法重载', name);
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
drive.reloadScript();
|
|
158
|
+
this._fullList();
|
|
159
|
+
return true;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 注册插件集合(供驱动 plugin() 查找,替代原全局插件池)
|
|
164
|
+
* @param {string} name 插件作用域名称(app 名)
|
|
165
|
+
* @param {object} obj 插件集合对象(需含 get 方法)
|
|
166
|
+
* @returns {object} 当前管理器实例
|
|
167
|
+
*/
|
|
168
|
+
Cmd.prototype.usePlugin = function (name, obj) {
|
|
169
|
+
this.plugins[name] = obj;
|
|
170
|
+
return this;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* 获取所有驱动配置列表(方案B:来自注册表,按 sort 升序,替代旧基类 getInfos)
|
|
175
|
+
* @returns {Array} 驱动配置数组
|
|
176
|
+
* @private
|
|
177
|
+
*/
|
|
178
|
+
Cmd.prototype._getInfos = function () {
|
|
179
|
+
var infos = this.values().map((d) => d.config);
|
|
180
|
+
infos.sort((a, b) => (a.sort || 0) - (b.sort || 0));
|
|
181
|
+
return infos;
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 获取会话缓存实例(config.cache 注入优先,否则内置进程内缓存)
|
|
186
|
+
* @returns {object} 含 get/set/del 的缓存实例
|
|
187
|
+
* @private
|
|
188
|
+
*/
|
|
189
|
+
Cmd.prototype._getCache = function () {
|
|
190
|
+
var cache = this.config.cache;
|
|
191
|
+
if (cache && typeof cache.get === 'function' && typeof cache.set === 'function') {
|
|
192
|
+
return cache;
|
|
193
|
+
}
|
|
194
|
+
return this._local_cache;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 构建指令列表(按 tense 分桶,仅启用项)
|
|
199
|
+
* @returns {void}
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
Cmd.prototype._fullList = function () {
|
|
203
|
+
this.list_before = [];
|
|
204
|
+
this.list_main = [];
|
|
205
|
+
this.list_after = [];
|
|
206
|
+
|
|
207
|
+
var infos = this._getInfos();
|
|
208
|
+
for (var i = 0; i < infos.length; i++) {
|
|
209
|
+
var info = infos[i];
|
|
210
|
+
if (info.state !== 1) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
var mod = this.get(info.name);
|
|
214
|
+
if (!mod) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
switch (mod.config.tense) {
|
|
218
|
+
case 'before':
|
|
219
|
+
this.list_before.push(info);
|
|
220
|
+
break;
|
|
221
|
+
case 'after':
|
|
222
|
+
this.list_after.push(info);
|
|
223
|
+
break;
|
|
224
|
+
default:
|
|
225
|
+
this.list_main.push(info);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 更新后回调
|
|
233
|
+
* @param {string} dir_path 目录路径
|
|
234
|
+
*/
|
|
235
|
+
Cmd.prototype.updateAfter = async function (dir_path) {
|
|
236
|
+
this._fullList();
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 执行指令
|
|
241
|
+
* @param {object} msg 消息对象
|
|
242
|
+
* @param {object} db 数据管理器
|
|
243
|
+
* @returns {object|string} 返回执行结果
|
|
244
|
+
*/
|
|
245
|
+
Cmd.prototype.run = async function (msg, db) {
|
|
246
|
+
if (!msg || !msg.content) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
var ret;
|
|
250
|
+
var session = await this._getSession(msg);
|
|
251
|
+
|
|
252
|
+
if (session) {
|
|
253
|
+
ret = await this._runSession(msg, db, session);
|
|
254
|
+
} else {
|
|
255
|
+
ret = await this.runSub(msg, db, 'before');
|
|
256
|
+
if (!ret) {
|
|
257
|
+
ret = await this.runSub(msg, db, 'main');
|
|
258
|
+
}
|
|
259
|
+
if (!ret) {
|
|
260
|
+
ret = await this.runSub(msg, db, 'after');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
ret = this._makeResult(msg, ret);
|
|
264
|
+
db.ret = ret;
|
|
265
|
+
return ret;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* 按列表执行指令
|
|
270
|
+
* @param {object} msg 消息对象
|
|
271
|
+
* @param {object} db 数据管理器
|
|
272
|
+
* @param {string} tense 时态
|
|
273
|
+
* @returns {object|string} 返回执行结果
|
|
274
|
+
*/
|
|
275
|
+
Cmd.prototype.runSub = async function (msg, db, tense) {
|
|
276
|
+
var list = this['list_' + tense];
|
|
277
|
+
if (!list) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
var ret;
|
|
281
|
+
for (var i = 0; i < list.length; i++) {
|
|
282
|
+
var info = list[i];
|
|
283
|
+
if (info.state !== 1) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
var mod = this.get(info.name);
|
|
287
|
+
if (!mod) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
ret = await mod.run(msg, db);
|
|
291
|
+
if (ret) {
|
|
292
|
+
msg.cmd = info.name;
|
|
293
|
+
if (msg.end === 0) {
|
|
294
|
+
this._saveSession(msg, {
|
|
295
|
+
cmd: msg.cmd,
|
|
296
|
+
form: msg.form,
|
|
297
|
+
stage: msg.stage,
|
|
298
|
+
});
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
if (mod.config.end) {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return ret;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* 继续执行会话指令
|
|
311
|
+
* @param {object} msg 消息对象
|
|
312
|
+
* @param {object} db 数据管理器
|
|
313
|
+
* @param {object} session 会话信息
|
|
314
|
+
* @returns {object|string} 返回执行结果
|
|
315
|
+
* @private
|
|
316
|
+
*/
|
|
317
|
+
Cmd.prototype._runSession = async function (msg, db, session) {
|
|
318
|
+
var mod = this.get(session.cmd);
|
|
319
|
+
if (!mod) {
|
|
320
|
+
this._clearSession(msg);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
msg.cmd = session.cmd;
|
|
324
|
+
msg.form = session.form;
|
|
325
|
+
msg.stage = session.stage;
|
|
326
|
+
msg.end = 0;
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
var ret = await mod.run(msg, db);
|
|
330
|
+
if (msg.end === 0) {
|
|
331
|
+
this._saveSession(msg, {
|
|
332
|
+
cmd: msg.cmd,
|
|
333
|
+
form: msg.form,
|
|
334
|
+
stage: msg.stage,
|
|
335
|
+
});
|
|
336
|
+
} else {
|
|
337
|
+
this._clearSession(msg);
|
|
338
|
+
}
|
|
339
|
+
return ret;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
this.log('error', '会话指令执行失败', error.message);
|
|
342
|
+
this._clearSession(msg);
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* 包装出站消息, from_user/to_user 反转
|
|
349
|
+
* @param {object} msg 入站消息
|
|
350
|
+
* @param {object|string} ret 原始返回值
|
|
351
|
+
* @returns {object} 出站消息
|
|
352
|
+
* @private
|
|
353
|
+
*/
|
|
354
|
+
Cmd.prototype._makeResult = function (msg, ret) {
|
|
355
|
+
if (!ret) return ret;
|
|
356
|
+
var result = typeof ret === 'string' ? { content: ret } : { ...ret };
|
|
357
|
+
if (msg.to_user) result.from_user = msg.to_user;
|
|
358
|
+
if (msg.from_user) result.to_user = msg.from_user;
|
|
359
|
+
if (msg.chatid) result.chatid = msg.chatid;
|
|
360
|
+
if (msg.cmd) result.cmd = msg.cmd;
|
|
361
|
+
if (msg.form) result.form = msg.form;
|
|
362
|
+
if (msg.end != null) result.end = msg.end;
|
|
363
|
+
return result;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* 获取会话
|
|
368
|
+
* @param {object} msg 消息对象
|
|
369
|
+
* @returns {object|null} 会话信息
|
|
370
|
+
* @private
|
|
371
|
+
*/
|
|
372
|
+
Cmd.prototype._getSession = async function (msg) {
|
|
373
|
+
var key = this._getKey(msg);
|
|
374
|
+
var val = await this._getCache().get(key);
|
|
375
|
+
if (!val) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
if (typeof val === 'string') {
|
|
379
|
+
return JSON.parse(val);
|
|
380
|
+
}
|
|
381
|
+
return val;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* 保存会话
|
|
386
|
+
* @param {object} msg 消息对象
|
|
387
|
+
* @param {object} session 会话信息
|
|
388
|
+
* @private
|
|
389
|
+
*/
|
|
390
|
+
Cmd.prototype._saveSession = function (msg, session) {
|
|
391
|
+
var key = this._getKey(msg);
|
|
392
|
+
this._getCache().set(key, session, this.config.session_timeout);
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* 清除会话
|
|
397
|
+
* @param {object} msg 消息对象
|
|
398
|
+
* @private
|
|
399
|
+
*/
|
|
400
|
+
Cmd.prototype._clearSession = function (msg) {
|
|
401
|
+
var key = this._getKey(msg);
|
|
402
|
+
this._getCache().del(key);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 获取会话缓存键
|
|
407
|
+
* @param {object} msg 消息对象
|
|
408
|
+
* @returns {string} 缓存键
|
|
409
|
+
* @private
|
|
410
|
+
*/
|
|
411
|
+
Cmd.prototype._getKey = function (msg) {
|
|
412
|
+
return 'cmd_session_' + (msg.chatid || msg.from_user || 'default');
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* @module 导出指令管理器类
|
|
417
|
+
*/
|
|
418
|
+
module.exports = Cmd;
|
package/msg.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
// 消息ID
|
|
3
|
+
"message_id": 0,
|
|
4
|
+
// 结束状态 0-正常 1-结束
|
|
5
|
+
"end": 0,
|
|
6
|
+
// 阶段 1-9阶段
|
|
7
|
+
"stage": 1,
|
|
8
|
+
// 微信/公众号消息ID
|
|
9
|
+
"msgid": "",
|
|
10
|
+
// 更新时间
|
|
11
|
+
"update_time": "",
|
|
12
|
+
// 创建时间
|
|
13
|
+
"create_time": "",
|
|
14
|
+
// 群组
|
|
15
|
+
"group": "",
|
|
16
|
+
// 消息类型 text-文本 image-图片 voice-语音 video-视频 file-文件 location-位置 link-链接 event-事件 other-其他
|
|
17
|
+
"msg_type": "text",
|
|
18
|
+
// 媒体ID,图片、语音、视频、文件
|
|
19
|
+
"media_id": "",
|
|
20
|
+
// 发信人姓名
|
|
21
|
+
"name": "",
|
|
22
|
+
// 一般情况下,1永久会话/群、2临时会话/群
|
|
23
|
+
"type": 1,
|
|
24
|
+
// 指令
|
|
25
|
+
"cmd": "",
|
|
26
|
+
// 应用ID
|
|
27
|
+
"appid": "",
|
|
28
|
+
// 发信人
|
|
29
|
+
"from_user": "",
|
|
30
|
+
// 收信人
|
|
31
|
+
"to_user": "",
|
|
32
|
+
// 机器人
|
|
33
|
+
"robot": "",
|
|
34
|
+
// 会话ID,用于判断是否是同一个会话
|
|
35
|
+
"chatid": "",
|
|
36
|
+
// 发信人头像
|
|
37
|
+
"avatar": "",
|
|
38
|
+
// 除指令外,过滤、抽取后的词
|
|
39
|
+
"keyword": "",
|
|
40
|
+
// 表单,用于记录用户已填参数,json格式
|
|
41
|
+
"form": "",
|
|
42
|
+
// 聊天内容
|
|
43
|
+
"content": "",
|
|
44
|
+
// 消息传递数据,json格式
|
|
45
|
+
"data": "",
|
|
46
|
+
// 备注信息
|
|
47
|
+
"note": ""
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mm_cmd",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "指令框架,用于以聊天的方式操作程序,前端发送字符串,后端判断是否为指令并执行(基于 mm_corejs,内置管理器池,无全局/原型扩展依赖)",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mm_mod",
|
|
8
|
+
"mm_adapter",
|
|
9
|
+
"mm_in_adapter",
|
|
10
|
+
"cmd",
|
|
11
|
+
"command",
|
|
12
|
+
"chat",
|
|
13
|
+
"指令管理器"
|
|
14
|
+
],
|
|
15
|
+
"layer": "adapter",
|
|
16
|
+
"group": "in_adapter",
|
|
17
|
+
"author": "邱文武",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"mm_corejs": "^1.1.4",
|
|
21
|
+
"mm_matchs": "^1.1.10"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "node test.js"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/script.tpl.js
ADDED
package/test.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const assert = require('assert');
|
|
3
|
+
const mod = require('./index.js');
|
|
4
|
+
|
|
5
|
+
// 验证导出结构
|
|
6
|
+
assert.ok(mod.Manager, '应导出 Manager');
|
|
7
|
+
assert.ok(mod.admin, '应导出 admin');
|
|
8
|
+
assert.strictEqual(typeof mod.cmdAdmin, 'function', '应导出 cmdAdmin 函数');
|
|
9
|
+
|
|
10
|
+
// 验证通过工厂函数获取实例
|
|
11
|
+
const inst = mod.cmdAdmin('test');
|
|
12
|
+
assert.ok(inst instanceof mod.Manager, 'cmdAdmin 返回的实例应为 Manager 类型');
|
|
13
|
+
|
|
14
|
+
// 验证通过 admin 获取实例
|
|
15
|
+
const inst2 = mod.admin.get('test2');
|
|
16
|
+
assert.ok(inst2 instanceof mod.Manager, 'admin.get 返回的实例应为 Manager 类型');
|
|
17
|
+
|
|
18
|
+
console.log('mm_cmd 模块测试通过');
|