@yanhaidao/wecom 2.4.160 → 2.5.110
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/index.js +68 -0
- package/dist/src/accounts.js +20 -0
- package/dist/src/agent/handler.js +895 -0
- package/dist/src/agent/index.js +5 -0
- package/dist/src/app/account-runtime.js +216 -0
- package/dist/src/app/bootstrap.js +19 -0
- package/dist/src/app/index.js +118 -0
- package/dist/src/capability/agent/delivery-service.js +63 -0
- package/dist/src/capability/agent/fallback-policy.js +6 -0
- package/dist/src/capability/agent/ingress-service.js +33 -0
- package/dist/src/capability/agent/upstream-delivery-service.js +71 -0
- package/dist/src/capability/bot/dispatch-config.js +45 -0
- package/dist/src/capability/bot/fallback-delivery.js +147 -0
- package/dist/src/capability/bot/local-path-delivery.js +178 -0
- package/dist/src/capability/bot/sandbox-media.js +138 -0
- package/dist/src/capability/bot/service.js +49 -0
- package/dist/src/capability/bot/stream-delivery.js +321 -0
- package/dist/src/capability/bot/stream-finalizer.js +81 -0
- package/dist/src/capability/bot/stream-orchestrator.js +318 -0
- package/dist/src/capability/bot/types.js +1 -0
- package/{src/capability/calendar/client.ts → dist/src/capability/calendar/client.js} +118 -241
- package/{src/capability/calendar/schema.ts → dist/src/capability/calendar/schema.js} +0 -38
- package/dist/src/capability/calendar/tool.js +365 -0
- package/dist/src/capability/calendar/types.js +12 -0
- package/{src/capability/doc/client.ts → dist/src/capability/doc/client.js} +370 -605
- package/{src/capability/doc/schema.ts → dist/src/capability/doc/schema.js} +345 -394
- package/dist/src/capability/doc/tool.js +1556 -0
- package/dist/src/capability/doc/types.js +113 -0
- package/dist/src/capability/mcp/index.js +3 -0
- package/dist/src/capability/mcp/schema.js +102 -0
- package/dist/src/capability/mcp/tool.js +146 -0
- package/dist/src/capability/mcp/transport.js +293 -0
- package/dist/src/channel.js +224 -0
- package/dist/src/config/accounts.js +236 -0
- package/dist/src/config/derived-paths.js +31 -0
- package/dist/src/config/index.js +7 -0
- package/dist/src/config/media.js +110 -0
- package/dist/src/config/network.js +32 -0
- package/dist/src/config/routing.js +20 -0
- package/dist/src/config/runtime-config.js +25 -0
- package/dist/src/config/schema.js +4 -0
- package/{src/config-schema.ts → dist/src/config-schema.js} +1 -1
- package/dist/src/context-store.js +219 -0
- package/{src/crypto/aes.ts → dist/src/crypto/aes.js} +11 -28
- package/dist/src/crypto/index.js +9 -0
- package/{src/crypto/signature.ts → dist/src/crypto/signature.js} +3 -18
- package/{src/crypto/xml.ts → dist/src/crypto/xml.js} +3 -11
- package/dist/src/crypto.js +145 -0
- package/dist/src/domain/models.js +1 -0
- package/dist/src/domain/policies.js +32 -0
- package/{src/dynamic-agent.ts → dist/src/dynamic-agent.js} +36 -73
- package/dist/src/gateway-monitor.js +139 -0
- package/dist/src/http.js +114 -0
- package/{src/media.ts → dist/src/media.js} +21 -40
- package/dist/src/monitor/limits.js +7 -0
- package/dist/src/monitor/state.js +28 -0
- package/dist/src/monitor.js +84 -0
- package/dist/src/observability/audit-log.js +30 -0
- package/dist/src/observability/legacy-operational-event-store.js +22 -0
- package/dist/src/observability/raw-envelope-log.js +24 -0
- package/dist/src/observability/status-registry.js +9 -0
- package/dist/src/observability/transport-session-view.js +14 -0
- package/dist/src/onboarding.js +546 -0
- package/dist/src/outbound.js +557 -0
- package/dist/src/runtime/dispatcher.js +57 -0
- package/{src/runtime/index.ts → dist/src/runtime/index.js} +0 -1
- package/dist/src/runtime/outbound-intent.js +1 -0
- package/dist/src/runtime/reply-orchestrator.js +38 -0
- package/dist/src/runtime/routing-bridge.js +26 -0
- package/dist/src/runtime/session-manager.js +112 -0
- package/dist/src/runtime/source-registry.js +174 -0
- package/dist/src/runtime.js +1 -0
- package/dist/src/shared/command-auth.js +57 -0
- package/{src/shared/index.ts → dist/src/shared/index.js} +0 -1
- package/dist/src/shared/media-asset.js +65 -0
- package/dist/src/shared/media-service.js +59 -0
- package/dist/src/shared/media-types.js +1 -0
- package/{src/shared/xml-parser.ts → dist/src/shared/xml-parser.js} +72 -63
- package/dist/src/store/active-reply-store.js +41 -0
- package/dist/src/store/interfaces.js +1 -0
- package/dist/src/store/memory-store.js +33 -0
- package/dist/src/store/stream-batch-store.js +319 -0
- package/{src/target.ts → dist/src/target.js} +15 -48
- package/dist/src/transport/agent-api/client.js +168 -0
- package/dist/src/transport/agent-api/core.js +337 -0
- package/dist/src/transport/agent-api/delivery.js +28 -0
- package/dist/src/transport/agent-api/media-upload.js +4 -0
- package/dist/src/transport/agent-api/reply.js +24 -0
- package/dist/src/transport/agent-api/upstream-delivery.js +30 -0
- package/dist/src/transport/agent-api/upstream-media-upload.js +46 -0
- package/dist/src/transport/agent-api/upstream-reply.js +26 -0
- package/dist/src/transport/agent-callback/http-handler.js +30 -0
- package/dist/src/transport/agent-callback/inbound.js +4 -0
- package/dist/src/transport/agent-callback/reply.js +8 -0
- package/dist/src/transport/agent-callback/request-handler.js +189 -0
- package/dist/src/transport/agent-callback/session.js +15 -0
- package/dist/src/transport/bot-webhook/active-reply.js +27 -0
- package/dist/src/transport/bot-webhook/http-handler.js +31 -0
- package/dist/src/transport/bot-webhook/inbound-normalizer.js +496 -0
- package/dist/src/transport/bot-webhook/inbound.js +4 -0
- package/dist/src/transport/bot-webhook/message-shape.js +98 -0
- package/dist/src/transport/bot-webhook/protocol.js +124 -0
- package/dist/src/transport/bot-webhook/reply.js +9 -0
- package/dist/src/transport/bot-webhook/request-handler.js +285 -0
- package/dist/src/transport/bot-webhook/session.js +15 -0
- package/dist/src/transport/bot-ws/inbound.js +147 -0
- package/dist/src/transport/bot-ws/media.js +236 -0
- package/dist/src/transport/bot-ws/reply.js +310 -0
- package/dist/src/transport/bot-ws/sdk-adapter.js +257 -0
- package/dist/src/transport/bot-ws/session.js +15 -0
- package/dist/src/transport/http/common.js +78 -0
- package/dist/src/transport/http/registry.js +71 -0
- package/dist/src/transport/http/request-handler.js +51 -0
- package/{src/transport/index.ts → dist/src/transport/index.js} +2 -10
- package/dist/src/types/account.js +1 -0
- package/dist/src/types/config.js +1 -0
- package/dist/src/types/constants.js +28 -0
- package/dist/src/types/events.js +1 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/types/legacy-stream.js +1 -0
- package/dist/src/types/message.js +5 -0
- package/dist/src/types/runtime-context.js +1 -0
- package/dist/src/types/runtime.js +1 -0
- package/dist/src/types.js +1 -0
- package/dist/src/upstream/index.js +111 -0
- package/dist/src/wecom_msg_adapter/markdown_adapter.js +280 -0
- package/openclaw.plugin.json +15 -0
- package/package.json +18 -1
- package/.github/workflows/release.yml +0 -143
- package/GOVERNANCE.md +0 -26
- package/SKILLS_CAL.md +0 -895
- package/SKILLS_DOC.md +0 -2288
- package/UPSTREAM_CONFIG.md +0 -170
- package/UPSTREAM_PLAN.md +0 -175
- package/assets/01.bot-add.png +0 -0
- package/assets/01.bot-setp2.png +0 -0
- package/assets/01.image.jpg +0 -0
- package/assets/02.agent.add.png +0 -0
- package/assets/02.agent.api-set.png +0 -0
- package/assets/02.image.jpg +0 -0
- package/assets/03.agent.page.png +0 -0
- package/assets/03.bot.page.png +0 -0
- package/assets/link-me.jpg +0 -0
- package/assets/register.png +0 -0
- package/changelog/v2.2.28.md +0 -70
- package/changelog/v2.3.10.md +0 -17
- package/changelog/v2.3.11.md +0 -19
- package/changelog/v2.3.12.md +0 -25
- package/changelog/v2.3.13.md +0 -19
- package/changelog/v2.3.14.md +0 -48
- package/changelog/v2.3.15.md +0 -15
- package/changelog/v2.3.16.md +0 -11
- package/changelog/v2.3.18.md +0 -22
- package/changelog/v2.3.19.md +0 -73
- package/changelog/v2.3.2.md +0 -28
- package/changelog/v2.3.26.md +0 -21
- package/changelog/v2.3.27.md +0 -33
- package/changelog/v2.3.4.md +0 -20
- package/changelog/v2.3.9.md +0 -22
- package/changelog/v2.4.12.md +0 -37
- package/changelog/v2.4.16.md +0 -19
- package/compat-single-account.md +0 -148
- package/index.test.ts +0 -38
- package/scripts/test-proxy.ts +0 -70
- package/src/accounts.ts +0 -34
- package/src/agent/api-client.upload.test.ts +0 -109
- package/src/agent/handler.event-filter.test.ts +0 -100
- package/src/agent/handler.ts +0 -1105
- package/src/agent/index.ts +0 -12
- package/src/app/account-runtime.ts +0 -276
- package/src/app/bootstrap.ts +0 -29
- package/src/app/index.ts +0 -192
- package/src/capability/agent/delivery-service.ts +0 -87
- package/src/capability/agent/fallback-policy.ts +0 -13
- package/src/capability/agent/ingress-service.ts +0 -38
- package/src/capability/agent/upstream-delivery-service.ts +0 -96
- package/src/capability/bot/dispatch-config.ts +0 -47
- package/src/capability/bot/fallback-delivery.ts +0 -178
- package/src/capability/bot/local-path-delivery.ts +0 -215
- package/src/capability/bot/sandbox-media.test.ts +0 -221
- package/src/capability/bot/sandbox-media.ts +0 -176
- package/src/capability/bot/service.ts +0 -56
- package/src/capability/bot/stream-delivery.ts +0 -379
- package/src/capability/bot/stream-finalizer.ts +0 -120
- package/src/capability/bot/stream-orchestrator.ts +0 -371
- package/src/capability/bot/types.ts +0 -8
- package/src/capability/calendar/SKILLS_CHECKLIST.md +0 -251
- package/src/capability/calendar/tool.ts +0 -417
- package/src/capability/calendar/types.ts +0 -309
- package/src/capability/doc/tool.ts +0 -1629
- package/src/capability/doc/types.ts +0 -792
- package/src/capability/mcp/index.ts +0 -10
- package/src/capability/mcp/schema.ts +0 -107
- package/src/capability/mcp/tool.ts +0 -174
- package/src/capability/mcp/transport.ts +0 -394
- package/src/channel.config.test.ts +0 -147
- package/src/channel.lifecycle.test.ts +0 -255
- package/src/channel.meta.test.ts +0 -26
- package/src/channel.ts +0 -256
- package/src/config/accounts.resolve.test.ts +0 -75
- package/src/config/accounts.ts +0 -296
- package/src/config/derived-paths.test.ts +0 -111
- package/src/config/derived-paths.ts +0 -41
- package/src/config/index.ts +0 -26
- package/src/config/media.test.ts +0 -113
- package/src/config/media.ts +0 -139
- package/src/config/network.ts +0 -53
- package/src/config/routing.test.ts +0 -88
- package/src/config/routing.ts +0 -26
- package/src/config/runtime-config.ts +0 -46
- package/src/config/schema.ts +0 -90
- package/src/context-store.ts +0 -297
- package/src/crypto/index.ts +0 -24
- package/src/crypto.test.ts +0 -32
- package/src/crypto.ts +0 -176
- package/src/domain/models.ts +0 -7
- package/src/domain/policies.ts +0 -36
- package/src/dynamic-agent.account-scope.test.ts +0 -17
- package/src/gateway-monitor.ts +0 -181
- package/src/http.ts +0 -145
- package/src/media.test.ts +0 -82
- package/src/monitor/limits.ts +0 -7
- package/src/monitor/state.queue.test.ts +0 -185
- package/src/monitor/state.ts +0 -34
- package/src/monitor.active.test.ts +0 -245
- package/src/monitor.inbound-filter.test.ts +0 -63
- package/src/monitor.integration.test.ts +0 -208
- package/src/monitor.ts +0 -121
- package/src/monitor.webhook.test.ts +0 -774
- package/src/observability/audit-log.ts +0 -48
- package/src/observability/legacy-operational-event-store.ts +0 -36
- package/src/observability/raw-envelope-log.ts +0 -28
- package/src/observability/status-registry.ts +0 -13
- package/src/observability/transport-session-view.ts +0 -14
- package/src/onboarding.test.ts +0 -336
- package/src/onboarding.ts +0 -704
- package/src/outbound.test.ts +0 -1271
- package/src/outbound.ts +0 -746
- package/src/runtime/dispatcher.ts +0 -71
- package/src/runtime/outbound-intent.ts +0 -4
- package/src/runtime/reply-orchestrator.test.ts +0 -71
- package/src/runtime/reply-orchestrator.ts +0 -67
- package/src/runtime/routing-bridge.test.ts +0 -115
- package/src/runtime/routing-bridge.ts +0 -44
- package/src/runtime/session-manager.test.ts +0 -174
- package/src/runtime/session-manager.ts +0 -139
- package/src/runtime/source-registry.ts +0 -249
- package/src/runtime.ts +0 -14
- package/src/shared/command-auth.ts +0 -87
- package/src/shared/media-asset.ts +0 -78
- package/src/shared/media-service.test.ts +0 -111
- package/src/shared/media-service.ts +0 -84
- package/src/shared/media-types.ts +0 -5
- package/src/shared/xml-parser.test.ts +0 -50
- package/src/store/active-reply-store.ts +0 -42
- package/src/store/interfaces.ts +0 -11
- package/src/store/memory-store.ts +0 -43
- package/src/store/stream-batch-store.ts +0 -350
- package/src/transport/agent-api/client.ts +0 -277
- package/src/transport/agent-api/core.ts +0 -463
- package/src/transport/agent-api/delivery.ts +0 -41
- package/src/transport/agent-api/media-upload.ts +0 -11
- package/src/transport/agent-api/reply.ts +0 -39
- package/src/transport/agent-api/upstream-delivery.ts +0 -45
- package/src/transport/agent-api/upstream-media-upload.ts +0 -70
- package/src/transport/agent-api/upstream-reply.ts +0 -43
- package/src/transport/agent-callback/http-handler.ts +0 -47
- package/src/transport/agent-callback/inbound.ts +0 -5
- package/src/transport/agent-callback/reply.ts +0 -13
- package/src/transport/agent-callback/request-handler.ts +0 -244
- package/src/transport/agent-callback/session.ts +0 -23
- package/src/transport/bot-webhook/active-reply.ts +0 -39
- package/src/transport/bot-webhook/http-handler.ts +0 -48
- package/src/transport/bot-webhook/inbound-normalizer.test.ts +0 -433
- package/src/transport/bot-webhook/inbound-normalizer.ts +0 -558
- package/src/transport/bot-webhook/inbound.ts +0 -5
- package/src/transport/bot-webhook/message-shape.ts +0 -92
- package/src/transport/bot-webhook/protocol.ts +0 -148
- package/src/transport/bot-webhook/reply.ts +0 -15
- package/src/transport/bot-webhook/request-handler.ts +0 -394
- package/src/transport/bot-webhook/session.ts +0 -23
- package/src/transport/bot-ws/inbound.test.ts +0 -290
- package/src/transport/bot-ws/inbound.ts +0 -163
- package/src/transport/bot-ws/media.test.ts +0 -44
- package/src/transport/bot-ws/media.ts +0 -321
- package/src/transport/bot-ws/reply.test.ts +0 -450
- package/src/transport/bot-ws/reply.ts +0 -365
- package/src/transport/bot-ws/sdk-adapter.test.ts +0 -187
- package/src/transport/bot-ws/sdk-adapter.ts +0 -314
- package/src/transport/bot-ws/session.ts +0 -28
- package/src/transport/http/common.ts +0 -109
- package/src/transport/http/registry.ts +0 -92
- package/src/transport/http/request-handler.ts +0 -84
- package/src/types/account.ts +0 -70
- package/src/types/config.ts +0 -114
- package/src/types/constants.ts +0 -31
- package/src/types/events.ts +0 -21
- package/src/types/global.d.ts +0 -9
- package/src/types/index.ts +0 -17
- package/src/types/legacy-stream.ts +0 -50
- package/src/types/message.ts +0 -189
- package/src/types/runtime-context.ts +0 -28
- package/src/types/runtime.ts +0 -165
- package/src/types.ts +0 -41
- package/src/upstream/index.ts +0 -150
- package/src/upstream.test.ts +0 -84
- package/src/wecom_msg_adapter/markdown_adapter.ts +0 -331
- package/tsconfig.json +0 -22
- package/vitest.config.ts +0 -26
- /package/{src/capability/agent/index.ts → dist/src/capability/agent/index.js} +0 -0
- /package/{src/capability/bot/index.ts → dist/src/capability/bot/index.js} +0 -0
- /package/{src/capability/calendar/index.ts → dist/src/capability/calendar/index.js} +0 -0
- /package/{src/capability/index.ts → dist/src/capability/index.js} +0 -0
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export { createWeComMcpToolFactory } from "./tool.js";
|
|
2
|
-
export {
|
|
3
|
-
clearWecomMcpAccountCache,
|
|
4
|
-
clearWecomMcpCategoryCache,
|
|
5
|
-
McpHttpError,
|
|
6
|
-
McpRpcError,
|
|
7
|
-
sendJsonRpc,
|
|
8
|
-
type McpToolInfo,
|
|
9
|
-
} from "./transport.js";
|
|
10
|
-
export { cleanSchemaForGemini } from "./schema.js";
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
const GEMINI_UNSUPPORTED_KEYWORDS = new Set([
|
|
2
|
-
"patternProperties",
|
|
3
|
-
"additionalProperties",
|
|
4
|
-
"$schema",
|
|
5
|
-
"$id",
|
|
6
|
-
"$ref",
|
|
7
|
-
"$defs",
|
|
8
|
-
"definitions",
|
|
9
|
-
"examples",
|
|
10
|
-
"minLength",
|
|
11
|
-
"maxLength",
|
|
12
|
-
"minimum",
|
|
13
|
-
"maximum",
|
|
14
|
-
"multipleOf",
|
|
15
|
-
"pattern",
|
|
16
|
-
"format",
|
|
17
|
-
"minItems",
|
|
18
|
-
"maxItems",
|
|
19
|
-
"uniqueItems",
|
|
20
|
-
"minProperties",
|
|
21
|
-
"maxProperties",
|
|
22
|
-
]);
|
|
23
|
-
|
|
24
|
-
export function cleanSchemaForGemini(schema: unknown): unknown {
|
|
25
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
26
|
-
if (Array.isArray(schema)) return schema.map(cleanSchemaForGemini);
|
|
27
|
-
|
|
28
|
-
const obj = schema as Record<string, unknown>;
|
|
29
|
-
const defs: Record<string, unknown> = {
|
|
30
|
-
...(obj.$defs && typeof obj.$defs === "object" ? (obj.$defs as Record<string, unknown>) : {}),
|
|
31
|
-
...(obj.definitions && typeof obj.definitions === "object"
|
|
32
|
-
? (obj.definitions as Record<string, unknown>)
|
|
33
|
-
: {}),
|
|
34
|
-
};
|
|
35
|
-
return cleanWithDefs(obj, defs, new Set());
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function cleanWithDefs(
|
|
39
|
-
schema: unknown,
|
|
40
|
-
defs: Record<string, unknown>,
|
|
41
|
-
refStack: Set<string>,
|
|
42
|
-
): unknown {
|
|
43
|
-
if (!schema || typeof schema !== "object") return schema;
|
|
44
|
-
if (Array.isArray(schema)) return schema.map((item) => cleanWithDefs(item, defs, refStack));
|
|
45
|
-
|
|
46
|
-
const obj = schema as Record<string, unknown>;
|
|
47
|
-
if (obj.$defs && typeof obj.$defs === "object") {
|
|
48
|
-
Object.assign(defs, obj.$defs as Record<string, unknown>);
|
|
49
|
-
}
|
|
50
|
-
if (obj.definitions && typeof obj.definitions === "object") {
|
|
51
|
-
Object.assign(defs, obj.definitions as Record<string, unknown>);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (typeof obj.$ref === "string") {
|
|
55
|
-
const ref = obj.$ref;
|
|
56
|
-
if (refStack.has(ref)) return {};
|
|
57
|
-
const match = ref.match(/^#\/(?:\$defs|definitions)\/(.+)$/);
|
|
58
|
-
if (match?.[1] && defs[match[1]]) {
|
|
59
|
-
const nextStack = new Set(refStack);
|
|
60
|
-
nextStack.add(ref);
|
|
61
|
-
return cleanWithDefs(defs[match[1]], defs, nextStack);
|
|
62
|
-
}
|
|
63
|
-
return {};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const cleaned: Record<string, unknown> = {};
|
|
67
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
68
|
-
if (GEMINI_UNSUPPORTED_KEYWORDS.has(key)) continue;
|
|
69
|
-
if (key === "const") {
|
|
70
|
-
cleaned.enum = [value];
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
if (key === "properties" && value && typeof value === "object" && !Array.isArray(value)) {
|
|
74
|
-
cleaned[key] = Object.fromEntries(
|
|
75
|
-
Object.entries(value as Record<string, unknown>).map(([propKey, propValue]) => [
|
|
76
|
-
propKey,
|
|
77
|
-
cleanWithDefs(propValue, defs, refStack),
|
|
78
|
-
]),
|
|
79
|
-
);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (key === "items" && value) {
|
|
83
|
-
cleaned[key] = Array.isArray(value)
|
|
84
|
-
? value.map((item) => cleanWithDefs(item, defs, refStack))
|
|
85
|
-
: cleanWithDefs(value, defs, refStack);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if ((key === "anyOf" || key === "oneOf" || key === "allOf") && Array.isArray(value)) {
|
|
89
|
-
const nonNull = value.filter((variant) => {
|
|
90
|
-
if (!variant || typeof variant !== "object") return true;
|
|
91
|
-
return (variant as Record<string, unknown>).type !== "null";
|
|
92
|
-
});
|
|
93
|
-
if (nonNull.length === 1) {
|
|
94
|
-
const single = cleanWithDefs(nonNull[0], defs, refStack);
|
|
95
|
-
if (single && typeof single === "object" && !Array.isArray(single)) {
|
|
96
|
-
Object.assign(cleaned, single as Record<string, unknown>);
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
cleaned[key] = nonNull.map((variant) => cleanWithDefs(variant, defs, refStack));
|
|
100
|
-
}
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
cleaned[key] = value;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return cleaned;
|
|
107
|
-
}
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
OpenClawPluginToolContext,
|
|
3
|
-
OpenClawPluginToolFactory,
|
|
4
|
-
} from "openclaw/plugin-sdk/core";
|
|
5
|
-
import { resolveWecomSourceSnapshot } from "../../runtime/source-registry.js";
|
|
6
|
-
import { cleanSchemaForGemini } from "./schema.js";
|
|
7
|
-
import { clearWecomMcpCategoryCache, sendJsonRpc, type McpToolInfo } from "./transport.js";
|
|
8
|
-
|
|
9
|
-
type WecomMcpParams = {
|
|
10
|
-
action: "list" | "call";
|
|
11
|
-
category: string;
|
|
12
|
-
method?: string;
|
|
13
|
-
args?: string | Record<string, unknown>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const BIZ_CACHE_CLEAR_ERROR_CODES = new Set([850002]);
|
|
17
|
-
|
|
18
|
-
function textResult<TDetails>(data: TDetails) {
|
|
19
|
-
return {
|
|
20
|
-
content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
|
|
21
|
-
details: data,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function errorResult(error: unknown) {
|
|
26
|
-
if (error && typeof error === "object" && "errcode" in error) {
|
|
27
|
-
const errcode = Number((error as { errcode?: number }).errcode ?? 0);
|
|
28
|
-
const errmsg = String((error as { errmsg?: string }).errmsg ?? `错误码: ${errcode}`);
|
|
29
|
-
return textResult({ error: errmsg, errcode });
|
|
30
|
-
}
|
|
31
|
-
return textResult({
|
|
32
|
-
error: error instanceof Error ? error.message : String(error),
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function parseArgs(args: string | Record<string, unknown> | undefined): Record<string, unknown> {
|
|
37
|
-
if (!args) return {};
|
|
38
|
-
if (typeof args === "object") return args;
|
|
39
|
-
try {
|
|
40
|
-
return JSON.parse(args) as Record<string, unknown>;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
const detail = error instanceof SyntaxError ? error.message : String(error);
|
|
43
|
-
throw new Error(`args 不是合法的 JSON: ${args} (${detail})`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function extractToolAccountId(ctx: OpenClawPluginToolContext): string | undefined {
|
|
48
|
-
const explicit = String((ctx as { accountId?: string }).accountId ?? "").trim();
|
|
49
|
-
if (explicit) return explicit;
|
|
50
|
-
const agentAccountId = String(ctx.agentAccountId ?? "").trim();
|
|
51
|
-
return agentAccountId || undefined;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function handleList(accountId: string, category: string): Promise<unknown> {
|
|
55
|
-
const result = (await sendJsonRpc(accountId, category, "tools/list")) as
|
|
56
|
-
| { tools?: McpToolInfo[] }
|
|
57
|
-
| undefined;
|
|
58
|
-
const tools = result?.tools ?? [];
|
|
59
|
-
return {
|
|
60
|
-
accountId,
|
|
61
|
-
category,
|
|
62
|
-
count: tools.length,
|
|
63
|
-
tools: tools.map((tool) => ({
|
|
64
|
-
name: tool.name,
|
|
65
|
-
description: tool.description ?? "",
|
|
66
|
-
inputSchema: tool.inputSchema ? cleanSchemaForGemini(tool.inputSchema) : undefined,
|
|
67
|
-
})),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function checkBizErrorAndClearCache(result: unknown, accountId: string, category: string): void {
|
|
72
|
-
if (!result || typeof result !== "object") return;
|
|
73
|
-
const content = (result as { content?: Array<{ type: string; text?: string }> }).content;
|
|
74
|
-
if (!Array.isArray(content)) return;
|
|
75
|
-
for (const item of content) {
|
|
76
|
-
if (item.type !== "text" || !item.text) continue;
|
|
77
|
-
try {
|
|
78
|
-
const parsed = JSON.parse(item.text) as { errcode?: number };
|
|
79
|
-
if (typeof parsed.errcode === "number" && BIZ_CACHE_CLEAR_ERROR_CODES.has(parsed.errcode)) {
|
|
80
|
-
clearWecomMcpCategoryCache(accountId, category);
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
} catch {
|
|
84
|
-
// Ignore non-JSON content.
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async function handleCall(
|
|
90
|
-
accountId: string,
|
|
91
|
-
category: string,
|
|
92
|
-
method: string,
|
|
93
|
-
args: Record<string, unknown>,
|
|
94
|
-
): Promise<unknown> {
|
|
95
|
-
const result = await sendJsonRpc(accountId, category, "tools/call", {
|
|
96
|
-
name: method,
|
|
97
|
-
arguments: args,
|
|
98
|
-
});
|
|
99
|
-
checkBizErrorAndClearCache(result, accountId, category);
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function createWeComMcpToolFactory(): OpenClawPluginToolFactory {
|
|
104
|
-
return (toolContext: OpenClawPluginToolContext) => {
|
|
105
|
-
if (toolContext.messageChannel !== "wecom") {
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
const accountId = extractToolAccountId(toolContext);
|
|
109
|
-
const source = resolveWecomSourceSnapshot({
|
|
110
|
-
accountId,
|
|
111
|
-
sessionKey: toolContext.sessionKey,
|
|
112
|
-
sessionId: toolContext.sessionId,
|
|
113
|
-
});
|
|
114
|
-
if (!source || source.source !== "bot-ws") {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
name: "wecom_mcp",
|
|
120
|
-
label: "WeCom MCP",
|
|
121
|
-
description:
|
|
122
|
-
"企业微信 Bot WS MCP 工具。仅在 WeCom Bot WS 会话中可用,用于列出和调用企业微信 MCP 能力。",
|
|
123
|
-
parameters: {
|
|
124
|
-
type: "object" as const,
|
|
125
|
-
properties: {
|
|
126
|
-
action: {
|
|
127
|
-
type: "string",
|
|
128
|
-
enum: ["list", "call"],
|
|
129
|
-
description: "操作类型:list 或 call",
|
|
130
|
-
},
|
|
131
|
-
category: {
|
|
132
|
-
type: "string",
|
|
133
|
-
description: "MCP 品类,如 contact、todo、meeting、doc",
|
|
134
|
-
},
|
|
135
|
-
method: {
|
|
136
|
-
type: "string",
|
|
137
|
-
description: "action=call 时要调用的工具方法名",
|
|
138
|
-
},
|
|
139
|
-
args: {
|
|
140
|
-
type: "string",
|
|
141
|
-
description: "action=call 时传入的 JSON 字符串参数,默认 {}",
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
required: ["action", "category"],
|
|
145
|
-
},
|
|
146
|
-
async execute(_toolCallId: string, rawParams: unknown) {
|
|
147
|
-
try {
|
|
148
|
-
const params = rawParams as WecomMcpParams;
|
|
149
|
-
const effectiveAccountId = extractToolAccountId(toolContext);
|
|
150
|
-
if (!effectiveAccountId) {
|
|
151
|
-
throw new Error("当前会话缺少 WeCom accountId,无法调用 wecom_mcp。");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (params.action === "list") {
|
|
155
|
-
return textResult(await handleList(effectiveAccountId, params.category));
|
|
156
|
-
}
|
|
157
|
-
if (!params.method) {
|
|
158
|
-
return textResult({ error: "action=call 时必须提供 method" });
|
|
159
|
-
}
|
|
160
|
-
return textResult(
|
|
161
|
-
await handleCall(
|
|
162
|
-
effectiveAccountId,
|
|
163
|
-
params.category,
|
|
164
|
-
params.method,
|
|
165
|
-
parseArgs(params.args),
|
|
166
|
-
),
|
|
167
|
-
);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
return errorResult(error);
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
};
|
|
174
|
-
}
|
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
import { generateReqId } from "@wecom/aibot-node-sdk";
|
|
2
|
-
import { getBotWsPushHandle } from "../../runtime.js";
|
|
3
|
-
|
|
4
|
-
const HTTP_REQUEST_TIMEOUT_MS = 30_000;
|
|
5
|
-
const MCP_CONFIG_FETCH_TIMEOUT_MS = 15_000;
|
|
6
|
-
const MCP_GET_CONFIG_CMD = "aibot_get_mcp_config";
|
|
7
|
-
const MCP_PLUGIN_VERSION = "wecom-dual-plane";
|
|
8
|
-
const LOG_TAG = "[wecom-mcp]";
|
|
9
|
-
|
|
10
|
-
interface JsonRpcRequest {
|
|
11
|
-
jsonrpc: "2.0";
|
|
12
|
-
id?: string;
|
|
13
|
-
method: string;
|
|
14
|
-
params?: Record<string, unknown>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface JsonRpcResponse {
|
|
18
|
-
jsonrpc: "2.0";
|
|
19
|
-
id: number | string;
|
|
20
|
-
result?: unknown;
|
|
21
|
-
error?: {
|
|
22
|
-
code: number;
|
|
23
|
-
message: string;
|
|
24
|
-
data?: unknown;
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface McpSession {
|
|
29
|
-
sessionId: string | null;
|
|
30
|
-
initialized: boolean;
|
|
31
|
-
stateless: boolean;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const CACHE_CLEAR_ERROR_CODES = new Set([-32001, -32002, -32003]);
|
|
35
|
-
|
|
36
|
-
const mcpConfigCache = new Map<string, Record<string, unknown>>();
|
|
37
|
-
const mcpSessionCache = new Map<string, McpSession>();
|
|
38
|
-
const statelessKeys = new Set<string>();
|
|
39
|
-
const inflightInitRequests = new Map<string, Promise<McpSession>>();
|
|
40
|
-
|
|
41
|
-
function cacheKey(accountId: string, category: string): string {
|
|
42
|
-
return `${accountId}::${category}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, message: string): Promise<T> {
|
|
46
|
-
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
47
|
-
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
48
|
-
timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
49
|
-
});
|
|
50
|
-
return Promise.race([promise, timeoutPromise]).finally(() => {
|
|
51
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export class McpRpcError extends Error {
|
|
56
|
-
constructor(
|
|
57
|
-
public readonly code: number,
|
|
58
|
-
message: string,
|
|
59
|
-
public readonly data?: unknown,
|
|
60
|
-
) {
|
|
61
|
-
super(message);
|
|
62
|
-
this.name = "McpRpcError";
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class McpHttpError extends Error {
|
|
67
|
-
constructor(
|
|
68
|
-
public readonly statusCode: number,
|
|
69
|
-
message: string,
|
|
70
|
-
) {
|
|
71
|
-
super(message);
|
|
72
|
-
this.name = "McpHttpError";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function fetchMcpConfig(
|
|
77
|
-
accountId: string,
|
|
78
|
-
category: string,
|
|
79
|
-
): Promise<Record<string, unknown>> {
|
|
80
|
-
const handle = getBotWsPushHandle(accountId);
|
|
81
|
-
if (!handle?.isConnected()) {
|
|
82
|
-
throw new Error(`当前企微账号 MCP 服务未就绪:account=${accountId} 的 Bot WS 未连接。`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const response = await withTimeout(
|
|
86
|
-
handle.replyCommand({
|
|
87
|
-
cmd: MCP_GET_CONFIG_CMD,
|
|
88
|
-
body: {
|
|
89
|
-
biz_type: category,
|
|
90
|
-
plugin_version: MCP_PLUGIN_VERSION,
|
|
91
|
-
},
|
|
92
|
-
headers: {
|
|
93
|
-
req_id: generateReqId("mcp_config"),
|
|
94
|
-
},
|
|
95
|
-
}),
|
|
96
|
-
MCP_CONFIG_FETCH_TIMEOUT_MS,
|
|
97
|
-
`MCP config fetch timed out after ${MCP_CONFIG_FETCH_TIMEOUT_MS}ms`,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
const errcode = Number((response as { errcode?: number }).errcode ?? 0);
|
|
101
|
-
if (errcode !== 0) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
`MCP 配置请求失败: errcode=${String((response as { errcode?: number }).errcode)} errmsg=${String((response as { errmsg?: string }).errmsg ?? "unknown")}`,
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const body = (response as { body?: { url?: string } }).body;
|
|
108
|
-
if (!body?.url) {
|
|
109
|
-
throw new Error(`MCP 配置响应缺少 url 字段 (account=${accountId}, category=${category})`);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
console.log(`${LOG_TAG} config ready account=${accountId} category=${category} url=${body.url}`);
|
|
113
|
-
return body as Record<string, unknown>;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function getMcpUrl(accountId: string, category: string): Promise<string> {
|
|
117
|
-
const key = cacheKey(accountId, category);
|
|
118
|
-
const cached = mcpConfigCache.get(key);
|
|
119
|
-
if (cached?.url) {
|
|
120
|
-
return String(cached.url);
|
|
121
|
-
}
|
|
122
|
-
const body = await fetchMcpConfig(accountId, category);
|
|
123
|
-
mcpConfigCache.set(key, body);
|
|
124
|
-
return String(body.url);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async function sendRawJsonRpc(
|
|
128
|
-
url: string,
|
|
129
|
-
session: McpSession,
|
|
130
|
-
body: JsonRpcRequest,
|
|
131
|
-
): Promise<{ rpcResult: unknown; newSessionId: string | null }> {
|
|
132
|
-
const controller = new AbortController();
|
|
133
|
-
const timeoutId = setTimeout(() => controller.abort(), HTTP_REQUEST_TIMEOUT_MS);
|
|
134
|
-
try {
|
|
135
|
-
const headers: Record<string, string> = {
|
|
136
|
-
"Content-Type": "application/json",
|
|
137
|
-
Accept: "application/json, text/event-stream",
|
|
138
|
-
};
|
|
139
|
-
if (session.sessionId) {
|
|
140
|
-
headers["Mcp-Session-Id"] = session.sessionId;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const response = await fetch(url, {
|
|
144
|
-
method: "POST",
|
|
145
|
-
headers,
|
|
146
|
-
body: JSON.stringify(body),
|
|
147
|
-
signal: controller.signal,
|
|
148
|
-
});
|
|
149
|
-
const newSessionId = response.headers.get("mcp-session-id");
|
|
150
|
-
|
|
151
|
-
if (!response.ok) {
|
|
152
|
-
throw new McpHttpError(
|
|
153
|
-
response.status,
|
|
154
|
-
`MCP HTTP 请求失败: ${response.status} ${response.statusText}`,
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const contentLength = response.headers.get("content-length");
|
|
159
|
-
if (response.status === 204 || contentLength === "0") {
|
|
160
|
-
return { rpcResult: undefined, newSessionId };
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
164
|
-
if (contentType.includes("text/event-stream")) {
|
|
165
|
-
return {
|
|
166
|
-
rpcResult: await parseSseResponse(response),
|
|
167
|
-
newSessionId,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const text = await response.text();
|
|
172
|
-
if (!text.trim()) {
|
|
173
|
-
return { rpcResult: undefined, newSessionId };
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const rpc = JSON.parse(text) as JsonRpcResponse;
|
|
177
|
-
if (rpc.error) {
|
|
178
|
-
throw new McpRpcError(
|
|
179
|
-
rpc.error.code,
|
|
180
|
-
`MCP 调用错误 [${rpc.error.code}]: ${rpc.error.message}`,
|
|
181
|
-
rpc.error.data,
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
return { rpcResult: rpc.result, newSessionId };
|
|
185
|
-
} catch (error) {
|
|
186
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
187
|
-
throw new Error(`MCP 请求超时 (${HTTP_REQUEST_TIMEOUT_MS}ms)`);
|
|
188
|
-
}
|
|
189
|
-
throw error;
|
|
190
|
-
} finally {
|
|
191
|
-
clearTimeout(timeoutId);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async function initializeSession(
|
|
196
|
-
accountId: string,
|
|
197
|
-
category: string,
|
|
198
|
-
url: string,
|
|
199
|
-
): Promise<McpSession> {
|
|
200
|
-
const key = cacheKey(accountId, category);
|
|
201
|
-
const session: McpSession = { sessionId: null, initialized: false, stateless: false };
|
|
202
|
-
|
|
203
|
-
const initializeRequest: JsonRpcRequest = {
|
|
204
|
-
jsonrpc: "2.0",
|
|
205
|
-
id: generateReqId("mcp_init"),
|
|
206
|
-
method: "initialize",
|
|
207
|
-
params: {
|
|
208
|
-
protocolVersion: "2025-03-26",
|
|
209
|
-
capabilities: {},
|
|
210
|
-
clientInfo: { name: "wecom_mcp", version: MCP_PLUGIN_VERSION },
|
|
211
|
-
},
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
const initResult = await sendRawJsonRpc(url, session, initializeRequest);
|
|
215
|
-
if (initResult.newSessionId) {
|
|
216
|
-
session.sessionId = initResult.newSessionId;
|
|
217
|
-
}
|
|
218
|
-
if (!session.sessionId) {
|
|
219
|
-
session.stateless = true;
|
|
220
|
-
session.initialized = true;
|
|
221
|
-
statelessKeys.add(key);
|
|
222
|
-
mcpSessionCache.set(key, session);
|
|
223
|
-
return session;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const notifyRequest: JsonRpcRequest = {
|
|
227
|
-
jsonrpc: "2.0",
|
|
228
|
-
method: "notifications/initialized",
|
|
229
|
-
};
|
|
230
|
-
const notifyResult = await sendRawJsonRpc(url, session, notifyRequest);
|
|
231
|
-
if (notifyResult.newSessionId) {
|
|
232
|
-
session.sessionId = notifyResult.newSessionId;
|
|
233
|
-
}
|
|
234
|
-
session.initialized = true;
|
|
235
|
-
mcpSessionCache.set(key, session);
|
|
236
|
-
return session;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async function getOrCreateSession(
|
|
240
|
-
accountId: string,
|
|
241
|
-
category: string,
|
|
242
|
-
url: string,
|
|
243
|
-
): Promise<McpSession> {
|
|
244
|
-
const key = cacheKey(accountId, category);
|
|
245
|
-
if (statelessKeys.has(key)) {
|
|
246
|
-
const cached = mcpSessionCache.get(key);
|
|
247
|
-
if (cached) return cached;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const cached = mcpSessionCache.get(key);
|
|
251
|
-
if (cached?.initialized) {
|
|
252
|
-
return cached;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const inflight = inflightInitRequests.get(key);
|
|
256
|
-
if (inflight) {
|
|
257
|
-
return inflight;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const promise = initializeSession(accountId, category, url).finally(() => {
|
|
261
|
-
inflightInitRequests.delete(key);
|
|
262
|
-
});
|
|
263
|
-
inflightInitRequests.set(key, promise);
|
|
264
|
-
return promise;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
async function rebuildSession(
|
|
268
|
-
accountId: string,
|
|
269
|
-
category: string,
|
|
270
|
-
url: string,
|
|
271
|
-
): Promise<McpSession> {
|
|
272
|
-
const key = cacheKey(accountId, category);
|
|
273
|
-
const inflight = inflightInitRequests.get(key);
|
|
274
|
-
if (inflight) return inflight;
|
|
275
|
-
const promise = initializeSession(accountId, category, url).finally(() => {
|
|
276
|
-
inflightInitRequests.delete(key);
|
|
277
|
-
});
|
|
278
|
-
inflightInitRequests.set(key, promise);
|
|
279
|
-
return promise;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async function parseSseResponse(response: Response): Promise<unknown> {
|
|
283
|
-
const text = await response.text();
|
|
284
|
-
const lines = text.split("\n");
|
|
285
|
-
let currentParts: string[] = [];
|
|
286
|
-
let lastEventData = "";
|
|
287
|
-
|
|
288
|
-
for (const line of lines) {
|
|
289
|
-
if (line.startsWith("data: ")) {
|
|
290
|
-
currentParts.push(line.slice(6));
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
293
|
-
if (line.startsWith("data:")) {
|
|
294
|
-
currentParts.push(line.slice(5));
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
if (line.trim() === "" && currentParts.length > 0) {
|
|
298
|
-
lastEventData = currentParts.join("\n").trim();
|
|
299
|
-
currentParts = [];
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (currentParts.length > 0) {
|
|
303
|
-
lastEventData = currentParts.join("\n").trim();
|
|
304
|
-
}
|
|
305
|
-
if (!lastEventData) {
|
|
306
|
-
throw new Error("SSE 响应中未包含有效数据");
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const rpc = JSON.parse(lastEventData) as JsonRpcResponse;
|
|
310
|
-
if (rpc.error) {
|
|
311
|
-
throw new McpRpcError(
|
|
312
|
-
rpc.error.code,
|
|
313
|
-
`MCP 调用错误 [${rpc.error.code}]: ${rpc.error.message}`,
|
|
314
|
-
rpc.error.data,
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
return rpc.result;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function clearWecomMcpCategoryCache(accountId: string, category: string): void {
|
|
321
|
-
const key = cacheKey(accountId, category);
|
|
322
|
-
console.log(`${LOG_TAG} clear cache account=${accountId} category=${category}`);
|
|
323
|
-
mcpConfigCache.delete(key);
|
|
324
|
-
mcpSessionCache.delete(key);
|
|
325
|
-
statelessKeys.delete(key);
|
|
326
|
-
inflightInitRequests.delete(key);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
export function clearWecomMcpAccountCache(accountId: string): void {
|
|
330
|
-
const prefix = `${accountId}::`;
|
|
331
|
-
for (const key of [...mcpConfigCache.keys()]) {
|
|
332
|
-
if (key.startsWith(prefix)) mcpConfigCache.delete(key);
|
|
333
|
-
}
|
|
334
|
-
for (const key of [...mcpSessionCache.keys()]) {
|
|
335
|
-
if (key.startsWith(prefix)) mcpSessionCache.delete(key);
|
|
336
|
-
}
|
|
337
|
-
for (const key of [...statelessKeys]) {
|
|
338
|
-
if (key.startsWith(prefix)) statelessKeys.delete(key);
|
|
339
|
-
}
|
|
340
|
-
for (const key of [...inflightInitRequests.keys()]) {
|
|
341
|
-
if (key.startsWith(prefix)) inflightInitRequests.delete(key);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
export interface McpToolInfo {
|
|
346
|
-
name: string;
|
|
347
|
-
description?: string;
|
|
348
|
-
inputSchema?: Record<string, unknown>;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
export async function sendJsonRpc(
|
|
352
|
-
accountId: string,
|
|
353
|
-
category: string,
|
|
354
|
-
method: string,
|
|
355
|
-
params?: Record<string, unknown>,
|
|
356
|
-
): Promise<unknown> {
|
|
357
|
-
const url = await getMcpUrl(accountId, category);
|
|
358
|
-
const body: JsonRpcRequest = {
|
|
359
|
-
jsonrpc: "2.0",
|
|
360
|
-
id: generateReqId("mcp_rpc"),
|
|
361
|
-
method,
|
|
362
|
-
...(params !== undefined ? { params } : {}),
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
let session = await getOrCreateSession(accountId, category, url);
|
|
366
|
-
|
|
367
|
-
try {
|
|
368
|
-
const result = await sendRawJsonRpc(url, session, body);
|
|
369
|
-
if (result.newSessionId) {
|
|
370
|
-
session.sessionId = result.newSessionId;
|
|
371
|
-
}
|
|
372
|
-
return result.rpcResult;
|
|
373
|
-
} catch (error) {
|
|
374
|
-
if (error instanceof McpRpcError && CACHE_CLEAR_ERROR_CODES.has(error.code)) {
|
|
375
|
-
clearWecomMcpCategoryCache(accountId, category);
|
|
376
|
-
}
|
|
377
|
-
if (session.stateless) {
|
|
378
|
-
throw error;
|
|
379
|
-
}
|
|
380
|
-
if (error instanceof McpHttpError && error.statusCode === 404) {
|
|
381
|
-
mcpSessionCache.delete(cacheKey(accountId, category));
|
|
382
|
-
session = await rebuildSession(accountId, category, url);
|
|
383
|
-
const result = await sendRawJsonRpc(url, session, body);
|
|
384
|
-
if (result.newSessionId) {
|
|
385
|
-
session.sessionId = result.newSessionId;
|
|
386
|
-
}
|
|
387
|
-
return result.rpcResult;
|
|
388
|
-
}
|
|
389
|
-
console.error(
|
|
390
|
-
`${LOG_TAG} rpc failed account=${accountId} category=${category} method=${method} error=${error instanceof Error ? error.message : String(error)}`,
|
|
391
|
-
);
|
|
392
|
-
throw error;
|
|
393
|
-
}
|
|
394
|
-
}
|