@numa-tech/numa 1.14.31 → 1.14.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -1
- package/dist/cli.js +65 -22
- package/dist/cli.js.map +1 -1
- package/dist/command-catalog.js +76 -0
- package/dist/command-catalog.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +15 -1
- package/dist/config.js.map +1 -1
- package/dist/oss/client.d.ts +18 -0
- package/dist/oss/client.js +90 -0
- package/dist/oss/client.js.map +1 -0
- package/dist/oss/commands.d.ts +2 -0
- package/dist/oss/commands.js +137 -0
- package/dist/oss/commands.js.map +1 -0
- package/dist/oss/errors.d.ts +15 -0
- package/dist/oss/errors.js +42 -0
- package/dist/oss/errors.js.map +1 -0
- package/dist/oss/schemas.d.ts +74 -0
- package/dist/oss/schemas.js +53 -0
- package/dist/oss/schemas.js.map +1 -0
- package/dist/oss/transfers.d.ts +36 -0
- package/dist/oss/transfers.js +261 -0
- package/dist/oss/transfers.js.map +1 -0
- package/dist/server.d.ts +4 -1
- package/dist/server.js +3 -1
- package/dist/server.js.map +1 -1
- package/dist/wecom/client.d.ts +120 -0
- package/dist/wecom/client.js +143 -0
- package/dist/wecom/client.js.map +1 -0
- package/dist/wecom/commands.d.ts +8 -0
- package/dist/wecom/commands.js +153 -0
- package/dist/wecom/commands.js.map +1 -0
- package/dist/wecom/errors.d.ts +7 -0
- package/dist/wecom/errors.js +27 -0
- package/dist/wecom/errors.js.map +1 -0
- package/dist/wecom/schemas.d.ts +109 -0
- package/dist/wecom/schemas.js +49 -0
- package/dist/wecom/schemas.js.map +1 -0
- package/dist/wecom/tools.d.ts +6 -0
- package/dist/wecom/tools.js +65 -0
- package/dist/wecom/tools.js.map +1 -0
- package/docs/wecom-messaging.md +81 -0
- package/package.json +7 -4
- package/skills/numa-cli/SKILL.md +1 -0
- package/skills/numa-oss-access/SKILL.md +86 -0
- package/skills/numa-oss-access/agents/openai.yaml +4 -0
- package/skills/numa-oss-access/evals/evals.json +35 -0
- package/skills/numa-oss-access/references/local-development.md +40 -0
|
@@ -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":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAgB,MAAM,cAAc,CAAC;AAMhF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAwB,EAAE,MAA0B,EAAE,KAAyC;IACtI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,iEAAiE,EAAE,qBAAqB,CAAC,CAAC;IACxJ,IAAI,MAAM,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,UAAU,OAAO,CAAC,MAA0C;YAC/D,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAClC,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;gBACvB,IAAI,KAAK,GAAG,IAAI;oBAAE,MAAM,IAAI,UAAU,CAAC,mCAAmC,EAAE,qBAAqB,CAAC,CAAC;gBACnG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtB,CAAC;YACD,OAAO,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACjF,CAAC;QACD,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAK,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI;gBAAE,MAAM,IAAI,UAAU,CAAC,kEAAkE,EAAE,qBAAqB,CAAC,CAAC;YACxJ,OAAO,MAAM,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACtE,CAAC;gBAAS,CAAC;YAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,UAAU;YAAE,MAAM,KAAK,CAAC;QAC7C,MAAM,IAAI,UAAU,CAAC,8EAA8E,EAAE,qBAAqB,CAAC,CAAC;IAC9H,CAAC;AACH,CAAC;AACD,SAAS,KAAK,CAAC,KAAc;IAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACxL,OAAO,KAAK,CAAC;AACf,CAAC;AACD,KAAK,UAAU,MAAM,CAAC,OAAgB,EAAE,KAAc,EAAE,IAAY;IAClE,MAAM,GAAG,GAAG,MAAM,aAAa,EAAE,CAAC;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,EAAsB,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;QAC9F,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;AAClE,CAAC;AACD,SAAS,YAAY,CAAC,MAAyB;IAC7C,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACnC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IAC3D,OAAO,CAAC,CAAC;AACX,CAAC;AACD,SAAS,UAAU,CAAC,OAAgB;IAClC,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,0DAA0D;QAC5G,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,yDAAyD,CAAC,CAAC,CAAC,EAAE,CAAC;IACrH,OAAO,GAAG,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;AAChF,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAE,YAAY,GAA6B,EAAE;IACjG,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,IAAI,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC;IACrF,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;IACzF,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC;SAC1D,MAAM,CAAC,KAAK,EAAE,QAAiB,EAAE,OAAgB,EAAE,EAAE;QACpD,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,YAAY,EAAE,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,MAAM,CAAC,OAAO,iBAAiB,MAAM,CAAC,UAAU,YAAY,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,aAAa,0BAA0B,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACvP,CAAC,CAAC,CAAC;IACL,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,WAAW,CAAC,qCAAqC,CAAC;SAC3E,cAAc,CAAC,gBAAgB,EAAE,UAAU,CAAC;SAC5C,MAAM,CAAC,kBAAkB,EAAE,eAAe,EAAE,IAAI,CAAC;SACjD,MAAM,CAAC,KAAK,EAAE,OAAyC,EAAE,OAAgB,EAAE,EAAE;QAC5E,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/E,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,yBAAyB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,mEAAmE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnQ,CAAC,CAAC,CAAC;IACL,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,4CAA4C,CAAC;SAC5E,MAAM,CAAC,mBAAmB,EAAE,iCAAiC,CAAC;SAC9D,MAAM,CAAC,+BAA+B,EAAE,oCAAoC,CAAC;SAC7E,cAAc,CAAC,mBAAmB,EAAE,kCAAkC,CAAC;SACvE,MAAM,CAAC,eAAe,EAAE,iBAAiB,EAAE,MAAM,CAAC;SAClD,MAAM,CAAC,sBAAsB,EAAE,4BAA4B,CAAC;SAC5D,MAAM,CAAC,kBAAkB,EAAE,qBAAqB,CAAC;SACjD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,OAAO,EAAE,gBAAgB,CAAC;SACjC,MAAM,CAAC,KAAK,EAAE,OAAqJ,EAAE,OAAgB,EAAE,EAAE;QACxL,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,UAAU,CAAC,oEAAoE,EAAE,6BAA6B,CAAC,CAAC;QAC5I,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO;YAAE,MAAM,IAAI,UAAU,CAAC,8DAA8D,EAAE,qBAAqB,CAAC,CAAC;QACvK,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9G,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrP,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,IAAI,UAAU,CAAC,mHAAmH,EAAE,qBAAqB,CAAC,CAAC;QACtL,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACnE,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,yQAAyQ,CAAC,CAAC;IACrS,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,sCAAsC,CAAC,CAAC;IAC/F,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,kBAAkB,CAAC;SACrD,MAAM,CAAC,gBAAgB,EAAE,8BAA8B,CAAC;SACxD,MAAM,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,CAAC;SACzC,MAAM,CAAC,KAAK,EAAE,OAA0C,EAAE,OAAgB,EAAE,EAAE;QAC7E,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7E,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,GAAG,KAAK,IAAI,CAAC,WAAW,IAAI,GAAG,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,2CAA2C,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,oBAAoB,CAAC,CAAC;IAC3Q,CAAC,CAAC,CAAC;IACL,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,+BAA+B,CAAC;SAClE,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC;SACpC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC;SAC3B,MAAM,CAAC,KAAK,EAAE,OAA2C,EAAE,OAAgB,EAAE,EAAE;QAC9E,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,UAAU,CAAC,oEAAoE,EAAE,6BAA6B,CAAC,CAAC;QAC7J,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3D,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,KAAK,MAAM,CAAC,WAAW,YAAY,MAAM,CAAC,WAAW,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,WAAW,IAAI,GAAG,KAAK,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzR,CAAC,CAAC,CAAC;IACL,QAAQ,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,WAAW,CAAC,uCAAuC,CAAC;SACpF,cAAc,CAAC,uBAAuB,EAAE,eAAe,CAAC;SACxD,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;SACzB,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAA6C,EAAE,OAAgB,EAAE,EAAE;QACjG,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,UAAU,CAAC,gEAAgE,EAAE,6BAA6B,CAAC,CAAC;QACxI,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QACtE,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,WAAW,IAAI,GAAG,KAAK,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACvH,CAAC,CAAC,CAAC;IACL,QAAQ,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,WAAW,CAAC,oBAAoB,CAAC;SACnE,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC;SACzB,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAA0B,EAAE,OAAgB,EAAE,EAAE;QAC9E,IAAI,CAAC,OAAO,CAAC,GAAG;YAAE,MAAM,IAAI,UAAU,CAAC,8DAA8D,EAAE,6BAA6B,CAAC,CAAC;QACtI,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IACL,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,WAAW,CAAC,gCAAgC,CAAC;SAC/E,MAAM,CAAC,mBAAmB,EAAE,wBAAwB,CAAC;SACrD,MAAM,CAAC,KAAK,EAAE,EAAsB,EAAE,OAA+B,EAAE,OAAgB,EAAE,EAAE;QAC1F,IAAI,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,mDAAmD,EAAE,qBAAqB,CAAC,CAAC;QACjJ,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,EAAG,CAAC,CAAC;QACxG,MAAM,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -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":"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"}
|
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/wecom/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Enterprise WeCom messaging through Numa
|
|
2
|
+
|
|
3
|
+
Numa calls DevOps platform APIs using its existing Keycloak identity. The WeCom corporate secret, token cache and directory access stay on the server. Do not copy the old `wecom-send-message` skill's credentials into Numa config, environment variables, command arguments or MCP input.
|
|
4
|
+
|
|
5
|
+
## Identity and availability
|
|
6
|
+
|
|
7
|
+
For PRD, confirm the intended environment and inspect the safe configuration/authentication commands before calling protected APIs:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
numa config --show --json
|
|
11
|
+
numa auth status --json
|
|
12
|
+
numa wecom capabilities --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
PRD uses issuer `https://kc.mcisaas.com/auth/realms/numa-realm`, CLI client `mcp-client`, application role client `mci-devops-platform`, and API base `https://apps-gw-prd.mcisaas.com/mci-devops-platform`. The browser's `web-client` session is separate from Numa's login. Gateway and backend enforce canonical application Client Roles; the CLI catalog does not make the authorization decision.
|
|
16
|
+
|
|
17
|
+
## Map enterprise users by email
|
|
18
|
+
|
|
19
|
+
Administrators with `mci-devops-platform-ops-admin` can preview matching Keycloak users and visible WeCom members by unique normalized email. Conflicts and missing email need review; automatic sync preserves manual mappings and manual unbinding records.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
numa wecom mappings sync --json
|
|
23
|
+
numa wecom mappings sync --apply --yes --json
|
|
24
|
+
numa wecom mappings list --query zhangsan --json
|
|
25
|
+
numa wecom mappings bind <keycloak-subject> --wecom-user zhangsan --yes --json
|
|
26
|
+
numa wecom mappings unbind <keycloak-subject> --yes --json
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`sync` defaults to `dryRun:true`. Apply rereads both directories and recomputes the mapping, so changes made after preview can affect its result. Review `status`, `candidate_user_ids` and masked email in the preview. Unbind retains a `MANUAL/UNMAPPED` record to prevent the next automatic sync from recreating the association. Manual binding validates the platform subject and explicit WeCom userid on the server. Resolve and review both identities before changing a mapping.
|
|
30
|
+
|
|
31
|
+
## Choose recipients and send
|
|
32
|
+
|
|
33
|
+
Search by name or userid (2–100 characters). Review candidates and choose exact userids; matching a name does not authorize sending to all matches. The recipient search returns at most 50 items; `has_more:true` means the query should be narrowed.
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
numa wecom recipients --query 张三 --json
|
|
37
|
+
numa wecom send --to zhangsan --request-id release-20260905-01 --text-file ./message.txt --yes --json
|
|
38
|
+
numa wecom send --platform-user <keycloak-subject> --request-id release-20260905-02 --type markdown --text-file ./message.md --yes --json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Use either `--to <userids...>` or `--platform-user <subjects...>`, never both. Platform user IDs are Keycloak subjects whose mappings must be active and whose Keycloak accounts must be enabled; the backend resolves them to actual WeCom userids. `@all`, duplicate recipients and department/tag broadcasts are not supported. The platform limit is 100 recipients; text and markdown each permit at most 2048 UTF-8 bytes. `--safe` is available for text only.
|
|
42
|
+
|
|
43
|
+
`--text-file -` reads UTF-8 content from standard input. Files and standard input avoid placing message content in shell history. `--text` is supported for short non-sensitive content but can be recorded in history. Neither CLI results nor error messages echo the message content.
|
|
44
|
+
|
|
45
|
+
`--yes` confirms the reviewed recipients and content and immediately sends. The request ID is mandatory, consists of 8–128 ASCII letters, digits or `._:-`, and is sent as `Idempotency-Key`. Numa prints it to stderr before sending; JSON stdout remains machine-readable. Use a stable ID for one logical request, and keep both recipients and content unchanged when recovering it.
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
numa wecom status --request-id release-20260905-01 --json
|
|
49
|
+
numa wecom status <message-id> --json
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Send writes the complete result before exiting: `ACCEPTED` exits 0, `PARTIAL` exits 7, `FAILED` exits 1, and `UNKNOWN`/`SENDING` exit 6. A successful status lookup exits 0 regardless of the recorded send status. MCP send returns `isError:true` for every non-`ACCEPTED` result while retaining the full structured record.
|
|
53
|
+
|
|
54
|
+
`ACCEPTED` means acceptance by the WeCom API, not delivery or reading. `PARTIAL` includes invalid recipient userids; inspect them without resending to accepted recipients. `FAILED` means the request failed. `SENDING`, `UNKNOWN`, HTTP 5xx or a lost/malformed send response require read-only status investigation. Do not generate a fresh request ID or blindly repeat the send. A status 404 after a lost response does not prove no message was sent. Numa performs no network/5xx send retry; a definitive 401 may refresh authentication and repeat with the same key and body.
|
|
55
|
+
|
|
56
|
+
## API and MCP contract
|
|
57
|
+
|
|
58
|
+
Message routes are under `/api/v1/notifications/wecom`: `GET /capabilities`, `GET /recipients?query=&limit=`, `POST /messages`, `GET /messages/{id}`, and `GET /messages/by-request/{requestId}`. A send body contains either `recipientUserIds` or `platformUserIds`, plus `messageType`, `content` and `safe`; the stable key is only in `Idempotency-Key`.
|
|
59
|
+
|
|
60
|
+
Numa bounds response reads while streaming: message APIs allow up to 1 MiB, and mapping APIs up to 32 MiB to accommodate full-directory sync results (up to 9,999 users). An interrupted, malformed or oversized apply response is reported as an unknown operation outcome; inspect mappings before retrying. Keycloak subjects are opaque identifiers up to 256 characters, including federated/Unicode IDs; whitespace padding and control characters are rejected without normalizing the identity.
|
|
61
|
+
|
|
62
|
+
Mapping routes are under `/api/v1/admin/notifications/wecom/mappings`: `GET /?query=&limit=`, `POST /sync` with `{dryRun:true|false}`, `PUT /{subject}` with `{wecomUserId}`, and `DELETE /{subject}` returning a tombstone mapping. Responses include metadata and masked email, never secrets or message content.
|
|
63
|
+
|
|
64
|
+
`numa serve` exposes these tools directly:
|
|
65
|
+
|
|
66
|
+
| Tool | Purpose |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `numa_wecom_capabilities` | Read channel state and limits |
|
|
69
|
+
| `numa_wecom_recipients` | Search and explicitly select recipients |
|
|
70
|
+
| `numa_wecom_send` | Send to `recipient_user_ids` or `platform_user_ids`; requires stable `request_id` and `confirmed:true` |
|
|
71
|
+
| `numa_wecom_status` | Read by exactly one `message_id` or `request_id` |
|
|
72
|
+
| `numa_wecom_mappings_list` | Read mappings and review conflicts |
|
|
73
|
+
| `numa_wecom_mappings_sync` | Preview by default; `apply:true` requires `confirmed:true` |
|
|
74
|
+
| `numa_wecom_mappings_bind` | Manual mapping; requires `confirmed:true` |
|
|
75
|
+
| `numa_wecom_mappings_unbind` | Manual unbinding tombstone; requires `confirmed:true` |
|
|
76
|
+
|
|
77
|
+
AI clients must obtain the user's authorization for the exact recipients and content before invoking the send tool. A lookup request alone never authorizes a send. Tool metadata marks send and mapping mutations as external side effects. The client must preserve `request_id` and use the status tool after uncertain outcomes. CLI JSON uses snake_case keys; MCP structured responses follow the API's camelCase DTOs.
|
|
78
|
+
|
|
79
|
+
## Verification
|
|
80
|
+
|
|
81
|
+
`npm run check` includes WeCom client, Commander registration, bounded UTF-8 input, mapping preview/confirmation, idempotency/unknown-result behavior and actual MCP `tools/list` + `tools/call` tests. These use stubbed platform responses and in-memory MCP transport; they send no real enterprise messages. A separate operator-authorized live smoke test is needed after server configuration and deployment.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@numa-tech/numa",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.33",
|
|
4
4
|
"description": "Cross-platform CLI for the Numa internal developer platform with Keycloak authentication and MCP support.",
|
|
5
5
|
"author": "numa-tech",
|
|
6
6
|
"keywords": [
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist",
|
|
23
23
|
"README.md",
|
|
24
|
+
"docs/wecom-messaging.md",
|
|
24
25
|
"examples",
|
|
25
26
|
"skills",
|
|
26
27
|
"THIRD_PARTY_NOTICES.md"
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
"typecheck": "turbo run typecheck root:typecheck",
|
|
33
34
|
"root:typecheck": "tsc --noEmit",
|
|
34
35
|
"test": "turbo run test root:test",
|
|
35
|
-
"root:test": "node --import tsx --test src/branding.test.ts src/app-config.test.ts src/platform-profile.test.ts src/identity.test.ts src/backend.test.ts src/oauth.test.ts src/web-console.test.ts src/cli-options.test.ts src/mail/client.test.ts src/ai-registry/client.test.ts src/ai-registry/workspace.test.ts src/ai-registry/namespace.test.ts src/ai-registry/sync.test.ts src/ai-registry/bridge.test.ts src/ai-registry/skill-installer.test.ts src/ai-registry/mcp-installer.test.ts src/media/client.test.ts src/media/commands.test.ts src/media/transfers.test.ts src/pipeline/client.test.ts src/pipeline/commands.test.ts src/gitops/client.test.ts src/gitops/commands.test.ts src/repositories/client.test.ts src/repositories/commands.test.ts src/publications/scanner.test.ts src/publications/client.test.ts src/publications/commands.test.ts src/jenkins-jobs/client.test.ts src/jenkins-jobs/commands.test.ts src/clusters/client.test.ts src/clusters/commands.test.ts src/clusters/kubeconfig-discovery.test.ts src/teams/client.test.ts src/teams/commands.test.ts src/authorization/client.test.ts src/authorization/commands.test.ts src/authorization/access-request-client.test.ts src/authorization/access-request-commands.test.ts src/application-candidates/client.test.ts src/application-candidates/commands.test.ts src/application-onboarding/client.test.ts src/application-onboarding/commands.test.ts src/application-onboarding/tui.test.ts src/chatgpt-desktop.test.ts src/codex-integration.test.ts scripts/release-all.test.mjs",
|
|
36
|
+
"root:test": "node --import tsx --test src/branding.test.ts src/app-config.test.ts src/platform-profile.test.ts src/identity.test.ts src/backend.test.ts src/oauth.test.ts src/web-console.test.ts src/cli-options.test.ts src/mail/client.test.ts src/wecom/client.test.ts src/wecom/commands.test.ts src/wecom/tools.test.ts src/ai-registry/client.test.ts src/ai-registry/workspace.test.ts src/ai-registry/namespace.test.ts src/ai-registry/sync.test.ts src/ai-registry/bridge.test.ts src/ai-registry/skill-installer.test.ts src/ai-registry/mcp-installer.test.ts src/media/client.test.ts src/media/commands.test.ts src/media/transfers.test.ts src/oss/client.test.ts src/oss/transfers.test.ts src/pipeline/client.test.ts src/pipeline/commands.test.ts src/gitops/client.test.ts src/gitops/commands.test.ts src/repositories/client.test.ts src/repositories/commands.test.ts src/publications/scanner.test.ts src/publications/client.test.ts src/publications/commands.test.ts src/jenkins-jobs/client.test.ts src/jenkins-jobs/commands.test.ts src/clusters/client.test.ts src/clusters/commands.test.ts src/clusters/kubeconfig-discovery.test.ts src/teams/client.test.ts src/teams/commands.test.ts src/authorization/client.test.ts src/authorization/commands.test.ts src/authorization/access-request-client.test.ts src/authorization/access-request-commands.test.ts src/application-candidates/client.test.ts src/application-candidates/commands.test.ts src/application-onboarding/client.test.ts src/application-onboarding/commands.test.ts src/application-onboarding/tui.test.ts src/chatgpt-desktop.test.ts src/codex-integration.test.ts scripts/release-all.test.mjs",
|
|
36
37
|
"pack:check": "turbo run pack:check root:pack:check",
|
|
37
38
|
"root:pack:check": "node scripts/check-root-package.mjs",
|
|
38
39
|
"boundaries": "node scripts/check-workspace-boundaries.mjs",
|
|
@@ -55,8 +56,9 @@
|
|
|
55
56
|
"dependencies": {
|
|
56
57
|
"@clack/prompts": "^1.7.0",
|
|
57
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
58
|
-
"@numa-tech/cli-auth": "1.14.
|
|
59
|
-
"@tokensrc/codex": "1.14.
|
|
59
|
+
"@numa-tech/cli-auth": "1.14.33",
|
|
60
|
+
"@tokensrc/codex": "1.14.33",
|
|
61
|
+
"ali-oss": "^6.23.0",
|
|
60
62
|
"commander": "^14.0.3",
|
|
61
63
|
"ink": "^6.8.0",
|
|
62
64
|
"open": "^10.2.0",
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
"zod": "^4.4.3"
|
|
66
68
|
},
|
|
67
69
|
"devDependencies": {
|
|
70
|
+
"@types/ali-oss": "^6.23.3",
|
|
68
71
|
"@types/node": "^25.0.0",
|
|
69
72
|
"@types/react": "^19.2.18",
|
|
70
73
|
"@types/yauzl": "^3.4.0",
|
package/skills/numa-cli/SKILL.md
CHANGED
|
@@ -29,6 +29,7 @@ Never read or print token caches, inject a raw Bearer token, request Jenkins/Nac
|
|
|
29
29
|
- When the user explicitly requires a local image build and direct Flux release because the governed pipeline is unavailable, use the dedicated `$numa-local-flux-deploy` Skill instead of this Skill's pipeline path. Do not infer that fallback merely from a pipeline error.
|
|
30
30
|
- For other application publishing, production deployment, Jenkins Pipeline build/status/log/stop/retry, idempotency, rollout order, or release recovery, read [pipeline-release.md](references/pipeline-release.md) completely before acting.
|
|
31
31
|
- For `numa registry`, Nacos Skills/MCP, namespace routing, Registry approval, or enterprise Router work, use the dedicated `$numa-ai-registry` Skill when it is available. Otherwise inspect `numa registry capabilities --json` and command help before every write.
|
|
32
|
+
- For `numa oss`, OSS Grant/STS, bucket or prefix object access, and machine-client OSS workflows, use the dedicated `$numa-oss-access` Skill when it is available. Do not extract raw STS credentials for `ossutil` or an ad-hoc SDK script.
|
|
32
33
|
- For application onboarding and SCM, inspect `numa app --help`, `numa app scm --help`, and the corresponding entries in `numa commands --json`; preserve server-owned session state and optimistic-lock revisions.
|
|
33
34
|
- For Codex, media, configuration, identity, or admin modules, use the command catalog plus exact subcommand help. Do not invent missing behavior. Add a focused reference module to this Skill when that workflow becomes repetitive or safety-critical.
|
|
34
35
|
|