@clowder-ai/connector-wecom-agent 0.0.0-stage → 0.1.0-alpha.7

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.
Files changed (58) hide show
  1. package/README.en.md +56 -0
  2. package/README.md +24 -2
  3. package/assets/icon.svg +6 -0
  4. package/dist/WeComAgentAdapter.d.ts +149 -0
  5. package/dist/WeComAgentAdapter.d.ts.map +1 -0
  6. package/dist/WeComAgentAdapter.js +525 -0
  7. package/dist/WeComAgentAdapter.js.map +1 -0
  8. package/dist/collect-media.d.ts +12 -0
  9. package/dist/collect-media.d.ts.map +1 -0
  10. package/dist/collect-media.js +27 -0
  11. package/dist/collect-media.js.map +1 -0
  12. package/dist/command-ingress.d.ts +10 -0
  13. package/dist/command-ingress.d.ts.map +1 -0
  14. package/dist/command-ingress.js +13 -0
  15. package/dist/command-ingress.js.map +1 -0
  16. package/dist/inbound-download.d.ts +13 -0
  17. package/dist/inbound-download.d.ts.map +1 -0
  18. package/dist/inbound-download.js +70 -0
  19. package/dist/inbound-download.js.map +1 -0
  20. package/dist/inbound-media-source.d.ts +25 -0
  21. package/dist/inbound-media-source.d.ts.map +1 -0
  22. package/dist/inbound-media-source.js +111 -0
  23. package/dist/inbound-media-source.js.map +1 -0
  24. package/dist/index.d.ts +4 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +2 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/media-notice.d.ts +6 -0
  29. package/dist/media-notice.d.ts.map +1 -0
  30. package/dist/media-notice.js +50 -0
  31. package/dist/media-notice.js.map +1 -0
  32. package/dist/plugin-entrypoint.d.ts +7 -0
  33. package/dist/plugin-entrypoint.d.ts.map +1 -0
  34. package/dist/plugin-entrypoint.js +292 -0
  35. package/dist/plugin-entrypoint.js.map +1 -0
  36. package/dist/reply-sender-map.d.ts +10 -0
  37. package/dist/reply-sender-map.d.ts.map +1 -0
  38. package/dist/reply-sender-map.js +123 -0
  39. package/dist/reply-sender-map.js.map +1 -0
  40. package/dist/rich-block-plaintext.d.ts +5 -0
  41. package/dist/rich-block-plaintext.d.ts.map +1 -0
  42. package/dist/rich-block-plaintext.js +45 -0
  43. package/dist/rich-block-plaintext.js.map +1 -0
  44. package/dist/runtime.d.ts +65 -0
  45. package/dist/runtime.d.ts.map +1 -0
  46. package/dist/runtime.js +84 -0
  47. package/dist/runtime.js.map +1 -0
  48. package/dist/thread-mappings.d.ts +46 -0
  49. package/dist/thread-mappings.d.ts.map +1 -0
  50. package/dist/thread-mappings.js +62 -0
  51. package/dist/thread-mappings.js.map +1 -0
  52. package/dist/types.d.ts +15 -0
  53. package/dist/types.d.ts.map +1 -0
  54. package/dist/types.js +2 -0
  55. package/dist/types.js.map +1 -0
  56. package/npm-shrinkwrap.json +215 -0
  57. package/package.json +55 -4
  58. package/plugin.yaml +83 -0
