nodebb-plugin-integration 0.4.1 → 0.4.4

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 CHANGED
@@ -1,115 +1,93 @@
1
- "use strict";
2
-
3
- /**
4
- * NodeBB Integration Layer
5
- *
6
- * Main plugin entry point
7
- */
8
-
9
- const Config = require("./src/config");
10
- const Routes = require("./src/routes");
11
- const Events = require("./src/events");
12
- const Indexer = require("./src/ai/indexer");
13
- const MCP = require("./src/mcp/server");
14
-
15
- const Plugin = {};
16
-
17
- /**
18
- * static:app.load
19
- */
20
- Plugin.init = async function (params = {}) {
21
-
22
- console.log("[NodeBB Integration] initializing");
23
-
24
- const { router, middleware } = params;
25
-
26
- if (router) {
27
- Routes.init(router, middleware);
28
- } else {
29
- console.log("[NodeBB Integration] router unavailable (standalone mode)");
30
- }
31
-
32
- if (Events && typeof Events.init === "function") {
33
- await Events.init();
34
- }
35
-
36
- const settings = await Config.get();
37
-
38
- if (
39
- settings.mcpEnabled &&
40
- MCP &&
41
- typeof MCP.start === "function"
42
- ) {
43
- try {
44
- await MCP.start();
45
- console.log("[NodeBB Integration] MCP server started");
46
- } catch (err) {
47
- console.error("[NodeBB Integration] MCP start failed:", err.message);
48
- }
49
- }
50
-
51
- console.log("[NodeBB Integration] ready");
52
- };
53
-
54
- /**
55
- * action:post.save
56
- */
57
- Plugin.onPostSave = async function (post) {
58
-
59
- try {
60
-
61
- if (Events && typeof Events.postSave === "function") {
62
- await Events.postSave(post);
63
- }
64
-
65
- if (Indexer && typeof Indexer.indexPost === "function") {
66
- await Indexer.indexPost(post);
67
- }
68
-
69
- } catch (err) {
70
-
71
- console.error("[Plugin] post save error:", err.message);
72
-
73
- }
74
-
75
- };
76
-
77
- /**
78
- * action:topic.save
79
- */
80
- Plugin.onTopicSave = async function (topic) {
81
-
82
- try {
83
-
84
- if (Events && typeof Events.topicCreate === "function") {
85
- await Events.topicCreate(topic);
86
- }
87
-
88
- if (Indexer && typeof Indexer.indexTopic === "function") {
89
- await Indexer.indexTopic(topic);
90
- }
91
-
92
- } catch (err) {
93
-
94
- console.error("[Plugin] topic save error:", err.message);
95
-
96
- }
97
-
98
- };
99
-
100
- /**
101
- * ACP menu item
102
- */
103
- Plugin.addAdminNavigation = async function (header) {
104
-
105
- header.plugins.push({
106
- route: "/plugins/integration",
107
- icon: "fa-robot",
108
- name: "NodeBB Integration"
109
- });
110
-
111
- return header;
112
-
113
- };
114
-
115
- module.exports = Plugin;
1
+ "use strict";
2
+
3
+ const Routes = require("./src/routes");
4
+ const Events = require("./src/events");
5
+ const Config = require("./src/config");
6
+ const MCPManager = require("./src/mcp/manager");
7
+
8
+ const Plugin = {};
9
+
10
+ function registerSocketHandlers() {
11
+ if (typeof nodebb === "undefined" || !nodebb ||
12
+ typeof nodebb.require !== "function") {
13
+ throw new Error("Cannot access the NodeBB module loader");
14
+ }
15
+
16
+ const SocketPlugins = nodebb.require("./src/socket.io/plugins");
17
+ const SocketAdmin = nodebb.require("./src/socket.io/admin");
18
+
19
+ SocketPlugins.integration = SocketPlugins.integration || {};
20
+
21
+ SocketPlugins.integration.save = async function (socket, data) {
22
+ await SocketAdmin.before(socket, "admin.settings.set");
23
+
24
+ await Config.set(data);
25
+
26
+ MCPManager.refresh().catch((error) => {
27
+ console.error(
28
+ "[NodeBB Integration] MCP configuration refresh failed:",
29
+ error.message
30
+ );
31
+ });
32
+
33
+ return Config.info();
34
+ };
35
+ }
36
+
37
+ Plugin.init = async function (params = {}) {
38
+ console.log("[NodeBB Integration] initializing");
39
+
40
+ const { router, middleware } = params;
41
+
42
+ registerSocketHandlers();
43
+
44
+ if (router) {
45
+ Routes.init(router, middleware);
46
+ }
47
+
48
+ await Events.init();
49
+
50
+ MCPManager.init();
51
+
52
+ console.log("[NodeBB Integration] ready");
53
+ };
54
+
55
+ Plugin.onPostSave = async function (data) {
56
+ return Events.postSave(data);
57
+ };
58
+
59
+ Plugin.onPostEdit = async function (data) {
60
+ return Events.postEdit(data);
61
+ };
62
+
63
+ Plugin.onPostDelete = async function (data) {
64
+ return Events.postDelete(data);
65
+ };
66
+
67
+ Plugin.onPostRestore = async function (data) {
68
+ return Events.postRestore(data);
69
+ };
70
+
71
+ Plugin.onPostsPurge = async function (data) {
72
+ return Events.postsPurge(data);
73
+ };
74
+
75
+ Plugin.onTopicDelete = async function (data) {
76
+ return Events.topicDelete(data);
77
+ };
78
+
79
+ Plugin.onTopicRestore = async function (data) {
80
+ return Events.topicRestore(data);
81
+ };
82
+
83
+ Plugin.addAdminNavigation = async function (header) {
84
+ header.plugins.push({
85
+ route: "/plugins/integration",
86
+ icon: "fa-robot",
87
+ name: "NodeBB Integration"
88
+ });
89
+
90
+ return header;
91
+ };
92
+
93
+ module.exports = Plugin;
package/package.json CHANGED
@@ -1,53 +1,60 @@
1
- {
2
- "name": "nodebb-plugin-integration",
3
- "version": "0.4.1",
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
- }
1
+ {
2
+ "name": "nodebb-plugin-integration",
3
+ "version": "0.4.4",
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
+ "files": [
12
+ "library.js",
13
+ "plugin.json",
14
+ "src",
15
+ "public",
16
+ "languages",
17
+ "README.md",
18
+ "README.ru.md",
19
+ "LICENSE"
20
+ ],
21
+ "keywords": [
22
+ "nodebb",
23
+ "nodebb-plugin",
24
+ "ai",
25
+ "mcp",
26
+ "model-context-protocol",
27
+ "rag",
28
+ "qdrant",
29
+ "embeddings",
30
+ "semantic-search",
31
+ "knowledge-base"
32
+ ],
33
+ "engines": {
34
+ "node": ">=22"
35
+ },
36
+ "scripts": {
37
+ "test": "cross-env NODE_ENV=test mocha test/*.test.js",
38
+ "check": "find . -type d -name node_modules -prune -o -type f -name \"*.js\" -print0 | xargs -0 -r -n1 node --check"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.0.0",
42
+ "@qdrant/js-client-rest": "^1.12.0",
43
+ "dotenv": "^16.4.7",
44
+ "node-fetch": "^3.3.2",
45
+ "openai": "^6.45.0"
46
+ },
47
+ "devDependencies": {
48
+ "cross-env": "^10.0.0",
49
+ "mocha": "^12.0.3",
50
+ "proxyquire": "^2.1.3"
51
+ },
52
+ "nbbpm": {
53
+ "compatibility": "^4.4.1"
54
+ },
55
+ "overrides": {
56
+ "undici": "^7.30.1",
57
+ "glob": "^14.0.2",
58
+ "minimatch": "^11.0.1"
59
+ }
60
+ }
package/plugin.json CHANGED
@@ -1,33 +1,50 @@
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
-
7
- "staticDirs": {
8
- "public": "public"
9
- },
10
-
11
- "modules": {
12
- "admin/plugins/integration.js": "public/js/admin.js"
13
- },
14
-
15
- "hooks": [
16
- {
17
- "hook": "static:app.load",
18
- "method": "init"
19
- },
20
- {
21
- "hook": "action:post.save",
22
- "method": "onPostSave"
23
- },
24
- {
25
- "hook": "action:topic.save",
26
- "method": "onTopicSave"
27
- },
28
- {
29
- "hook": "filter:admin.header.build",
30
- "method": "addAdminNavigation"
31
- }
32
- ]
33
- }
1
+ {
2
+ "id": "nodebb-plugin-integration",
3
+ "name": "NodeBB Integration Layer",
4
+ "description": "AI integration, semantic search, RAG and Qdrant knowledge pipeline for NodeBB",
5
+ "staticDirs": {
6
+ "public": "public"
7
+ },
8
+ "modules": {
9
+ "../admin/plugins/integration.js": "public/js/admin.js"
10
+ },
11
+ "hooks": [
12
+ {
13
+ "hook": "static:app.load",
14
+ "method": "init"
15
+ },
16
+ {
17
+ "hook": "action:post.save",
18
+ "method": "onPostSave"
19
+ },
20
+ {
21
+ "hook": "action:post.edit",
22
+ "method": "onPostEdit"
23
+ },
24
+ {
25
+ "hook": "action:post.delete",
26
+ "method": "onPostDelete"
27
+ },
28
+ {
29
+ "hook": "action:post.restore",
30
+ "method": "onPostRestore"
31
+ },
32
+ {
33
+ "hook": "action:posts.purge",
34
+ "method": "onPostsPurge"
35
+ },
36
+ {
37
+ "hook": "action:topic.delete",
38
+ "method": "onTopicDelete"
39
+ },
40
+ {
41
+ "hook": "action:topic.restore",
42
+ "method": "onTopicRestore"
43
+ },
44
+ {
45
+ "hook": "filter:admin.header.build",
46
+ "method": "addAdminNavigation"
47
+ }
48
+ ],
49
+ "templates": "./public/templates"
50
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ define("admin/plugins/integration", ["alerts"], function (Alerts) {
4
+ const ACP = {};
5
+
6
+ ACP.init = function () {
7
+ const container = $(".acp-page-container");
8
+ const form = container.find("form");
9
+ const saveButton = $("#save");
10
+
11
+ saveButton.on("click", function () {
12
+ const values = {};
13
+
14
+ form.find("[name]").each(function () {
15
+ const field = $(this);
16
+ const name = field.attr("name");
17
+
18
+ if (field.is("select[multiple]")) {
19
+ values[name] = field.val() || [];
20
+ } else if (field.is(":checkbox")) {
21
+ values[name] = field.is(":checked");
22
+ } else if (field.attr("type") === "number") {
23
+ values[name] = Number(field.val());
24
+ } else {
25
+ values[name] = field.val();
26
+ }
27
+ });
28
+
29
+ for (const name of ["openaiApiKey", "mcpBearerToken"]) {
30
+ if (typeof values[name] !== "string" || !values[name].trim()) {
31
+ delete values[name];
32
+ }
33
+ }
34
+
35
+ saveButton.prop("disabled", true);
36
+
37
+ socket.emit("plugins.integration.save", values, function (error, result) {
38
+ saveButton.prop("disabled", false);
39
+
40
+ if (error) {
41
+ Alerts.error(error.message || "Failed to save settings");
42
+ return;
43
+ }
44
+
45
+ form.find('[name="openaiApiKey"], [name="mcpBearerToken"]').val("");
46
+
47
+ container.find(".integration-save-status").remove();
48
+
49
+ $("<div>", {
50
+ class: "alert alert-success integration-save-status",
51
+ text: result ? "Settings saved successfully" : "Settings saved"
52
+ }).insertBefore(form);
53
+ });
54
+ });
55
+ };
56
+
57
+ return ACP;
58
+ });