kiku-agent 1.0.0 → 1.0.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 +293 -122
- package/package.json +60 -61
- package/server/agent/agentCore.js +892 -782
- package/server/agent/appPaths.js +21 -0
- package/server/agent/audioPlayer.js +739 -673
- package/server/agent/cli.js +1414 -1111
- package/server/agent/commandRegistry.js +74 -58
- package/server/agent/commands.js +1694 -788
- package/server/agent/config.js +264 -90
- package/server/agent/contextCompactor.js +79 -0
- package/server/agent/doctor.js +69 -0
- package/server/agent/eventBus.js +107 -103
- package/server/agent/gpuMetrics.js +189 -0
- package/server/agent/llmClient.js +369 -200
- package/server/agent/mcpClient.js +364 -0
- package/server/agent/memory.js +670 -566
- package/server/agent/modelManager.js +77 -0
- package/server/agent/research.js +202 -0
- package/server/agent/session.js +244 -239
- package/server/agent/subagent.js +116 -0
- package/server/agent/theme.js +125 -124
- package/server/agent/toolRouter.js +201 -0
- package/server/agent/tools.js +1679 -1528
- package/server/agent/ui.js +760 -469
- package/server/mcp/claude-desktop-config.example.json +1 -1
- package/server/mcp/data/discography.js +36 -36
- package/server/mcp/dataAdapter.js +102 -0
- package/server/mcp/examples/claude-code.mcp.example.json +19 -0
- package/server/mcp/examples/claude-desktop.example.json +11 -0
- package/server/mcp/examples/cline.example.json +13 -0
- package/server/mcp/examples/cursor.example.json +14 -0
- package/server/mcp/examples/gemini-cli.example.json +17 -0
- package/server/mcp/examples/opencode.example.json +21 -0
- package/server/mcp/examples/vscode.example.json +19 -0
- package/server/mcp/examples/windsurf.example.json +14 -0
- package/server/mcp/handler.js +85 -159
- package/server/mcp/security.js +140 -138
- package/server/mcp/server.js +129 -100
- package/server/mcp/stdio.js +4 -2
package/server/agent/theme.js
CHANGED
|
@@ -1,124 +1,125 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
fs.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { KIKU_DATA_DIR } from './appPaths.js';
|
|
4
|
+
|
|
5
|
+
const THEME_STATE_DIR = KIKU_DATA_DIR;
|
|
6
|
+
const THEME_STATE_FILE = path.join(THEME_STATE_DIR, 'theme.json');
|
|
7
|
+
|
|
8
|
+
export const THEMES = Object.freeze({
|
|
9
|
+
sakura: Object.freeze({
|
|
10
|
+
label: 'Sakura',
|
|
11
|
+
description: 'Pink accents with neutral gray surfaces',
|
|
12
|
+
colors: Object.freeze({
|
|
13
|
+
primary: '255;110;180', secondary: '255;165;195', highlight: '255;215;80',
|
|
14
|
+
warning: '245;158;11', info: '56;210;255', success: '50;235;130',
|
|
15
|
+
tertiary: '180;140;255', muted: '130;140;160', subdued: '80;85;95',
|
|
16
|
+
text: '255;241;247', error: '255;82;115', panel: '45;48;54', panelHeader: '72;52;65',
|
|
17
|
+
}),
|
|
18
|
+
}),
|
|
19
|
+
midnight: Object.freeze({
|
|
20
|
+
label: 'Midnight',
|
|
21
|
+
description: 'Deep blue surfaces with cool accents',
|
|
22
|
+
colors: Object.freeze({
|
|
23
|
+
primary: '96;165;250', secondary: '125;211;252', highlight: '250;204;21',
|
|
24
|
+
warning: '251;191;36', info: '34;211;238', success: '74;222;128',
|
|
25
|
+
tertiary: '167;139;250', muted: '148;163;184', subdued: '71;85;105',
|
|
26
|
+
text: '219;234;254', error: '251;113;133', panel: '15;23;42', panelHeader: '30;41;69',
|
|
27
|
+
}),
|
|
28
|
+
}),
|
|
29
|
+
ocean: Object.freeze({
|
|
30
|
+
label: 'Ocean',
|
|
31
|
+
description: 'Cyan and blue accents on marine-gray surfaces',
|
|
32
|
+
colors: Object.freeze({
|
|
33
|
+
primary: '34;211;238', secondary: '103;232;249', highlight: '253;224;71',
|
|
34
|
+
warning: '251;191;36', info: '56;189;248', success: '45;212;191',
|
|
35
|
+
tertiary: '96;165;250', muted: '148;163;184', subdued: '71;85;105',
|
|
36
|
+
text: '207;250;254', error: '251;113;133', panel: '8;47;59', panelHeader: '14;74;89',
|
|
37
|
+
}),
|
|
38
|
+
}),
|
|
39
|
+
forest: Object.freeze({
|
|
40
|
+
label: 'Forest',
|
|
41
|
+
description: 'Green and teal accents on natural dark surfaces',
|
|
42
|
+
colors: Object.freeze({
|
|
43
|
+
primary: '74;222;128', secondary: '110;231;183', highlight: '250;204;21',
|
|
44
|
+
warning: '251;191;36', info: '45;212;191', success: '134;239;172',
|
|
45
|
+
tertiary: '163;230;53', muted: '148;163;153', subdued: '71;85;79',
|
|
46
|
+
text: '220;252;231', error: '252;129;129', panel: '20;45;34', panelHeader: '30;64;46',
|
|
47
|
+
}),
|
|
48
|
+
}),
|
|
49
|
+
sunset: Object.freeze({
|
|
50
|
+
label: 'Sunset',
|
|
51
|
+
description: 'Warm orange accents on charcoal-brown surfaces',
|
|
52
|
+
colors: Object.freeze({
|
|
53
|
+
primary: '251;146;60', secondary: '253;186;116', highlight: '250;204;21',
|
|
54
|
+
warning: '251;191;36', info: '56;189;248', success: '74;222;128',
|
|
55
|
+
tertiary: '244;114;182', muted: '168;151;143', subdued: '87;72;64',
|
|
56
|
+
text: '255;237;213', error: '253;106;106', panel: '61;35;28', panelHeader: '87;48;38',
|
|
57
|
+
}),
|
|
58
|
+
}),
|
|
59
|
+
monochrome: Object.freeze({
|
|
60
|
+
label: 'Monochrome',
|
|
61
|
+
description: 'Low-distraction grayscale presentation',
|
|
62
|
+
colors: Object.freeze({
|
|
63
|
+
primary: '229;231;235', secondary: '209;213;219', highlight: '255;255;255',
|
|
64
|
+
warning: '209;213;219', info: '229;231;235', success: '229;231;235',
|
|
65
|
+
tertiary: '209;213;219', muted: '156;163;175', subdued: '107;114;128',
|
|
66
|
+
text: '245;245;245', error: '212;212;212', panel: '31;31;31', panelHeader: '64;64;64',
|
|
67
|
+
}),
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export const THEME_NAMES = Object.freeze(Object.keys(THEMES));
|
|
72
|
+
|
|
73
|
+
function loadSavedTheme() {
|
|
74
|
+
const envTheme = process.env.KIKU_THEME?.toLowerCase();
|
|
75
|
+
if (envTheme && THEMES[envTheme]) return envTheme;
|
|
76
|
+
try {
|
|
77
|
+
const saved = JSON.parse(fs.readFileSync(THEME_STATE_FILE, 'utf8'));
|
|
78
|
+
if (typeof saved.theme === 'string' && THEMES[saved.theme]) return saved.theme;
|
|
79
|
+
} catch {}
|
|
80
|
+
return 'sakura';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let activeThemeName = loadSavedTheme();
|
|
84
|
+
|
|
85
|
+
export function getThemeName() {
|
|
86
|
+
return activeThemeName;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function getTheme(name = activeThemeName) {
|
|
90
|
+
return THEMES[name] || THEMES.sakura;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function getThemePalette(name = activeThemeName) {
|
|
94
|
+
return getTheme(name).colors;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function setTheme(name, { persist = true } = {}) {
|
|
98
|
+
const normalized = String(name || '').toLowerCase();
|
|
99
|
+
if (!THEMES[normalized]) return null;
|
|
100
|
+
activeThemeName = normalized;
|
|
101
|
+
process.env.KIKU_THEME = normalized;
|
|
102
|
+
|
|
103
|
+
if (persist) {
|
|
104
|
+
fs.mkdirSync(THEME_STATE_DIR, { recursive: true });
|
|
105
|
+
fs.writeFileSync(THEME_STATE_FILE, JSON.stringify({ theme: normalized }, null, 2) + '\n', 'utf8');
|
|
106
|
+
}
|
|
107
|
+
return getTheme(normalized);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function ansiForeground(rgb) {
|
|
111
|
+
return `\x1b[38;2;${rgb}m`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function ansiBackground(rgb) {
|
|
115
|
+
return `\x1b[48;2;${rgb}m`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function inkColor(rgb) {
|
|
119
|
+
return `rgb(${rgb.replaceAll(';', ',')})`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function getInkPalette(name = activeThemeName) {
|
|
123
|
+
const palette = getThemePalette(name);
|
|
124
|
+
return Object.fromEntries(Object.entries(palette).map(([key, value]) => [key, inkColor(value)]));
|
|
125
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { toolsSchema } from './tools.js';
|
|
2
|
+
|
|
3
|
+
export const TOOL_CLUSTERS = Object.freeze({
|
|
4
|
+
CATALOG: [
|
|
5
|
+
'global_search',
|
|
6
|
+
'search_works',
|
|
7
|
+
'get_work_detail',
|
|
8
|
+
'get_character_roles',
|
|
9
|
+
'search_discography',
|
|
10
|
+
'get_profile_and_fandom',
|
|
11
|
+
'get_awards',
|
|
12
|
+
'get_brand_endorsements',
|
|
13
|
+
'get_career_timeline',
|
|
14
|
+
'get_social_feed',
|
|
15
|
+
'get_gallery_photos',
|
|
16
|
+
'get_quotes',
|
|
17
|
+
],
|
|
18
|
+
MEDIA: [
|
|
19
|
+
'play_music',
|
|
20
|
+
'stop_music',
|
|
21
|
+
'pause_music',
|
|
22
|
+
'resume_music',
|
|
23
|
+
'next_track',
|
|
24
|
+
'prev_track',
|
|
25
|
+
'add_to_queue',
|
|
26
|
+
'remove_from_queue',
|
|
27
|
+
'clear_queue',
|
|
28
|
+
'get_queue',
|
|
29
|
+
'search_youtube',
|
|
30
|
+
'get_current_track',
|
|
31
|
+
'get_music_status',
|
|
32
|
+
'get_music_videos',
|
|
33
|
+
'get_media_links',
|
|
34
|
+
],
|
|
35
|
+
RESEARCH: [
|
|
36
|
+
'web_search',
|
|
37
|
+
'web_extract',
|
|
38
|
+
'fetch_webpage',
|
|
39
|
+
'fetch_api',
|
|
40
|
+
'fetch_json',
|
|
41
|
+
'screenshot_webpage',
|
|
42
|
+
'verify_claim',
|
|
43
|
+
'run_subagent',
|
|
44
|
+
'run_deep_research',
|
|
45
|
+
],
|
|
46
|
+
SYSTEM: [
|
|
47
|
+
'get_system_stats',
|
|
48
|
+
'manage_skill',
|
|
49
|
+
'read_local_file',
|
|
50
|
+
'list_directory',
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const DEFAULT_UNIVERSAL_TOOLS = Object.freeze([
|
|
55
|
+
'global_search',
|
|
56
|
+
'search_works',
|
|
57
|
+
'search_discography',
|
|
58
|
+
'get_profile_and_fandom',
|
|
59
|
+
'get_media_links',
|
|
60
|
+
'web_search',
|
|
61
|
+
'run_subagent',
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const INTENT_PATTERNS = [
|
|
65
|
+
{
|
|
66
|
+
type: 'MEDIA',
|
|
67
|
+
regex: /(เพลง|ร้อง|ฟัง|เอ็มวี|mv|music|song|play|queue|track|audio|sound|listen|stage|dance|concert|ost|bilibili|qqmusic|歌曲|单曲|首发|舞台|演唱)/i,
|
|
68
|
+
clusters: ['MEDIA'],
|
|
69
|
+
essentialTools: ['search_discography', 'get_music_videos', 'get_media_links', 'play_music', 'get_queue', 'search_youtube', 'get_current_track', 'get_music_status']
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'WORKS',
|
|
73
|
+
regex: /(ละคร|ซีรีส์|หนัง|ภาพยนตร์|บทบาท|ตัวละคร|ตอน|ช่อง|ฉาย|drama|series|movie|film|role|character|synopsis|actor|tv|works|broadcast|weibo|影视|剧|新剧|角色|饰演)/i,
|
|
74
|
+
clusters: ['CATALOG'],
|
|
75
|
+
essentialTools: ['search_works', 'get_work_detail', 'get_character_roles', 'global_search', 'get_career_timeline']
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'PROFILE_AWARDS_BRANDS',
|
|
79
|
+
regex: /(ประวัติ|วันเกิด|อายุ|บ้านเกิด|แฟนคลับ|สโลแกน|แบรนด์|พรีเซนเตอร์|โฆษณา|ทูต|รางวัล|เกียรติยศ|นิตยสาร|แฟชั่น|ปก|โซเชียล|ไอจี|profile|bio|birthday|fandom|miju|蜜橘|brand|ambassador|award|honor|endorsement|quote|fashion|magazine|cover|snh48|election|hall of fame|senbatsu|win|social|instagram|weibo|douyin|wechat|account|คำพูด|代言|彩妆|品牌|荣誉|奖项|微博之夜)/i,
|
|
80
|
+
clusters: ['CATALOG'],
|
|
81
|
+
essentialTools: ['get_profile_and_fandom', 'get_brand_endorsements', 'get_awards', 'get_quotes', 'get_career_timeline', 'get_gallery_photos', 'get_social_feed']
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: 'RESEARCH_VERIFY',
|
|
85
|
+
regex: /(ข่าว|ล่าสุด|จริงไหม|ข่าวลือ|สืบ|ตรวจสอบ|สัญญา|ศาล|star48|siba|news|update|latest|recent|rumor|verify|claim|dispute|legal|lawsuit|contract|truth)/i,
|
|
86
|
+
clusters: ['RESEARCH'],
|
|
87
|
+
essentialTools: ['verify_claim', 'web_search', 'web_extract', 'fetch_webpage', 'run_subagent', 'run_deep_research', 'global_search']
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: 'SYSTEM_SKILL',
|
|
91
|
+
regex: /(skill|learn|teach|tui|system|stats|status|memory|pref|file|dir|directory|สถิติ|จำนวน|ระบบ|สอน)/i,
|
|
92
|
+
clusters: ['SYSTEM'],
|
|
93
|
+
essentialTools: ['manage_skill', 'get_system_stats', 'read_local_file', 'list_directory']
|
|
94
|
+
}
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
export class DynamicToolRouter {
|
|
98
|
+
constructor(allTools = toolsSchema) {
|
|
99
|
+
this.allTools = Array.isArray(allTools) ? allTools : toolsSchema;
|
|
100
|
+
this.toolsByName = new Map(this.allTools.map(t => [t.function?.name, t]));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setTools(allTools) {
|
|
104
|
+
this.allTools = Array.isArray(allTools) ? allTools : toolsSchema;
|
|
105
|
+
this.toolsByName = new Map(this.allTools.map(t => [t.function?.name, t]));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
detectIntents(prompt = '') {
|
|
109
|
+
const text = String(prompt || '');
|
|
110
|
+
const matched = [];
|
|
111
|
+
|
|
112
|
+
for (const pattern of INTENT_PATTERNS) {
|
|
113
|
+
if (pattern.regex.test(text)) {
|
|
114
|
+
matched.push(pattern);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return matched;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
extractAtMentions(prompt = '') {
|
|
121
|
+
const text = String(prompt || '');
|
|
122
|
+
const matches = text.match(/@([a-zA-Z0-9_-]+)/g);
|
|
123
|
+
if (!matches) return [];
|
|
124
|
+
return matches.map(m => m.slice(1));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
routeToolsForQuery(prompt = '', options = {}) {
|
|
128
|
+
if (options.showAll || options.disableRouting) {
|
|
129
|
+
return [...this.allTools];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const atMentions = this.extractAtMentions(prompt);
|
|
133
|
+
const pinnedTools = [];
|
|
134
|
+
const selectedNames = new Set();
|
|
135
|
+
|
|
136
|
+
for (const mention of atMentions) {
|
|
137
|
+
const mentionLower = mention.toLowerCase();
|
|
138
|
+
for (const tool of this.allTools) {
|
|
139
|
+
const name = tool.function?.name;
|
|
140
|
+
if (!name) continue;
|
|
141
|
+
const nameLower = name.toLowerCase();
|
|
142
|
+
if (
|
|
143
|
+
nameLower === mentionLower ||
|
|
144
|
+
nameLower === `mcp_${mentionLower}` ||
|
|
145
|
+
nameLower.endsWith(`_${mentionLower}`) ||
|
|
146
|
+
nameLower.startsWith(`mcp_${mentionLower}_`)
|
|
147
|
+
) {
|
|
148
|
+
selectedNames.add(name);
|
|
149
|
+
if (!pinnedTools.some(t => t.function?.name === name)) {
|
|
150
|
+
pinnedTools.push(tool);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const matchedIntents = this.detectIntents(prompt);
|
|
157
|
+
|
|
158
|
+
if (matchedIntents.length === 0) {
|
|
159
|
+
for (const name of DEFAULT_UNIVERSAL_TOOLS) {
|
|
160
|
+
selectedNames.add(name);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
for (const intent of matchedIntents) {
|
|
164
|
+
for (const toolName of intent.essentialTools) {
|
|
165
|
+
selectedNames.add(toolName);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
selectedNames.add('global_search');
|
|
169
|
+
selectedNames.add('web_search');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (options.includeExtra && Array.isArray(options.includeExtra)) {
|
|
173
|
+
for (const name of options.includeExtra) {
|
|
174
|
+
selectedNames.add(name);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const routed = [...pinnedTools];
|
|
179
|
+
const seenNames = new Set(pinnedTools.map(t => t.function?.name));
|
|
180
|
+
|
|
181
|
+
for (const tool of this.allTools) {
|
|
182
|
+
const name = tool.function?.name;
|
|
183
|
+
if (name && !seenNames.has(name) && (selectedNames.has(name) || (name.startsWith('mcp_') && options.includeMcp !== false))) {
|
|
184
|
+
seenNames.add(name);
|
|
185
|
+
routed.push(tool);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (routed.length === 0) {
|
|
190
|
+
return this.allTools.slice(0, 8);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (routed.length > 15 && !options.showAll) {
|
|
194
|
+
return routed.slice(0, 15);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return routed;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export const dynamicToolRouter = new DynamicToolRouter();
|