package/README.en.md ADDED
@@ -0,0 +1,56 @@
1
+ # WeCom Agent Connector
2
+
3
+ [Chinese](README.md) · English
4
+
5
+ `@clowder-ai/connector-wecom-agent` packages WeCom custom-application callback verification, AES-256-CBC
6
+ decryption, normalized text/media ingress, token refresh, message sending, media transfer, and byte-bounded
7
+ reply chunking for a Clowder AI Host.
8
+
9
+ ## Configuration
10
+
11
+ - `corpId` and `agentId`: WeCom enterprise and application identifiers.
12
+ - `agentSecret`: secret used only to obtain short-lived provider access tokens.
13
+ - `callbackToken`: secret used to authenticate callback signatures.
14
+ - `encodingAesKey`: secret used to decrypt callback bodies and URL-verification challenges.
15
+
16
+ The package does not read repository `.env` files or ambient Host configuration.
17
+
18
+ ## Security and authority
19
+
20
+ The Host owns installation, grants, configuration, secrets, webhook admission, connector bindings, wake
21
+ policy, delivery, and durable state. Callback bodies are accepted only after signature, decryption, and corp
22
+ identity verification. Ordinary message text, including `@`, cannot mint a binding or wake an Agent.
23
+
24
+ ## Lifecycle and recovery
25
+
26
+ Provider access tokens are short-lived process caches. Host webhook routing must drain before rollback so
27
+ the package and preserved Core callback handler never consume the same event concurrently. The package does
28
+ not persist callback bodies or credentials.
29
+
30
+ The Host-loadable builtin runtime is implemented in this package and uses only the existing authenticated
31
+ binding and declared configuration/secret surfaces. No package-local environment or persistence fallback is
32
+ provided.
33
+
34
+ Inbound provider media IDs remain in manifest-declared private plugin state. Public messages expose only a
35
+ `pmr_*` reference plus `sourceEventId`; the declared media source returns bounded chunks and settlement
36
+ deletes the private locator.
37
+
38
+ ## Supported messaging
39
+
40
+ The connector supports authenticated WeCom webhooks, messages and media. It does not provide audio/AI
41
+ services or custom UI extensions.
42
+
43
+ ## Commands and configuration
44
+
45
+ The builtin entrypoint intercepts `/where`, `/new`, `/threads`, `/use`, `/thread`, `/commands`, `/cats`,
46
+ `/status`, `/history`, `/unbind`, `/allow-group`, `/deny-group`, `/focus`, and `/ask` before ordinary ingress.
47
+ Commands reply through the provider and keep their audit quiet. Only an explicit body in `/thread` or
48
+ `/ask` requests Agent invocation; successful delivery is acknowledged only after the Host accepts it.
49
+ `/use` traverses the owner-visible live thread inventory beyond the first 50 rows, deduplicates IDs and
50
+ rejects a non-progressing or exhausted traversal instead of selecting from an incomplete list.
51
+
52
+ This adapter supports direct conversations only. Configure the original account/login fields and receive messages;
53
+ there is no group-permission page, permission initialization or legacy-import step. Group management commands
54
+ do not grant a direct-message sender administrator authority. Existing stored data is not deleted.
55
+
56
+ The Host owns installation, grants, credentials and lifecycle; saving configuration preserves enable intent. The language link opens a separate document and does not imply automatic language selection.
package/README.md CHANGED
@@ -1,3 +1,25 @@
1
- # Temporary Holding Version
1
+ # 企业微信应用连接器
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 简体中文 · [English](README.en.md)
4
+
5
+ 将企业微信自建应用接入 Clowder AI,支持回调验证、加密消息解密、私聊文本与媒体消息以及分段回复。
6
+
7
+ ## 开始使用
8
+
9
+ 1. 创建企业微信自建应用,在插件设置填写企业标识 `corpId` 和应用标识 `agentId`。
10
+ 2. 保存应用密钥 `agentSecret`、回调令牌 `callbackToken` 和加密密钥 `encodingAesKey`。
11
+ 3. 按宿主提供的回调地址完成企业微信端配置,然后启用插件并发送私聊消息。
12
+
13
+ 仅在签名、解密和企业身份校验通过后接收回调。此连接器只支持私聊,不提供群设置或权限初始化。
14
+
15
+ ## 消息与恢复
16
+
17
+ 短期访问令牌只缓存在当前进程。媒体标识保存在私有状态,对话只携带受限媒体引用。停用或切换实现时先停止回调消费,避免同一事件被两份连接器处理。包不从仓库环境文件读取凭据,也不保存原始回调正文。
18
+
19
+ ## 对话命令
20
+
21
+ 可使用 `/where`、`/new`、`/threads`、`/use`、`/thread`、`/commands`、`/cats`、`/status`、`/history`、`/unbind`、`/focus` 和 `/ask` 查看位置、切换对话或发起请求。`/thread`、`/ask` 只有显式携带正文时才请求猫执行,宿主接受投递后才确认成功。`/use` 会遍历可见对话,列表不完整时不猜测选择。
22
+
23
+ `/allow-group`、`/deny-group` 仅适用于支持群聊且已配置管理员的连接器,不能通过普通私聊文本取得管理权。
24
+
25
+ 宿主管理安装、授权、凭据和启停;配置保存不自动改变启用意图。英文文档是独立文件,互链不代表页面已支持自动语言切换。
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="WeCom agent connector">
2
+ <rect width="64" height="64" rx="14" fill="#7C3AED"/>
3
+ <path d="M17 21h30v18H31l-9 7v-7h-5z" fill="#fff"/>
4
+ <circle cx="26" cy="30" r="2" fill="#7C3AED"/>
5
+ <circle cx="38" cy="30" r="2" fill="#7C3AED"/>
6
+ </svg>
@@ -0,0 +1,149 @@
1
+ /**
2
+ * WeCom Agent (企微自建应用) Adapter
3
+ * Inbound: HTTP callback with AES-256-CBC encrypted XML → parse text/image/voice/video/file
4
+ * Outbound: message/send API (text/markdown/textcard) — final-only, no streaming
5
+ *
6
+ * F132 DingTalk + WeCom Chat Gateway — Phase C
7
+ */
8
+ import type { ConnectorLogger, MessageEnvelope } from './types.js';
9
+ export interface WeComAgentAttachment {
10
+ type: 'image' | 'file' | 'audio' | 'video';
11
+ /** WeCom media_id for download via /media/get */
12
+ mediaId: string;
13
+ fileName?: string;
14
+ }
15
+ export interface WeComAgentInboundMessage {
16
+ /** FromUserName (企微 userid) */
17
+ chatId: string;
18
+ text: string;
19
+ messageId: string;
20
+ senderId: string;
21
+ attachments?: WeComAgentAttachment[];
22
+ }
23
+ export interface WeComAgentAdapterOptions {
24
+ corpId: string;
25
+ agentId: string;
26
+ agentSecret: string;
27
+ /** 回调 Token for SHA1 signature verification */
28
+ token: string;
29
+ /** 回调 EncodingAESKey (43 chars, no trailing =) */
30
+ encodingAesKey: string;
31
+ }
32
+ /**
33
+ * Compute SHA1 signature for WeCom callback verification.
34
+ * sig = sha1(sort([token, timestamp, nonce, encrypt]).join(''))
35
+ */
36
+ export declare function computeSignature(token: string, timestamp: string, nonce: string, encrypt: string): string;
37
+ /**
38
+ * Decrypt an AES-256-CBC encrypted WeCom message.
39
+ * Returns { message, corpId } after PKCS7 unpadding.
40
+ *
41
+ * Layout after decryption:
42
+ * [16 bytes random] [4 bytes msg_len BE] [msg_len bytes XML] [corpId bytes]
43
+ */
44
+ export declare function decryptMessage(encryptedBase64: string, aesKey: Buffer, iv: Buffer): {
45
+ message: string;
46
+ receivedCorpId: string;
47
+ };
48
+ /**
49
+ * Encrypt a plaintext message for WeCom callback response.
50
+ * Used for echostr challenge response.
51
+ */
52
+ export declare function encryptMessage(plaintext: string, aesKey: Buffer, iv: Buffer, corpId: string): string;
53
+ export declare class WeComAgentAdapter {
54
+ readonly connectorId = "wecom-agent";
55
+ private readonly log;
56
+ private readonly corpId;
57
+ private readonly agentId;
58
+ private readonly agentSecret;
59
+ private readonly token;
60
+ private readonly aesKey;
61
+ private readonly iv;
62
+ private accessToken;
63
+ private tokenExpiresAt;
64
+ private fetchFn;
65
+ constructor(log: ConnectorLogger, options: WeComAgentAdapterOptions);
66
+ /**
67
+ * Handle GET echostr challenge for URL verification.
68
+ * WeCom sends: ?msg_signature=X&timestamp=T&nonce=N&echostr=E
69
+ * We decrypt echostr and return plaintext if signature matches.
70
+ */
71
+ verifyCallback(query: {
72
+ msg_signature: string;
73
+ timestamp: string;
74
+ nonce: string;
75
+ echostr: string;
76
+ }): string | null;
77
+ /**
78
+ * Verify signature and decrypt an incoming POST body (encrypted XML).
79
+ * Returns the decrypted XML string, or null if verification fails.
80
+ */
81
+ decryptInbound(body: string, query: {
82
+ msg_signature: string;
83
+ timestamp: string;
84
+ nonce: string;
85
+ }): string | null;
86
+ /**
87
+ * Parse decrypted XML into a normalized inbound message.
88
+ * Supports text, image, voice, video, file (location is logged and skipped).
89
+ */
90
+ parseEvent(decryptedXml: string): WeComAgentInboundMessage | null;
91
+ /**
92
+ * Get a valid access_token, refreshing if expired.
93
+ * WeCom access_token has 7200s (2h) TTL.
94
+ */
95
+ getAccessToken(): Promise<string>;
96
+ /** Force token refresh on next call (e.g., after 401/40001 error). */
97
+ invalidateToken(): void;
98
+ /**
99
+ * Send a text reply via message/send API.
100
+ * Long messages are chunked at TEXT_BODY_LIMIT_BYTES (2048 bytes).
101
+ * AC-C6: final-only mode, long reply chunking.
102
+ */
103
+ sendReply(externalChatId: string, content: string, _metadata?: Record<string, unknown>): Promise<void>;
104
+ /**
105
+ * Send a formatted reply as markdown (body) or textcard (with link).
106
+ * AC-C4: markdown + textcard + 图文卡片
107
+ */
108
+ sendFormattedReply(externalChatId: string, envelope: MessageEnvelope, _metadata?: Record<string, unknown>): Promise<void>;
109
+ /**
110
+ * Send a media message (image/file/audio) via temporary material upload.
111
+ * AC-C5: 临时素材 API 收发
112
+ */
113
+ sendMedia(externalChatId: string, payload: {
114
+ type: 'image' | 'file' | 'audio';
115
+ content: AsyncIterable<Uint8Array>;
116
+ fileName?: string;
117
+ }): Promise<void>;
118
+ /**
119
+ * Download a media file by media_id via /media/get API.
120
+ * AC-C5: inbound media download
121
+ */
122
+ downloadMedia(mediaId: string): Promise<Buffer>;
123
+ /**
124
+ * Upload a temporary material via /media/upload.
125
+ * Returns media_id on success, null on failure.
126
+ */
127
+ private uploadMedia;
128
+ /**
129
+ * Send a message via /message/send API.
130
+ * Handles token refresh on 40001/42001 errors.
131
+ */
132
+ private messageSend;
133
+ /**
134
+ * Split text into chunks respecting byte limit, breaking at newlines/spaces.
135
+ * AC-C6: long reply chunking at 2048 bytes.
136
+ */
137
+ chunkMessage(text: string, maxBytes: number): string[];
138
+ /** @internal */
139
+ _injectFetch(fn: typeof fetch): void;
140
+ /** @internal */
141
+ _injectAccessToken(token: string, expiresAt?: number): void;
142
+ /** @internal — expose crypto params for testing */
143
+ _getCryptoParams(): {
144
+ aesKey: Buffer;
145
+ iv: Buffer;
146
+ token: string;
147
+ };
148
+ }
149
+ //# sourceMappingURL=WeComAgentAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WeComAgentAdapter.d.ts","sourceRoot":"","sources":["../src/WeComAgentAdapter.ts"],"names":[],"mappings":"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"}