johankit-runtime 0.0.2 → 0.4.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/README.md +93 -115
- package/dist/src/app.d.ts +7 -0
- package/dist/src/app.d.ts.map +1 -0
- package/dist/src/app.js +18 -0
- package/dist/src/app.js.map +1 -0
- package/dist/src/core/cache.d.ts +6 -0
- package/dist/src/core/cache.d.ts.map +1 -0
- package/dist/src/core/cache.js +18 -0
- package/dist/src/core/cache.js.map +1 -0
- package/dist/src/core/conditions/resolver.d.ts +2 -0
- package/dist/src/core/conditions/resolver.d.ts.map +1 -0
- package/dist/src/core/conditions/resolver.js +20 -0
- package/dist/src/core/conditions/resolver.js.map +1 -0
- package/dist/src/core/config-loader.d.ts +2 -0
- package/dist/src/core/config-loader.d.ts.map +1 -0
- package/dist/src/core/config-loader.js +18 -0
- package/dist/src/core/config-loader.js.map +1 -0
- package/dist/src/core/context.d.ts +2 -0
- package/dist/src/core/context.d.ts.map +1 -0
- package/dist/src/core/context.js +5 -0
- package/dist/src/core/context.js.map +1 -0
- package/dist/src/core/discovery.d.ts +4 -0
- package/dist/src/core/discovery.d.ts.map +1 -0
- package/dist/src/core/discovery.js +6 -0
- package/dist/src/core/discovery.js.map +1 -0
- package/dist/src/core/event/dispatch.d.ts +2 -0
- package/dist/src/core/event/dispatch.d.ts.map +1 -0
- package/dist/src/core/event/dispatch.js +30 -0
- package/dist/src/core/event/dispatch.js.map +1 -0
- package/dist/src/core/mcp/http-server.d.ts +11 -0
- package/dist/src/core/mcp/http-server.d.ts.map +1 -0
- package/dist/src/core/mcp/http-server.js +48 -0
- package/dist/src/core/mcp/http-server.js.map +1 -0
- package/dist/src/core/mcp/tools-mcp.d.ts +14 -0
- package/dist/src/core/mcp/tools-mcp.d.ts.map +1 -0
- package/dist/src/core/mcp/tools-mcp.js +55 -0
- package/dist/src/core/mcp/tools-mcp.js.map +1 -0
- package/dist/src/core/middleware/run.d.ts +3 -0
- package/dist/src/core/middleware/run.d.ts.map +1 -0
- package/dist/src/core/middleware/run.js +39 -0
- package/dist/src/core/middleware/run.js.map +1 -0
- package/dist/src/core/package.d.ts +8 -0
- package/dist/src/core/package.d.ts.map +1 -0
- package/dist/src/core/package.js +68 -0
- package/dist/src/core/package.js.map +1 -0
- package/dist/src/core/parse/cognites.d.ts +5 -0
- package/dist/src/core/parse/cognites.d.ts.map +1 -0
- package/{src → dist/src}/core/parse/cognites.js +7 -10
- package/dist/src/core/parse/cognites.js.map +1 -0
- package/dist/src/core/parse/hooks.d.ts +10 -0
- package/dist/src/core/parse/hooks.d.ts.map +1 -0
- package/dist/src/core/parse/hooks.js +31 -0
- package/dist/src/core/parse/hooks.js.map +1 -0
- package/dist/src/core/parse/middleware.d.ts +6 -0
- package/dist/src/core/parse/middleware.d.ts.map +1 -0
- package/{src → dist/src}/core/parse/middleware.js +9 -14
- package/dist/src/core/parse/middleware.js.map +1 -0
- package/dist/src/core/parse/predicates.d.ts +5 -0
- package/dist/src/core/parse/predicates.d.ts.map +1 -0
- package/{src → dist/src}/core/parse/predicates.js +9 -10
- package/dist/src/core/parse/predicates.js.map +1 -0
- package/dist/src/core/parse/routes.d.ts +6 -0
- package/dist/src/core/parse/routes.d.ts.map +1 -0
- package/{src → dist/src}/core/parse/routes.js +12 -17
- package/dist/src/core/parse/routes.js.map +1 -0
- package/dist/src/core/parse/tools.d.ts +12 -0
- package/dist/src/core/parse/tools.d.ts.map +1 -0
- package/dist/src/core/parse/tools.js +77 -0
- package/dist/src/core/parse/tools.js.map +1 -0
- package/dist/src/core/register/decorators.d.ts +7 -0
- package/dist/src/core/register/decorators.d.ts.map +1 -0
- package/dist/src/core/register/decorators.js +114 -0
- package/dist/src/core/register/decorators.js.map +1 -0
- package/dist/src/core/register/decorators.test.d.ts +2 -0
- package/dist/src/core/register/decorators.test.d.ts.map +1 -0
- package/dist/src/core/register/decorators.test.js +17 -0
- package/dist/src/core/register/decorators.test.js.map +1 -0
- package/dist/src/core/register.d.ts +2 -0
- package/dist/src/core/register.d.ts.map +1 -0
- package/dist/src/core/register.js +12 -0
- package/dist/src/core/register.js.map +1 -0
- package/dist/src/core/routes.d.ts +2 -0
- package/dist/src/core/routes.d.ts.map +1 -0
- package/dist/src/core/routes.js +65 -0
- package/dist/src/core/routes.js.map +1 -0
- package/dist/src/core/schema/export.d.ts +5 -0
- package/dist/src/core/schema/export.d.ts.map +1 -0
- package/dist/src/core/schema/export.js +15 -0
- package/dist/src/core/schema/export.js.map +1 -0
- package/dist/src/core/tools/capabilities.d.ts +2 -0
- package/dist/src/core/tools/capabilities.d.ts.map +1 -0
- package/dist/src/core/tools/capabilities.js +11 -0
- package/dist/src/core/tools/capabilities.js.map +1 -0
- package/dist/src/core/tools/lifecycle.d.ts +2 -0
- package/dist/src/core/tools/lifecycle.d.ts.map +1 -0
- package/dist/src/core/tools/lifecycle.js +6 -0
- package/dist/src/core/tools/lifecycle.js.map +1 -0
- package/dist/src/core/tools/middleware.d.ts +2 -0
- package/dist/src/core/tools/middleware.d.ts.map +1 -0
- package/dist/src/core/tools/middleware.js +7 -0
- package/dist/src/core/tools/middleware.js.map +1 -0
- package/dist/src/core/wiring.d.ts +2 -0
- package/dist/src/core/wiring.d.ts.map +1 -0
- package/dist/src/core/wiring.js +8 -0
- package/dist/src/core/wiring.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +40 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/server.d.ts +2 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +19 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/types.d.ts +87 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tests/cognites.test.js +21 -0
- package/dist/tests/dispatch.test.js +73 -0
- package/dist/tests/hooks.test.js +31 -0
- package/dist/tests/middleware.test.js +19 -0
- package/dist/tests/predicates.test.js +20 -0
- package/dist/tests/routes.test.js +26 -0
- package/dist/tests/tools.test.js +38 -0
- package/package.json +13 -12
- package/src/app.ts +0 -18
- package/src/core/event/dispatch.js +0 -31
- package/src/core/mcp/http-server.js +0 -52
- package/src/core/mcp/tools-mcp.js +0 -50
- package/src/core/package.js +0 -79
- package/src/core/parse/hooks.js +0 -37
- package/src/core/parse/tools.js +0 -82
- package/src/core/register/decorators.js +0 -150
- package/src/core/register/decorators.test.js +0 -13
- package/src/core/register.js +0 -37
- package/src/core/routes.js +0 -89
- package/src/index.ts +0 -42
- package/src/server.ts +0 -15
- package/src/types.ts +0 -104
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
(0, vitest_1.describe)('dispatchEvent', () => {
|
|
5
|
+
let dispatchEvent;
|
|
6
|
+
let packageModule;
|
|
7
|
+
let decoratorModule;
|
|
8
|
+
(0, vitest_1.beforeEach)(() => {
|
|
9
|
+
vitest_1.vi.resetModules();
|
|
10
|
+
vitest_1.vi.clearAllMocks();
|
|
11
|
+
packageModule = require('../src/core/package');
|
|
12
|
+
decoratorModule = require('../src/core/register/decorators');
|
|
13
|
+
vitest_1.vi.spyOn(packageModule, 'getPackages');
|
|
14
|
+
vitest_1.vi.spyOn(decoratorModule, 'registerHooks');
|
|
15
|
+
({ dispatchEvent } = require('../src/core/event/dispatch'));
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('processa hooks de todos os packages e retorna o resultado final', async () => {
|
|
18
|
+
packageModule.getPackages.mockResolvedValue([
|
|
19
|
+
{ folder: 'pkg1' },
|
|
20
|
+
{ folder: 'pkg2' },
|
|
21
|
+
]);
|
|
22
|
+
const hook1 = vitest_1.vi.fn(async (d) => ({ ...d, p1: true }));
|
|
23
|
+
const hook2 = vitest_1.vi.fn(async (d) => ({ ...d, p2: true }));
|
|
24
|
+
decoratorModule.registerHooks.mockImplementation(async (folder) => {
|
|
25
|
+
if (folder === 'pkg1')
|
|
26
|
+
return [{ event: 'test', call: hook1 }];
|
|
27
|
+
if (folder === 'pkg2')
|
|
28
|
+
return [{ event: 'test', call: hook2 }];
|
|
29
|
+
return [];
|
|
30
|
+
});
|
|
31
|
+
const result = await dispatchEvent('test', { base: true });
|
|
32
|
+
(0, vitest_1.expect)(result).toEqual({
|
|
33
|
+
base: true,
|
|
34
|
+
p1: true,
|
|
35
|
+
p2: true,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
(0, vitest_1.it)('continua execução se um package falhar ao registrar hooks', async () => {
|
|
39
|
+
packageModule.getPackages.mockResolvedValue([
|
|
40
|
+
{ folder: 'fail' },
|
|
41
|
+
{ folder: 'ok' },
|
|
42
|
+
]);
|
|
43
|
+
decoratorModule.registerHooks.mockImplementation(async (folder) => {
|
|
44
|
+
if (folder === 'fail')
|
|
45
|
+
throw new Error('Load error');
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
event: 'test',
|
|
49
|
+
call: async (d) => ({ ...d, ok: true }),
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
});
|
|
53
|
+
const result = await dispatchEvent('test', {});
|
|
54
|
+
(0, vitest_1.expect)(result).toEqual({ ok: true });
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.it)('ignora hooks de outros eventos', async () => {
|
|
57
|
+
packageModule.getPackages.mockResolvedValue([{ folder: 'pkg' }]);
|
|
58
|
+
decoratorModule.registerHooks.mockResolvedValue([
|
|
59
|
+
{ event: 'other', call: vitest_1.vi.fn() },
|
|
60
|
+
{ event: 'test', call: async (d) => ({ ...d, hit: true }) },
|
|
61
|
+
]);
|
|
62
|
+
const result = await dispatchEvent('test', {});
|
|
63
|
+
(0, vitest_1.expect)(result).toEqual({ hit: true });
|
|
64
|
+
});
|
|
65
|
+
(0, vitest_1.it)('ignora retorno null ou undefined do hook', async () => {
|
|
66
|
+
packageModule.getPackages.mockResolvedValue([{ folder: 'pkg' }]);
|
|
67
|
+
decoratorModule.registerHooks.mockResolvedValue([
|
|
68
|
+
{ event: 'test', call: async () => null },
|
|
69
|
+
]);
|
|
70
|
+
const result = await dispatchEvent('test', { base: true });
|
|
71
|
+
(0, vitest_1.expect)(result).toEqual({ base: true });
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const { parseHook } = require('../src/core/parse/hooks');
|
|
5
|
+
(0, vitest_1.describe)('parseHook', () => {
|
|
6
|
+
(0, vitest_1.it)('should return null if input is not a function', () => {
|
|
7
|
+
(0, vitest_1.expect)(parseHook('not a function')).toBeNull();
|
|
8
|
+
});
|
|
9
|
+
(0, vitest_1.it)('should parse a valid hook with conditions', () => {
|
|
10
|
+
const testHook = () => {
|
|
11
|
+
/**
|
|
12
|
+
* @register_hook user_login
|
|
13
|
+
* @only web
|
|
14
|
+
* @never mobile
|
|
15
|
+
* @when authenticated
|
|
16
|
+
*/
|
|
17
|
+
return true;
|
|
18
|
+
};
|
|
19
|
+
const result = parseHook(testHook);
|
|
20
|
+
(0, vitest_1.expect)(result.event).toBe('user_login');
|
|
21
|
+
(0, vitest_1.expect)(result.condition.only).toContain('web');
|
|
22
|
+
(0, vitest_1.expect)(result.condition.never).toContain('mobile');
|
|
23
|
+
(0, vitest_1.expect)(result.condition.when).toContain('authenticated');
|
|
24
|
+
});
|
|
25
|
+
(0, vitest_1.it)('should return null if no register_hook decorator is found', () => {
|
|
26
|
+
const noHook = () => {
|
|
27
|
+
/** @description just a comment */
|
|
28
|
+
};
|
|
29
|
+
(0, vitest_1.expect)(parseHook(noHook)).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const { parseMiddleware } = require('../src/core/parse/middleware');
|
|
5
|
+
(0, vitest_1.describe)('parseMiddleware', () => {
|
|
6
|
+
(0, vitest_1.it)('should parse middleware inside function body', () => {
|
|
7
|
+
const myMiddleware = () => {
|
|
8
|
+
/**
|
|
9
|
+
* @register_middleware checkLog
|
|
10
|
+
* @predicate is_admin
|
|
11
|
+
* @predicate is_owner
|
|
12
|
+
*/
|
|
13
|
+
};
|
|
14
|
+
const result = parseMiddleware(myMiddleware);
|
|
15
|
+
(0, vitest_1.expect)(result.name).toBe('checkLog');
|
|
16
|
+
(0, vitest_1.expect)(result.predicates).toContain('is_admin');
|
|
17
|
+
(0, vitest_1.expect)(result.predicates).toContain('is_owner');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const { parsePredicate } = require('../src/core/parse/predicates');
|
|
5
|
+
(0, vitest_1.describe)('parsePredicate', () => {
|
|
6
|
+
(0, vitest_1.it)('should parse predicate with custom name', () => {
|
|
7
|
+
const isAuth = () => {
|
|
8
|
+
/** @register_predicate auth_check */
|
|
9
|
+
};
|
|
10
|
+
const result = parsePredicate(isAuth);
|
|
11
|
+
(0, vitest_1.expect)(result.name).toBe('auth_check');
|
|
12
|
+
});
|
|
13
|
+
(0, vitest_1.it)('should use function name if decorator name is missing', () => {
|
|
14
|
+
function isAdmin() {
|
|
15
|
+
/** @register_predicate */
|
|
16
|
+
}
|
|
17
|
+
const result = parsePredicate(isAdmin);
|
|
18
|
+
(0, vitest_1.expect)(result.name).toBe('isAdmin');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const { parseRouter } = require('../src/core/parse/routes');
|
|
5
|
+
(0, vitest_1.describe)('parseRouter', () => {
|
|
6
|
+
(0, vitest_1.it)('should parse valid route decorators', () => {
|
|
7
|
+
const handler = () => {
|
|
8
|
+
/**
|
|
9
|
+
* @register_router /test
|
|
10
|
+
* @method POST
|
|
11
|
+
*/
|
|
12
|
+
};
|
|
13
|
+
const result = parseRouter(handler);
|
|
14
|
+
(0, vitest_1.expect)(result.path).toBe('/test');
|
|
15
|
+
(0, vitest_1.expect)(result.method).toBe('post');
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)('should default method to get', () => {
|
|
18
|
+
const handler = () => {
|
|
19
|
+
/**
|
|
20
|
+
* @register_router /simple
|
|
21
|
+
*/
|
|
22
|
+
};
|
|
23
|
+
const result = parseRouter(handler);
|
|
24
|
+
(0, vitest_1.expect)(result.method).toBe('get');
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const { parseTool } = require('../src/core/parse/tools');
|
|
5
|
+
(0, vitest_1.describe)('parseTool', () => {
|
|
6
|
+
(0, vitest_1.it)('should parse tool parameters and descriptions', () => {
|
|
7
|
+
function calculate(args) {
|
|
8
|
+
/**
|
|
9
|
+
* @register_tool calc
|
|
10
|
+
* @description useful tool
|
|
11
|
+
* @param {number} x - first number
|
|
12
|
+
* @param {number?} y - second optional number
|
|
13
|
+
*/
|
|
14
|
+
}
|
|
15
|
+
const result = parseTool(calculate);
|
|
16
|
+
(0, vitest_1.expect)(result.name).toBe('calc');
|
|
17
|
+
(0, vitest_1.expect)(result.description).toBe('useful tool');
|
|
18
|
+
(0, vitest_1.expect)(result.parameters).toHaveLength(2);
|
|
19
|
+
(0, vitest_1.expect)(result.parameters[0].name).toBe('x');
|
|
20
|
+
(0, vitest_1.expect)(result.parameters[0].required).toBe(true);
|
|
21
|
+
(0, vitest_1.expect)(result.parameters[1].name).toBe('y');
|
|
22
|
+
(0, vitest_1.expect)(result.parameters[1].required).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)('should parse complex nested object parameters', () => {
|
|
25
|
+
const tool = () => {
|
|
26
|
+
/**
|
|
27
|
+
* @register_tool complex
|
|
28
|
+
* @param {string} user.name - name
|
|
29
|
+
* @param {number} user.age - age
|
|
30
|
+
*/
|
|
31
|
+
};
|
|
32
|
+
const result = parseTool(tool);
|
|
33
|
+
const userParam = result.parameters.find(p => p.name === 'user');
|
|
34
|
+
(0, vitest_1.expect)(userParam).toBeDefined();
|
|
35
|
+
(0, vitest_1.expect)(userParam.type).toBe('object');
|
|
36
|
+
(0, vitest_1.expect)(result.parameters).toHaveLength(3);
|
|
37
|
+
});
|
|
38
|
+
});
|
package/package.json
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "johankit-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "A pluggable runtime for any back-end",
|
|
5
|
-
"main": "src/index.js",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/src/index.d.ts",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "tsc",
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
8
10
|
"test": "vitest run",
|
|
9
11
|
"test:coverage": "vitest run --coverage"
|
|
10
12
|
},
|
|
11
13
|
"files": [
|
|
12
|
-
"
|
|
14
|
+
"dist",
|
|
13
15
|
"README.md"
|
|
14
16
|
],
|
|
15
17
|
"dependencies": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
+
"@sveltejs/kit": "^2.49.4",
|
|
19
|
+
"express": "^4.22.1",
|
|
20
|
+
"js-yaml": "^4.1.0",
|
|
21
|
+
"mdsvex": "^0.12.6"
|
|
18
22
|
},
|
|
19
23
|
"devDependencies": {
|
|
20
|
-
"
|
|
24
|
+
"@types/express": "^4.17.21",
|
|
25
|
+
"@types/node": "^20.0.0",
|
|
21
26
|
"@vitest/coverage-v8": "latest",
|
|
22
27
|
"typescript": "^5.4.0",
|
|
23
|
-
"
|
|
24
|
-
"@types/express": "^4.17.21"
|
|
25
|
-
},
|
|
26
|
-
"peerDependencies": {
|
|
27
|
-
"express": "^4.x"
|
|
28
|
+
"vitest": "latest"
|
|
28
29
|
}
|
|
29
|
-
}
|
|
30
|
+
}
|
package/src/app.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import express, { Application } from 'express'
|
|
2
|
-
import { registerPackages } from './core/register'
|
|
3
|
-
|
|
4
|
-
type JohankitApp = Application & {
|
|
5
|
-
setup: () => Promise<void>
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const app = express() as Application
|
|
9
|
-
|
|
10
|
-
;(app as JohankitApp).setup = async () => {
|
|
11
|
-
try {
|
|
12
|
-
await registerPackages(app)
|
|
13
|
-
} catch (error: any) {
|
|
14
|
-
console.log(`Error registering packages: ${error.message}`, 'error')
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default app as JohankitApp
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
const { registerHooks } = require('../register/decorators');
|
|
2
|
-
const { getPackages } = require('../package');
|
|
3
|
-
|
|
4
|
-
async function dispatchEvent(eventName, payload) {
|
|
5
|
-
const packages = await getPackages();
|
|
6
|
-
let finalResult = payload;
|
|
7
|
-
|
|
8
|
-
for (const pkg of packages) {
|
|
9
|
-
try {
|
|
10
|
-
const hooks = await registerHooks(pkg.folder);
|
|
11
|
-
const relevant = hooks.filter(h => h.event === eventName);
|
|
12
|
-
|
|
13
|
-
for (const hook of relevant) {
|
|
14
|
-
try {
|
|
15
|
-
const result = await hook.call(finalResult);
|
|
16
|
-
if (result !== undefined && result !== null) {
|
|
17
|
-
finalResult = result;
|
|
18
|
-
}
|
|
19
|
-
} catch (err) {
|
|
20
|
-
console.warn(`[Hook Error] Package ${pkg.folder} failed on event ${eventName}:`, err.message);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
} catch (err) {
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return finalResult;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = { dispatchEvent };
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const express = require('express');
|
|
2
|
-
const bodyParser = require('body-parser');
|
|
3
|
-
const { bootstrapMcp } = require('./tools-mcp');
|
|
4
|
-
|
|
5
|
-
async function createMcpHttpServer(options = {}) {
|
|
6
|
-
const app = express();
|
|
7
|
-
const port = options.port || 3333;
|
|
8
|
-
const workspace = options.workspace || process.env.PACKAGES_PATH;
|
|
9
|
-
|
|
10
|
-
if (!workspace) {
|
|
11
|
-
throw new Error('PACKAGES_PATH or workspace option is required');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const mcp = await bootstrapMcp(workspace);
|
|
15
|
-
|
|
16
|
-
app.use(bodyParser.json());
|
|
17
|
-
|
|
18
|
-
app.get('/mcp', (req, res) => {
|
|
19
|
-
res.json({ protocol: mcp.protocol, version: mcp.version });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
app.get('/mcp/tools', (req, res) => {
|
|
23
|
-
try {
|
|
24
|
-
res.json(mcp.listTools());
|
|
25
|
-
} catch (err) {
|
|
26
|
-
res.status(500).json({ error: err.message });
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
app.post('/mcp/call', async (req, res) => {
|
|
31
|
-
const { name, args } = req.body || {};
|
|
32
|
-
|
|
33
|
-
if (!name) {
|
|
34
|
-
return res.status(400).json({ error: 'Tool name is required' });
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const result = await mcp.callTool(name, args || {});
|
|
39
|
-
res.json({ result });
|
|
40
|
-
} catch (err) {
|
|
41
|
-
res.status(500).json({ error: err.message });
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const server = app.listen(port, () => {
|
|
46
|
-
console.log(`MCP HTTP server running on port ${port}`);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
return { app, server, mcp };
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = { createMcpHttpServer };
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
const { registerTools } = require('../register/decorators');
|
|
2
|
-
|
|
3
|
-
function toolToMcp(tool) {
|
|
4
|
-
return {
|
|
5
|
-
name: tool.name,
|
|
6
|
-
description: tool.description || '',
|
|
7
|
-
inputSchema: tool.parameters || { type: 'object', properties: {} },
|
|
8
|
-
handler: async (args) => {
|
|
9
|
-
return await tool.call(args);
|
|
10
|
-
},
|
|
11
|
-
condition: tool.condition || null
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function loadMcpTools(agentFolder) {
|
|
16
|
-
const tools = await registerTools(agentFolder);
|
|
17
|
-
return tools.map(toolToMcp);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function createMcpServer(tools) {
|
|
21
|
-
return {
|
|
22
|
-
protocol: 'mcp',
|
|
23
|
-
version: '1.0.0',
|
|
24
|
-
listTools() {
|
|
25
|
-
return tools.map(t => ({
|
|
26
|
-
name: t.name,
|
|
27
|
-
description: t.description,
|
|
28
|
-
inputSchema: t.inputSchema
|
|
29
|
-
}));
|
|
30
|
-
},
|
|
31
|
-
async callTool(name, args) {
|
|
32
|
-
const tool = tools.find(t => t.name === name);
|
|
33
|
-
if (!tool) {
|
|
34
|
-
throw new Error(`Tool not found: ${name}`);
|
|
35
|
-
}
|
|
36
|
-
return await tool.handler(args);
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function bootstrapMcp(agentFolder) {
|
|
42
|
-
const tools = await loadMcpTools(agentFolder);
|
|
43
|
-
return createMcpServer(tools);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
module.exports = {
|
|
47
|
-
loadMcpTools,
|
|
48
|
-
createMcpServer,
|
|
49
|
-
bootstrapMcp
|
|
50
|
-
};
|
package/src/core/package.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const { log } = console;
|
|
5
|
-
|
|
6
|
-
async function getPackage(folderName) {
|
|
7
|
-
try {
|
|
8
|
-
const pluginsPath = process.env.PACKAGES_PATH || path.join(__dirname, '..', 'packages');
|
|
9
|
-
const manifestPath = path.join(pluginsPath, folderName, 'package.json');
|
|
10
|
-
|
|
11
|
-
let manifest = {};
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
if (fs.existsSync(manifestPath)) {
|
|
15
|
-
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
16
|
-
}
|
|
17
|
-
} catch (error) {
|
|
18
|
-
console.error(`Manifest not found in ${folderName}:`, error);
|
|
19
|
-
throw new Error(`Manifest not found in folder ${folderName}`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
...manifest,
|
|
24
|
-
folder: folderName,
|
|
25
|
-
};
|
|
26
|
-
} catch (error) {
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function getPackages() {
|
|
32
|
-
try {
|
|
33
|
-
const pluginsPath = process.env.PACKAGES_PATH || path.join(__dirname, '..', 'packages');
|
|
34
|
-
const pluginFolders = fs.readdirSync(pluginsPath).filter(f => fs.statSync(path.join(pluginsPath, f)).isDirectory());
|
|
35
|
-
|
|
36
|
-
const folderPackages = await Promise.all(pluginFolders.map(async folderName => {
|
|
37
|
-
return await getPackage(folderName);
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
return folderPackages;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const registerPackage = async (app, pkg) => {
|
|
47
|
-
const basePackagesPath = process.env.PACKAGES_PATH || path.join(__dirname, '..', 'packages');
|
|
48
|
-
const packageDir = path.join(basePackagesPath, pkg.folder);
|
|
49
|
-
const manifestPath = path.join(packageDir, 'package.json');
|
|
50
|
-
pkg.dir = packageDir;
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
if (!fs.existsSync(manifestPath)) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!fs.existsSync(path.join(packageDir, 'routes.js'))) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
62
|
-
const usePackage = manifest.entry ? require(path.join(packageDir, manifest.entry)) : () => { };
|
|
63
|
-
|
|
64
|
-
if (typeof usePackage !== 'function') {
|
|
65
|
-
log(`${pkg.name} invalid entry point. Expected a function.`, 'error');
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const packg = usePackage(app) ?? usePackage;
|
|
70
|
-
packg?.setup && packg.setup();
|
|
71
|
-
|
|
72
|
-
log(`${pkg.name} ${pkg.version ? `version ${pkg.version}` : ''} is running.\n`);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
log(`Error with package ${pkg.name}: ${error.message}`, 'error');
|
|
75
|
-
log(`${pkg.name} ${pkg.version ? `version ${pkg.version}` : ''} isn't running.\n`);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
module.exports = { getPackage, getPackages, registerPackage };
|
package/src/core/parse/hooks.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
function parseHook(fn) {
|
|
2
|
-
if (typeof fn !== 'function') return null;
|
|
3
|
-
|
|
4
|
-
const fnStr = fn.toString();
|
|
5
|
-
const docMatch = fnStr.match(/\/\*\*([\s\S]*?)\*\//);
|
|
6
|
-
if (!docMatch) return null;
|
|
7
|
-
|
|
8
|
-
const doc = docMatch[1];
|
|
9
|
-
|
|
10
|
-
const hookMatch = doc.match(/@register_hook\s+([^\s]+)/);
|
|
11
|
-
if (!hookMatch) return null;
|
|
12
|
-
|
|
13
|
-
const event = hookMatch[1].trim();
|
|
14
|
-
|
|
15
|
-
const condition = { only: [], never: [], when: [] };
|
|
16
|
-
|
|
17
|
-
const onlyMatches = [...doc.matchAll(/@only\s+([^\s]+)/g)];
|
|
18
|
-
onlyMatches.forEach(m => condition.only.push(m[1]));
|
|
19
|
-
|
|
20
|
-
const neverMatches = [...doc.matchAll(/@never\s+([^\s]+)/g)];
|
|
21
|
-
neverMatches.forEach(m => condition.never.push(m[1]));
|
|
22
|
-
|
|
23
|
-
const whenMatches = [...doc.matchAll(/@when\s+([^\s]+)/g)];
|
|
24
|
-
whenMatches.forEach(m => condition.when.push(m[1]));
|
|
25
|
-
|
|
26
|
-
Object.keys(condition).forEach(k => {
|
|
27
|
-
if (condition[k].length === 0) delete condition[k];
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
event,
|
|
32
|
-
call: fn,
|
|
33
|
-
condition
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
module.exports = { parseHook };
|
package/src/core/parse/tools.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
function parseTool(fn) {
|
|
2
|
-
const meta = {
|
|
3
|
-
name: fn.name || null,
|
|
4
|
-
description: null,
|
|
5
|
-
parameters: [],
|
|
6
|
-
call: fn,
|
|
7
|
-
condition: { only: [], never: [], when: [] }
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const fnString = fn.toString();
|
|
11
|
-
const jsdocMatch = fnString.match(/\/\*\*([\s\S]*?)\*\//);
|
|
12
|
-
if (!jsdocMatch) return null;
|
|
13
|
-
|
|
14
|
-
const lines = jsdocMatch[1].split('\n').map(l => l.trim().replace(/^\*\s?/, ''));
|
|
15
|
-
|
|
16
|
-
const registerLine = lines.find(l => l.startsWith('@register_tool'));
|
|
17
|
-
if (!registerLine) return null;
|
|
18
|
-
|
|
19
|
-
const registerMatch = registerLine.match(/@register_tool\s*(\S+)?/);
|
|
20
|
-
if (registerMatch && registerMatch[1]) {
|
|
21
|
-
meta.name = registerMatch[1];
|
|
22
|
-
} else if (!meta.name) {
|
|
23
|
-
meta.name = 'anonymousTool';
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const paramMap = {};
|
|
27
|
-
|
|
28
|
-
const descriptionLine = lines.find(l => l.startsWith('@description'));
|
|
29
|
-
if (descriptionLine) meta.description = descriptionLine.replace('@description', '').trim();
|
|
30
|
-
|
|
31
|
-
lines.forEach(line => {
|
|
32
|
-
if (line.startsWith('@param')) {
|
|
33
|
-
const match = line.match(/@param {([\w\[\]\?]+)} (\S+) - (.+)/);
|
|
34
|
-
if (!match) return;
|
|
35
|
-
|
|
36
|
-
let [_, type, name, description] = match;
|
|
37
|
-
let required = !type.endsWith('?');
|
|
38
|
-
if (!required) type = type.slice(0, -1);
|
|
39
|
-
|
|
40
|
-
const cleanName = name.replace(/\[\]/g, '');
|
|
41
|
-
const isArray = name.includes('[]');
|
|
42
|
-
const isNested = cleanName.includes('.');
|
|
43
|
-
|
|
44
|
-
if (isNested) {
|
|
45
|
-
const parentName = cleanName.split('.')[0];
|
|
46
|
-
if (!paramMap[parentName]) {
|
|
47
|
-
const parent = {
|
|
48
|
-
name: parentName,
|
|
49
|
-
type: isArray ? 'array' : 'object',
|
|
50
|
-
description: `${parentName} object`,
|
|
51
|
-
required
|
|
52
|
-
};
|
|
53
|
-
meta.parameters.push(parent);
|
|
54
|
-
paramMap[parentName] = parent;
|
|
55
|
-
} else if (required) {
|
|
56
|
-
paramMap[parentName].required = true;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const param = { name, type, description, required };
|
|
61
|
-
meta.parameters.push(param);
|
|
62
|
-
paramMap[name] = param;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const onlyMatch = line.match(/@only\s+([^\s]+)/);
|
|
66
|
-
if (onlyMatch) meta.condition.only.push(onlyMatch[1]);
|
|
67
|
-
|
|
68
|
-
const neverMatch = line.match(/@never\s+([^\s]+)/);
|
|
69
|
-
if (neverMatch) meta.condition.never.push(neverMatch[1]);
|
|
70
|
-
|
|
71
|
-
const whenMatch = line.match(/@when\s+([^\s]+)/);
|
|
72
|
-
if (whenMatch) meta.condition.when.push(whenMatch[1]);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
Object.keys(meta.condition).forEach(k => {
|
|
76
|
-
if (meta.condition[k].length === 0) delete meta.condition[k];
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
return meta;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
module.exports = { parseTool };
|