@lucid-fdn/plugin-policy 0.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/dist/__tests__/audit.test.d.ts +2 -0
- package/dist/__tests__/audit.test.d.ts.map +1 -0
- package/dist/__tests__/audit.test.js +54 -0
- package/dist/__tests__/audit.test.js.map +1 -0
- package/dist/__tests__/manifest.test.d.ts +2 -0
- package/dist/__tests__/manifest.test.d.ts.map +1 -0
- package/dist/__tests__/manifest.test.js +83 -0
- package/dist/__tests__/manifest.test.js.map +1 -0
- package/dist/__tests__/policy.test.d.ts +2 -0
- package/dist/__tests__/policy.test.d.ts.map +1 -0
- package/dist/__tests__/policy.test.js +71 -0
- package/dist/__tests__/policy.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +65 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/router.test.d.ts +2 -0
- package/dist/__tests__/router.test.d.ts.map +1 -0
- package/dist/__tests__/router.test.js +80 -0
- package/dist/__tests__/router.test.js.map +1 -0
- package/dist/audit.d.ts +20 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +46 -0
- package/dist/audit.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +13 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +56 -0
- package/dist/manifest.js.map +1 -0
- package/dist/policy.d.ts +23 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +65 -0
- package/dist/policy.js.map +1 -0
- package/dist/registry.d.ts +24 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +52 -0
- package/dist/registry.js.map +1 -0
- package/dist/router.d.ts +28 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +68 -0
- package/dist/router.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/audit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { AuditEmitter } from '../audit.js';
|
|
3
|
+
const event = {
|
|
4
|
+
timestamp: new Date().toISOString(),
|
|
5
|
+
pluginSlug: 'lucid-seo',
|
|
6
|
+
toolName: 'research_keywords',
|
|
7
|
+
executionPath: 'embedded',
|
|
8
|
+
durationMs: 5,
|
|
9
|
+
success: true,
|
|
10
|
+
};
|
|
11
|
+
describe('AuditEmitter', () => {
|
|
12
|
+
it('emits events to registered handlers', () => {
|
|
13
|
+
const emitter = new AuditEmitter();
|
|
14
|
+
const handler = vi.fn();
|
|
15
|
+
emitter.on(handler);
|
|
16
|
+
emitter.emit(event);
|
|
17
|
+
expect(handler).toHaveBeenCalledWith(event);
|
|
18
|
+
});
|
|
19
|
+
it('supports multiple handlers', () => {
|
|
20
|
+
const emitter = new AuditEmitter();
|
|
21
|
+
const h1 = vi.fn();
|
|
22
|
+
const h2 = vi.fn();
|
|
23
|
+
emitter.on(h1);
|
|
24
|
+
emitter.on(h2);
|
|
25
|
+
emitter.emit(event);
|
|
26
|
+
expect(h1).toHaveBeenCalledOnce();
|
|
27
|
+
expect(h2).toHaveBeenCalledOnce();
|
|
28
|
+
});
|
|
29
|
+
it('supports unsubscribe', () => {
|
|
30
|
+
const emitter = new AuditEmitter();
|
|
31
|
+
const handler = vi.fn();
|
|
32
|
+
const unsub = emitter.on(handler);
|
|
33
|
+
unsub();
|
|
34
|
+
emitter.emit(event);
|
|
35
|
+
expect(handler).not.toHaveBeenCalled();
|
|
36
|
+
});
|
|
37
|
+
it('does not throw when handler throws (fire-and-forget)', () => {
|
|
38
|
+
const emitter = new AuditEmitter();
|
|
39
|
+
emitter.on(() => { throw new Error('boom'); });
|
|
40
|
+
const good = vi.fn();
|
|
41
|
+
emitter.on(good);
|
|
42
|
+
expect(() => emitter.emit(event)).not.toThrow();
|
|
43
|
+
expect(good).toHaveBeenCalled();
|
|
44
|
+
});
|
|
45
|
+
it('clears all handlers', () => {
|
|
46
|
+
const emitter = new AuditEmitter();
|
|
47
|
+
emitter.on(vi.fn());
|
|
48
|
+
emitter.on(vi.fn());
|
|
49
|
+
expect(emitter.handlerCount).toBe(2);
|
|
50
|
+
emitter.clear();
|
|
51
|
+
expect(emitter.handlerCount).toBe(0);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=audit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.test.js","sourceRoot":"","sources":["../../src/__tests__/audit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG1C,MAAM,KAAK,GAAe;IACxB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;IACnC,UAAU,EAAE,WAAW;IACvB,QAAQ,EAAE,mBAAmB;IAC7B,aAAa,EAAE,UAAU;IACzB,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,IAAI;CACd,CAAA;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;QAClC,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACvB,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAA;QACnB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnB,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;QAClC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAClB,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAClB,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QACd,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAA;QACd,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnB,MAAM,CAAC,EAAE,CAAC,CAAC,oBAAoB,EAAE,CAAA;QACjC,MAAM,CAAC,EAAE,CAAC,CAAC,oBAAoB,EAAE,CAAA;IACnC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;QAClC,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACvB,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,CAAA;QACjC,KAAK,EAAE,CAAA;QACP,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnB,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;QAClC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAA,CAAC,CAAC,CAAC,CAAA;QAC7C,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QACpB,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,CAAA;QAEhB,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAA;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAA;IACjC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAA;QAClC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;QACnB,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;QACnB,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACpC,OAAO,CAAC,KAAK,EAAE,CAAA;QACf,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/manifest.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { normalizePluginRow } from '../manifest.js';
|
|
3
|
+
describe('normalizePluginRow', () => {
|
|
4
|
+
it('transforms snake_case DB row to camelCase ActivatedPlugin', () => {
|
|
5
|
+
const row = {
|
|
6
|
+
plugin_slug: 'lucid-seo',
|
|
7
|
+
plugin_name: 'Lucid SEO',
|
|
8
|
+
tool_manifest: [{ name: 'research', description: 'Research', parameters: {} }],
|
|
9
|
+
enabled_tools: null,
|
|
10
|
+
plugin_config: { depth: 3 },
|
|
11
|
+
org_config: { orgKey: 'val' },
|
|
12
|
+
source: 'first-party',
|
|
13
|
+
mcpgate_server_id: null,
|
|
14
|
+
kind: 'plugin',
|
|
15
|
+
transport: 'embedded',
|
|
16
|
+
trust_level: 'internal',
|
|
17
|
+
execution_mode: 'in_process',
|
|
18
|
+
auth_type: 'none',
|
|
19
|
+
auth_provider: null,
|
|
20
|
+
};
|
|
21
|
+
const result = normalizePluginRow(row);
|
|
22
|
+
expect(result.slug).toBe('lucid-seo');
|
|
23
|
+
expect(result.name).toBe('Lucid SEO');
|
|
24
|
+
expect(result.tools).toHaveLength(1);
|
|
25
|
+
expect(result.config).toEqual({ orgKey: 'val', depth: 3 });
|
|
26
|
+
expect(result.kind).toBe('plugin');
|
|
27
|
+
expect(result.transport).toBe('embedded');
|
|
28
|
+
expect(result.trustLevel).toBe('internal');
|
|
29
|
+
expect(result.executionMode).toBe('in_process');
|
|
30
|
+
expect(result.authType).toBe('none');
|
|
31
|
+
});
|
|
32
|
+
it('filters tools by enabled_tools', () => {
|
|
33
|
+
const row = {
|
|
34
|
+
plugin_slug: 'lucid-seo',
|
|
35
|
+
plugin_name: 'SEO',
|
|
36
|
+
tool_manifest: [
|
|
37
|
+
{ name: 'research', description: 'A', parameters: {} },
|
|
38
|
+
{ name: 'analyze', description: 'B', parameters: {} },
|
|
39
|
+
],
|
|
40
|
+
enabled_tools: ['research'],
|
|
41
|
+
plugin_config: {},
|
|
42
|
+
org_config: {},
|
|
43
|
+
source: 'first-party',
|
|
44
|
+
};
|
|
45
|
+
const result = normalizePluginRow(row);
|
|
46
|
+
expect(result.tools).toHaveLength(1);
|
|
47
|
+
expect(result.tools[0].name).toBe('research');
|
|
48
|
+
});
|
|
49
|
+
it('handles missing unified fields (old format)', () => {
|
|
50
|
+
const row = {
|
|
51
|
+
plugin_slug: 'old-plugin',
|
|
52
|
+
plugin_name: 'Old Plugin',
|
|
53
|
+
manifest_snapshot: [{ name: 'tool1', description: 'T', parameters: {} }],
|
|
54
|
+
enabled_tools: null,
|
|
55
|
+
config: { key: 'val' },
|
|
56
|
+
source: 'mcpgate',
|
|
57
|
+
mcpgate_server_id: 'server-1',
|
|
58
|
+
};
|
|
59
|
+
const result = normalizePluginRow(row);
|
|
60
|
+
expect(result.slug).toBe('old-plugin');
|
|
61
|
+
expect(result.tools).toHaveLength(1);
|
|
62
|
+
expect(result.mcpgateServerId).toBe('server-1');
|
|
63
|
+
// Missing UCA fields get safe defaults (least-privileged posture)
|
|
64
|
+
expect(result.kind).toBe('plugin');
|
|
65
|
+
expect(result.transport).toBe('remote-mcp');
|
|
66
|
+
expect(result.trustLevel).toBe('community');
|
|
67
|
+
expect(result.executionMode).toBe('gateway');
|
|
68
|
+
});
|
|
69
|
+
it('merges org config and plugin config (plugin wins)', () => {
|
|
70
|
+
const row = {
|
|
71
|
+
plugin_slug: 'test',
|
|
72
|
+
plugin_name: 'Test',
|
|
73
|
+
tool_manifest: [],
|
|
74
|
+
enabled_tools: null,
|
|
75
|
+
org_config: { shared: 'org', orgOnly: 'yes' },
|
|
76
|
+
plugin_config: { shared: 'plugin', pluginOnly: 'yes' },
|
|
77
|
+
source: 'first-party',
|
|
78
|
+
};
|
|
79
|
+
const result = normalizePluginRow(row);
|
|
80
|
+
expect(result.config).toEqual({ shared: 'plugin', orgOnly: 'yes', pluginOnly: 'yes' });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=manifest.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.test.js","sourceRoot":"","sources":["../../src/__tests__/manifest.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/policy.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { resolvePolicy } from '../policy.js';
|
|
3
|
+
describe('resolvePolicy', () => {
|
|
4
|
+
it('blocks plugins on the admin blocklist', () => {
|
|
5
|
+
const result = resolvePolicy({ slug: 'bad-plugin', trustLevel: 'internal', executionMode: 'in_process' }, { blockedPlugins: ['bad-plugin'] });
|
|
6
|
+
expect(result.decision).toBe('block');
|
|
7
|
+
expect(result.reason).toContain('blocklist');
|
|
8
|
+
});
|
|
9
|
+
it('forces gateway when forceGateway is set', () => {
|
|
10
|
+
const result = resolvePolicy({ slug: 'lucid-seo', trustLevel: 'internal', executionMode: 'in_process' }, { forceGateway: true });
|
|
11
|
+
expect(result.decision).toBe('allow_gateway');
|
|
12
|
+
expect(result.effectiveMode).toBe('gateway');
|
|
13
|
+
});
|
|
14
|
+
it('allows internal plugins in-process', () => {
|
|
15
|
+
const result = resolvePolicy({
|
|
16
|
+
slug: 'lucid-seo',
|
|
17
|
+
trustLevel: 'internal',
|
|
18
|
+
executionMode: 'in_process',
|
|
19
|
+
});
|
|
20
|
+
expect(result.decision).toBe('allow_in_process');
|
|
21
|
+
expect(result.effectiveMode).toBe('in_process');
|
|
22
|
+
});
|
|
23
|
+
it('allows verified plugins in-process', () => {
|
|
24
|
+
const result = resolvePolicy({
|
|
25
|
+
slug: 'partner-plugin',
|
|
26
|
+
trustLevel: 'verified',
|
|
27
|
+
executionMode: 'in_process',
|
|
28
|
+
});
|
|
29
|
+
expect(result.decision).toBe('allow_in_process');
|
|
30
|
+
expect(result.effectiveMode).toBe('in_process');
|
|
31
|
+
});
|
|
32
|
+
it('forces community plugins to gateway even if in_process requested', () => {
|
|
33
|
+
const result = resolvePolicy({
|
|
34
|
+
slug: 'community-plugin',
|
|
35
|
+
trustLevel: 'community',
|
|
36
|
+
executionMode: 'in_process',
|
|
37
|
+
});
|
|
38
|
+
expect(result.decision).toBe('allow_gateway');
|
|
39
|
+
expect(result.effectiveMode).toBe('gateway');
|
|
40
|
+
expect(result.reason).toContain('gateway-only');
|
|
41
|
+
});
|
|
42
|
+
it('allows community plugins in gateway mode', () => {
|
|
43
|
+
const result = resolvePolicy({
|
|
44
|
+
slug: 'community-plugin',
|
|
45
|
+
trustLevel: 'community',
|
|
46
|
+
executionMode: 'gateway',
|
|
47
|
+
});
|
|
48
|
+
expect(result.decision).toBe('allow_gateway');
|
|
49
|
+
expect(result.effectiveMode).toBe('gateway');
|
|
50
|
+
});
|
|
51
|
+
it('allows internal plugins in gateway mode when requested', () => {
|
|
52
|
+
const result = resolvePolicy({
|
|
53
|
+
slug: 'lucid-seo',
|
|
54
|
+
trustLevel: 'internal',
|
|
55
|
+
executionMode: 'gateway',
|
|
56
|
+
});
|
|
57
|
+
expect(result.decision).toBe('allow_gateway');
|
|
58
|
+
expect(result.effectiveMode).toBe('gateway');
|
|
59
|
+
});
|
|
60
|
+
it('defaults undefined trustLevel to community (safe default)', () => {
|
|
61
|
+
const result = resolvePolicy({
|
|
62
|
+
slug: 'old-plugin',
|
|
63
|
+
trustLevel: undefined,
|
|
64
|
+
executionMode: undefined,
|
|
65
|
+
});
|
|
66
|
+
expect(result.decision).toBe('allow_gateway');
|
|
67
|
+
expect(result.effectiveMode).toBe('gateway');
|
|
68
|
+
expect(result.reason).toContain('gateway-only');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=policy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.test.js","sourceRoot":"","sources":["../../src/__tests__/policy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PluginRegistry } from '../registry.js';
|
|
3
|
+
const seoPlugin = {
|
|
4
|
+
slug: 'lucid-seo',
|
|
5
|
+
name: 'Lucid SEO',
|
|
6
|
+
tools: [
|
|
7
|
+
{ name: 'research_keywords', description: 'Research keywords', parameters: {} },
|
|
8
|
+
{ name: 'analyze_serp', description: 'Analyze SERP', parameters: {} },
|
|
9
|
+
],
|
|
10
|
+
config: {},
|
|
11
|
+
kind: 'plugin',
|
|
12
|
+
transport: 'embedded',
|
|
13
|
+
trustLevel: 'internal',
|
|
14
|
+
executionMode: 'in_process',
|
|
15
|
+
authType: 'none',
|
|
16
|
+
authProvider: null,
|
|
17
|
+
};
|
|
18
|
+
const slackPlugin = {
|
|
19
|
+
slug: 'slack',
|
|
20
|
+
name: 'Slack',
|
|
21
|
+
tools: [{ name: 'send_message', description: 'Send a Slack message', parameters: {} }],
|
|
22
|
+
config: {},
|
|
23
|
+
kind: 'integration',
|
|
24
|
+
transport: 'remote-mcp',
|
|
25
|
+
trustLevel: 'community',
|
|
26
|
+
executionMode: 'gateway',
|
|
27
|
+
authType: 'oauth2',
|
|
28
|
+
authProvider: 'slack',
|
|
29
|
+
};
|
|
30
|
+
describe('PluginRegistry', () => {
|
|
31
|
+
it('registers and retrieves plugins', () => {
|
|
32
|
+
const reg = new PluginRegistry([seoPlugin]);
|
|
33
|
+
expect(reg.get('lucid-seo')).toEqual(seoPlugin);
|
|
34
|
+
expect(reg.has('lucid-seo')).toBe(true);
|
|
35
|
+
expect(reg.has('missing')).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
it('returns all plugins', () => {
|
|
38
|
+
const reg = new PluginRegistry([seoPlugin, slackPlugin]);
|
|
39
|
+
expect(reg.getAll()).toHaveLength(2);
|
|
40
|
+
expect(reg.size).toBe(2);
|
|
41
|
+
});
|
|
42
|
+
it('resolves wire tool names with double underscore', () => {
|
|
43
|
+
const reg = new PluginRegistry([seoPlugin]);
|
|
44
|
+
const resolved = reg.resolveWireToolName('lucid-seo__research_keywords');
|
|
45
|
+
expect(resolved?.plugin.slug).toBe('lucid-seo');
|
|
46
|
+
expect(resolved?.tool.name).toBe('research_keywords');
|
|
47
|
+
});
|
|
48
|
+
it('resolves wire tool names with underscore slug (sanitized)', () => {
|
|
49
|
+
const reg = new PluginRegistry([seoPlugin]);
|
|
50
|
+
// Wire names replace hyphens with underscores
|
|
51
|
+
const resolved = reg.resolveWireToolName('lucid_seo__research_keywords');
|
|
52
|
+
expect(resolved?.plugin.slug).toBe('lucid-seo');
|
|
53
|
+
expect(resolved?.tool.name).toBe('research_keywords');
|
|
54
|
+
});
|
|
55
|
+
it('returns null for unknown wire tool names', () => {
|
|
56
|
+
const reg = new PluginRegistry([seoPlugin]);
|
|
57
|
+
expect(reg.resolveWireToolName('unknown__tool')).toBeNull();
|
|
58
|
+
expect(reg.resolveWireToolName('no-separator')).toBeNull();
|
|
59
|
+
});
|
|
60
|
+
it('returns null for known plugin but unknown tool', () => {
|
|
61
|
+
const reg = new PluginRegistry([seoPlugin]);
|
|
62
|
+
expect(reg.resolveWireToolName('lucid-seo__nonexistent_tool')).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../src/__tests__/registry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/router.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { routePlugin } from '../router.js';
|
|
3
|
+
function makePlugin(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
slug: 'test-plugin',
|
|
6
|
+
name: 'Test Plugin',
|
|
7
|
+
tools: [],
|
|
8
|
+
config: {},
|
|
9
|
+
kind: 'plugin',
|
|
10
|
+
transport: 'embedded',
|
|
11
|
+
trustLevel: 'internal',
|
|
12
|
+
executionMode: 'in_process',
|
|
13
|
+
authType: 'none',
|
|
14
|
+
authProvider: null,
|
|
15
|
+
...overrides,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe('routePlugin', () => {
|
|
19
|
+
it('routes embedded + internal to embedded path', () => {
|
|
20
|
+
const result = routePlugin(makePlugin({
|
|
21
|
+
transport: 'embedded',
|
|
22
|
+
trustLevel: 'internal',
|
|
23
|
+
executionMode: 'in_process',
|
|
24
|
+
}));
|
|
25
|
+
expect(result.path).toBe('embedded');
|
|
26
|
+
});
|
|
27
|
+
it('routes embedded + community to gateway-mcp (policy override)', () => {
|
|
28
|
+
const result = routePlugin(makePlugin({
|
|
29
|
+
transport: 'embedded',
|
|
30
|
+
trustLevel: 'community',
|
|
31
|
+
executionMode: 'in_process',
|
|
32
|
+
}));
|
|
33
|
+
expect(result.path).toBe('gateway-mcp');
|
|
34
|
+
expect(result.policy.reason).toContain('gateway-only');
|
|
35
|
+
});
|
|
36
|
+
it('routes remote-mcp to gateway-mcp', () => {
|
|
37
|
+
const result = routePlugin(makePlugin({
|
|
38
|
+
transport: 'remote-mcp',
|
|
39
|
+
trustLevel: 'verified',
|
|
40
|
+
executionMode: 'gateway',
|
|
41
|
+
mcpgateServerId: 'server-xyz',
|
|
42
|
+
}));
|
|
43
|
+
expect(result.path).toBe('gateway-mcp');
|
|
44
|
+
expect(result.target).toBe('server-xyz');
|
|
45
|
+
});
|
|
46
|
+
it('routes rest transport to gateway-rest with endpointUrl as target', () => {
|
|
47
|
+
const result = routePlugin(makePlugin({
|
|
48
|
+
transport: 'rest',
|
|
49
|
+
trustLevel: 'verified',
|
|
50
|
+
executionMode: 'gateway',
|
|
51
|
+
endpointUrl: 'https://api.weather.com/v1',
|
|
52
|
+
}));
|
|
53
|
+
expect(result.path).toBe('gateway-rest');
|
|
54
|
+
expect(result.target).toBe('https://api.weather.com/v1');
|
|
55
|
+
});
|
|
56
|
+
it('blocks plugins on admin blocklist', () => {
|
|
57
|
+
const result = routePlugin(makePlugin({ slug: 'bad-plugin', transport: 'embedded', trustLevel: 'internal' }), { policy: { blockedPlugins: ['bad-plugin'] } });
|
|
58
|
+
expect(result.path).toBe('blocked');
|
|
59
|
+
});
|
|
60
|
+
it('defaults unknown fields to safe values', () => {
|
|
61
|
+
// Plugin with no unified fields (old format) — cast to bypass required fields
|
|
62
|
+
const result = routePlugin(makePlugin({
|
|
63
|
+
transport: undefined,
|
|
64
|
+
trustLevel: undefined,
|
|
65
|
+
executionMode: undefined,
|
|
66
|
+
}));
|
|
67
|
+
// Defaults: transport=remote-mcp, trustLevel=community, executionMode=gateway
|
|
68
|
+
expect(result.path).toBe('gateway-mcp');
|
|
69
|
+
expect(result.policy.effectiveMode).toBe('gateway');
|
|
70
|
+
});
|
|
71
|
+
it('uses builtin: prefix for fallback server ID', () => {
|
|
72
|
+
const result = routePlugin(makePlugin({
|
|
73
|
+
slug: 'lucid-seo',
|
|
74
|
+
transport: 'remote-mcp',
|
|
75
|
+
mcpgateServerId: undefined,
|
|
76
|
+
}));
|
|
77
|
+
expect(result.target).toBe('builtin:lucid-seo');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=router.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.test.js","sourceRoot":"","sources":["../../src/__tests__/router.test.ts"],"names":[],"mappings":"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"}
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Audit Hooks
|
|
3
|
+
*
|
|
4
|
+
* Pluggable audit system for plugin tool executions.
|
|
5
|
+
* Consumers register handlers; the audit emitter fires after each tool call.
|
|
6
|
+
*
|
|
7
|
+
* Handlers are fire-and-forget — audit failures never block tool execution.
|
|
8
|
+
*/
|
|
9
|
+
import type { AuditEvent, AuditHandler } from './types.js';
|
|
10
|
+
export declare class AuditEmitter {
|
|
11
|
+
private readonly handlers;
|
|
12
|
+
/** Register an audit handler. Returns unsubscribe function. */
|
|
13
|
+
on(handler: AuditHandler): () => void;
|
|
14
|
+
/** Emit an audit event to all registered handlers (fire-and-forget). */
|
|
15
|
+
emit(event: AuditEvent): void;
|
|
16
|
+
/** Remove all handlers. */
|
|
17
|
+
clear(): void;
|
|
18
|
+
get handlerCount(): number;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE1D,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C,+DAA+D;IAC/D,EAAE,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI;IAQrC,wEAAwE;IACxE,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAgB7B,2BAA2B;IAC3B,KAAK,IAAI,IAAI;IAIb,IAAI,YAAY,IAAI,MAAM,CAEzB;CACF"}
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Audit Hooks
|
|
3
|
+
*
|
|
4
|
+
* Pluggable audit system for plugin tool executions.
|
|
5
|
+
* Consumers register handlers; the audit emitter fires after each tool call.
|
|
6
|
+
*
|
|
7
|
+
* Handlers are fire-and-forget — audit failures never block tool execution.
|
|
8
|
+
*/
|
|
9
|
+
export class AuditEmitter {
|
|
10
|
+
handlers = [];
|
|
11
|
+
/** Register an audit handler. Returns unsubscribe function. */
|
|
12
|
+
on(handler) {
|
|
13
|
+
this.handlers.push(handler);
|
|
14
|
+
return () => {
|
|
15
|
+
const idx = this.handlers.indexOf(handler);
|
|
16
|
+
if (idx >= 0)
|
|
17
|
+
this.handlers.splice(idx, 1);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Emit an audit event to all registered handlers (fire-and-forget). */
|
|
21
|
+
emit(event) {
|
|
22
|
+
for (const handler of this.handlers) {
|
|
23
|
+
try {
|
|
24
|
+
const result = handler(event);
|
|
25
|
+
// If handler returns a promise, catch errors silently
|
|
26
|
+
if (result && typeof result === 'object' && 'catch' in result) {
|
|
27
|
+
;
|
|
28
|
+
result.catch((err) => {
|
|
29
|
+
console.error('[plugin-policy:audit] Handler error:', err);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
console.error('[plugin-policy:audit] Handler error:', err);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Remove all handlers. */
|
|
39
|
+
clear() {
|
|
40
|
+
this.handlers.length = 0;
|
|
41
|
+
}
|
|
42
|
+
get handlerCount() {
|
|
43
|
+
return this.handlers.length;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,OAAO,YAAY;IACN,QAAQ,GAAmB,EAAE,CAAA;IAE9C,+DAA+D;IAC/D,EAAE,CAAC,OAAqB;QACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC3B,OAAO,GAAG,EAAE;YACV,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;YAC1C,IAAI,GAAG,IAAI,CAAC;gBAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;QAC5C,CAAC,CAAA;IACH,CAAC;IAED,wEAAwE;IACxE,IAAI,CAAC,KAAiB;QACpB,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;gBAC7B,sDAAsD;gBACtD,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;oBAC9D,CAAC;oBAAC,MAAwB,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;wBACvC,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAA;oBAC5D,CAAC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAA;YAC5D,CAAC;QACH,CAAC;IACH,CAAC;IAED,2BAA2B;IAC3B,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;IAC1B,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAA;IAC7B,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lucid/plugin-policy
|
|
3
|
+
*
|
|
4
|
+
* Unified capability registry, router, policy engine, and audit hooks.
|
|
5
|
+
* Shared between the worker (in-process execution) and MCPGate (gateway execution).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { PluginRegistry, routePlugin, resolvePolicy, AuditEmitter } from '@lucid/plugin-policy'
|
|
9
|
+
*/
|
|
10
|
+
export type { PluginCatalogEntry, ToolDef, ActivatedPlugin, PolicyDecision, PolicyResult, PolicyConfig, ExecutionPath, RouteResult, AuditEvent, AuditHandler, } from './types.js';
|
|
11
|
+
export { PluginRegistry } from './registry.js';
|
|
12
|
+
export { resolvePolicy } from './policy.js';
|
|
13
|
+
export { routePlugin } from './router.js';
|
|
14
|
+
export type { RouterConfig } from './router.js';
|
|
15
|
+
export { AuditEmitter } from './audit.js';
|
|
16
|
+
export { normalizePluginRow } from './manifest.js';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,YAAY,EACV,kBAAkB,EAClB,OAAO,EACP,eAAe,EACf,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAG9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lucid/plugin-policy
|
|
3
|
+
*
|
|
4
|
+
* Unified capability registry, router, policy engine, and audit hooks.
|
|
5
|
+
* Shared between the worker (in-process execution) and MCPGate (gateway execution).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { PluginRegistry, routePlugin, resolvePolicy, AuditEmitter } from '@lucid/plugin-policy'
|
|
9
|
+
*/
|
|
10
|
+
// Registry
|
|
11
|
+
export { PluginRegistry } from './registry.js';
|
|
12
|
+
// Policy
|
|
13
|
+
export { resolvePolicy } from './policy.js';
|
|
14
|
+
// Router
|
|
15
|
+
export { routePlugin } from './router.js';
|
|
16
|
+
// Audit
|
|
17
|
+
export { AuditEmitter } from './audit.js';
|
|
18
|
+
// Manifest normalization
|
|
19
|
+
export { normalizePluginRow } from './manifest.js';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgBH,WAAW;AACX,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,SAAS;AACT,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,SAAS;AACT,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAGzC,QAAQ;AACR,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC,yBAAyB;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Manifest Normalization
|
|
3
|
+
*
|
|
4
|
+
* Normalizes DB rows (snake_case) into the internal ActivatedPlugin format (camelCase).
|
|
5
|
+
* Used by the worker when transforming RPC results.
|
|
6
|
+
*/
|
|
7
|
+
import type { ActivatedPlugin } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Transform a raw DB row from get_assistant_active_plugins RPC into an ActivatedPlugin.
|
|
10
|
+
* Handles both old (source/mcpgate_server_id only) and new (unified fields) formats.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizePluginRow(row: Record<string, unknown>): ActivatedPlugin;
|
|
13
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CAiDhF"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Manifest Normalization
|
|
3
|
+
*
|
|
4
|
+
* Normalizes DB rows (snake_case) into the internal ActivatedPlugin format (camelCase).
|
|
5
|
+
* Used by the worker when transforming RPC results.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Transform a raw DB row from get_assistant_active_plugins RPC into an ActivatedPlugin.
|
|
9
|
+
* Handles both old (source/mcpgate_server_id only) and new (unified fields) formats.
|
|
10
|
+
*/
|
|
11
|
+
export function normalizePluginRow(row) {
|
|
12
|
+
// Tool manifest: may be JSONB array or stringified
|
|
13
|
+
let tools = [];
|
|
14
|
+
const rawManifest = row.tool_manifest ?? row.manifest_snapshot;
|
|
15
|
+
if (Array.isArray(rawManifest)) {
|
|
16
|
+
tools = rawManifest;
|
|
17
|
+
}
|
|
18
|
+
else if (typeof rawManifest === 'string') {
|
|
19
|
+
try {
|
|
20
|
+
tools = JSON.parse(rawManifest);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
tools = [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Filter tools by enabled_tools if present
|
|
27
|
+
const enabledTools = row.enabled_tools;
|
|
28
|
+
if (enabledTools && Array.isArray(enabledTools)) {
|
|
29
|
+
tools = tools.filter((t) => enabledTools.includes(t.name ?? ''));
|
|
30
|
+
}
|
|
31
|
+
// Merge org config + plugin config (plugin config takes precedence)
|
|
32
|
+
const orgConfig = (row.org_config ?? {});
|
|
33
|
+
const pluginConfig = (row.plugin_config ?? row.config ?? {});
|
|
34
|
+
const config = { ...orgConfig, ...pluginConfig };
|
|
35
|
+
return {
|
|
36
|
+
slug: row.plugin_slug,
|
|
37
|
+
name: row.plugin_name,
|
|
38
|
+
tools,
|
|
39
|
+
config,
|
|
40
|
+
// UCA dimensions (required — safe defaults match DB column defaults)
|
|
41
|
+
kind: (row.kind ?? 'plugin'),
|
|
42
|
+
transport: (row.transport ?? 'remote-mcp'),
|
|
43
|
+
trustLevel: (row.trust_level ?? 'community'),
|
|
44
|
+
executionMode: (row.execution_mode ?? 'gateway'),
|
|
45
|
+
authType: (row.auth_type ?? 'none'),
|
|
46
|
+
authProvider: row.auth_provider ?? null,
|
|
47
|
+
// Routing targets
|
|
48
|
+
mcpgateServerId: row.mcpgate_server_id ?? undefined,
|
|
49
|
+
endpointUrl: row.endpoint_url ?? undefined,
|
|
50
|
+
// Fallback policy
|
|
51
|
+
fallbackMode: (row.fallback_mode ?? null),
|
|
52
|
+
// Deprecated (kept for wire compat)
|
|
53
|
+
source: row.source ?? undefined,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAA4B;IAC7D,mDAAmD;IACnD,IAAI,KAAK,GAAG,EAAE,CAAA;IACd,MAAM,WAAW,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,iBAAiB,CAAA;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,KAAK,GAAG,WAAW,CAAA;IACrB,CAAC;SAAM,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,EAAE,CAAA;QACZ,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,MAAM,YAAY,GAAG,GAAG,CAAC,aAAgC,CAAA;IACzD,IAAI,YAAY,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAChD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAoB,EAAE,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAA;IACrF,CAAC;IAED,oEAAoE;IACpE,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAA4B,CAAA;IACnE,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAA4B,CAAA;IACvF,MAAM,MAAM,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,YAAY,EAAE,CAAA;IAEhD,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,WAAqB;QAC/B,IAAI,EAAE,GAAG,CAAC,WAAqB;QAC/B,KAAK;QACL,MAAM;QAEN,qEAAqE;QACrE,IAAI,EAAE,CAAE,GAAG,CAAC,IAAe,IAAI,QAAQ,CAA4B;QACnE,SAAS,EAAE,CAAE,GAAG,CAAC,SAAoB,IAAI,YAAY,CAAiC;QACtF,UAAU,EAAE,CAAE,GAAG,CAAC,WAAsB,IAAI,WAAW,CAAkC;QACzF,aAAa,EAAE,CAAE,GAAG,CAAC,cAAyB,IAAI,SAAS,CAAqC;QAChG,QAAQ,EAAE,CAAE,GAAG,CAAC,SAAoB,IAAI,MAAM,CAAgC;QAC9E,YAAY,EAAG,GAAG,CAAC,aAAwB,IAAI,IAAI;QAEnD,kBAAkB;QAClB,eAAe,EAAG,GAAG,CAAC,iBAA4B,IAAI,SAAS;QAC/D,WAAW,EAAG,GAAG,CAAC,YAAuB,IAAI,SAAS;QAEtD,kBAAkB;QAClB,YAAY,EAAE,CAAE,GAAG,CAAC,aAAwB,IAAI,IAAI,CAAoC;QAExF,oCAAoC;QACpC,MAAM,EAAG,GAAG,CAAC,MAAoC,IAAI,SAAS;KAC/D,CAAA;AACH,CAAC"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Policy Engine
|
|
3
|
+
*
|
|
4
|
+
* Resolves the effective execution mode for a plugin based on:
|
|
5
|
+
* 1. Admin blocklist
|
|
6
|
+
* 2. Trust level → execution mode matrix
|
|
7
|
+
* 3. Environment overrides (e.g., force gateway in staging)
|
|
8
|
+
*
|
|
9
|
+
* Policy matrix (RFC Section 4.2):
|
|
10
|
+
* internal + in_process → allow_in_process
|
|
11
|
+
* internal + gateway → allow_gateway
|
|
12
|
+
* verified + in_process → allow_in_process
|
|
13
|
+
* verified + gateway → allow_gateway
|
|
14
|
+
* community + in_process → allow_gateway (OVERRIDE — community never runs in-process)
|
|
15
|
+
* community + gateway → allow_gateway
|
|
16
|
+
*/
|
|
17
|
+
import type { PluginCatalogEntry, PolicyConfig, PolicyResult } from './types.js';
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the execution policy for a plugin.
|
|
20
|
+
* Takes the full catalog entry so policy can inspect any field.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolvePolicy(plugin: Pick<PluginCatalogEntry, 'slug' | 'trustLevel' | 'executionMode'>, config?: PolicyConfig): PolicyResult;
|
|
23
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEhF;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAAC,EACzE,MAAM,CAAC,EAAE,YAAY,GACpB,YAAY,CAgDd"}
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Policy Engine
|
|
3
|
+
*
|
|
4
|
+
* Resolves the effective execution mode for a plugin based on:
|
|
5
|
+
* 1. Admin blocklist
|
|
6
|
+
* 2. Trust level → execution mode matrix
|
|
7
|
+
* 3. Environment overrides (e.g., force gateway in staging)
|
|
8
|
+
*
|
|
9
|
+
* Policy matrix (RFC Section 4.2):
|
|
10
|
+
* internal + in_process → allow_in_process
|
|
11
|
+
* internal + gateway → allow_gateway
|
|
12
|
+
* verified + in_process → allow_in_process
|
|
13
|
+
* verified + gateway → allow_gateway
|
|
14
|
+
* community + in_process → allow_gateway (OVERRIDE — community never runs in-process)
|
|
15
|
+
* community + gateway → allow_gateway
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the execution policy for a plugin.
|
|
19
|
+
* Takes the full catalog entry so policy can inspect any field.
|
|
20
|
+
*/
|
|
21
|
+
export function resolvePolicy(plugin, config) {
|
|
22
|
+
// 1. Admin blocklist
|
|
23
|
+
if (config?.blockedPlugins?.includes(plugin.slug)) {
|
|
24
|
+
return {
|
|
25
|
+
decision: 'block',
|
|
26
|
+
reason: `Plugin "${plugin.slug}" is on the admin blocklist`,
|
|
27
|
+
effectiveMode: 'gateway',
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// 2. Environment override — force everything through gateway
|
|
31
|
+
if (config?.forceGateway) {
|
|
32
|
+
return {
|
|
33
|
+
decision: 'allow_gateway',
|
|
34
|
+
reason: 'Environment forces gateway mode',
|
|
35
|
+
effectiveMode: 'gateway',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// 3. Trust level → execution mode matrix
|
|
39
|
+
// Default undefined to safest posture (community + gateway) — matches DB column defaults.
|
|
40
|
+
// Without this, an old DB row missing these fields would fall through to allow_in_process.
|
|
41
|
+
const trustLevel = plugin.trustLevel ?? 'community';
|
|
42
|
+
const executionMode = plugin.executionMode ?? 'gateway';
|
|
43
|
+
// Community plugins: NEVER in-process, regardless of requested mode
|
|
44
|
+
if (trustLevel === 'community') {
|
|
45
|
+
return {
|
|
46
|
+
decision: 'allow_gateway',
|
|
47
|
+
reason: 'Community plugins are gateway-only (policy enforced)',
|
|
48
|
+
effectiveMode: 'gateway',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Internal or verified: respect the requested execution mode
|
|
52
|
+
if (executionMode === 'in_process') {
|
|
53
|
+
return {
|
|
54
|
+
decision: 'allow_in_process',
|
|
55
|
+
reason: `${trustLevel} plugin allowed in-process`,
|
|
56
|
+
effectiveMode: 'in_process',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
decision: 'allow_gateway',
|
|
61
|
+
reason: `${trustLevel} plugin requested gateway mode`,
|
|
62
|
+
effectiveMode: 'gateway',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAyE,EACzE,MAAqB;IAErB,qBAAqB;IACrB,IAAI,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,WAAW,MAAM,CAAC,IAAI,6BAA6B;YAC3D,aAAa,EAAE,SAAS;SACzB,CAAA;IACH,CAAC;IAED,6DAA6D;IAC7D,IAAI,MAAM,EAAE,YAAY,EAAE,CAAC;QACzB,OAAO;YACL,QAAQ,EAAE,eAAe;YACzB,MAAM,EAAE,iCAAiC;YACzC,aAAa,EAAE,SAAS;SACzB,CAAA;IACH,CAAC;IAED,yCAAyC;IACzC,0FAA0F;IAC1F,2FAA2F;IAC3F,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,WAAW,CAAA;IACnD,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,SAAS,CAAA;IAEvD,oEAAoE;IACpE,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;QAC/B,OAAO;YACL,QAAQ,EAAE,eAAe;YACzB,MAAM,EAAE,sDAAsD;YAC9D,aAAa,EAAE,SAAS;SACzB,CAAA;IACH,CAAC;IAED,6DAA6D;IAC7D,IAAI,aAAa,KAAK,YAAY,EAAE,CAAC;QACnC,OAAO;YACL,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EAAE,GAAG,UAAU,4BAA4B;YACjD,aAAa,EAAE,YAAY;SAC5B,CAAA;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,eAAe;QACzB,MAAM,EAAE,GAAG,UAAU,gCAAgC;QACrD,aAAa,EAAE,SAAS;KACzB,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Registry
|
|
3
|
+
*
|
|
4
|
+
* In-memory registry of activated plugins for a given context (e.g., assistant run).
|
|
5
|
+
* Provides lookup by slug and wire tool name parsing.
|
|
6
|
+
*
|
|
7
|
+
* Not a singleton — each agent run creates its own registry from the DB result.
|
|
8
|
+
*/
|
|
9
|
+
import type { ActivatedPlugin, ToolDef } from './types.js';
|
|
10
|
+
export declare class PluginRegistry {
|
|
11
|
+
private readonly plugins;
|
|
12
|
+
constructor(plugins?: ActivatedPlugin[]);
|
|
13
|
+
register(plugin: ActivatedPlugin): void;
|
|
14
|
+
get(slug: string): ActivatedPlugin | undefined;
|
|
15
|
+
has(slug: string): boolean;
|
|
16
|
+
getAll(): ActivatedPlugin[];
|
|
17
|
+
/** Resolve a wire tool name (e.g., 'lucid_seo__research_keywords') to plugin + tool. */
|
|
18
|
+
resolveWireToolName(wireName: string): {
|
|
19
|
+
plugin: ActivatedPlugin;
|
|
20
|
+
tool: ToolDef;
|
|
21
|
+
} | null;
|
|
22
|
+
get size(): number;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAK1D,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;gBAEjD,OAAO,CAAC,EAAE,eAAe,EAAE;IAQvC,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAIvC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI9C,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,MAAM,IAAI,eAAe,EAAE;IAI3B,wFAAwF;IACxF,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,eAAe,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAiBxF,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Registry
|
|
3
|
+
*
|
|
4
|
+
* In-memory registry of activated plugins for a given context (e.g., assistant run).
|
|
5
|
+
* Provides lookup by slug and wire tool name parsing.
|
|
6
|
+
*
|
|
7
|
+
* Not a singleton — each agent run creates its own registry from the DB result.
|
|
8
|
+
*/
|
|
9
|
+
/** Wire tool name format: {pluginSlug}__{toolName} (double underscore separator). */
|
|
10
|
+
const WIRE_SEPARATOR = '__';
|
|
11
|
+
export class PluginRegistry {
|
|
12
|
+
plugins = new Map();
|
|
13
|
+
constructor(plugins) {
|
|
14
|
+
if (plugins) {
|
|
15
|
+
for (const p of plugins) {
|
|
16
|
+
this.register(p);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
register(plugin) {
|
|
21
|
+
this.plugins.set(plugin.slug, plugin);
|
|
22
|
+
}
|
|
23
|
+
get(slug) {
|
|
24
|
+
return this.plugins.get(slug);
|
|
25
|
+
}
|
|
26
|
+
has(slug) {
|
|
27
|
+
return this.plugins.has(slug);
|
|
28
|
+
}
|
|
29
|
+
getAll() {
|
|
30
|
+
return Array.from(this.plugins.values());
|
|
31
|
+
}
|
|
32
|
+
/** Resolve a wire tool name (e.g., 'lucid_seo__research_keywords') to plugin + tool. */
|
|
33
|
+
resolveWireToolName(wireName) {
|
|
34
|
+
const idx = wireName.indexOf(WIRE_SEPARATOR);
|
|
35
|
+
if (idx === -1)
|
|
36
|
+
return null;
|
|
37
|
+
const slugPart = wireName.slice(0, idx);
|
|
38
|
+
const toolName = wireName.slice(idx + WIRE_SEPARATOR.length);
|
|
39
|
+
// Wire names sanitize hyphens to underscores, so try both
|
|
40
|
+
const plugin = this.plugins.get(slugPart) ?? this.plugins.get(slugPart.replace(/_/g, '-'));
|
|
41
|
+
if (!plugin)
|
|
42
|
+
return null;
|
|
43
|
+
const tool = plugin.tools.find((t) => t.name === toolName);
|
|
44
|
+
if (!tool)
|
|
45
|
+
return null;
|
|
46
|
+
return { plugin, tool };
|
|
47
|
+
}
|
|
48
|
+
get size() {
|
|
49
|
+
return this.plugins.size;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,qFAAqF;AACrF,MAAM,cAAc,GAAG,IAAI,CAAA;AAE3B,MAAM,OAAO,cAAc;IACR,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAA;IAE7D,YAAY,OAA2B;QACrC,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;gBACxB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,MAAuB;QAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACvC,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAC/B,CAAC;IAED,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC1C,CAAC;IAED,wFAAwF;IACxF,mBAAmB,CAAC,QAAgB;QAClC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QAC5C,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QAE3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACvC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;QAE5D,0DAA0D;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAA;QAC1F,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QAExB,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;QAC1D,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAA;QAEtB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACzB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;IAC1B,CAAC;CACF"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Router
|
|
3
|
+
*
|
|
4
|
+
* Determines the execution path for a plugin tool call:
|
|
5
|
+
* embedded → in-process MCP via InMemoryTransport
|
|
6
|
+
* gateway-mcp → HTTP call to MCPGate (remote MCP)
|
|
7
|
+
* gateway-rest → HTTP call to REST API endpoint
|
|
8
|
+
* blocked → execution denied by policy
|
|
9
|
+
*
|
|
10
|
+
* Decision table (RFC Section 3.2):
|
|
11
|
+
* transport=embedded + policy=in_process → embedded
|
|
12
|
+
* transport=embedded + policy=gateway → gateway-mcp (fallback)
|
|
13
|
+
* transport=remote-mcp + any → gateway-mcp
|
|
14
|
+
* transport=rest + any → gateway-rest
|
|
15
|
+
* policy=block → blocked
|
|
16
|
+
*/
|
|
17
|
+
import type { ActivatedPlugin, PolicyConfig, RouteResult } from './types.js';
|
|
18
|
+
export interface RouterConfig {
|
|
19
|
+
/** MCPGate gateway URL (from MCPGATE_URL env). */
|
|
20
|
+
mcpgateUrl?: string;
|
|
21
|
+
/** Policy configuration. */
|
|
22
|
+
policy?: PolicyConfig;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Route a plugin to its execution path.
|
|
26
|
+
*/
|
|
27
|
+
export declare function routePlugin(plugin: ActivatedPlugin, config?: RouterConfig): RouteResult;
|
|
28
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG5E,MAAM,WAAW,YAAY;IAC3B,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4BAA4B;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAA;CACtB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,WAAW,CAuDvF"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Router
|
|
3
|
+
*
|
|
4
|
+
* Determines the execution path for a plugin tool call:
|
|
5
|
+
* embedded → in-process MCP via InMemoryTransport
|
|
6
|
+
* gateway-mcp → HTTP call to MCPGate (remote MCP)
|
|
7
|
+
* gateway-rest → HTTP call to REST API endpoint
|
|
8
|
+
* blocked → execution denied by policy
|
|
9
|
+
*
|
|
10
|
+
* Decision table (RFC Section 3.2):
|
|
11
|
+
* transport=embedded + policy=in_process → embedded
|
|
12
|
+
* transport=embedded + policy=gateway → gateway-mcp (fallback)
|
|
13
|
+
* transport=remote-mcp + any → gateway-mcp
|
|
14
|
+
* transport=rest + any → gateway-rest
|
|
15
|
+
* policy=block → blocked
|
|
16
|
+
*/
|
|
17
|
+
import { resolvePolicy } from './policy.js';
|
|
18
|
+
/**
|
|
19
|
+
* Route a plugin to its execution path.
|
|
20
|
+
*/
|
|
21
|
+
export function routePlugin(plugin, config) {
|
|
22
|
+
const trustLevel = plugin.trustLevel ?? 'community';
|
|
23
|
+
const executionMode = plugin.executionMode ?? 'gateway';
|
|
24
|
+
const transport = plugin.transport ?? 'remote-mcp';
|
|
25
|
+
// Resolve policy
|
|
26
|
+
const policy = resolvePolicy({ slug: plugin.slug, trustLevel, executionMode }, config?.policy);
|
|
27
|
+
// Blocked by policy
|
|
28
|
+
if (policy.decision === 'block') {
|
|
29
|
+
return { path: 'blocked', policy };
|
|
30
|
+
}
|
|
31
|
+
// Route based on transport + effective mode
|
|
32
|
+
switch (transport) {
|
|
33
|
+
case 'embedded': {
|
|
34
|
+
if (policy.effectiveMode === 'in_process') {
|
|
35
|
+
return { path: 'embedded', policy };
|
|
36
|
+
}
|
|
37
|
+
// Embedded plugin forced to gateway by policy → route through MCPGate
|
|
38
|
+
return {
|
|
39
|
+
path: 'gateway-mcp',
|
|
40
|
+
target: plugin.mcpgateServerId ?? `builtin:${plugin.slug}`,
|
|
41
|
+
policy,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
case 'remote-mcp': {
|
|
45
|
+
return {
|
|
46
|
+
path: 'gateway-mcp',
|
|
47
|
+
target: plugin.mcpgateServerId ?? `builtin:${plugin.slug}`,
|
|
48
|
+
policy,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
case 'rest': {
|
|
52
|
+
return {
|
|
53
|
+
path: 'gateway-rest',
|
|
54
|
+
target: plugin.endpointUrl ?? plugin.slug,
|
|
55
|
+
policy,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
default: {
|
|
59
|
+
// Unknown transport → gateway as safe default
|
|
60
|
+
return {
|
|
61
|
+
path: 'gateway-mcp',
|
|
62
|
+
target: plugin.mcpgateServerId ?? `builtin:${plugin.slug}`,
|
|
63
|
+
policy,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAS3C;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,MAAuB,EAAE,MAAqB;IACxE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,WAAW,CAAA;IACnD,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,SAAS,CAAA;IACvD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,YAAY,CAAA;IAElD,iBAAiB;IACjB,MAAM,MAAM,GAAG,aAAa,CAC1B,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,EAChD,MAAM,EAAE,MAAM,CACf,CAAA;IAED,oBAAoB;IACpB,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;IACpC,CAAC;IAED,4CAA4C;IAC5C,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,IAAI,MAAM,CAAC,aAAa,KAAK,YAAY,EAAE,CAAC;gBAC1C,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,CAAA;YACrC,CAAC;YACD,sEAAsE;YACtE,OAAO;gBACL,IAAI,EAAE,aAAa;gBACnB,MAAM,EAAE,MAAM,CAAC,eAAe,IAAI,WAAW,MAAM,CAAC,IAAI,EAAE;gBAC1D,MAAM;aACP,CAAA;QACH,CAAC;QAED,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,OAAO;gBACL,IAAI,EAAE,aAAa;gBACnB,MAAM,EAAE,MAAM,CAAC,eAAe,IAAI,WAAW,MAAM,CAAC,IAAI,EAAE;gBAC1D,MAAM;aACP,CAAA;QACH,CAAC;QAED,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,OAAO;gBACL,IAAI,EAAE,cAAc;gBACpB,MAAM,EAAE,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI;gBACzC,MAAM;aACP,CAAA;QACH,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,8CAA8C;YAC9C,OAAO;gBACL,IAAI,EAAE,aAAa;gBACnB,MAAM,EAAE,MAAM,CAAC,eAAe,IAAI,WAAW,MAAM,CAAC,IAAI,EAAE;gBAC1D,MAAM;aACP,CAAA;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability Core — Types
|
|
3
|
+
*
|
|
4
|
+
* Canonical types for the unified capability architecture.
|
|
5
|
+
* These mirror contracts/plugin.ts but are framework-independent.
|
|
6
|
+
*/
|
|
7
|
+
export interface PluginCatalogEntry {
|
|
8
|
+
id: string;
|
|
9
|
+
slug: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string | null;
|
|
12
|
+
version: string;
|
|
13
|
+
source: 'first-party' | 'mcpgate' | 'community';
|
|
14
|
+
kind: 'plugin' | 'integration';
|
|
15
|
+
transport: 'embedded' | 'remote-mcp' | 'rest';
|
|
16
|
+
trustLevel: 'internal' | 'verified' | 'community';
|
|
17
|
+
executionMode: 'in_process' | 'gateway';
|
|
18
|
+
authType: 'none' | 'oauth2' | 'api-key' | 'env-var';
|
|
19
|
+
authProvider: string | null;
|
|
20
|
+
toolManifest: ToolDef[];
|
|
21
|
+
mcpgateServerId?: string | null;
|
|
22
|
+
riskLevel: 'read' | 'write' | 'destructive';
|
|
23
|
+
verified: boolean;
|
|
24
|
+
isPublished: boolean;
|
|
25
|
+
maxTools: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ToolDef {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
parameters: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export interface ActivatedPlugin {
|
|
33
|
+
slug: string;
|
|
34
|
+
name: string;
|
|
35
|
+
tools: ToolDef[];
|
|
36
|
+
config: Record<string, unknown>;
|
|
37
|
+
kind: 'plugin' | 'integration';
|
|
38
|
+
transport: 'embedded' | 'remote-mcp' | 'rest';
|
|
39
|
+
trustLevel: 'internal' | 'verified' | 'community';
|
|
40
|
+
executionMode: 'in_process' | 'gateway';
|
|
41
|
+
authType: 'none' | 'oauth2' | 'api-key' | 'env-var';
|
|
42
|
+
authProvider: string | null;
|
|
43
|
+
mcpgateServerId?: string;
|
|
44
|
+
endpointUrl?: string;
|
|
45
|
+
fallbackMode?: 'gateway' | null;
|
|
46
|
+
/** @deprecated Use trustLevel + transport instead. Kept for DB wire compat. */
|
|
47
|
+
source?: 'first-party' | 'mcpgate' | 'community';
|
|
48
|
+
}
|
|
49
|
+
export type PolicyDecision = 'allow_in_process' | 'allow_gateway' | 'block';
|
|
50
|
+
export interface PolicyResult {
|
|
51
|
+
decision: PolicyDecision;
|
|
52
|
+
reason: string;
|
|
53
|
+
/** Effective execution mode after policy resolution. */
|
|
54
|
+
effectiveMode: 'in_process' | 'gateway';
|
|
55
|
+
}
|
|
56
|
+
export interface PolicyConfig {
|
|
57
|
+
/** Admin blocklist — slugs that are blocked regardless of trust. */
|
|
58
|
+
blockedPlugins?: string[];
|
|
59
|
+
/** Environment override (e.g., force gateway in staging). */
|
|
60
|
+
forceGateway?: boolean;
|
|
61
|
+
}
|
|
62
|
+
export type ExecutionPath = 'embedded' | 'gateway-mcp' | 'gateway-rest' | 'blocked';
|
|
63
|
+
export interface RouteResult {
|
|
64
|
+
path: ExecutionPath;
|
|
65
|
+
/** The target URL or server ID for gateway paths. */
|
|
66
|
+
target?: string;
|
|
67
|
+
/** Policy result that determined this route. */
|
|
68
|
+
policy: PolicyResult;
|
|
69
|
+
}
|
|
70
|
+
export interface AuditEvent {
|
|
71
|
+
timestamp: string;
|
|
72
|
+
pluginSlug: string;
|
|
73
|
+
toolName: string;
|
|
74
|
+
executionPath: ExecutionPath;
|
|
75
|
+
durationMs: number;
|
|
76
|
+
success: boolean;
|
|
77
|
+
error?: string;
|
|
78
|
+
/** Who triggered it (assistant ID, org ID). */
|
|
79
|
+
context?: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
export type AuditHandler = (event: AuditEvent) => void | Promise<void>;
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lucid-fdn/plugin-policy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Unified capability registry, router, policy engine, and audit hooks for plugins and integrations",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"typescript": "^5.7.3"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
]
|
|
30
|
+
}
|