nodebb-plugin-admin-ai-connect 1.0.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/package.json +37 -0
- package/src/ProModule.js +377 -0
- package/src/index.js +51 -0
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nodebb-plugin-admin-ai-connect",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Pro tool-set for Goldnat AI Connect — create topics, reply, edit, message, follow. Requires nodebb-plugin-ai-connect (core) to run.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"nodebb-plugin-ai-connect": "*"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test"
|
|
11
|
+
},
|
|
12
|
+
"author": "Goldnat",
|
|
13
|
+
"nbbpm": {
|
|
14
|
+
"compatibility": "^3.0.0 || ^4.0.0"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"nodebb",
|
|
18
|
+
"plugin",
|
|
19
|
+
"ai",
|
|
20
|
+
"mcp",
|
|
21
|
+
"gacp",
|
|
22
|
+
"webmcp",
|
|
23
|
+
"goldnat",
|
|
24
|
+
"ai-connect",
|
|
25
|
+
"admin",
|
|
26
|
+
"pro"
|
|
27
|
+
],
|
|
28
|
+
"homepage": "https://goldnat.ai/",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/chgold/nodebb-plugin-admin-ai-connect"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/chgold/nodebb-plugin-admin-ai-connect/issues"
|
|
35
|
+
},
|
|
36
|
+
"license": "PROPRIETARY"
|
|
37
|
+
}
|
package/src/ProModule.js
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ProModule — the Pro tool-set for NodeBB AI Connect.
|
|
5
|
+
*
|
|
6
|
+
* Lives in a SEPARATE package (nodebb-admin-ai-connect) and is loaded into the
|
|
7
|
+
* core registry only when the Pro package is installed AND the site edition
|
|
8
|
+
* grants Pro. Mirrors the Ghost model: a standalone Pro plugin that registers
|
|
9
|
+
* its tools into the core's shared registry via a factory that receives the
|
|
10
|
+
* core's ModuleBase.
|
|
11
|
+
*
|
|
12
|
+
* NodeBB core APIs (topics/posts/messaging) are resolved from the running
|
|
13
|
+
* NodeBB instance via require.main.require, or injected in tests through
|
|
14
|
+
* ProModule._setApis().
|
|
15
|
+
*/
|
|
16
|
+
module.exports = function createProModule({ ModuleBase }) {
|
|
17
|
+
class ProModule extends ModuleBase {
|
|
18
|
+
getModuleName() { return 'nodebb'; }
|
|
19
|
+
|
|
20
|
+
registerTools() {
|
|
21
|
+
this.registerTool('createTopic', {
|
|
22
|
+
description: 'Create a new topic in a category',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
required: ['cid', 'title', 'content'],
|
|
26
|
+
properties: {
|
|
27
|
+
cid: { type: 'integer' },
|
|
28
|
+
title: { type: 'string' },
|
|
29
|
+
content: { type: 'string' },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
requiredScope: 'write',
|
|
33
|
+
proOnly: true,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.registerTool('replyToTopic', {
|
|
37
|
+
description: 'Post a reply to an existing topic',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
required: ['tid', 'content'],
|
|
41
|
+
properties: {
|
|
42
|
+
tid: { type: 'integer' },
|
|
43
|
+
content: { type: 'string' },
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
requiredScope: 'write',
|
|
47
|
+
proOnly: true,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.registerTool('editPost', {
|
|
51
|
+
description: 'Edit an existing post',
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
required: ['pid', 'content'],
|
|
55
|
+
properties: {
|
|
56
|
+
pid: { type: 'integer' },
|
|
57
|
+
content: { type: 'string' },
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
requiredScope: 'write',
|
|
61
|
+
proOnly: true,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.registerTool('sendMessage', {
|
|
65
|
+
description: 'Send a private chat message to a user',
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
required: ['touid', 'content'],
|
|
69
|
+
properties: {
|
|
70
|
+
touid: { type: 'integer' },
|
|
71
|
+
content: { type: 'string' },
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
requiredScope: 'write',
|
|
75
|
+
proOnly: true,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.registerTool('followTopic', {
|
|
79
|
+
description: 'Follow or unfollow a topic for notifications',
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
required: ['tid'],
|
|
83
|
+
properties: {
|
|
84
|
+
tid: { type: 'integer' },
|
|
85
|
+
follow: { type: 'boolean' },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
requiredScope: 'write',
|
|
89
|
+
proOnly: true,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.registerTool('deleteTopic', {
|
|
93
|
+
description: 'Soft-delete a topic (hides from lists; can be restored). Use purgeTopic to hard-delete.',
|
|
94
|
+
inputSchema: { type: 'object', required: ['tid'], properties: { tid: { type: 'integer' } } },
|
|
95
|
+
requiredScope: 'delete',
|
|
96
|
+
proOnly: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this.registerTool('purgeTopic', {
|
|
100
|
+
description: 'HARD-DELETE a topic and all its posts. Destructive and irreversible.',
|
|
101
|
+
inputSchema: { type: 'object', required: ['tid'], properties: { tid: { type: 'integer' } } },
|
|
102
|
+
requiredScope: 'delete',
|
|
103
|
+
proOnly: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.registerTool('lockTopic', {
|
|
107
|
+
description: 'Lock a topic (prevents new replies).',
|
|
108
|
+
inputSchema: { type: 'object', required: ['tid'], properties: { tid: { type: 'integer' } } },
|
|
109
|
+
requiredScope: 'write',
|
|
110
|
+
proOnly: true,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
this.registerTool('unlockTopic', {
|
|
114
|
+
description: 'Unlock a previously locked topic.',
|
|
115
|
+
inputSchema: { type: 'object', required: ['tid'], properties: { tid: { type: 'integer' } } },
|
|
116
|
+
requiredScope: 'write',
|
|
117
|
+
proOnly: true,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
this.registerTool('pinTopic', {
|
|
121
|
+
description: 'Pin a topic to the top of its category.',
|
|
122
|
+
inputSchema: { type: 'object', required: ['tid'], properties: { tid: { type: 'integer' } } },
|
|
123
|
+
requiredScope: 'write',
|
|
124
|
+
proOnly: true,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
this.registerTool('unpinTopic', {
|
|
128
|
+
description: 'Unpin a previously pinned topic.',
|
|
129
|
+
inputSchema: { type: 'object', required: ['tid'], properties: { tid: { type: 'integer' } } },
|
|
130
|
+
requiredScope: 'write',
|
|
131
|
+
proOnly: true,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
this.registerTool('moveTopic', {
|
|
135
|
+
description: 'Move a topic to a different category.',
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
required: ['tid', 'cid'],
|
|
139
|
+
properties: { tid: { type: 'integer' }, cid: { type: 'integer', description: 'Target category id' } },
|
|
140
|
+
},
|
|
141
|
+
requiredScope: 'write',
|
|
142
|
+
proOnly: true,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.registerTool('deletePost', {
|
|
146
|
+
description: 'Soft-delete a post (hidden but recoverable). Use purgePost to hard-delete.',
|
|
147
|
+
inputSchema: { type: 'object', required: ['pid'], properties: { pid: { type: 'integer' } } },
|
|
148
|
+
requiredScope: 'delete',
|
|
149
|
+
proOnly: true,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this.registerTool('purgePost', {
|
|
153
|
+
description: 'HARD-DELETE a post. Destructive and irreversible.',
|
|
154
|
+
inputSchema: { type: 'object', required: ['pid'], properties: { pid: { type: 'integer' } } },
|
|
155
|
+
requiredScope: 'delete',
|
|
156
|
+
proOnly: true,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
this.registerTool('upvotePost', {
|
|
160
|
+
description: 'Upvote a post as the acting user.',
|
|
161
|
+
inputSchema: { type: 'object', required: ['pid'], properties: { pid: { type: 'integer' } } },
|
|
162
|
+
requiredScope: 'write',
|
|
163
|
+
proOnly: true,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this.registerTool('downvotePost', {
|
|
167
|
+
description: 'Downvote a post as the acting user.',
|
|
168
|
+
inputSchema: { type: 'object', required: ['pid'], properties: { pid: { type: 'integer' } } },
|
|
169
|
+
requiredScope: 'write',
|
|
170
|
+
proOnly: true,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
this.registerTool('bookmarkPost', {
|
|
174
|
+
description: 'Bookmark a post for the acting user. Provide bookmark:false to remove.',
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
required: ['pid'],
|
|
178
|
+
properties: { pid: { type: 'integer' }, bookmark: { type: 'boolean' } },
|
|
179
|
+
},
|
|
180
|
+
requiredScope: 'write',
|
|
181
|
+
proOnly: true,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
this.registerTool('banUser', {
|
|
185
|
+
description: 'Ban a user. Provide optional reason and until (Unix timestamp; omit for permanent).',
|
|
186
|
+
inputSchema: {
|
|
187
|
+
type: 'object',
|
|
188
|
+
required: ['uid'],
|
|
189
|
+
properties: {
|
|
190
|
+
uid: { type: 'integer' },
|
|
191
|
+
reason: { type: 'string' },
|
|
192
|
+
until: { type: 'integer', description: 'Unix timestamp — ban expires at this time; omit for permanent' },
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
requiredScope: 'admin',
|
|
196
|
+
proOnly: true,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
this.registerTool('unbanUser', {
|
|
200
|
+
description: 'Lift a ban on a user.',
|
|
201
|
+
inputSchema: { type: 'object', required: ['uid'], properties: { uid: { type: 'integer' } } },
|
|
202
|
+
requiredScope: 'admin',
|
|
203
|
+
proOnly: true,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
this.registerTool('deleteUserAccount', {
|
|
207
|
+
description: 'DELETE a user account. Destructive. Provide keepPosts=true to preserve their posts.',
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: 'object',
|
|
210
|
+
required: ['uid'],
|
|
211
|
+
properties: {
|
|
212
|
+
uid: { type: 'integer' },
|
|
213
|
+
keepPosts: { type: 'boolean', description: 'Retain the user posts (default false)' },
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
requiredScope: 'admin',
|
|
217
|
+
proOnly: true,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async execute_createTopic({ cid, title, content }, { uid } = {}) {
|
|
222
|
+
const { topics } = ProModule._getApis();
|
|
223
|
+
const req = { ip: '127.0.0.1', headers: {}, uid };
|
|
224
|
+
const result = await topics.post({ uid, cid, title, content, req });
|
|
225
|
+
return { tid: result.topicData.tid, title: result.topicData.title, pid: result.postData.pid };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async execute_replyToTopic({ tid, content }, { uid } = {}) {
|
|
229
|
+
const { topics } = ProModule._getApis();
|
|
230
|
+
const req = { ip: '127.0.0.1', headers: {}, uid };
|
|
231
|
+
const result = await topics.reply({ uid, tid, content, req });
|
|
232
|
+
return { pid: result.pid, tid };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async execute_editPost({ pid, content }, { uid } = {}) {
|
|
236
|
+
const { posts } = ProModule._getApis();
|
|
237
|
+
const req = { ip: '127.0.0.1', headers: {}, uid };
|
|
238
|
+
const result = await posts.edit({ uid, pid, content, req });
|
|
239
|
+
return { pid: result.pid, edited: true };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async execute_sendMessage({ touid, content }, { uid } = {}) {
|
|
243
|
+
const { messaging } = ProModule._getApis();
|
|
244
|
+
const roomId = await messaging.newRoom(uid, { uids: [touid] });
|
|
245
|
+
await messaging.sendMessage({ uid, roomId, content });
|
|
246
|
+
return { sent: true, touid };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async execute_followTopic({ tid, follow = true }, { uid } = {}) {
|
|
250
|
+
const { topics } = ProModule._getApis();
|
|
251
|
+
if (follow) await topics.follow(tid, uid);
|
|
252
|
+
else await topics.unfollow(tid, uid);
|
|
253
|
+
return { followed: follow, tid };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async execute_deleteTopic({ tid }, { uid } = {}) {
|
|
257
|
+
const { topics } = ProModule._getApis();
|
|
258
|
+
await topics.delete(tid, uid);
|
|
259
|
+
return { tid, deleted: true, hard: false };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async execute_purgeTopic({ tid }, { uid } = {}) {
|
|
263
|
+
const { topics } = ProModule._getApis();
|
|
264
|
+
await topics.purge(tid, uid);
|
|
265
|
+
return { tid, purged: true, hard: true };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async execute_lockTopic({ tid }, { uid } = {}) {
|
|
269
|
+
const { topics } = ProModule._getApis();
|
|
270
|
+
await topics.tools.lock(tid, uid);
|
|
271
|
+
return { tid, locked: true };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async execute_unlockTopic({ tid }, { uid } = {}) {
|
|
275
|
+
const { topics } = ProModule._getApis();
|
|
276
|
+
await topics.tools.unlock(tid, uid);
|
|
277
|
+
return { tid, locked: false };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async execute_pinTopic({ tid }, { uid } = {}) {
|
|
281
|
+
const { topics } = ProModule._getApis();
|
|
282
|
+
await topics.tools.pin(tid, uid);
|
|
283
|
+
return { tid, pinned: true };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async execute_unpinTopic({ tid }, { uid } = {}) {
|
|
287
|
+
const { topics } = ProModule._getApis();
|
|
288
|
+
await topics.tools.unpin(tid, uid);
|
|
289
|
+
return { tid, pinned: false };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async execute_moveTopic({ tid, cid }, { uid } = {}) {
|
|
293
|
+
const { topics } = ProModule._getApis();
|
|
294
|
+
await topics.tools.move(tid, { cid, uid });
|
|
295
|
+
return { tid, moved_to_cid: cid };
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async execute_deletePost({ pid }, { uid } = {}) {
|
|
299
|
+
const { posts } = ProModule._getApis();
|
|
300
|
+
await posts.delete(pid, uid);
|
|
301
|
+
return { pid, deleted: true, hard: false };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async execute_purgePost({ pid }, { uid } = {}) {
|
|
305
|
+
const { posts } = ProModule._getApis();
|
|
306
|
+
await posts.purge(pid, uid);
|
|
307
|
+
return { pid, purged: true, hard: true };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async execute_upvotePost({ pid }, { uid } = {}) {
|
|
311
|
+
const { posts } = ProModule._getApis();
|
|
312
|
+
await posts.upvote(pid, uid);
|
|
313
|
+
return { pid, vote: 'up' };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async execute_downvotePost({ pid }, { uid } = {}) {
|
|
317
|
+
const { posts } = ProModule._getApis();
|
|
318
|
+
await posts.downvote(pid, uid);
|
|
319
|
+
return { pid, vote: 'down' };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async execute_bookmarkPost({ pid, bookmark = true }, { uid } = {}) {
|
|
323
|
+
const { posts } = ProModule._getApis();
|
|
324
|
+
if (bookmark) await posts.bookmark(pid, uid);
|
|
325
|
+
else await posts.unbookmark(pid, uid);
|
|
326
|
+
return { pid, bookmarked: bookmark };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async execute_banUser({ uid: targetUid, reason, until }, { uid } = {}) {
|
|
330
|
+
const { user } = ProModule._getApis();
|
|
331
|
+
if (!(await user.exists(targetUid))) {
|
|
332
|
+
return { error: 'user_not_found' };
|
|
333
|
+
}
|
|
334
|
+
const payload = { uid: targetUid, reason: reason || '' };
|
|
335
|
+
if (until) payload.until = until;
|
|
336
|
+
await user.bans.ban(payload);
|
|
337
|
+
return { uid: targetUid, banned: true, reason: reason || null, until: until || null };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async execute_unbanUser({ uid: targetUid }, { uid } = {}) {
|
|
341
|
+
const { user } = ProModule._getApis();
|
|
342
|
+
if (!(await user.exists(targetUid))) {
|
|
343
|
+
return { error: 'user_not_found' };
|
|
344
|
+
}
|
|
345
|
+
await user.bans.unban(targetUid);
|
|
346
|
+
return { uid: targetUid, banned: false };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async execute_deleteUserAccount({ uid: targetUid, keepPosts = false }, { uid } = {}) {
|
|
350
|
+
const { user } = ProModule._getApis();
|
|
351
|
+
if (!(await user.exists(targetUid))) {
|
|
352
|
+
return { error: 'user_not_found' };
|
|
353
|
+
}
|
|
354
|
+
if (keepPosts) {
|
|
355
|
+
await user.deleteAccount(targetUid);
|
|
356
|
+
} else {
|
|
357
|
+
await user.delete(uid, targetUid);
|
|
358
|
+
}
|
|
359
|
+
return { uid: targetUid, deleted: true, kept_posts: keepPosts };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
ProModule._getApis = function () {
|
|
364
|
+
if (ProModule._nodebb) return ProModule._nodebb;
|
|
365
|
+
/* istanbul ignore next */
|
|
366
|
+
return {
|
|
367
|
+
topics: require.main.require('./src/topics'),
|
|
368
|
+
posts: require.main.require('./src/posts'),
|
|
369
|
+
messaging: require.main.require('./src/messaging'),
|
|
370
|
+
user: require.main.require('./src/user'),
|
|
371
|
+
};
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
ProModule._setApis = function (apis) { ProModule._nodebb = apis; };
|
|
375
|
+
|
|
376
|
+
return ProModule;
|
|
377
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* nodebb-admin-ai-connect — Pro package entry point.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors the Ghost model: a standalone Pro plugin that registers its tool-set
|
|
7
|
+
* into the core registry, gated by edition. The core calls loadPro() during
|
|
8
|
+
* registry construction with a context describing the core internals + the
|
|
9
|
+
* site edition. If the edition does not grant Pro, this returns without
|
|
10
|
+
* registering anything — so Pro tools never appear in the manifest and cannot
|
|
11
|
+
* be executed.
|
|
12
|
+
*/
|
|
13
|
+
const createProModule = require('./ProModule');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Bundle contract (BUNDLES-LICENSE-SPEC v1.0) — ctx.bundles wins over
|
|
17
|
+
* legacy ctx.edition. See ghost-members-ai-connect/src/index.js for the
|
|
18
|
+
* full contract description.
|
|
19
|
+
*
|
|
20
|
+
* @param {object} ctx
|
|
21
|
+
* @param {Function} ctx.ModuleRegistry Core ModuleRegistry class (for onInit).
|
|
22
|
+
* @param {Function} ctx.ModuleBase Core ModuleBase class.
|
|
23
|
+
* @param {string} [ctx.edition] Legacy: 'free' | 'pro'.
|
|
24
|
+
* @param {string[]} [ctx.bundles] New: array of granted bundle keys. Precedes edition.
|
|
25
|
+
* @returns {boolean} true if Pro tools were registered, false if gated out.
|
|
26
|
+
*/
|
|
27
|
+
function loadPro(ctx) {
|
|
28
|
+
const { ModuleRegistry, ModuleBase, edition, bundles } = ctx;
|
|
29
|
+
|
|
30
|
+
const resolvedBundles = Array.isArray(bundles)
|
|
31
|
+
? bundles
|
|
32
|
+
: (edition === 'pro' ? ['*'] : []);
|
|
33
|
+
|
|
34
|
+
if (resolvedBundles.length === 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!ModuleRegistry || !ModuleBase) {
|
|
39
|
+
throw new Error('nodebb-admin-ai-connect: incompatible core — missing ModuleRegistry/ModuleBase');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const ProModule = createProModule({ ModuleBase });
|
|
43
|
+
|
|
44
|
+
ModuleRegistry.onInit((registry) => {
|
|
45
|
+
registry.register(new ProModule());
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { loadPro };
|