nodebb-plugin-integration 0.3.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/library.js ADDED
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * NodeBB Integration Layer
5
+ *
6
+ * Main plugin entry point
7
+ */
8
+
9
+ require("dotenv").config();
10
+
11
+ const Config = require("./src/config");
12
+ const Routes = require("./src/routes");
13
+ const Events = require("./src/events");
14
+ const Indexer = require("./src/ai/indexer");
15
+ const MCP = require("./src/mcp/server");
16
+
17
+
18
+ const Plugin = {};
19
+
20
+
21
+ /**
22
+ * NodeBB hook:
23
+ *
24
+ * static:app.load
25
+ */
26
+ Plugin.init = async function ({ router } = {}) {
27
+
28
+ console.log(
29
+ "[NodeBB Integration] initializing"
30
+ );
31
+
32
+
33
+ if (router) {
34
+
35
+ Routes.init(
36
+ router
37
+ );
38
+
39
+ } else {
40
+
41
+ console.log(
42
+ "[NodeBB Integration] router unavailable (standalone mode)"
43
+ );
44
+
45
+ }
46
+
47
+
48
+ if (
49
+ Events &&
50
+ typeof Events.init === "function"
51
+ ) {
52
+
53
+ await Events.init();
54
+
55
+ }
56
+
57
+
58
+ if (
59
+ Config?.mcp?.enabled &&
60
+ process.env.MCP_AUTOSTART === "true" &&
61
+ MCP &&
62
+ typeof MCP.start === "function"
63
+ ) {
64
+
65
+ await MCP.start();
66
+
67
+ console.log(
68
+ "[NodeBB Integration] MCP started"
69
+ );
70
+
71
+ }
72
+
73
+
74
+ console.log(
75
+ "[NodeBB Integration] ready"
76
+ );
77
+
78
+ };
79
+
80
+
81
+ /**
82
+ * NodeBB hook:
83
+ *
84
+ * action:post.save
85
+ */
86
+ Plugin.onPostSave = async function (post) {
87
+
88
+ try {
89
+
90
+ if (
91
+ Events &&
92
+ typeof Events.postSave === "function"
93
+ ) {
94
+
95
+ await Events.postSave(
96
+ post
97
+ );
98
+
99
+ }
100
+
101
+
102
+ if (
103
+ Indexer &&
104
+ typeof Indexer.indexPost === "function"
105
+ ) {
106
+
107
+ await Indexer.indexPost(
108
+ post
109
+ );
110
+
111
+ }
112
+
113
+
114
+ } catch (error) {
115
+
116
+ console.error(
117
+ "[Plugin] post save error",
118
+ error.message
119
+ );
120
+
121
+ }
122
+
123
+ };
124
+
125
+
126
+ /**
127
+ * NodeBB hook:
128
+ *
129
+ * action:topic.save
130
+ */
131
+ Plugin.onTopicSave = async function (topic) {
132
+
133
+ try {
134
+
135
+ if (
136
+ Events &&
137
+ typeof Events.topicCreate === "function"
138
+ ) {
139
+
140
+ await Events.topicCreate(
141
+ topic
142
+ );
143
+
144
+ }
145
+
146
+
147
+ if (
148
+ Indexer &&
149
+ typeof Indexer.indexTopic === "function"
150
+ ) {
151
+
152
+ await Indexer.indexTopic(
153
+ topic
154
+ );
155
+
156
+ }
157
+
158
+
159
+ } catch (error) {
160
+
161
+ console.error(
162
+ "[Plugin] topic save error",
163
+ error.message
164
+ );
165
+
166
+ }
167
+
168
+ };
169
+
170
+
171
+ module.exports = Plugin;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "nodebb-plugin-integration",
3
+ "version": "0.3.0",
4
+ "description": "AI integration layer for NodeBB forums with MCP, semantic search, RAG and Qdrant knowledge pipeline",
5
+ "main": "library.js",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/Sergey-ek/nodebb-plugin-integration.git"
10
+ },
11
+ "keywords": [
12
+ "nodebb",
13
+ "nodebb-plugin",
14
+ "ai",
15
+ "mcp",
16
+ "model-context-protocol",
17
+ "rag",
18
+ "qdrant",
19
+ "embeddings",
20
+ "semantic-search",
21
+ "knowledge-base"
22
+ ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "files": [
27
+ "library.js",
28
+ "plugin.json",
29
+ "src",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "test": "cross-env NODE_ENV=test mocha test/*.test.js",
35
+ "check": "node --check library.js && node --check src/routes/index.js && node --check src/routes/api.js && node --check src/mcp/server.js && node --check src/mcp/tools.js",
36
+ "diagnose": "node -e \"console.log(require('./src/config').info())\"",
37
+ "mcp": "node src/mcp/server.js"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.0.0",
41
+ "@qdrant/js-client-rest": "^1.12.0",
42
+ "dotenv": "^16.4.7",
43
+ "node-fetch": "^3.3.2",
44
+ "openai": "^6.45.0"
45
+ },
46
+ "devDependencies": {
47
+ "cross-env": "^10.0.0",
48
+ "mocha": "^11.7.6"
49
+ },
50
+ "nbbpm": {
51
+ "compatibility": "^4.4.1"
52
+ }
53
+ }
package/plugin.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "nodebb-plugin-integration",
3
+ "name": "NodeBB Integration Layer",
4
+ "description": "AI integration layer for NodeBB with MCP, semantic search, RAG and Qdrant knowledge pipeline",
5
+ "library": "./library.js",
6
+ "staticDirs": {
7
+ "public": "public"
8
+ },
9
+ "hooks": [
10
+ {
11
+ "hook": "static:app.load",
12
+ "method": "init"
13
+ },
14
+ {
15
+ "hook": "action:post.save",
16
+ "method": "onPostSave"
17
+ },
18
+ {
19
+ "hook": "action:topic.save",
20
+ "method": "onTopicSave"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+
3
+
4
+ /*
5
+ * NodeBB Integration Layer
6
+ *
7
+ * Embeddings Service
8
+ *
9
+ * Unified interface for:
10
+ *
11
+ * - OpenAI embeddings
12
+ * - Fake test embeddings
13
+ *
14
+ */
15
+
16
+
17
+
18
+ const Config =
19
+ require("../config");
20
+
21
+
22
+ const OpenAI =
23
+ require("./openai");
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+ /*
32
+ * Embedding size
33
+ *
34
+ * text-embedding-3-small = 1536
35
+ */
36
+
37
+
38
+ const SIZE =
39
+
40
+ Number(
41
+
42
+ process.env.EMBEDDING_SIZE ||
43
+
44
+ 1536
45
+
46
+ );
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+ /*
57
+ * Generate fake vector
58
+ *
59
+ * Used for:
60
+ *
61
+ * - tests
62
+ * - development
63
+ *
64
+ */
65
+
66
+
67
+ function fakeEmbedding() {
68
+
69
+
70
+ const vector = [];
71
+
72
+
73
+
74
+ for(let i = 0; i < SIZE; i++) {
75
+
76
+
77
+ vector.push(
78
+
79
+ Math.random()
80
+
81
+ );
82
+
83
+
84
+ }
85
+
86
+
87
+
88
+ return vector;
89
+
90
+
91
+ }
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+
101
+ /*
102
+ * Select provider
103
+ */
104
+
105
+
106
+ function provider() {
107
+
108
+
109
+
110
+ return (
111
+
112
+ process.env.AI_PROVIDER ||
113
+
114
+ "fake"
115
+
116
+ );
117
+
118
+
119
+ }
120
+
121
+
122
+
123
+
124
+
125
+
126
+
127
+
128
+
129
+ /*
130
+ * Create embedding
131
+ */
132
+
133
+
134
+ async function create(text) {
135
+
136
+
137
+
138
+ if(!text || !text.trim()) {
139
+
140
+
141
+ return null;
142
+
143
+
144
+ }
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+ /*
154
+ * OpenAI mode
155
+ */
156
+
157
+
158
+ if(
159
+
160
+ provider() === "openai"
161
+
162
+ ) {
163
+
164
+
165
+
166
+ const vector =
167
+
168
+ await OpenAI.embeddings(
169
+
170
+ text
171
+
172
+ );
173
+
174
+
175
+
176
+
177
+
178
+ return vector;
179
+
180
+
181
+ }
182
+
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+ /*
191
+ * Development/Test mode
192
+ */
193
+
194
+
195
+ return fakeEmbedding();
196
+
197
+
198
+
199
+ }
200
+
201
+
202
+
203
+
204
+
205
+
206
+
207
+
208
+
209
+ /*
210
+ * Service information
211
+ */
212
+
213
+
214
+ function info() {
215
+
216
+
217
+ return {
218
+
219
+
220
+ provider:
221
+ provider(),
222
+
223
+
224
+ size:
225
+ SIZE
226
+
227
+
228
+ };
229
+
230
+
231
+ }
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+
241
+ module.exports = {
242
+
243
+
244
+ create,
245
+
246
+
247
+ info,
248
+
249
+
250
+ SIZE
251
+
252
+
253
+
254
+ };