aitoearn-dsh-plugin-test 0.0.2 → 0.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/package.json +3 -3
- package/presets/aitoearn/lib/mcp.js +80 -20
- package/presets/aitoearn/lib/mcp.js.map +2 -2
- package/src/dsh/mcp.js +83 -20
- package/src/dsh/mcp.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aitoearn-dsh-plugin-test",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "0.0.3",
|
|
5
|
+
"description": "Aitoearn agent preset, skills, and MCP tools for DeepSeek Harness",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"types": "./src/dsh/index.d.ts",
|
|
@@ -135,4 +135,4 @@
|
|
|
135
135
|
"zustand": "~4.4.7"
|
|
136
136
|
},
|
|
137
137
|
"module": "./src/dsh/index.js"
|
|
138
|
-
}
|
|
138
|
+
}
|
|
@@ -102,45 +102,105 @@ async function apply(ctx, config = {}) {
|
|
|
102
102
|
let current = () => config;
|
|
103
103
|
let restoreNames;
|
|
104
104
|
let fiber;
|
|
105
|
-
|
|
105
|
+
let generation = 0;
|
|
106
|
+
let mountChain = Promise.resolve();
|
|
107
|
+
let remountTimer;
|
|
108
|
+
let mountedFingerprint;
|
|
109
|
+
const unmountAsync = async () => {
|
|
106
110
|
restoreNames?.();
|
|
107
111
|
restoreNames = void 0;
|
|
108
|
-
|
|
112
|
+
const previous = fiber;
|
|
109
113
|
fiber = void 0;
|
|
114
|
+
if (previous) {
|
|
115
|
+
try {
|
|
116
|
+
await previous.dispose();
|
|
117
|
+
} catch (error) {
|
|
118
|
+
ctx.logger.warn(error, "aitoearn-mcp: failed to dispose previous MCP client");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
110
121
|
};
|
|
111
|
-
const
|
|
112
|
-
unmount();
|
|
122
|
+
const doMount = async (gen) => {
|
|
113
123
|
const auth = resolveAuth(current());
|
|
114
124
|
if (auth.apiKey.length === 0 || auth.mcpUrl.length === 0) {
|
|
125
|
+
await unmountAsync();
|
|
126
|
+
mountedFingerprint = void 0;
|
|
115
127
|
ctx.logger.warn("aitoearn-mcp: skip mounting; API key or MCP URL is not configured yet");
|
|
116
128
|
return;
|
|
117
129
|
}
|
|
130
|
+
const fingerprint = `${auth.mcpUrl}
|
|
131
|
+
${auth.apiKey}`;
|
|
132
|
+
if (fingerprint === mountedFingerprint && fiber) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await unmountAsync();
|
|
136
|
+
if (gen !== generation) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
118
139
|
restoreNames = patchRawToolNames(ctx.tools, MCP_SERVER_NAME, (publicName) => {
|
|
119
140
|
ctx.logger.info(`kept qualified MCP tool name ${publicName}`);
|
|
120
141
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
142
|
+
try {
|
|
143
|
+
const mcpClient = runtimeRequire("@deepseek-ai/dsh-mcp-client");
|
|
144
|
+
const mcpConfig = {
|
|
145
|
+
serverName: MCP_SERVER_NAME,
|
|
146
|
+
transport: "streamable-http",
|
|
147
|
+
url: auth.mcpUrl,
|
|
148
|
+
headers: authorizationHeader(auth.apiKey),
|
|
149
|
+
failOnStartupError: false,
|
|
150
|
+
toolCallTimeoutMs: 12e4
|
|
151
|
+
};
|
|
152
|
+
const next = ctx.plugin(mcpClient, mcpConfig);
|
|
153
|
+
fiber = next;
|
|
154
|
+
await next;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
restoreNames?.();
|
|
157
|
+
restoreNames = void 0;
|
|
158
|
+
fiber = void 0;
|
|
159
|
+
ctx.logger.warn(error, "aitoearn-mcp: mount failed; will retry on next settings change");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (gen !== generation) {
|
|
163
|
+
await unmountAsync();
|
|
164
|
+
mountedFingerprint = void 0;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
mountedFingerprint = fingerprint;
|
|
132
168
|
ctx.logger.info(`aitoearn-mcp: mounted MCP tools from ${auth.mcpUrl}`);
|
|
133
169
|
};
|
|
170
|
+
const scheduleMount = () => {
|
|
171
|
+
generation += 1;
|
|
172
|
+
const gen = generation;
|
|
173
|
+
mountChain = mountChain.then(() => doMount(gen)).catch((error) => {
|
|
174
|
+
ctx.logger.warn(error, "aitoearn-mcp: mount failed");
|
|
175
|
+
});
|
|
176
|
+
return mountChain;
|
|
177
|
+
};
|
|
178
|
+
const scheduleRemountSoon = () => {
|
|
179
|
+
if (remountTimer) {
|
|
180
|
+
clearTimeout(remountTimer);
|
|
181
|
+
}
|
|
182
|
+
remountTimer = setTimeout(() => {
|
|
183
|
+
remountTimer = void 0;
|
|
184
|
+
void scheduleMount();
|
|
185
|
+
}, 50);
|
|
186
|
+
};
|
|
134
187
|
ctx.inject(["settings"], (scoped) => {
|
|
135
188
|
current = () => pluginConfigFromSettings(scoped.settings.get(SETTINGS_NAMESPACE), config);
|
|
136
189
|
ctx.on("settings/document-updated", (namespace) => {
|
|
137
|
-
if (String(namespace) === SETTINGS_NAMESPACE)
|
|
138
|
-
|
|
190
|
+
if (String(namespace) === SETTINGS_NAMESPACE) {
|
|
191
|
+
scheduleRemountSoon();
|
|
192
|
+
}
|
|
139
193
|
});
|
|
140
|
-
void
|
|
194
|
+
void scheduleMount();
|
|
141
195
|
});
|
|
142
|
-
ctx.effect(() =>
|
|
143
|
-
|
|
196
|
+
ctx.effect(() => () => {
|
|
197
|
+
if (remountTimer) {
|
|
198
|
+
clearTimeout(remountTimer);
|
|
199
|
+
remountTimer = void 0;
|
|
200
|
+
}
|
|
201
|
+
void unmountAsync();
|
|
202
|
+
}, "aitoearn-mcp.client");
|
|
203
|
+
await scheduleMount();
|
|
144
204
|
}
|
|
145
205
|
export {
|
|
146
206
|
apply,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../tools/agent-plugin/src/dsh/mcp.ts", "../../../../../../../tools/agent-plugin/src/dsh/auth.ts", "../../../../../../../tools/agent-plugin/src/dsh/tool-name.ts"],
|
|
4
|
-
"sourcesContent": ["import { createRequire } from 'node:module'\nimport { Context, Fiber } from '@deepseek-ai/cordis'\nimport { StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client'\nimport {} from '@deepseek-ai/dsh-settings'\nimport {} from '@deepseek-ai/dsh-settings/types'\nimport {} from '@deepseek-ai/dsh-tools'\nimport { AitoearnPluginConfig, authorizationHeader, pluginConfigFromSettings, resolveAuth, SETTINGS_NAMESPACE } from './auth.js'\nimport { MCP_SERVER_NAME, patchRawToolNames } from './tool-name.js'\n\nconst runtimeRequire = createRequire(import.meta.url)\n\nexport const name = 'aitoearn-mcp'\nexport const inject = ['tools']\n\nexport async function apply(ctx: Context, config: AitoearnPluginConfig = {}): Promise<void> {\n let current = () => config\n let restoreNames: (() => void) | undefined\n let fiber: Fiber | undefined\n\n const
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { createRequire } from 'node:module'\nimport { Context, Fiber } from '@deepseek-ai/cordis'\nimport { StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client'\nimport {} from '@deepseek-ai/dsh-settings'\nimport {} from '@deepseek-ai/dsh-settings/types'\nimport {} from '@deepseek-ai/dsh-tools'\nimport { AitoearnPluginConfig, authorizationHeader, pluginConfigFromSettings, resolveAuth, SETTINGS_NAMESPACE } from './auth.js'\nimport { MCP_SERVER_NAME, patchRawToolNames } from './tool-name.js'\n\nconst runtimeRequire = createRequire(import.meta.url)\n\nexport const name = 'aitoearn-mcp'\nexport const inject = ['tools']\n\nexport async function apply(ctx: Context, config: AitoearnPluginConfig = {}): Promise<void> {\n let current = () => config\n let restoreNames: (() => void) | undefined\n let fiber: Fiber | undefined\n let generation = 0\n let mountChain: Promise<void> = Promise.resolve()\n let remountTimer: ReturnType<typeof setTimeout> | undefined\n let mountedFingerprint: string | undefined\n\n const unmountAsync = async () => {\n restoreNames?.()\n restoreNames = undefined\n const previous = fiber\n fiber = undefined\n if (previous) {\n try {\n await previous.dispose()\n }\n catch (error) {\n ctx.logger.warn(error, 'aitoearn-mcp: failed to dispose previous MCP client')\n }\n }\n }\n\n const doMount = async (gen: number) => {\n const auth = resolveAuth(current())\n if (auth.apiKey.length === 0 || auth.mcpUrl.length === 0) {\n await unmountAsync()\n mountedFingerprint = undefined\n ctx.logger.warn('aitoearn-mcp: skip mounting; API key or MCP URL is not configured yet')\n return\n }\n const fingerprint = `${auth.mcpUrl}\\n${auth.apiKey}`\n if (fingerprint === mountedFingerprint && fiber) {\n return\n }\n await unmountAsync()\n if (gen !== generation) {\n return\n }\n restoreNames = patchRawToolNames(ctx.tools, MCP_SERVER_NAME, (publicName) => {\n ctx.logger.info(`kept qualified MCP tool name ${publicName}`)\n })\n try {\n const mcpClient: typeof import('@deepseek-ai/dsh-mcp-client') = runtimeRequire('@deepseek-ai/dsh-mcp-client')\n const mcpConfig: StreamableHttpConfig = {\n serverName: MCP_SERVER_NAME,\n transport: 'streamable-http',\n url: auth.mcpUrl,\n headers: authorizationHeader(auth.apiKey),\n failOnStartupError: false,\n toolCallTimeoutMs: 120000,\n }\n const next = ctx.plugin(mcpClient, mcpConfig)\n fiber = next\n await next\n }\n catch (error) {\n restoreNames?.()\n restoreNames = undefined\n fiber = undefined\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed; will retry on next settings change')\n return\n }\n if (gen !== generation) {\n await unmountAsync()\n mountedFingerprint = undefined\n return\n }\n mountedFingerprint = fingerprint\n ctx.logger.info(`aitoearn-mcp: mounted MCP tools from ${auth.mcpUrl}`)\n }\n\n const scheduleMount = () => {\n generation += 1\n const gen = generation\n mountChain = mountChain\n .then(() => doMount(gen))\n .catch((error: Error) => {\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed')\n })\n return mountChain\n }\n\n const scheduleRemountSoon = () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n }\n remountTimer = setTimeout(() => {\n remountTimer = undefined\n void scheduleMount()\n }, 50)\n }\n\n ctx.inject(['settings'], (scoped) => {\n current = () => pluginConfigFromSettings(scoped.settings.get(SETTINGS_NAMESPACE), config)\n ctx.on('settings/document-updated', (namespace) => {\n if (String(namespace) === SETTINGS_NAMESPACE) {\n scheduleRemountSoon()\n }\n })\n // The initial apply-time mount may run before the settings section is\n // readable; mount again once it is, so a stored key takes effect without\n // requiring a manual settings save.\n void scheduleMount()\n })\n\n ctx.effect(() => () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n remountTimer = undefined\n }\n void unmountAsync()\n }, 'aitoearn-mcp.client')\n await scheduleMount()\n}\n", "export const MCP_PATH = '/api/ai/agent/mcp'\nexport const API_KEY_ENV = 'AITOEARN_API_KEY'\nexport const DEFAULT_ENVIRONMENT = 'global'\nexport const SETTINGS_NAMESPACE = 'aitoearn'\n\nexport const ENVIRONMENT_ORIGINS = {\n global: 'https://aitoearn.ai',\n cn: 'https://aitoearn.cn',\n} as const\n\nexport const DEFAULT_MCP_URL = `${ENVIRONMENT_ORIGINS.global}${MCP_PATH}`\n\nexport type AitoearnEnvironment = 'global' | 'cn' | 'custom'\n\nexport interface AitoearnAuth {\n mcpUrl: string\n apiKey: string\n}\n\nexport interface AitoearnPluginConfig {\n environment?: string\n baseUrl?: string\n apiKey?: string\n}\n\nexport function pluginConfigFromSettings(\n section: unknown,\n fallback: AitoearnPluginConfig = {},\n): AitoearnPluginConfig {\n if (section === null || typeof section !== 'object' || Array.isArray(section))\n return fallback\n const record = section as Record<string, unknown>\n return {\n ...fallback,\n ...(typeof record['environment'] === 'string' ? { environment: record['environment'] } : {}),\n ...(typeof record['baseUrl'] === 'string' ? { baseUrl: record['baseUrl'] } : {}),\n ...(typeof record['apiKey'] === 'string' ? { apiKey: record['apiKey'] } : {}),\n }\n}\n\nexport function resolveEnvironment(value: string | undefined): AitoearnEnvironment {\n if (value === 'cn' || value === 'custom' || value === 'global')\n return value\n return DEFAULT_ENVIRONMENT\n}\n\nexport function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string {\n if (environment !== 'custom')\n return ENVIRONMENT_ORIGINS[environment]\n const trimmed = baseUrl?.trim() ?? ''\n if (trimmed.length === 0)\n return ''\n try {\n const url = new URL(trimmed)\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n return ''\n return url.origin\n }\n catch {\n return ''\n }\n}\n\nexport function resolveMcpUrl(config: AitoearnPluginConfig): string {\n const environment = resolveEnvironment(config.environment)\n const origin = resolveOrigin(environment, config.baseUrl)\n if (origin.length === 0)\n return ''\n return `${origin}${MCP_PATH}`\n}\n\nexport function resolveAuth(\n config: AitoearnPluginConfig,\n env: NodeJS.ProcessEnv = process.env,\n): AitoearnAuth {\n return {\n mcpUrl: resolveMcpUrl(config),\n apiKey: config.apiKey?.trim() || env[API_KEY_ENV]?.trim() || '',\n }\n}\n\nexport function authorizationHeader(apiKey: string): Record<string, string> {\n if (apiKey.length === 0)\n return {}\n return { Authorization: `Bearer ${apiKey}` }\n}\n", "import { ToolRuntime } from '@deepseek-ai/dsh-tools'\n\nexport const MCP_SERVER_NAME = 'aitoearn'\n\nconst HASHED_PUBLIC_NAME = /_[0-9a-f]{12}$/\n\nexport interface RawPublicName {\n modelName: string\n stripped: boolean\n}\n\nexport function qualifiedPrefix(serverName = MCP_SERVER_NAME): string {\n return `mcp__${serverName}__`\n}\n\nexport function toRawPublicName(publicName: string, serverName = MCP_SERVER_NAME): RawPublicName {\n const prefix = qualifiedPrefix(serverName)\n if (!publicName.startsWith(prefix))\n return { modelName: publicName, stripped: false }\n if (publicName.length === 64 && HASHED_PUBLIC_NAME.test(publicName))\n return { modelName: publicName, stripped: false }\n const raw = publicName.slice(prefix.length)\n if (raw.length === 0)\n return { modelName: publicName, stripped: false }\n return { modelName: raw, stripped: true }\n}\n\nexport function patchRawToolNames(\n tools: Pick<ToolRuntime, 'register'>,\n serverName = MCP_SERVER_NAME,\n onKeepQualified?: (publicName: string) => void,\n): () => void {\n const original = tools.register.bind(tools)\n tools.register = (definition) => {\n const next = toRawPublicName(definition.name, serverName)\n if (!next.stripped) {\n if (definition.name.startsWith(qualifiedPrefix(serverName)))\n onKeepQualified?.(definition.name)\n return original(definition)\n }\n return original({ ...definition, name: next.modelName })\n }\n return () => {\n tools.register = original\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/src/dsh/mcp.js
CHANGED
|
@@ -8,47 +8,110 @@ export async function apply(ctx, config = {}) {
|
|
|
8
8
|
let current = () => config;
|
|
9
9
|
let restoreNames;
|
|
10
10
|
let fiber;
|
|
11
|
-
|
|
11
|
+
let generation = 0;
|
|
12
|
+
let mountChain = Promise.resolve();
|
|
13
|
+
let remountTimer;
|
|
14
|
+
let mountedFingerprint;
|
|
15
|
+
const unmountAsync = async () => {
|
|
12
16
|
restoreNames?.();
|
|
13
17
|
restoreNames = undefined;
|
|
14
|
-
|
|
18
|
+
const previous = fiber;
|
|
15
19
|
fiber = undefined;
|
|
20
|
+
if (previous) {
|
|
21
|
+
try {
|
|
22
|
+
await previous.dispose();
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
ctx.logger.warn(error, 'aitoearn-mcp: failed to dispose previous MCP client');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
16
28
|
};
|
|
17
|
-
const
|
|
18
|
-
unmount();
|
|
29
|
+
const doMount = async (gen) => {
|
|
19
30
|
const auth = resolveAuth(current());
|
|
20
31
|
if (auth.apiKey.length === 0 || auth.mcpUrl.length === 0) {
|
|
32
|
+
await unmountAsync();
|
|
33
|
+
mountedFingerprint = undefined;
|
|
21
34
|
ctx.logger.warn('aitoearn-mcp: skip mounting; API key or MCP URL is not configured yet');
|
|
22
35
|
return;
|
|
23
36
|
}
|
|
37
|
+
const fingerprint = `${auth.mcpUrl}\n${auth.apiKey}`;
|
|
38
|
+
if (fingerprint === mountedFingerprint && fiber) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await unmountAsync();
|
|
42
|
+
if (gen !== generation) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
24
45
|
restoreNames = patchRawToolNames(ctx.tools, MCP_SERVER_NAME, (publicName) => {
|
|
25
46
|
ctx.logger.info(`kept qualified MCP tool name ${publicName}`);
|
|
26
47
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
48
|
+
try {
|
|
49
|
+
const mcpClient = runtimeRequire('@deepseek-ai/dsh-mcp-client');
|
|
50
|
+
const mcpConfig = {
|
|
51
|
+
serverName: MCP_SERVER_NAME,
|
|
52
|
+
transport: 'streamable-http',
|
|
53
|
+
url: auth.mcpUrl,
|
|
54
|
+
headers: authorizationHeader(auth.apiKey),
|
|
55
|
+
failOnStartupError: false,
|
|
56
|
+
toolCallTimeoutMs: 120000,
|
|
57
|
+
};
|
|
58
|
+
const next = ctx.plugin(mcpClient, mcpConfig);
|
|
59
|
+
fiber = next;
|
|
60
|
+
await next;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
restoreNames?.();
|
|
64
|
+
restoreNames = undefined;
|
|
65
|
+
fiber = undefined;
|
|
66
|
+
ctx.logger.warn(error, 'aitoearn-mcp: mount failed; will retry on next settings change');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (gen !== generation) {
|
|
70
|
+
await unmountAsync();
|
|
71
|
+
mountedFingerprint = undefined;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
mountedFingerprint = fingerprint;
|
|
38
75
|
ctx.logger.info(`aitoearn-mcp: mounted MCP tools from ${auth.mcpUrl}`);
|
|
39
76
|
};
|
|
77
|
+
const scheduleMount = () => {
|
|
78
|
+
generation += 1;
|
|
79
|
+
const gen = generation;
|
|
80
|
+
mountChain = mountChain
|
|
81
|
+
.then(() => doMount(gen))
|
|
82
|
+
.catch((error) => {
|
|
83
|
+
ctx.logger.warn(error, 'aitoearn-mcp: mount failed');
|
|
84
|
+
});
|
|
85
|
+
return mountChain;
|
|
86
|
+
};
|
|
87
|
+
const scheduleRemountSoon = () => {
|
|
88
|
+
if (remountTimer) {
|
|
89
|
+
clearTimeout(remountTimer);
|
|
90
|
+
}
|
|
91
|
+
remountTimer = setTimeout(() => {
|
|
92
|
+
remountTimer = undefined;
|
|
93
|
+
void scheduleMount();
|
|
94
|
+
}, 50);
|
|
95
|
+
};
|
|
40
96
|
ctx.inject(['settings'], (scoped) => {
|
|
41
97
|
current = () => pluginConfigFromSettings(scoped.settings.get(SETTINGS_NAMESPACE), config);
|
|
42
98
|
ctx.on('settings/document-updated', (namespace) => {
|
|
43
|
-
if (String(namespace) === SETTINGS_NAMESPACE)
|
|
44
|
-
|
|
99
|
+
if (String(namespace) === SETTINGS_NAMESPACE) {
|
|
100
|
+
scheduleRemountSoon();
|
|
101
|
+
}
|
|
45
102
|
});
|
|
46
103
|
// The initial apply-time mount may run before the settings section is
|
|
47
104
|
// readable; mount again once it is, so a stored key takes effect without
|
|
48
105
|
// requiring a manual settings save.
|
|
49
|
-
void
|
|
106
|
+
void scheduleMount();
|
|
50
107
|
});
|
|
51
|
-
ctx.effect(() =>
|
|
52
|
-
|
|
108
|
+
ctx.effect(() => () => {
|
|
109
|
+
if (remountTimer) {
|
|
110
|
+
clearTimeout(remountTimer);
|
|
111
|
+
remountTimer = undefined;
|
|
112
|
+
}
|
|
113
|
+
void unmountAsync();
|
|
114
|
+
}, 'aitoearn-mcp.client');
|
|
115
|
+
await scheduleMount();
|
|
53
116
|
}
|
|
54
117
|
//# sourceMappingURL=mcp.js.map
|
package/src/dsh/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAM3C,OAAO,EAAwB,mBAAmB,EAAE,wBAAwB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAChI,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEnE,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAErD,MAAM,CAAC,MAAM,IAAI,GAAG,cAAc,CAAA;AAClC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,CAAA;AAE/B,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,SAA+B,EAAE;IACzE,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAA;IAC1B,IAAI,YAAsC,CAAA;IAC1C,IAAI,KAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../../../../../tools/agent-plugin/src/dsh/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAM3C,OAAO,EAAwB,mBAAmB,EAAE,wBAAwB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAChI,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEnE,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAErD,MAAM,CAAC,MAAM,IAAI,GAAG,cAAc,CAAA;AAClC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,CAAA;AAE/B,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY,EAAE,SAA+B,EAAE;IACzE,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAA;IAC1B,IAAI,YAAsC,CAAA;IAC1C,IAAI,KAAwB,CAAA;IAC5B,IAAI,UAAU,GAAG,CAAC,CAAA;IAClB,IAAI,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IACjD,IAAI,YAAuD,CAAA;IAC3D,IAAI,kBAAsC,CAAA;IAE1C,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;QAC9B,YAAY,EAAE,EAAE,CAAA;QAChB,YAAY,GAAG,SAAS,CAAA;QACxB,MAAM,QAAQ,GAAG,KAAK,CAAA;QACtB,KAAK,GAAG,SAAS,CAAA;QACjB,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAA;YAC1B,CAAC;YACD,OAAO,KAAK,EAAE,CAAC;gBACb,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,qDAAqD,CAAC,CAAA;YAC/E,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,KAAK,EAAE,GAAW,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;QACnC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzD,MAAM,YAAY,EAAE,CAAA;YACpB,kBAAkB,GAAG,SAAS,CAAA;YAC9B,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAA;YACxF,OAAM;QACR,CAAC;QACD,MAAM,WAAW,GAAG,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAA;QACpD,IAAI,WAAW,KAAK,kBAAkB,IAAI,KAAK,EAAE,CAAC;YAChD,OAAM;QACR,CAAC;QACD,MAAM,YAAY,EAAE,CAAA;QACpB,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACvB,OAAM;QACR,CAAC;QACD,YAAY,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,CAAC,UAAU,EAAE,EAAE;YAC1E,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,gCAAgC,UAAU,EAAE,CAAC,CAAA;QAC/D,CAAC,CAAC,CAAA;QACF,IAAI,CAAC;YACH,MAAM,SAAS,GAAiD,cAAc,CAAC,6BAA6B,CAAC,CAAA;YAC7G,MAAM,SAAS,GAAyB;gBACtC,UAAU,EAAE,eAAe;gBAC3B,SAAS,EAAE,iBAAiB;gBAC5B,GAAG,EAAE,IAAI,CAAC,MAAM;gBAChB,OAAO,EAAE,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC;gBACzC,kBAAkB,EAAE,KAAK;gBACzB,iBAAiB,EAAE,MAAM;aAC1B,CAAA;YACD,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;YAC7C,KAAK,GAAG,IAAI,CAAA;YACZ,MAAM,IAAI,CAAA;QACZ,CAAC;QACD,OAAO,KAAK,EAAE,CAAC;YACb,YAAY,EAAE,EAAE,CAAA;YAChB,YAAY,GAAG,SAAS,CAAA;YACxB,KAAK,GAAG,SAAS,CAAA;YACjB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,gEAAgE,CAAC,CAAA;YACxF,OAAM;QACR,CAAC;QACD,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACvB,MAAM,YAAY,EAAE,CAAA;YACpB,kBAAkB,GAAG,SAAS,CAAA;YAC9B,OAAM;QACR,CAAC;QACD,kBAAkB,GAAG,WAAW,CAAA;QAChC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,wCAAwC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAA;IACxE,CAAC,CAAA;IAED,MAAM,aAAa,GAAG,GAAG,EAAE;QACzB,UAAU,IAAI,CAAC,CAAA;QACf,MAAM,GAAG,GAAG,UAAU,CAAA;QACtB,UAAU,GAAG,UAAU;aACpB,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;aACxB,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;YACtB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAA;QACtD,CAAC,CAAC,CAAA;QACJ,OAAO,UAAU,CAAA;IACnB,CAAC,CAAA;IAED,MAAM,mBAAmB,GAAG,GAAG,EAAE;QAC/B,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,CAAC,YAAY,CAAC,CAAA;QAC5B,CAAC;QACD,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,YAAY,GAAG,SAAS,CAAA;YACxB,KAAK,aAAa,EAAE,CAAA;QACtB,CAAC,EAAE,EAAE,CAAC,CAAA;IACR,CAAC,CAAA;IAED,GAAG,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE;QAClC,OAAO,GAAG,GAAG,EAAE,CAAC,wBAAwB,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC,CAAA;QACzF,GAAG,CAAC,EAAE,CAAC,2BAA2B,EAAE,CAAC,SAAS,EAAE,EAAE;YAChD,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,kBAAkB,EAAE,CAAC;gBAC7C,mBAAmB,EAAE,CAAA;YACvB,CAAC;QACH,CAAC,CAAC,CAAA;QACF,sEAAsE;QACtE,yEAAyE;QACzE,oCAAoC;QACpC,KAAK,aAAa,EAAE,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE;QACpB,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,CAAC,YAAY,CAAC,CAAA;YAC1B,YAAY,GAAG,SAAS,CAAA;QAC1B,CAAC;QACD,KAAK,YAAY,EAAE,CAAA;IACrB,CAAC,EAAE,qBAAqB,CAAC,CAAA;IACzB,MAAM,aAAa,EAAE,CAAA;AACvB,CAAC"}
|