@numa-tech/numa 1.14.30 → 1.14.32

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.
@@ -0,0 +1,143 @@
1
+ import { resolveBackendUrl } from "../backend.js";
2
+ import { ensureAccessToken, fetchWithKeycloakIdentity } from "../oauth.js";
3
+ import { WecomError } from "./errors.js";
4
+ import { CapabilitiesSchema, MessageSchema, RecipientQuerySchema, RecipientsSchema, RequestIdSchema, SendMessageSchema, MappingListSchema, MappingSchema, MappingSyncSchema, PlatformUserIdSchema, UserIdSchema } from "./schemas.js";
5
+ export const WECOM_MAPPING_ROOT = "/api/v1/admin/notifications/wecom/mappings";
6
+ export const WECOM_API_ROOT = "/api/v1/notifications/wecom";
7
+ // Full-directory mapping sync can return 9,999 rows; ordinary message APIs stay small.
8
+ const MESSAGE_RESPONSE_LIMIT = 1024 * 1024;
9
+ const MAPPING_RESPONSE_LIMIT = 32 * 1024 * 1024;
10
+ async function boundedResponseText(response, maximumBytes) {
11
+ if (!response.body)
12
+ return "";
13
+ const reader = response.body.getReader();
14
+ const chunks = [];
15
+ let bytes = 0;
16
+ let completed = false;
17
+ try {
18
+ while (true) {
19
+ const chunk = await reader.read();
20
+ if (chunk.done) {
21
+ completed = true;
22
+ break;
23
+ }
24
+ bytes += chunk.value.byteLength;
25
+ if (bytes > maximumBytes)
26
+ throw new Error("Response exceeds the bounded read limit");
27
+ chunks.push(chunk.value);
28
+ }
29
+ return new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks, bytes));
30
+ }
31
+ finally {
32
+ if (!completed)
33
+ await reader.cancel().catch(() => undefined);
34
+ reader.releaseLock();
35
+ }
36
+ }
37
+ function validate(schema, input, label) {
38
+ const result = schema.safeParse(input);
39
+ if (!result.success)
40
+ throw new WecomError(`Invalid ${label}. Check numa wecom help; message content is never included in errors.`, "WECOM_INPUT_INVALID");
41
+ return result.data;
42
+ }
43
+ export class WecomClient {
44
+ config;
45
+ dependencies;
46
+ constructor(config, dependencies = {}) {
47
+ this.config = config;
48
+ this.dependencies = dependencies;
49
+ }
50
+ async json(path, schema, body, requestId, method = body == null ? "GET" : "POST") {
51
+ const url = resolveBackendUrl(this.config, { method, path });
52
+ let response;
53
+ try {
54
+ response = await fetchWithKeycloakIdentity(this.config, url, () => ({
55
+ method, redirect: "error", signal: AbortSignal.timeout(30_000),
56
+ headers: { Accept: "application/json", ...(body == null ? {} : { "Content-Type": "application/json", ...(requestId ? { "Idempotency-Key": requestId } : {}) }) },
57
+ ...(body == null ? {} : { body: JSON.stringify(body) })
58
+ }), { fetch: this.dependencies.fetch ?? fetch, tokenProvider: this.dependencies.tokenProvider ?? ensureAccessToken });
59
+ }
60
+ catch (error) {
61
+ // Authentication errors occur before sending and retain their login recovery code.
62
+ if (error instanceof Error && error.name === "AuthError")
63
+ throw error;
64
+ if (requestId)
65
+ throw this.unknown(requestId);
66
+ if (method !== "GET")
67
+ throw this.mappingUnknown();
68
+ throw new WecomError("WeCom platform request failed. Check the platform connection and authentication.", "WECOM_NETWORK_ERROR");
69
+ }
70
+ if (!response.ok) {
71
+ await response.body?.cancel().catch(() => undefined);
72
+ if (requestId && response.status >= 500)
73
+ throw this.unknown(requestId, response.status);
74
+ if (method !== "GET" && response.status >= 500)
75
+ throw this.mappingUnknown(response.status);
76
+ throw new WecomError(`WeCom platform API returned HTTP ${response.status}.`, "WECOM_API_ERROR", response.status, requestId);
77
+ }
78
+ try {
79
+ const raw = await boundedResponseText(response, path.startsWith(WECOM_MAPPING_ROOT) ? MAPPING_RESPONSE_LIMIT : MESSAGE_RESPONSE_LIMIT);
80
+ const result = schema.safeParse(JSON.parse(raw));
81
+ if (!result.success)
82
+ throw new Error("invalid response");
83
+ return result.data;
84
+ }
85
+ catch {
86
+ if (requestId)
87
+ throw this.unknown(requestId, response.status);
88
+ if (method !== "GET")
89
+ throw this.mappingUnknown(response.status);
90
+ throw new WecomError("WeCom platform returned an invalid response.", "WECOM_INVALID_RESPONSE", response.status);
91
+ }
92
+ }
93
+ mappingUnknown(status) {
94
+ return new WecomError("Mapping operation result is unknown. Run numa wecom mappings list to inspect current state before retrying.", "WECOM_MAPPING_RESULT_UNKNOWN", status);
95
+ }
96
+ unknown(requestId, status) {
97
+ return new WecomError(`Send result is unknown. Run numa wecom status --request-id ${requestId}; do not send again with a new request ID.`, "WECOM_RESULT_UNKNOWN", status, requestId);
98
+ }
99
+ mappings(query, limit = 100) {
100
+ if (!Number.isInteger(limit) || limit < 1 || limit > 1000)
101
+ throw new WecomError("Mapping limit must be between 1 and 1000.", "WECOM_INPUT_INVALID");
102
+ if (query != null && query.length > 256)
103
+ throw new WecomError("Mapping query is too long.", "WECOM_INPUT_INVALID");
104
+ const params = new URLSearchParams({ limit: String(limit) });
105
+ if (query?.trim())
106
+ params.set("query", query.trim());
107
+ return this.json(`${WECOM_MAPPING_ROOT}?${params}`, MappingListSchema);
108
+ }
109
+ syncMappings(dryRun = true) {
110
+ return this.json(`${WECOM_MAPPING_ROOT}/sync`, MappingSyncSchema, { dryRun });
111
+ }
112
+ bindMapping(subject, userId) {
113
+ const id = validate(PlatformUserIdSchema, subject, "Keycloak subject");
114
+ const wecomUserId = validate(UserIdSchema, userId, "WeCom userid");
115
+ return this.json(`${WECOM_MAPPING_ROOT}/${encodeURIComponent(id)}`, MappingSchema, { wecomUserId }, undefined, "PUT");
116
+ }
117
+ unbindMapping(subject) {
118
+ const id = validate(PlatformUserIdSchema, subject, "Keycloak subject");
119
+ return this.json(`${WECOM_MAPPING_ROOT}/${encodeURIComponent(id)}`, MappingSchema, undefined, undefined, "DELETE");
120
+ }
121
+ capabilities() { return this.json(`${WECOM_API_ROOT}/capabilities`, CapabilitiesSchema); }
122
+ recipients(query, limit = 20) {
123
+ const validated = validate(RecipientQuerySchema, query, "recipient query (2-100 characters)");
124
+ if (!Number.isInteger(limit) || limit < 1 || limit > 50)
125
+ throw new WecomError("limit must be between 1 and 50.", "WECOM_INPUT_INVALID");
126
+ return this.json(`${WECOM_API_ROOT}/recipients?${new URLSearchParams({ query: validated, limit: String(limit) })}`, RecipientsSchema);
127
+ }
128
+ send(input, requestId) {
129
+ const key = validate(RequestIdSchema, requestId, "request ID (8-128 safe ASCII characters)");
130
+ const body = validate(SendMessageSchema, input, "message or recipients");
131
+ return this.json(`${WECOM_API_ROOT}/messages`, MessageSchema, body, key);
132
+ }
133
+ status(id) {
134
+ if (!/^[A-Za-z0-9_-]{1,128}$/u.test(id))
135
+ throw new WecomError("Invalid message ID.", "WECOM_INPUT_INVALID");
136
+ return this.json(`${WECOM_API_ROOT}/messages/${encodeURIComponent(id)}`, MessageSchema);
137
+ }
138
+ statusByRequest(requestId) {
139
+ const key = validate(RequestIdSchema, requestId, "request ID");
140
+ return this.json(`${WECOM_API_ROOT}/messages/by-request/${encodeURIComponent(key)}`, MessageSchema);
141
+ }
142
+ }
143
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/wecom/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Command } from "commander";
2
+ import { WecomClient } from "./client.js";
3
+ export interface WecomCommandDependencies {
4
+ clientFactory?: () => WecomClient;
5
+ stdin?: AsyncIterable<Uint8Array | string>;
6
+ }
7
+ export declare function readMessageContent(file: string | undefined, inline: string | undefined, stdin: AsyncIterable<Uint8Array | string>): Promise<string>;
8
+ export declare function registerWecomCommands(program: Command, dependencies?: WecomCommandDependencies): void;
@@ -0,0 +1,153 @@
1
+ import { open } from "node:fs/promises";
2
+ import process from "node:process";
3
+ import { loadAppConfig } from "../app-config.js";
4
+ import { loadConfig } from "../config.js";
5
+ import { WecomClient } from "./client.js";
6
+ import { WecomError } from "./errors.js";
7
+ import { RequestIdSchema, SendMessageSchema } from "./schemas.js";
8
+ export async function readMessageContent(file, inline, stdin) {
9
+ if ((file == null) === (inline == null))
10
+ throw new WecomError("Choose exactly one of --text-file <path|-> or --text <content>.", "WECOM_INPUT_INVALID");
11
+ if (inline != null)
12
+ return inline;
13
+ try {
14
+ const chunks = [];
15
+ let bytes = 0;
16
+ async function collect(source) {
17
+ for await (const chunk of source) {
18
+ const buffer = Buffer.from(chunk);
19
+ bytes += buffer.length;
20
+ if (bytes > 2048)
21
+ throw new WecomError("Message exceeds 2048 UTF-8 bytes.", "WECOM_INPUT_INVALID");
22
+ chunks.push(buffer);
23
+ }
24
+ return new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks));
25
+ }
26
+ if (file === "-")
27
+ return await collect(stdin);
28
+ const handle = await open(file, "r");
29
+ try {
30
+ const stat = await handle.stat();
31
+ if (!stat.isFile() || stat.size > 2048)
32
+ throw new WecomError("Message file must be a regular UTF-8 file of at most 2048 bytes.", "WECOM_INPUT_INVALID");
33
+ return await collect(handle.createReadStream({ autoClose: false }));
34
+ }
35
+ finally {
36
+ await handle.close();
37
+ }
38
+ }
39
+ catch (error) {
40
+ if (error instanceof WecomError)
41
+ throw error;
42
+ throw new WecomError("Could not read message input as UTF-8. Check the input path and permissions.", "WECOM_INPUT_INVALID");
43
+ }
44
+ }
45
+ function snake(value) {
46
+ if (Array.isArray(value))
47
+ return value.map(snake);
48
+ if (value && typeof value === "object")
49
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key.replace(/([a-z0-9])([A-Z])/gu, "$1_$2").toLowerCase(), snake(item)]));
50
+ return value;
51
+ }
52
+ async function output(command, value, text) {
53
+ const app = await loadAppConfig();
54
+ process.stdout.write(command.optsWithGlobals().json || app.output === "json"
55
+ ? `${JSON.stringify(snake(value), null, 2)}\n` : `${text}\n`);
56
+ }
57
+ function sendExitCode(status) {
58
+ if (status === "ACCEPTED")
59
+ return 0;
60
+ if (status === "PARTIAL")
61
+ return 7;
62
+ if (status === "UNKNOWN" || status === "SENDING")
63
+ return 6;
64
+ return 1;
65
+ }
66
+ function statusLine(message) {
67
+ const description = message.status === "ACCEPTED" ? " (accepted by WeCom API; delivery/read is not confirmed)"
68
+ : ["UNKNOWN", "SENDING"].includes(message.status) ? " (do not resend; query status with the same request ID)" : "";
69
+ return `${message.id}\t${message.requestId}\t${message.status}${description}`;
70
+ }
71
+ export function registerWecomCommands(program, dependencies = {}) {
72
+ const client = () => dependencies.clientFactory?.() ?? new WecomClient(loadConfig());
73
+ const wecom = program.command("wecom").description("通过 DevOps 平台向企业微信成员发送消息(密钥仅保存在服务端)");
74
+ wecom.command("capabilities").description("查看通道配置状态、消息类型和配额")
75
+ .action(async (_options, command) => {
76
+ const result = await client().capabilities();
77
+ await output(command, result, `Enabled: ${result.enabled}; configured: ${result.configured}; types: ${result.messageTypes.join(", ")}; recipients: ${result.maxRecipients}; text/markdown bytes: ${result.maxTextBytes}/${result.maxMarkdownBytes}`);
78
+ });
79
+ wecom.command("recipients").description("按姓名或 userid 查询候选成员;查看结果后明确选择 userid")
80
+ .requiredOption("--query <text>", "至少 2 个字符")
81
+ .option("--limit <number>", "最多返回 1-50 个成员", "20")
82
+ .action(async (options, command) => {
83
+ const result = await client().recipients(options.query, Number(options.limit));
84
+ await output(command, result, (result.items.map(item => `${item.userId}\t${item.name}\t${item.departmentIds.join(",")}`).join("\n") || "No matching recipients.") + (result.hasMore ? "\nMore matches exist; narrow your query before choosing a userid." : ""));
85
+ });
86
+ wecom.command("send").description("发送 text/markdown;先核对 userid 和正文;结果未知时仅查询状态")
87
+ .option("--to <userids...>", "已确认的企微 userid(空格分隔);不支持姓名或 @all")
88
+ .option("--platform-user <subjects...>", "已映射的 Keycloak 用户 subject;与 --to 互斥")
89
+ .requiredOption("--request-id <id>", "稳定幂等键,8-128 位字母/数字/._:-;同一请求保持不变")
90
+ .option("--type <type>", "text 或 markdown", "text")
91
+ .option("--text-file <path|->", "从 UTF-8 文件或 - 标准输入读取正文(推荐)")
92
+ .option("--text <content>", "直接正文;可能留在 shell 历史中")
93
+ .option("--safe", "保密消息,仅 text 支持")
94
+ .option("--yes", "已确认收件人和正文,立即发送")
95
+ .action(async (options, command) => {
96
+ if (!options.yes)
97
+ throw new WecomError("Sending requires --yes after reviewing the recipients and content.", "WECOM_CONFIRMATION_REQUIRED");
98
+ if (!RequestIdSchema.safeParse(options.requestId).success)
99
+ throw new WecomError("Invalid request ID; use 8-128 ASCII letters, digits or ._:-.", "WECOM_INPUT_INVALID");
100
+ const content = await readMessageContent(options.textFile, options.text, dependencies.stdin ?? process.stdin);
101
+ const parsed = SendMessageSchema.safeParse({ ...(options.to ? { recipientUserIds: options.to } : {}), ...(options.platformUser ? { platformUserIds: options.platformUser } : {}), messageType: options.type, content, safe: Boolean(options.safe) });
102
+ if (!parsed.success)
103
+ throw new WecomError("Invalid message. Use explicit unique userids, text/markdown, at most 2048 UTF-8 bytes; --safe supports text only.", "WECOM_INPUT_INVALID");
104
+ process.stderr.write(`WeCom request ID: ${options.requestId}\n`);
105
+ const result = await client().send(parsed.data, options.requestId);
106
+ await output(command, result, statusLine(result));
107
+ process.exitCode = sendExitCode(result.status);
108
+ }).addHelpText("after", "\nExamples:\n numa wecom recipients --query 张三 --json\n numa wecom send --to zhangsan --request-id release-20260905-01 --text-file ./message.txt --yes --json\n numa wecom send --to zhangsan --request-id release-20260905-02 --type markdown --text-file - --yes\n");
109
+ const mappings = wecom.command("mappings").description("管理 Keycloak 与企微成员映射(服务端要求 ops-admin)");
110
+ mappings.command("list").description("查询映射、冲突候选和人工解绑记录")
111
+ .option("--query <text>", "按平台用户名、subject 或企微 userid 筛选")
112
+ .option("--limit <number>", "返回上限", "100")
113
+ .action(async (options, command) => {
114
+ const result = await client().mappings(options.query, Number(options.limit));
115
+ await output(command, result, result.items.map(item => `${item.subject}\t${item.username ?? "-"}\t${item.wecomUserId ?? "-"}\t${item.status}\t${item.source}`).join("\n") + (result.hasMore ? "\nMore mappings exist; narrow your query." : "") || "No mappings found.");
116
+ });
117
+ mappings.command("sync").description("默认预览按唯一邮箱批量匹配;冲突需人工处理,不覆盖人工映射")
118
+ .option("--apply", "重新计算并应用可唯一匹配的映射")
119
+ .option("--yes", "确认应用映射变更")
120
+ .action(async (options, command) => {
121
+ if (options.apply && !options.yes)
122
+ throw new WecomError("Applying mappings requires --yes after reviewing the sync preview.", "WECOM_CONFIRMATION_REQUIRED");
123
+ const result = await client().syncMappings(!options.apply);
124
+ await output(command, result, `${result.dryRun ? "Preview" : "Applied"}: ${result.mappedCount} mapped; ${result.reviewCount} need review.\n` + result.items.map(item => `${item.subject}\t${item.wecomUserId ?? "-"}\t${item.status}\t${item.candidateUserIds.join(",")}`).join("\n"));
125
+ });
126
+ mappings.command("bind <subject>").description("手动绑定一个明确的 Keycloak subject 与企微 userid")
127
+ .requiredOption("--wecom-user <userid>", "已确认的企微 userid")
128
+ .option("--yes", "确认人工绑定")
129
+ .action(async (subject, options, command) => {
130
+ if (!options.yes)
131
+ throw new WecomError("Manual binding requires --yes after reviewing both identities.", "WECOM_CONFIRMATION_REQUIRED");
132
+ const result = await client().bindMapping(subject, options.wecomUser);
133
+ await output(command, result, `${result.subject}\t${result.wecomUserId ?? "-"}\t${result.status}\t${result.source}`);
134
+ });
135
+ mappings.command("unbind <subject>").description("人工解绑并保留禁止邮箱自动重建的记录")
136
+ .option("--yes", "确认人工解绑")
137
+ .action(async (subject, options, command) => {
138
+ if (!options.yes)
139
+ throw new WecomError("Manual unbinding requires --yes after reviewing the mapping.", "WECOM_CONFIRMATION_REQUIRED");
140
+ const result = await client().unbindMapping(subject);
141
+ await output(command, result, `${result.subject}\t${result.status}\t${result.source}`);
142
+ });
143
+ wecom.command("status [message-id]").description("只读查询发送账本;ACCEPTED 仅表示企微 API 接收")
144
+ .option("--request-id <id>", "使用原始幂等键恢复 POST 响应丢失的请求")
145
+ .action(async (id, options, command) => {
146
+ if (Boolean(id) === Boolean(options.requestId))
147
+ throw new WecomError("Choose a message ID or --request-id, exactly one.", "WECOM_INPUT_INVALID");
148
+ const api = client();
149
+ const result = options.requestId ? await api.statusByRequest(options.requestId) : await api.status(id);
150
+ await output(command, result, statusLine(result));
151
+ });
152
+ }
153
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/wecom/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare class WecomError extends Error {
2
+ readonly code: string;
3
+ readonly status?: number | undefined;
4
+ readonly requestId?: string | undefined;
5
+ constructor(message: string, code: string, status?: number | undefined, requestId?: string | undefined);
6
+ }
7
+ export declare function wecomExitCode(error: WecomError): number;
@@ -0,0 +1,27 @@
1
+ // Keep provider error bodies and message contents out of CLI/MCP diagnostics.
2
+ export class WecomError extends Error {
3
+ code;
4
+ status;
5
+ requestId;
6
+ constructor(message, code, status, requestId) {
7
+ super(message);
8
+ this.code = code;
9
+ this.status = status;
10
+ this.requestId = requestId;
11
+ this.name = "WecomError";
12
+ }
13
+ }
14
+ export function wecomExitCode(error) {
15
+ if (error.status === 401)
16
+ return 3;
17
+ if (error.status === 403)
18
+ return 4;
19
+ if (error.status === 409)
20
+ return 5;
21
+ if ((error.status ?? 0) >= 500 || /UNKNOWN|NETWORK|INVALID_RESPONSE/u.test(error.code))
22
+ return 6;
23
+ if (/INPUT|CONFIRMATION/u.test(error.code))
24
+ return 2;
25
+ return 1;
26
+ }
27
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/wecom/errors.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,OAAO,UAAW,SAAQ,KAAK;IACU,IAAI;IAA0B,MAAM;IAA2B,SAAS;IAArH,YAAY,OAAe,EAAkB,IAAY,EAAkB,MAAe,EAAkB,SAAkB;QAC5H,KAAK,CAAC,OAAO,CAAC,CAAC;oBAD4B,IAAI;sBAA0B,MAAM;yBAA2B,SAAS;QAEnH,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AACD,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,GAAG,IAAI,mCAAmC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACjG,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACrD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ export declare const RequestIdSchema: z.ZodString;
3
+ export declare const UserIdSchema: z.ZodString;
4
+ export declare const PlatformUserIdSchema: z.ZodString;
5
+ export declare const MessageTypeSchema: z.ZodEnum<{
6
+ markdown: "markdown";
7
+ text: "text";
8
+ }>;
9
+ export declare const RecipientQuerySchema: z.ZodString;
10
+ export declare const SendMessageSchema: z.ZodObject<{
11
+ recipientUserIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
+ platformUserIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
13
+ messageType: z.ZodEnum<{
14
+ markdown: "markdown";
15
+ text: "text";
16
+ }>;
17
+ content: z.ZodString;
18
+ safe: z.ZodDefault<z.ZodBoolean>;
19
+ }, z.core.$strict>;
20
+ export declare const CapabilitiesSchema: z.ZodObject<{
21
+ enabled: z.ZodBoolean;
22
+ configured: z.ZodBoolean;
23
+ messageTypes: z.ZodArray<z.ZodEnum<{
24
+ markdown: "markdown";
25
+ text: "text";
26
+ }>>;
27
+ maxRecipients: z.ZodNumber;
28
+ maxTextBytes: z.ZodNumber;
29
+ maxMarkdownBytes: z.ZodNumber;
30
+ }, z.core.$strip>;
31
+ export declare const RecipientsSchema: z.ZodObject<{
32
+ items: z.ZodArray<z.ZodObject<{
33
+ userId: z.ZodString;
34
+ name: z.ZodString;
35
+ departmentIds: z.ZodArray<z.ZodNumber>;
36
+ }, z.core.$strip>>;
37
+ hasMore: z.ZodBoolean;
38
+ }, z.core.$strip>;
39
+ export declare const MessageSchema: z.ZodObject<{
40
+ id: z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<string, string | number>>;
41
+ requestId: z.ZodString;
42
+ status: z.ZodEnum<{
43
+ ACCEPTED: "ACCEPTED";
44
+ FAILED: "FAILED";
45
+ PARTIAL: "PARTIAL";
46
+ SENDING: "SENDING";
47
+ UNKNOWN: "UNKNOWN";
48
+ }>;
49
+ recipientUserIds: z.ZodArray<z.ZodString>;
50
+ messageType: z.ZodEnum<{
51
+ markdown: "markdown";
52
+ text: "text";
53
+ }>;
54
+ safe: z.ZodBoolean;
55
+ providerMessageId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
+ invalidUserIds: z.ZodArray<z.ZodString>;
57
+ errorCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
58
+ createdAt: z.ZodString;
59
+ updatedAt: z.ZodString;
60
+ }, z.core.$strip>;
61
+ export type SendMessageInput = z.input<typeof SendMessageSchema>;
62
+ export type Message = z.infer<typeof MessageSchema>;
63
+ export declare const MappingSchema: z.ZodObject<{
64
+ subject: z.ZodString;
65
+ username: z.ZodNullable<z.ZodString>;
66
+ status: z.ZodString;
67
+ source: z.ZodEnum<{
68
+ AUTO_EMAIL: "AUTO_EMAIL";
69
+ MANUAL: "MANUAL";
70
+ }>;
71
+ wecomUserId: z.ZodNullable<z.ZodString>;
72
+ candidateUserIds: z.ZodArray<z.ZodString>;
73
+ maskedEmail: z.ZodNullable<z.ZodString>;
74
+ updatedAt: z.ZodNullable<z.ZodString>;
75
+ }, z.core.$strip>;
76
+ export declare const MappingListSchema: z.ZodObject<{
77
+ items: z.ZodArray<z.ZodObject<{
78
+ subject: z.ZodString;
79
+ username: z.ZodNullable<z.ZodString>;
80
+ status: z.ZodString;
81
+ source: z.ZodEnum<{
82
+ AUTO_EMAIL: "AUTO_EMAIL";
83
+ MANUAL: "MANUAL";
84
+ }>;
85
+ wecomUserId: z.ZodNullable<z.ZodString>;
86
+ candidateUserIds: z.ZodArray<z.ZodString>;
87
+ maskedEmail: z.ZodNullable<z.ZodString>;
88
+ updatedAt: z.ZodNullable<z.ZodString>;
89
+ }, z.core.$strip>>;
90
+ hasMore: z.ZodBoolean;
91
+ }, z.core.$strip>;
92
+ export declare const MappingSyncSchema: z.ZodObject<{
93
+ dryRun: z.ZodBoolean;
94
+ items: z.ZodArray<z.ZodObject<{
95
+ subject: z.ZodString;
96
+ username: z.ZodNullable<z.ZodString>;
97
+ status: z.ZodString;
98
+ source: z.ZodEnum<{
99
+ AUTO_EMAIL: "AUTO_EMAIL";
100
+ MANUAL: "MANUAL";
101
+ }>;
102
+ wecomUserId: z.ZodNullable<z.ZodString>;
103
+ candidateUserIds: z.ZodArray<z.ZodString>;
104
+ maskedEmail: z.ZodNullable<z.ZodString>;
105
+ updatedAt: z.ZodNullable<z.ZodString>;
106
+ }, z.core.$strip>>;
107
+ mappedCount: z.ZodNumber;
108
+ reviewCount: z.ZodNumber;
109
+ }, z.core.$strip>;
@@ -0,0 +1,49 @@
1
+ import { z } from "zod";
2
+ export const RequestIdSchema = z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/u);
3
+ export const UserIdSchema = z.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9_@.-]*$/u).refine(value => value.toLowerCase() !== "@all", "Broadcast is not supported");
4
+ // Keycloak subjects are opaque, including federated IDs and Unicode; never normalize them.
5
+ export const PlatformUserIdSchema = z.string().min(1).max(256)
6
+ .refine(value => value.trim().length > 0 && value === value.trim() && !/[\u0000-\u001f\u007f-\u009f]/u.test(value), "Subject must be nonblank, unpadded and contain no control characters");
7
+ export const MessageTypeSchema = z.enum(["text", "markdown"]);
8
+ export const RecipientQuerySchema = z.string().trim().min(2).max(100);
9
+ export const SendMessageSchema = z.object({
10
+ recipientUserIds: z.array(UserIdSchema).min(1).max(100).refine(ids => new Set(ids).size === ids.length, "Duplicate recipients are not allowed").optional(),
11
+ platformUserIds: z.array(PlatformUserIdSchema).min(1).max(100).refine(ids => new Set(ids).size === ids.length, "Duplicate recipients are not allowed").optional(),
12
+ messageType: MessageTypeSchema,
13
+ content: z.string().min(1).refine(value => value.trim().length > 0, "Content must not be blank"),
14
+ safe: z.boolean().default(false)
15
+ }).strict().superRefine((value, ctx) => {
16
+ if (Boolean(value.recipientUserIds?.length) === Boolean(value.platformUserIds?.length)) {
17
+ ctx.addIssue({ code: "custom", path: ["recipientUserIds"], message: "Choose exactly one recipient identifier type" });
18
+ }
19
+ if (value.messageType === "markdown" && value.safe) {
20
+ ctx.addIssue({ code: "custom", path: ["safe"], message: "Safe mode is only supported for text" });
21
+ }
22
+ const maximum = 2048;
23
+ if (Buffer.byteLength(value.content, "utf8") > maximum) {
24
+ ctx.addIssue({ code: "custom", path: ["content"], message: `Content exceeds ${maximum} UTF-8 bytes` });
25
+ }
26
+ });
27
+ export const CapabilitiesSchema = z.object({
28
+ enabled: z.boolean(), configured: z.boolean(), messageTypes: z.array(MessageTypeSchema),
29
+ maxRecipients: z.number().int().positive(), maxTextBytes: z.number().int().positive(), maxMarkdownBytes: z.number().int().positive()
30
+ });
31
+ export const RecipientsSchema = z.object({
32
+ items: z.array(z.object({ userId: UserIdSchema, name: z.string(), departmentIds: z.array(z.number().int()) })),
33
+ hasMore: z.boolean()
34
+ });
35
+ export const MessageSchema = z.object({
36
+ id: z.union([z.string().min(1), z.number().int().positive()]).transform(String),
37
+ requestId: RequestIdSchema,
38
+ status: z.enum(["SENDING", "ACCEPTED", "PARTIAL", "FAILED", "UNKNOWN"]),
39
+ recipientUserIds: z.array(UserIdSchema), messageType: MessageTypeSchema, safe: z.boolean(),
40
+ providerMessageId: z.string().nullable().optional(), invalidUserIds: z.array(z.string()),
41
+ errorCode: z.string().nullable().optional(), createdAt: z.string(), updatedAt: z.string()
42
+ });
43
+ export const MappingSchema = z.object({
44
+ subject: PlatformUserIdSchema, username: z.string().nullable(), status: z.string().min(1), source: z.enum(["AUTO_EMAIL", "MANUAL"]),
45
+ wecomUserId: UserIdSchema.nullable(), candidateUserIds: z.array(UserIdSchema), maskedEmail: z.string().nullable(), updatedAt: z.string().nullable()
46
+ });
47
+ export const MappingListSchema = z.object({ items: z.array(MappingSchema), hasMore: z.boolean() });
48
+ export const MappingSyncSchema = z.object({ dryRun: z.boolean(), items: z.array(MappingSchema), mappedCount: z.number().int().nonnegative(), reviewCount: z.number().int().nonnegative() });
49
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/wecom/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAC7E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,4BAA4B,CAAC,CAAC;AACnL,2FAA2F;AAC3F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;KAC3D,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,KAAK,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,sEAAsE,CAAC,CAAC;AAC9L,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAC9D,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAC,QAAQ,EAAE;IAC1J,eAAe,EAAE,CAAC,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAC,QAAQ,EAAE;IACjK,WAAW,EAAE,iBAAiB;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,2BAA2B,CAAC;IAChG,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACjC,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IACrC,IAAI,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,CAAC;QACvF,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,kBAAkB,CAAC,EAAE,OAAO,EAAE,8CAA8C,EAAE,CAAC,CAAC;IACxH,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACnD,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,sCAAsC,EAAE,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC;IACrB,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,EAAE,CAAC;QACvD,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,mBAAmB,OAAO,cAAc,EAAE,CAAC,CAAC;IACzG,CAAC;AACH,CAAC,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC;IACvF,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACrI,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC9G,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC;IAC/E,SAAS,EAAE,eAAe;IAC1B,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACvE,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IAC1F,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IACxF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CAC1F,CAAC,CAAC;AAIH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACnI,WAAW,EAAE,YAAY,CAAC,QAAQ,EAAE,EAAE,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACpJ,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACnG,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { WecomClient } from "./client.js";
3
+ export interface WecomToolDependencies {
4
+ clientFactory?: () => WecomClient;
5
+ }
6
+ export declare function registerWecomTools(server: McpServer, dependencies?: WecomToolDependencies): void;
@@ -0,0 +1,65 @@
1
+ import { z } from "zod";
2
+ import { loadConfig } from "../config.js";
3
+ import { WecomClient } from "./client.js";
4
+ import { WecomError } from "./errors.js";
5
+ import { RecipientQuerySchema, RequestIdSchema, MessageTypeSchema, UserIdSchema, PlatformUserIdSchema } from "./schemas.js";
6
+ export function registerWecomTools(server, dependencies = {}) {
7
+ const client = () => dependencies.clientFactory?.() ?? new WecomClient(loadConfig());
8
+ async function result(operation, isSend = false) {
9
+ try {
10
+ const data = await operation();
11
+ return { ...(isSend ? { isError: data.status !== "ACCEPTED" } : {}), content: [{ type: "text", text: JSON.stringify(data) }], structuredContent: data };
12
+ }
13
+ catch (error) {
14
+ const safe = error instanceof WecomError ? { code: error.code, message: error.message, requestId: error.requestId }
15
+ : { code: "WECOM_REQUEST_FAILED", message: "WeCom request failed. Check Numa authentication and platform configuration." };
16
+ return { isError: true, content: [{ type: "text", text: JSON.stringify(safe) }], structuredContent: { ok: false, ...safe } };
17
+ }
18
+ }
19
+ const readAnnotations = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
20
+ server.registerTool("numa_wecom_capabilities", {
21
+ description: "Read the DevOps platform WeCom channel availability and limits; never returns provider credentials.",
22
+ inputSchema: {}, annotations: readAnnotations
23
+ }, async () => result(() => client().capabilities()));
24
+ server.registerTool("numa_wecom_recipients", {
25
+ description: "Search corporate members by name or userid. Return candidates for explicit recipient selection; never choose among ambiguous names automatically.",
26
+ inputSchema: { query: RecipientQuerySchema, limit: z.number().int().min(1).max(50).default(20) }, annotations: readAnnotations
27
+ }, async ({ query, limit }) => result(() => client().recipients(query, limit)));
28
+ server.registerTool("numa_wecom_send", {
29
+ description: "SIDE EFFECT: immediately sends a message to enterprise WeCom members through DevOps. Use only after the user authorizes the exact recipients and content. Use exactly one of previously resolved explicit recipient_user_ids or mapped Keycloak subjects in platform_user_ids. Keep request_id stable for the same request. On UNKNOWN, network failure, or SENDING, call numa_wecom_status with request_id; never generate a new key to retry. ACCEPTED means API acceptance, not delivery/read confirmation.",
30
+ inputSchema: { recipient_user_ids: z.array(UserIdSchema).min(1).max(100).optional(), platform_user_ids: z.array(PlatformUserIdSchema).min(1).max(100).optional(), message_type: MessageTypeSchema.default("text"), content: z.string().min(1).max(2048), safe: z.boolean().default(false), request_id: RequestIdSchema, confirmed: z.literal(true).describe("Caller confirms user authorized these recipients and this exact content") },
31
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
32
+ }, async ({ recipient_user_ids, platform_user_ids, message_type, content, safe, request_id }) => result(() => client().send({ ...(recipient_user_ids ? { recipientUserIds: recipient_user_ids } : {}), ...(platform_user_ids ? { platformUserIds: platform_user_ids } : {}), messageType: message_type, content, safe }, request_id), true));
33
+ server.registerTool("numa_wecom_mappings_list", {
34
+ description: "Ops-admin: read Keycloak-to-WeCom mappings, conflicts and manual unbinding records. Email addresses are masked by the platform.",
35
+ inputSchema: { query: z.string().max(256).optional(), limit: z.number().int().min(1).max(1000).default(100) }, annotations: readAnnotations
36
+ }, async ({ query, limit }) => result(() => client().mappings(query, limit)));
37
+ server.registerTool("numa_wecom_mappings_sync", {
38
+ description: "Ops-admin: preview matching users by unique normalized email by default. apply=true writes mappings and requires confirmed=true after review. Apply recomputes both directories, so the preview can change. Never overwrites manual mappings or guesses conflicts.",
39
+ inputSchema: { apply: z.boolean().default(false), confirmed: z.boolean().default(false) },
40
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
41
+ }, async ({ apply, confirmed }) => result(async () => {
42
+ if (apply && !confirmed)
43
+ throw new WecomError("Applying mapping sync requires confirmed=true after reviewing the preview.", "WECOM_CONFIRMATION_REQUIRED");
44
+ return client().syncMappings(!apply);
45
+ }));
46
+ server.registerTool("numa_wecom_mappings_bind", {
47
+ description: "Ops-admin: manually bind a reviewed Keycloak subject to an explicit WeCom userid. Changes future message routing. Requires user authorization for this mapping.",
48
+ inputSchema: { subject: PlatformUserIdSchema, wecom_user_id: UserIdSchema, confirmed: z.literal(true) },
49
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
50
+ }, async ({ subject, wecom_user_id }) => result(() => client().bindMapping(subject, wecom_user_id)));
51
+ server.registerTool("numa_wecom_mappings_unbind", {
52
+ description: "Ops-admin: remove a reviewed mapping and retain a MANUAL UNMAPPED record that prevents email sync from automatically recreating it. Requires user authorization.",
53
+ inputSchema: { subject: PlatformUserIdSchema, confirmed: z.literal(true) },
54
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }
55
+ }, async ({ subject }) => result(() => client().unbindMapping(subject)));
56
+ server.registerTool("numa_wecom_status", {
57
+ description: "Read the message ledger using exactly one of message_id or request_id. Use request_id to recover a lost send response. UNKNOWN/SENDING do not authorize resend.",
58
+ inputSchema: { message_id: z.string().min(1).optional(), request_id: RequestIdSchema.optional() }, annotations: readAnnotations
59
+ }, async ({ message_id, request_id }) => result(async () => {
60
+ if (Boolean(message_id) === Boolean(request_id))
61
+ throw new WecomError("Supply exactly one of message_id or request_id.", "WECOM_INPUT_INVALID");
62
+ return request_id ? client().statusByRequest(request_id) : client().status(message_id);
63
+ }));
64
+ }
65
+ //# sourceMappingURL=tools.js.map