@thinkingai/ae-cli 6.1.18 → 6.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +2 -0
  2. package/README.zh.md +6 -0
  3. package/dist/{auth-ROB2EDYV.js → auth-QPEVVBKT.js} +13 -7
  4. package/dist/auth-UMJAA72O.js +15 -0
  5. package/dist/{capability-DKMYUTLC.js → capability-ASI62I4A.js} +11 -10
  6. package/dist/{chunk-UW5UN47B.js → chunk-533JK3YX.js} +2 -0
  7. package/dist/{chunk-4XXOWOTA.js → chunk-6QK7SYYY.js} +5 -4
  8. package/dist/{chunk-TUKQZTMI.js → chunk-7K24F7T2.js} +2 -0
  9. package/dist/{chunk-OO6XR6LK.js → chunk-AMBFK2K3.js} +2 -0
  10. package/dist/{sync-HKIOZXQE.js → chunk-B5HMQAOM.js} +31 -540
  11. package/dist/{chunk-Y3LOALAV.js → chunk-DKEXH6Q5.js} +4 -3
  12. package/dist/{chunk-VTWMIC5L.js → chunk-E2JKXMVO.js} +2 -0
  13. package/dist/{chunk-JHENBQ5B.js → chunk-FR2EDAQM.js} +1 -0
  14. package/dist/{chunk-ZQ47LWTI.js → chunk-GJXRF7Y6.js} +6 -5
  15. package/dist/{chunk-DWO43OIB.js → chunk-JQ3ENZZH.js} +2 -0
  16. package/dist/chunk-JYMGHGCE.js +191 -0
  17. package/dist/{chunk-BYYS3ANB.js → chunk-KEWVSO64.js} +5 -4
  18. package/dist/{chunk-3FY3RJ26.js → chunk-KZ6ZHF5J.js} +2 -1
  19. package/dist/{chunk-ZQKDZXDO.js → chunk-O6K2ODUE.js} +3 -2
  20. package/dist/chunk-QL6R6TSK.js +25215 -0
  21. package/dist/{chunk-J2DEBMRF.js → chunk-RWXIXOZG.js} +22 -9
  22. package/dist/{chunk-QZ3AS4KK.js → chunk-S5PONFBM.js} +5 -4
  23. package/dist/{chunk-4NN5IWVN.js → chunk-T6OSFQZD.js} +2 -0
  24. package/dist/{chunk-3KWQYGYI.js → chunk-TKHSULJT.js} +2 -0
  25. package/dist/{chunk-6EIJSNBD.js → chunk-Y74WTIKJ.js} +2 -0
  26. package/dist/{client-L2YDMHQ6.js → client-N7NGZUNI.js} +4 -3
  27. package/dist/{community-report-client-C7WDGET3.js → community-report-client-6OL6GBQW.js} +3 -2
  28. package/dist/{config-BMYZX2UE.js → config-XNMACCKX.js} +8 -7
  29. package/dist/{data-integration-QEKDWQDY.js → data-integration-4NZGZ45E.js} +913 -92
  30. package/dist/index.js +38 -36
  31. package/dist/{local-data-upload-client-4YYHSYD6.js → local-data-upload-client-EFHDLXFG.js} +3 -2
  32. package/dist/{memory-I2WXDTV2.js → memory-HBJQ2GC5.js} +8 -7
  33. package/dist/{metadata-I4C2EWUN.js → metadata-62EWBEIG.js} +12 -11
  34. package/dist/{model-HLHIEFMU.js → model-BHLJ2QMT.js} +10 -9
  35. package/dist/{personal-semantic-preference-LIPACBDX.js → personal-semantic-preference-JBVMAI7L.js} +11 -10
  36. package/dist/{project-semantic-RT3R2VQD.js → project-semantic-BQ4MPUS5.js} +12 -11
  37. package/dist/sync-MUWPRM5I.js +522 -0
  38. package/dist/{te-agent-BR6VDBNX.js → te-agent-WQZTMEK4.js} +10 -9
  39. package/dist/{te-analysis-7VUNUYWZ.js → te-analysis-MNCG65LU.js} +107 -20
  40. package/dist/{te-community-5DMNKJWY.js → te-community-BNE6KREL.js} +8 -7
  41. package/dist/{te-dataops-6P5IKWNJ.js → te-dataops-TXLKM2W6.js} +467 -67
  42. package/dist/{te-engage-KZPR5R22.js → te-engage-TPKLRVTL.js} +84 -10
  43. package/dist/{te-experiment-6BITX4RD.js → te-experiment-6BYKTCY7.js} +85 -12
  44. package/dist/{te-kb-RCLSSH2Q.js → te-kb-BUD6N3NI.js} +311 -24
  45. package/dist/{te-system-FXITO2JG.js → te-system-44LZJDTQ.js} +573 -37
  46. package/dist/{te-team-ADOC2ROP.js → te-team-XMP47YXE.js} +10 -7
  47. package/dist/{update-YCYCKJOO.js → update-UODFOZ4P.js} +7 -6
  48. package/package.json +18 -9
  49. package/skills/ae-analysis/references/analysis_gateway_assets.md +1 -1
  50. package/skills/ae-analysis/references/audience_models.md +18 -0
  51. package/skills/ae-analysis/references/command_index.md +2 -2
  52. package/skills/ae-analysis/references/project_timezone_update.md +13 -4
  53. package/skills/ae-analysis/references/property_create.md +2 -0
  54. package/skills/ae-analysis/references/super_metadata_batch_create.md +2 -0
  55. package/skills/ae-analysis/references/user_cluster_models.md +2 -0
  56. package/skills/ae-analysis/references/user_cluster_update.md +8 -4
  57. package/skills/ae-analysis/references/user_tag_create.md +7 -1
  58. package/skills/ae-analysis/references/user_tag_models.md +8 -2
  59. package/skills/ae-data-integration/SKILL.md +1 -0
  60. package/skills/ae-data-integration/references/error-handling.md +54 -1
  61. package/skills/ae-data-integration/references/source-inspect.md +18 -2
  62. package/skills/ae-data-integration/references/transform.md +3 -1
  63. package/skills/ae-data-integration/references/ue-routing.md +23 -0
  64. package/skills/ae-dataops/SKILL.md +11 -1
  65. package/skills/ae-dataops/references/dataops-backfill.md +135 -0
  66. package/skills/ae-engage/SKILL.md +5 -0
  67. package/skills/ae-engage/references/build-task-save-guide.md +5 -1
  68. package/skills/ae-engage/references/save-flow.md +37 -1
  69. package/skills/ae-engage/references/save-task.md +6 -0
  70. package/skills/ae-experiment/SKILL.md +6 -2
  71. package/skills/ae-experiment/references/save_metric.md +20 -8
  72. package/skills/ae-kb/SKILL.md +120 -9
  73. package/skills/ae-system/SKILL.md +28 -30
  74. package/skills/ae-system/references/channel-management.md +303 -0
  75. package/dist/auth-GBMV6TEJ.js +0 -14
  76. package/dist/capability-HYVVPG25.js +0 -352
  77. package/dist/chunk-EFH4XWYC.js +0 -556
  78. package/dist/chunk-JRJY5DMJ.js +0 -71
  79. package/dist/chunk-OMPRXM3V.js +0 -349
  80. package/dist/chunk-QNOLN2LJ.js +0 -509
  81. package/dist/chunk-RJDU7NYP.js +0 -1198
  82. package/dist/chunk-RNAALWJK.js +0 -98
  83. package/dist/chunk-SERWF6G5.js +0 -13
  84. package/dist/memory-3ORCR7JH.js +0 -893
  85. package/dist/metadata-VUOQJE26.js +0 -339
  86. package/dist/model-UGRDX4MW.js +0 -139
  87. package/dist/personal-semantic-preference-OEISBRHM.js +0 -239
  88. package/dist/project-semantic-FFPWFPIW.js +0 -1114
  89. package/dist/sync-TFHU2UTG.js +0 -10261
  90. package/dist/te-agent-VLYOV7S4.js +0 -3894
  91. package/dist/te-analysis-4YGQL5RC.js +0 -9357
  92. package/dist/te-community-ISDQWJU7.js +0 -1859
  93. package/dist/te-dataops-CVULXNVB.js +0 -2209
  94. package/dist/te-engage-N5WI32H6.js +0 -4898
  95. package/dist/te-experiment-UVR4HLND.js +0 -988
  96. package/dist/te-system-K2GYMCTB.js +0 -2213
@@ -1,1859 +0,0 @@
1
- import {
2
- registerCapabilityGatewayRoute
3
- } from "./chunk-3KWQYGYI.js";
4
- import {
5
- internalCallSourceHeaders
6
- } from "./chunk-SERWF6G5.js";
7
- import {
8
- clearCliToken,
9
- getCliToken
10
- } from "./chunk-J2DEBMRF.js";
11
- import "./chunk-ZQKDZXDO.js";
12
- import {
13
- getActiveHost,
14
- safeJsonParse
15
- } from "./chunk-3FY3RJ26.js";
16
- import {
17
- CliValidationError,
18
- PermissionError
19
- } from "./chunk-UW5UN47B.js";
20
- import {
21
- logger
22
- } from "./chunk-JHENBQ5B.js";
23
-
24
- // src/core/mcp.ts
25
- async function permissionDetails(resp) {
26
- const text = await resp.text().catch(() => "");
27
- try {
28
- const d = safeJsonParse(text);
29
- const msg = d && (d.error?.message || (typeof d.error === "string" ? d.error : void 0) || d.message);
30
- if (msg && typeof msg === "string") {
31
- return {
32
- message: msg,
33
- code: typeof d.code === "string" ? d.code : typeof d.error?.code === "string" ? d.error.code : void 0,
34
- hint: typeof d.hint === "string" ? d.hint : typeof d.error?.hint === "string" ? d.error.hint : void 0
35
- };
36
- }
37
- } catch {
38
- }
39
- return { message: "Permission denied for this resource (HTTP 403)" };
40
- }
41
- var MCP_PROTOCOL_VERSION = "2025-11-25";
42
- var JSONRPC_VERSION = "2.0";
43
- var serviceMappingMap = /* @__PURE__ */ new Map();
44
- function registerMcpMappings(mappings) {
45
- for (const [serviceName, mapping] of Object.entries(mappings)) {
46
- serviceMappingMap.set(serviceName, mapping);
47
- }
48
- }
49
- function getMcpMapping(serviceName) {
50
- const mapping = serviceMappingMap.get(serviceName);
51
- if (!mapping) {
52
- throw new Error(`MCP mapping not found for service: '${serviceName}'. Please register it using registerMcpMapping() first.`);
53
- }
54
- return mapping;
55
- }
56
- function buildMcpUrl(host, serviceName) {
57
- const mapping = getMcpMapping(serviceName);
58
- const base = host.replace(/\/+$/, "");
59
- return `${base}/mcp/${mapping.componentName}/http/${mapping.mappingPath}`;
60
- }
61
- function resolveMcpUrl(mcpUrlOverride, host, serviceName) {
62
- if (mcpUrlOverride) {
63
- return mcpUrlOverride;
64
- }
65
- return buildMcpUrl(host, serviceName);
66
- }
67
- var requestIdCounter = 1;
68
- function genRequestId() {
69
- return requestIdCounter++;
70
- }
71
- function buildAuthHeaders(cliToken) {
72
- return {
73
- "cli-token": cliToken
74
- };
75
- }
76
- async function mcpRequest(url, method, params = {}, hostOverride) {
77
- const hostUrl = hostOverride || getActiveHost();
78
- const token = await getCliToken(hostUrl);
79
- const requestId = genRequestId();
80
- const paramsWithMeta = {
81
- ...params,
82
- _meta: { progressToken: requestId }
83
- };
84
- const body = {
85
- jsonrpc: JSONRPC_VERSION,
86
- id: requestId,
87
- method,
88
- params: paramsWithMeta
89
- };
90
- const headers = {
91
- "Content-Type": "application/json",
92
- "Accept": "application/json, text/event-stream",
93
- "mcp-protocol-version": MCP_PROTOCOL_VERSION,
94
- ...internalCallSourceHeaders(),
95
- ...buildAuthHeaders(token)
96
- };
97
- const resp = await fetch(url, {
98
- method: "POST",
99
- headers,
100
- body: JSON.stringify(body)
101
- });
102
- if (!resp.ok) {
103
- if (resp.status === 403) {
104
- const permission = await permissionDetails(resp);
105
- throw new PermissionError(permission.message, permission.code, permission.hint);
106
- }
107
- if (resp.status === 401) {
108
- logger.warn(`MCP request failed (HTTP 401) for ${hostUrl}, refreshing CLI token`);
109
- clearCliToken(hostUrl);
110
- const newToken = await getCliToken(hostUrl);
111
- logger.info(`CLI token refreshed for ${hostUrl}`);
112
- process.stderr.write(`[ae-cli] CLI token refreshed for ${hostUrl}
113
- `);
114
- Object.assign(headers, buildAuthHeaders(newToken));
115
- const retryResp = await fetch(url, {
116
- method: "POST",
117
- headers,
118
- body: JSON.stringify(body)
119
- });
120
- if (retryResp.status === 403) {
121
- const permission = await permissionDetails(retryResp);
122
- throw new PermissionError(permission.message, permission.code, permission.hint);
123
- }
124
- if (!retryResp.ok) {
125
- throw new Error(`MCP HTTP error: ${retryResp.status} ${retryResp.statusText}`);
126
- }
127
- const retryData = safeJsonParse(await retryResp.text());
128
- if (retryData.error) {
129
- throw new Error(`MCP error: ${retryData.error.message || JSON.stringify(retryData.error)}`);
130
- }
131
- return retryData.result;
132
- }
133
- throw new Error(`MCP HTTP error: ${resp.status} ${resp.statusText}`);
134
- }
135
- const data = safeJsonParse(await resp.text());
136
- if (data.error) {
137
- throw new Error(`MCP error: ${data.error.message || JSON.stringify(data.error)}`);
138
- }
139
- return data.result;
140
- }
141
- async function callMcpTool(url, toolName, args = {}, hostOverride) {
142
- const params = {
143
- name: toolName,
144
- arguments: args
145
- };
146
- logger.info(`MCP tool call: ${toolName}`);
147
- const result = await mcpRequest(url, "tools/call", params, hostOverride);
148
- if (result.isError) {
149
- logger.warn(`MCP tool call failed: ${toolName}`);
150
- }
151
- return {
152
- content: result.content.map((item) => {
153
- if (item.type === "text") {
154
- return { type: "text", text: item.text };
155
- } else if (item.type === "image") {
156
- return { type: "image", data: item.data, mimeType: item.mimeType };
157
- } else {
158
- return { type: "resource", ...item };
159
- }
160
- }),
161
- isError: result.isError
162
- };
163
- }
164
- function parseMcpResult(result) {
165
- if (result.isError) {
166
- const firstText = result.content.find((item) => item.type === "text")?.text || "";
167
- let errorMessage = firstText.trim() || "MCP tool call failed";
168
- try {
169
- const parsed = safeJsonParse(firstText);
170
- errorMessage = mcpResultErrorMessage(parsed) || errorMessage;
171
- } catch {
172
- }
173
- throwMcpError(errorMessage);
174
- }
175
- if (result.content.length > 0 && result.content[0].type === "text") {
176
- const text = result.content[0].text || "";
177
- let parsed;
178
- try {
179
- parsed = safeJsonParse(text);
180
- } catch {
181
- return text;
182
- }
183
- const errMsg = mcpResultErrorMessage(parsed);
184
- if (errMsg !== null) {
185
- throwMcpError(errMsg);
186
- }
187
- return parsed;
188
- }
189
- return result;
190
- }
191
- function mcpResultErrorMessage(parsed) {
192
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
193
- if (parsed.success === true || parsed.ok === true) return null;
194
- const explicitlyFailed = parsed.success === false || parsed.ok === false;
195
- const e = parsed.error;
196
- if (typeof e === "string" && e.trim()) return e;
197
- if (e && typeof e === "object") {
198
- const msg = typeof e.message === "string" && e.message || void 0;
199
- const code = typeof e.code === "string" && e.code || void 0;
200
- if (msg && code) return `${code}: ${msg}`;
201
- if (msg || code) return msg || code;
202
- }
203
- if (explicitlyFailed) {
204
- const msg = typeof parsed.message === "string" && parsed.message.trim() ? parsed.message.trim() : void 0;
205
- const codeValue = parsed.errorCode ?? parsed.error_code ?? parsed.code;
206
- const code = typeof codeValue === "string" && codeValue.trim() ? codeValue.trim() : void 0;
207
- if (msg && code) return `${code}: ${msg}`;
208
- return msg || code || "MCP tool call failed";
209
- }
210
- return null;
211
- }
212
- function throwMcpError(message) {
213
- if (/no_auth|auth_error|permission|forbidden|unauthor|\u65e0\u6743/i.test(message)) {
214
- throw new PermissionError(message);
215
- }
216
- throw new Error(message);
217
- }
218
-
219
- // src/commands/te-community/get_channel_info.ts
220
- var getChannelInfo = {
221
- service: "community",
222
- command: "+get_channel_info",
223
- description: "Valid channel list for a community space (channel id and name).",
224
- flags: [
225
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
226
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" }
227
- ],
228
- risk: "read",
229
- execute: async (ctx) => {
230
- const spaceId = ctx.num("space-id");
231
- const gameId = ctx.num("game-id");
232
- const host = ctx.host();
233
- const param = { gameId };
234
- const args = { spaceId, param };
235
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_analysis");
236
- const result = await callMcpTool(mcpUrl, "get_channel_info", args, host);
237
- return parseMcpResult(result);
238
- }
239
- };
240
-
241
- // src/commands/te-community/search_posts.ts
242
- var searchPosts = {
243
- service: "community",
244
- command: "+search_posts",
245
- description: "Search social posts/videos/feeds in a community space.",
246
- flags: [
247
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
248
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
249
- { name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info (channelId). Comma-separated." },
250
- { name: "keywords", type: "string", required: false, desc: "Keyword filter list. Comma-separated." },
251
- { name: "start-time", type: "string", required: true, desc: "Content publish time lower bound. Format: yyyy-MM-dd." },
252
- { name: "end-time", type: "string", required: true, desc: "Content publish time upper bound. Format: yyyy-MM-dd." },
253
- { name: "official", type: "boolean", required: false, desc: "If true, only official accounts." },
254
- { name: "resource-type", type: "string", required: false, desc: "Resource types: 0 post, 1 video. Comma-separated." },
255
- { name: "search-mode", type: "number", required: false, desc: 'Search mode: 0 tokenized (default), 1 exact substring (e.g. \\"#tag\\" matches only posts containing that full string), 2 author name.' },
256
- { name: "search-word", type: "string", required: false, desc: "Full-text query; used together with searchMode." },
257
- { name: "sentiment-types", type: "string", required: false, desc: "Sentiment filter: 0 negative, 1 positive, 2 neutral. Comma-separated." },
258
- { name: "order-by", type: "number", required: false, desc: "Sort: 0 publish time desc, 1 weight desc, 2 publish time asc, 3 reply count desc, 4 heat desc." },
259
- { name: "page-num", type: "number", required: false, desc: "Page number, starting at 1." },
260
- { name: "page-size", type: "number", required: false, desc: "Page size. Max 10000." }
261
- ],
262
- risk: "read",
263
- execute: async (ctx) => {
264
- const spaceId = ctx.num("space-id");
265
- const gameId = ctx.num("game-id");
266
- const startTime = ctx.str("start-time");
267
- const endTime = ctx.str("end-time");
268
- const host = ctx.host();
269
- const param = { gameId, startTime, endTime };
270
- const channelIdList = ctx.str("channel-id-list");
271
- if (channelIdList) param.channelIdList = channelIdList.split(",").map(Number);
272
- const keywords = ctx.str("keywords");
273
- if (keywords) param.keywords = keywords.split(",");
274
- const official = ctx.bool("official");
275
- if (official !== void 0) param.official = official;
276
- const resourceType = ctx.str("resource-type");
277
- if (resourceType) param.resourceType = resourceType.split(",").map(Number);
278
- const searchMode = ctx.num("search-mode");
279
- if (searchMode !== void 0) param.searchMode = searchMode;
280
- const searchWord = ctx.str("search-word");
281
- if (searchWord) param.searchWord = searchWord;
282
- const sentimentTypes = ctx.str("sentiment-types");
283
- if (sentimentTypes) param.sentimentTypes = sentimentTypes.split(",").map(Number);
284
- const orderBy = ctx.num("order-by");
285
- const pageNum = ctx.num("page-num");
286
- const pageSize = ctx.num("page-size");
287
- if (orderBy !== void 0 || pageNum || pageSize) {
288
- param.pagerHeader = {};
289
- if (orderBy !== void 0) param.pagerHeader.orderBy = orderBy;
290
- if (pageNum) param.pagerHeader.pageNum = pageNum;
291
- if (pageSize) param.pagerHeader.pageSize = pageSize;
292
- }
293
- const args = { spaceId, param };
294
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
295
- const result = await callMcpTool(mcpUrl, "search_posts", args, host);
296
- return parseMcpResult(result);
297
- }
298
- };
299
-
300
- // src/commands/te-community/get_post_detail.ts
301
- var getPostDetail = {
302
- service: "community",
303
- command: "+get_post_detail",
304
- description: "Full detail for one post/video: body, media, subtitles, danmu, sentiment, keywords, topics, tag values, and comment pager.",
305
- flags: [
306
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
307
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
308
- { name: "channel-id", type: "number", required: true, desc: "Channel ID." },
309
- { name: "uuid", type: "string", required: true, desc: "Content UUID (unique id)." },
310
- { name: "resource-type", type: "number", required: true, desc: "Resource type: 0 post, 1 video." },
311
- { name: "danmu-order-by", type: "number", required: false, desc: "Sort for danmu: 0 video timestamp desc, 1 publish time desc." },
312
- { name: "danmu-page-num", type: "number", required: false, desc: "Page number for danmu pagination, starting at 1." },
313
- { name: "danmu-page-size", type: "number", required: false, desc: "Page size for danmu pagination. Max 10000." },
314
- { name: "reply-order-by", type: "number", required: false, desc: "Sort for comments: 0 publish time desc, 1 publish time asc." },
315
- { name: "reply-page-num", type: "number", required: false, desc: "Page number for comments, starting at 1." },
316
- { name: "reply-page-size", type: "number", required: false, desc: "Page size for comments. Max 10000." }
317
- ],
318
- risk: "read",
319
- execute: async (ctx) => {
320
- const spaceId = ctx.num("space-id");
321
- const gameId = ctx.num("game-id");
322
- const channelId = ctx.num("channel-id");
323
- const uuid = ctx.str("uuid");
324
- const resourceType = ctx.num("resource-type");
325
- const host = ctx.host();
326
- const param = { gameId, channelId, uuid, resourceType };
327
- const danmuOrderBy = ctx.num("danmu-order-by");
328
- const danmuPageNum = ctx.num("danmu-page-num");
329
- const danmuPageSize = ctx.num("danmu-page-size");
330
- if (danmuOrderBy !== void 0 || danmuPageNum || danmuPageSize) {
331
- param.danmuPagerHeader = {};
332
- if (danmuOrderBy !== void 0) param.danmuPagerHeader.orderBy = danmuOrderBy;
333
- if (danmuPageNum) param.danmuPagerHeader.pageNum = danmuPageNum;
334
- if (danmuPageSize) param.danmuPagerHeader.pageSize = danmuPageSize;
335
- }
336
- const replyOrderBy = ctx.num("reply-order-by");
337
- const replyPageNum = ctx.num("reply-page-num");
338
- const replyPageSize = ctx.num("reply-page-size");
339
- if (replyOrderBy !== void 0 || replyPageNum || replyPageSize) {
340
- param.replyPagerHeader = {};
341
- if (replyOrderBy !== void 0) param.replyPagerHeader.orderBy = replyOrderBy;
342
- if (replyPageNum) param.replyPagerHeader.pageNum = replyPageNum;
343
- if (replyPageSize) param.replyPagerHeader.pageSize = replyPageSize;
344
- }
345
- const args = { spaceId, param };
346
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
347
- const result = await callMcpTool(mcpUrl, "get_post_detail", args, host);
348
- return parseMcpResult(result);
349
- }
350
- };
351
-
352
- // src/commands/te-community/get_overview_metrics.ts
353
- var getOverviewMetrics = {
354
- service: "community",
355
- command: "+get_overview_metrics",
356
- description: "Overall community metrics in a date range: post/reply counts, sentiment counts, per-channel breakdown.",
357
- flags: [
358
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
359
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
360
- { name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info (channelId)." },
361
- { name: "start-time", type: "string", required: true, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
362
- { name: "end-time", type: "string", required: true, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
363
- ],
364
- risk: "read",
365
- execute: async (ctx) => {
366
- const spaceId = ctx.num("space-id");
367
- const gameId = ctx.num("game-id");
368
- const startTime = ctx.str("start-time");
369
- const endTime = ctx.str("end-time");
370
- const host = ctx.host();
371
- const param = { gameId, startTime, endTime };
372
- const channelIdList = ctx.str("channel-id-list");
373
- if (channelIdList) param.channelIdList = channelIdList.split(",").map(Number);
374
- const args = { spaceId, param };
375
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_analysis");
376
- const result = await callMcpTool(mcpUrl, "get_overview_metrics", args, host);
377
- return parseMcpResult(result);
378
- }
379
- };
380
-
381
- // src/commands/te-community/get_sentiment_overview.ts
382
- var getSentimentOverview = {
383
- service: "community",
384
- command: "+get_sentiment_overview",
385
- description: "Sentiment distribution and hot-keyword overview: top keywords per sentiment with trends, plus overall keyword ranking.",
386
- flags: [
387
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
388
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
389
- { name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info (channelId)." },
390
- { name: "start-time", type: "string", required: true, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
391
- { name: "end-time", type: "string", required: true, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." },
392
- { name: "is-merged", type: "boolean", required: false, desc: "Whether to aggregate merged keywords." },
393
- { name: "limit", type: "number", required: false, desc: "Max number of keywords to return. Max 10000." }
394
- ],
395
- risk: "read",
396
- execute: async (ctx) => {
397
- const spaceId = ctx.num("space-id");
398
- const gameId = ctx.num("game-id");
399
- const startTime = ctx.str("start-time");
400
- const endTime = ctx.str("end-time");
401
- const host = ctx.host();
402
- const param = { gameId, startTime, endTime };
403
- const channelIdList = ctx.str("channel-id-list");
404
- if (channelIdList) param.channelIdList = channelIdList.split(",").map(Number);
405
- const isMerged = ctx.bool("is-merged");
406
- if (isMerged !== void 0) param.isMerged = isMerged;
407
- const limit = ctx.num("limit");
408
- if (limit) param.limit = limit;
409
- const args = { spaceId, param };
410
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_analysis");
411
- const result = await callMcpTool(mcpUrl, "get_sentiment_overview", args, host);
412
- return parseMcpResult(result);
413
- }
414
- };
415
-
416
- // src/commands/te-community/get_tag_trends.ts
417
- var getTagTrends = {
418
- service: "community",
419
- command: "+get_tag_trends",
420
- description: "Daily tag trends: given tagCode and date range, returns counts and time series per tag value.",
421
- flags: [
422
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
423
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
424
- { name: "tag-code", type: "string", required: true, desc: "Tag code (tagCode) from +get_corpus_tags." },
425
- { name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info." },
426
- { name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
427
- { name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
428
- ],
429
- risk: "read",
430
- execute: async (ctx) => {
431
- const spaceId = ctx.num("space-id");
432
- const gameId = ctx.num("game-id");
433
- const tagCode = ctx.str("tag-code");
434
- const host = ctx.host();
435
- const param = { gameId, tagCode };
436
- const channelIdList = ctx.str("channel-id-list");
437
- if (channelIdList) param.channelIdList = channelIdList.split(",").map(Number);
438
- const startTime = ctx.str("start-time");
439
- if (startTime) param.startTime = startTime;
440
- const endTime = ctx.str("end-time");
441
- if (endTime) param.endTime = endTime;
442
- const args = { spaceId, param };
443
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_analysis");
444
- const result = await callMcpTool(mcpUrl, "get_tag_trends", args, host);
445
- return parseMcpResult(result);
446
- }
447
- };
448
-
449
- // src/commands/te-community/get_daily_summary.ts
450
- var getDailySummary = {
451
- service: "community",
452
- command: "+get_daily_summary",
453
- description: "Daily community summary for a given date: hot topic rankings and highlighted opinions (positive/negative).",
454
- flags: [
455
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
456
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
457
- { name: "date", type: "string", required: true, desc: "Summary date. Format: yyyy-MM-dd." }
458
- ],
459
- risk: "read",
460
- execute: async (ctx) => {
461
- const spaceId = ctx.num("space-id");
462
- const gameId = ctx.num("game-id");
463
- const date = ctx.str("date");
464
- const host = ctx.host();
465
- const param = { gameId, date };
466
- const args = { spaceId, param };
467
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_hot");
468
- const result = await callMcpTool(mcpUrl, "get_daily_summary", args, host);
469
- return parseMcpResult(result);
470
- }
471
- };
472
-
473
- // src/commands/te-community/get_hot_topics.ts
474
- var getHotTopics = {
475
- service: "community",
476
- command: "+get_hot_topics",
477
- description: "Hot topics leaderboard over a time range.",
478
- flags: [
479
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
480
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
481
- { name: "start-time", type: "string", required: false, desc: "Topic statistics range start (inclusive). Format: yyyy-MM-dd." },
482
- { name: "end-time", type: "string", required: false, desc: "Topic statistics range end (inclusive). Format: yyyy-MM-dd." },
483
- { name: "order-by", type: "number", required: false, desc: "Sort: 0 by topic heat desc, 1 by topic start time desc." }
484
- ],
485
- risk: "read",
486
- execute: async (ctx) => {
487
- const spaceId = ctx.num("space-id");
488
- const gameId = ctx.num("game-id");
489
- const host = ctx.host();
490
- const param = { gameId };
491
- const startTime = ctx.str("start-time");
492
- if (startTime) param.startTime = startTime;
493
- const endTime = ctx.str("end-time");
494
- if (endTime) param.endTime = endTime;
495
- const orderBy = ctx.num("order-by");
496
- if (orderBy !== void 0) param.orderBy = orderBy;
497
- const args = { spaceId, param };
498
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_hot");
499
- const result = await callMcpTool(mcpUrl, "get_hot_topics", args, host);
500
- return parseMcpResult(result);
501
- }
502
- };
503
-
504
- // src/commands/te-community/get_topic_detail.ts
505
- var getTopicDetail = {
506
- service: "community",
507
- command: "+get_topic_detail",
508
- description: "Deep stats for one topic in a range: content/reply totals, sentiment, per-channel daily trends, hot keywords.",
509
- flags: [
510
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
511
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
512
- { name: "topic-id", type: "string", required: true, desc: "Topic ID from +get_hot_topics (topicId)." },
513
- { name: "start-time", type: "string", required: true, desc: "Statistics start date. Format: yyyy-MM-dd. Prefer topic startTime from +get_hot_topics." },
514
- { name: "end-time", type: "string", required: true, desc: "Statistics end date. Format: yyyy-MM-dd. Prefer topic endTime from +get_hot_topics." }
515
- ],
516
- risk: "read",
517
- execute: async (ctx) => {
518
- const spaceId = ctx.num("space-id");
519
- const gameId = ctx.num("game-id");
520
- const topicId = ctx.str("topic-id");
521
- const startTime = ctx.str("start-time");
522
- const endTime = ctx.str("end-time");
523
- const host = ctx.host();
524
- const param = { gameId, topicId, startTime, endTime };
525
- const args = { spaceId, param };
526
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_hot");
527
- const result = await callMcpTool(mcpUrl, "get_topic_detail", args, host);
528
- return parseMcpResult(result);
529
- }
530
- };
531
-
532
- // src/commands/te-community/get_livestream_overview.ts
533
- var getLivestreamOverview = {
534
- service: "community",
535
- command: "+get_livestream_overview",
536
- description: "Livestream overview trends: active rooms, session count, interactions, and streamed duration.",
537
- flags: [
538
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
539
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
540
- { name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
541
- { name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
542
- ],
543
- risk: "read",
544
- execute: async (ctx) => {
545
- const spaceId = ctx.num("space-id");
546
- const gameId = ctx.num("game-id");
547
- const host = ctx.host();
548
- const param = { gameId };
549
- const startTime = ctx.str("start-time");
550
- if (startTime) param.startTime = startTime;
551
- const endTime = ctx.str("end-time");
552
- if (endTime) param.endTime = endTime;
553
- const args = { spaceId, param };
554
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
555
- const result = await callMcpTool(mcpUrl, "get_livestream_overview", args, host);
556
- return parseMcpResult(result);
557
- }
558
- };
559
-
560
- // src/commands/te-community/get_livestream_rooms.ts
561
- var getLivestreamRooms = {
562
- service: "community",
563
- command: "+get_livestream_rooms",
564
- description: "Search live rooms by time range and channels; returns room list with stats.",
565
- flags: [
566
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
567
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
568
- { name: "channel-id-list", type: "string", required: false, desc: "Channel ID list." },
569
- { name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
570
- { name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
571
- ],
572
- risk: "read",
573
- execute: async (ctx) => {
574
- const spaceId = ctx.num("space-id");
575
- const gameId = ctx.num("game-id");
576
- const host = ctx.host();
577
- const param = { gameId };
578
- const channelIdList = ctx.str("channel-id-list");
579
- if (channelIdList) param.channelIdList = channelIdList.split(",").map(Number);
580
- const startTime = ctx.str("start-time");
581
- if (startTime) param.startTime = startTime;
582
- const endTime = ctx.str("end-time");
583
- if (endTime) param.endTime = endTime;
584
- const args = { spaceId, param };
585
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
586
- const result = await callMcpTool(mcpUrl, "get_livestream_rooms", args, host);
587
- return parseMcpResult(result);
588
- }
589
- };
590
-
591
- // src/commands/te-community/get_livestream_room_metrics.ts
592
- var getLivestreamRoomMetrics = {
593
- service: "community",
594
- command: "+get_livestream_room_metrics",
595
- description: "Core metrics and yearly calendar for one room: overview KPIs plus daily streamed-duration calendar.",
596
- flags: [
597
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
598
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
599
- { name: "channel-id", type: "number", required: true, desc: "Channel ID from +get_livestream_rooms." },
600
- { name: "room-id", type: "string", required: true, desc: "Live room ID (roomId) from +get_livestream_rooms." },
601
- { name: "year", type: "string", required: false, desc: "Calendar year (yyyy)." }
602
- ],
603
- risk: "read",
604
- execute: async (ctx) => {
605
- const spaceId = ctx.num("space-id");
606
- const gameId = ctx.num("game-id");
607
- const channelId = ctx.num("channel-id");
608
- const roomId = ctx.str("room-id");
609
- const host = ctx.host();
610
- const param = { gameId, channelId, roomId };
611
- const year = ctx.str("year");
612
- if (year) param.year = year;
613
- const args = { spaceId, param };
614
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
615
- const result = await callMcpTool(mcpUrl, "get_livestream_room_metrics", args, host);
616
- return parseMcpResult(result);
617
- }
618
- };
619
-
620
- // src/commands/te-community/get_livestream_list.ts
621
- var getLivestreamList = {
622
- service: "community",
623
- command: "+get_livestream_list",
624
- description: "List live sessions for a room in a time range (stream records).",
625
- flags: [
626
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
627
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
628
- { name: "channel-id", type: "number", required: false, desc: "Channel ID from +get_livestream_rooms." },
629
- { name: "room-id", type: "string", required: false, desc: "Live room ID (roomId) from +get_livestream_rooms." },
630
- { name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
631
- { name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
632
- ],
633
- risk: "read",
634
- execute: async (ctx) => {
635
- const spaceId = ctx.num("space-id");
636
- const gameId = ctx.num("game-id");
637
- const host = ctx.host();
638
- const param = { gameId };
639
- const channelId = ctx.num("channel-id");
640
- if (channelId) param.channelId = channelId;
641
- const roomId = ctx.str("room-id");
642
- if (roomId) param.roomId = roomId;
643
- const startTime = ctx.str("start-time");
644
- if (startTime) param.startTime = startTime;
645
- const endTime = ctx.str("end-time");
646
- if (endTime) param.endTime = endTime;
647
- const args = { spaceId, param };
648
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
649
- const result = await callMcpTool(mcpUrl, "get_livestream_list", args, host);
650
- return parseMcpResult(result);
651
- }
652
- };
653
-
654
- // src/commands/te-community/get_livestream_detail.ts
655
- var getLivestreamDetail = {
656
- service: "community",
657
- command: "+get_livestream_detail",
658
- description: "One live session detail: interaction leaderboards and paginated interaction feed.",
659
- flags: [
660
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
661
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
662
- { name: "stream-id", type: "string", required: true, desc: "Live session ID (streamId) from +get_livestream_list." },
663
- { name: "activity-types", type: "string", required: false, desc: "Activity type codes to filter details: 0 danmu, 1 gift, 2 superchat, 3 premium. Comma-separated." },
664
- { name: "anchor-time", type: "string", required: false, desc: "Anchor time for interactions. Format: yyyy-MM-dd HH:mm." },
665
- { name: "search-word", type: "string", required: false, desc: "Keyword to filter interaction detail rows." },
666
- { name: "order-by", type: "number", required: false, desc: "Sort: 0 interaction time desc, 1 interaction time asc." },
667
- { name: "page-num", type: "number", required: false, desc: "Page number, starting at 1." },
668
- { name: "page-size", type: "number", required: false, desc: "Page size. Max 10000." }
669
- ],
670
- risk: "read",
671
- execute: async (ctx) => {
672
- const spaceId = ctx.num("space-id");
673
- const gameId = ctx.num("game-id");
674
- const streamId = ctx.str("stream-id");
675
- const host = ctx.host();
676
- const param = { gameId, streamId };
677
- const activityTypes = ctx.str("activity-types");
678
- if (activityTypes) param.activityType = activityTypes.split(",").map(Number);
679
- const anchorTime = ctx.str("anchor-time");
680
- if (anchorTime) param.anchorTime = anchorTime;
681
- const searchWord = ctx.str("search-word");
682
- if (searchWord) param.searchWord = searchWord;
683
- const orderBy = ctx.num("order-by");
684
- const pageNum = ctx.num("page-num");
685
- const pageSize = ctx.num("page-size");
686
- if (orderBy !== void 0 || pageNum || pageSize) {
687
- param.pagerHeader = {};
688
- if (orderBy !== void 0) param.pagerHeader.orderBy = orderBy;
689
- if (pageNum) param.pagerHeader.pageNum = pageNum;
690
- if (pageSize) param.pagerHeader.pageSize = pageSize;
691
- }
692
- const args = { spaceId, param };
693
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
694
- const result = await callMcpTool(mcpUrl, "get_livestream_detail", args, host);
695
- return parseMcpResult(result);
696
- }
697
- };
698
-
699
- // src/commands/te-community/get_livestream_analysis.ts
700
- var getLivestreamAnalysis = {
701
- service: "community",
702
- command: "+get_livestream_analysis",
703
- description: "AI-generated live session analysis report: highlight moments (time slices with metrics and AI insights) and sentiment opinions.",
704
- flags: [
705
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
706
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
707
- { name: "stream-id", type: "string", required: true, desc: "Live session ID (streamId) from +get_livestream_list." }
708
- ],
709
- risk: "read",
710
- execute: async (ctx) => {
711
- const spaceId = ctx.num("space-id");
712
- const gameId = ctx.num("game-id");
713
- const streamId = ctx.str("stream-id");
714
- const host = ctx.host();
715
- const param = { gameId, streamId };
716
- const args = { spaceId, param };
717
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
718
- const result = await callMcpTool(mcpUrl, "get_livestream_analysis", args, host);
719
- return parseMcpResult(result);
720
- }
721
- };
722
-
723
- // src/commands/te-community/get_comments_summary.ts
724
- var getCommentsSummary = {
725
- service: "community",
726
- command: "+get_comments_summary",
727
- description: "Comment analytics summary for a post: sentiment mix and time trends. Aggregate stats only\u2014no raw comment list.",
728
- flags: [
729
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
730
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
731
- { name: "channel-id", type: "number", required: true, desc: "Channel ID from search_posts results." },
732
- { name: "uuid", type: "string", required: true, desc: "Content UUID from search_posts results." },
733
- { name: "start-time", type: "string", required: false, desc: "Comment publish time lower bound; defaults to this post's publishTime from search_posts/get_post_detail." },
734
- { name: "end-time", type: "string", required: false, desc: "Comment publish time upper bound. Format: yyyy-MM-dd." }
735
- ],
736
- risk: "read",
737
- execute: async (ctx) => {
738
- const spaceId = ctx.num("space-id");
739
- const gameId = ctx.num("game-id");
740
- const channelId = ctx.num("channel-id");
741
- const uuid = ctx.str("uuid");
742
- const host = ctx.host();
743
- const param = { gameId, channelId, uuid };
744
- const startTime = ctx.str("start-time");
745
- if (startTime) param.startTime = startTime;
746
- const endTime = ctx.str("end-time");
747
- if (endTime) param.endTime = endTime;
748
- const args = { spaceId, param };
749
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
750
- const result = await callMcpTool(mcpUrl, "get_comments_summary", args, host);
751
- return parseMcpResult(result);
752
- }
753
- };
754
-
755
- // src/commands/te-community/get_comment_tag_analysis.ts
756
- var getCommentTagAnalysis = {
757
- service: "community",
758
- command: "+get_comment_tag_analysis",
759
- description: "Comment tag analysis: for a tagCode, counts and trends per tag value over time.",
760
- flags: [
761
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
762
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
763
- { name: "channel-id", type: "number", required: true, desc: "Channel ID from search_posts results." },
764
- { name: "uuid", type: "string", required: true, desc: "Content UUID from search_posts results." },
765
- { name: "resource-type", type: "number", required: true, desc: "Resource type: 0 post, 1 video." },
766
- { name: "tag-code", type: "string", required: true, desc: "Tag code (tagCode) from +get_corpus_tags." },
767
- { name: "start-time", type: "string", required: false, desc: "Comment publish time lower bound; defaults to this post's publishTime from search_posts/get_post_detail." },
768
- { name: "end-time", type: "string", required: false, desc: "Comment publish time upper bound. Format: yyyy-MM-dd." }
769
- ],
770
- risk: "read",
771
- execute: async (ctx) => {
772
- const spaceId = ctx.num("space-id");
773
- const gameId = ctx.num("game-id");
774
- const channelId = ctx.num("channel-id");
775
- const uuid = ctx.str("uuid");
776
- const resourceType = ctx.num("resource-type");
777
- const tagCode = ctx.str("tag-code");
778
- const host = ctx.host();
779
- const param = { gameId, channelId, uuid, resourceType, tagCode };
780
- const startTime = ctx.str("start-time");
781
- if (startTime) param.startTime = startTime;
782
- const endTime = ctx.str("end-time");
783
- if (endTime) param.endTime = endTime;
784
- const args = { spaceId, param };
785
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
786
- const result = await callMcpTool(mcpUrl, "get_comment_tag_analysis", args, host);
787
- return parseMcpResult(result);
788
- }
789
- };
790
-
791
- // src/commands/te-community/get_corpus_tags.ts
792
- var getCorpusTags = {
793
- service: "community",
794
- command: "+get_corpus_tags",
795
- description: "List corpus tags (tagId, tagCode, tagName, inferenceType) to pick tagCode for filters.",
796
- flags: [
797
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
798
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" }
799
- ],
800
- risk: "read",
801
- execute: async (ctx) => {
802
- const spaceId = ctx.num("space-id");
803
- const gameId = ctx.num("game-id");
804
- const host = ctx.host();
805
- const param = { gameId };
806
- const args = { spaceId, param };
807
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
808
- const result = await callMcpTool(mcpUrl, "get_corpus_tags", args, host);
809
- return parseMcpResult(result);
810
- }
811
- };
812
-
813
- // src/commands/te-community/get_risk_content.ts
814
- var getRiskContent = {
815
- service: "community",
816
- command: "+get_risk_content",
817
- description: "List content flagged as risk/moderation items; filter by risk type and time.",
818
- flags: [
819
- { name: "space-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
820
- { name: "game-id", type: "number", required: true, desc: "Identifier of the community space. SpaceId" },
821
- { name: "start-time", type: "string", required: true, desc: "Risk content publish time lower bound. Format: yyyy-MM-dd." },
822
- { name: "end-time", type: "string", required: true, desc: "Risk content publish time upper bound. Format: yyyy-MM-dd." },
823
- { name: "risk-codes", type: "string", required: false, desc: "Risk type code list." },
824
- { name: "search-word", type: "string", required: false, desc: "Keyword search; fuzzy match on body text." },
825
- { name: "include-trends", type: "boolean", required: false, desc: "Whether to include trend aggregates in the response." },
826
- { name: "order-by", type: "number", required: false, desc: "Sort: 0 publish time desc." },
827
- { name: "page-num", type: "number", required: false, desc: "Page number, starting at 1." },
828
- { name: "page-size", type: "number", required: false, desc: "Page size. Max 10000." }
829
- ],
830
- risk: "read",
831
- execute: async (ctx) => {
832
- const spaceId = ctx.num("space-id");
833
- const gameId = ctx.num("game-id");
834
- const startTime = ctx.str("start-time");
835
- const endTime = ctx.str("end-time");
836
- const host = ctx.host();
837
- const param = { gameId, startTime, endTime };
838
- const riskCodes = ctx.str("risk-codes");
839
- if (riskCodes) param.riskCodes = riskCodes.split(",").map(Number);
840
- const searchWord = ctx.str("search-word");
841
- if (searchWord) param.searchWord = searchWord;
842
- const includeTrends = ctx.bool("include-trends");
843
- if (includeTrends !== void 0) param.includeTrends = includeTrends;
844
- const orderBy = ctx.num("order-by");
845
- const pageNum = ctx.num("page-num");
846
- const pageSize = ctx.num("page-size");
847
- if (orderBy !== void 0 || pageNum || pageSize) {
848
- param.pagerHeader = {};
849
- if (orderBy !== void 0) param.pagerHeader.orderBy = orderBy;
850
- if (pageNum) param.pagerHeader.pageNum = pageNum;
851
- if (pageSize) param.pagerHeader.pageSize = pageSize;
852
- }
853
- const args = { spaceId, param };
854
- const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
855
- const result = await callMcpTool(mcpUrl, "get_risk_content", args, host);
856
- return parseMcpResult(result);
857
- }
858
- };
859
-
860
- // src/commands/te-community/data/report.ts
861
- import { readFileSync } from "fs";
862
-
863
- // src/commands/te-community/data/lossless-json.ts
864
- import { createRequire } from "module";
865
- var require2 = createRequire(import.meta.url);
866
- var createJsonBigInt = require2("json-bigint");
867
- var losslessJson = createJsonBigInt({
868
- strict: true,
869
- useNativeBigInt: true,
870
- protoAction: "error",
871
- constructorAction: "error"
872
- });
873
- var CommunityJsonParseError = class extends Error {
874
- code = "community_invalid_json";
875
- constructor() {
876
- super("Input must be valid JSON.");
877
- this.name = "CommunityJsonParseError";
878
- }
879
- };
880
- function parseCommunityJson(text) {
881
- try {
882
- return losslessJson.parse(text);
883
- } catch {
884
- throw new CommunityJsonParseError();
885
- }
886
- }
887
- function stringifyCommunityJson(value) {
888
- try {
889
- const result = losslessJson.stringify(value);
890
- if (typeof result !== "string") {
891
- throw new Error("JSON value is not serializable.");
892
- }
893
- return result;
894
- } catch (error) {
895
- if (error instanceof CommunityJsonParseError) {
896
- throw error;
897
- }
898
- throw new CommunityJsonParseError();
899
- }
900
- }
901
-
902
- // src/commands/te-community/data/standard-v5.ts
903
- var COMMUNITY_DATA_TYPES = [
904
- "post",
905
- "video",
906
- "reply",
907
- "danmu",
908
- "live_room",
909
- "live_interaction",
910
- "chat",
911
- "interaction"
912
- ];
913
- var CommunityDataValidationError = class extends Error {
914
- code;
915
- segment;
916
- record;
917
- field;
918
- constructor(code, message, location = {}) {
919
- super(message);
920
- this.name = "CommunityDataValidationError";
921
- this.code = code;
922
- this.segment = location.segment;
923
- this.record = location.record;
924
- this.field = location.field;
925
- }
926
- };
927
- var INT64_MIN = -(1n << 63n);
928
- var INT64_MAX = (1n << 63n) - 1n;
929
- var CANONICAL_INTEGER = /^-?(?:0|[1-9]\d*)$/;
930
- var ROOT_TYPES = /* @__PURE__ */ new Set(["post", "video", "live"]);
931
- var ACTIVITY_TYPES = /* @__PURE__ */ new Set(["danmu", "gift", "superchat", "premium"]);
932
- var ALL_DATA_TYPES = new Set(COMMUNITY_DATA_TYPES);
933
- var POST_VIDEO_METRICS = /* @__PURE__ */ new Set([
934
- "views",
935
- "likes",
936
- "comments",
937
- "shares",
938
- "favorites",
939
- "coins",
940
- "danmaku",
941
- "dislikes"
942
- ]);
943
- var GENERAL_CONTENT_METRICS = /* @__PURE__ */ new Set(["favorites", "coins", "danmaku", "dislikes"]);
944
- var USER_METRICS = /* @__PURE__ */ new Set(["followers", "total_views", "total_likes"]);
945
- var StatsCollector = class {
946
- value = {
947
- truncatedFields: 0,
948
- defaultedFields: 0,
949
- convertedIntegerFields: 0,
950
- fields: {}
951
- };
952
- truncated(field) {
953
- this.value.truncatedFields += 1;
954
- this.increment(field);
955
- }
956
- defaulted(field) {
957
- this.value.defaultedFields += 1;
958
- this.increment(field);
959
- }
960
- convertedInteger(field) {
961
- this.value.convertedIntegerFields += 1;
962
- this.increment(field);
963
- }
964
- increment(field) {
965
- this.value.fields[field] = (this.value.fields[field] ?? 0) + 1;
966
- }
967
- };
968
- function validationError(code, message, location = {}) {
969
- throw new CommunityDataValidationError(code, message, location);
970
- }
971
- function isObject(value) {
972
- return value !== null && typeof value === "object" && !Array.isArray(value);
973
- }
974
- function hasOwn(object, field) {
975
- return Object.prototype.hasOwnProperty.call(object, field);
976
- }
977
- function fieldLocation(context, field) {
978
- return { ...context.location, field };
979
- }
980
- function statField(context, field) {
981
- return `${context.type}.${field}`;
982
- }
983
- function parseInteger(value, location, options = {}) {
984
- let integer;
985
- let converted = false;
986
- if (typeof value === "bigint") {
987
- integer = value;
988
- } else if (typeof value === "number") {
989
- if (!Number.isSafeInteger(value)) {
990
- validationError(
991
- "community_invalid_integer",
992
- "The field must be a lossless integer number or a canonical decimal string.",
993
- location
994
- );
995
- }
996
- integer = BigInt(value);
997
- } else if (typeof value === "string") {
998
- if (!CANONICAL_INTEGER.test(value) || value === "-0") {
999
- validationError(
1000
- "community_invalid_integer",
1001
- "The field must be a lossless integer number or a canonical decimal string.",
1002
- location
1003
- );
1004
- }
1005
- integer = BigInt(value);
1006
- converted = true;
1007
- } else {
1008
- validationError(
1009
- "community_invalid_integer",
1010
- "The field must be a lossless integer number or a canonical decimal string.",
1011
- location
1012
- );
1013
- }
1014
- const minimum = options.min ?? INT64_MIN;
1015
- const maximum = options.max ?? INT64_MAX;
1016
- if (integer < minimum || integer > maximum) {
1017
- validationError("community_integer_out_of_range", "The field is outside its supported integer range.", location);
1018
- }
1019
- if (options.positive && integer <= 0n) {
1020
- validationError("community_integer_out_of_range", "The field must be a positive integer.", location);
1021
- }
1022
- if (options.nonnegative && integer < 0n) {
1023
- validationError("community_integer_out_of_range", "The field must be a nonnegative integer.", location);
1024
- }
1025
- return { value: integer, converted };
1026
- }
1027
- function parsePositiveId(value, field) {
1028
- return parseInteger(value, { field }, { positive: true }).value;
1029
- }
1030
- function normalizeIntegerField(input, output, field, context, options = {}) {
1031
- if (!hasOwn(input, field) || input[field] === null) {
1032
- if (options.required) {
1033
- validationError("community_required_field", "A required field is missing.", fieldLocation(context, field));
1034
- }
1035
- delete output[field];
1036
- return void 0;
1037
- }
1038
- const parsed = parseInteger(input[field], fieldLocation(context, field), options);
1039
- output[field] = parsed.value;
1040
- if (parsed.converted) {
1041
- context.stats.convertedInteger(statField(context, field));
1042
- }
1043
- return parsed.value;
1044
- }
1045
- function normalizeSmallIntegerField(input, output, field, context, options = {}) {
1046
- const integer = normalizeIntegerField(input, output, field, context, options);
1047
- if (integer === void 0) return void 0;
1048
- const result = Number(integer);
1049
- output[field] = result;
1050
- return result;
1051
- }
1052
- function parseDateTime(value, location, requireTime = false) {
1053
- const match = /^(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?)?$/.exec(value);
1054
- if (!match) {
1055
- validationError("community_invalid_datetime", "The field must use a supported Iris date-time format.", location);
1056
- }
1057
- const hasTime = match[4] !== void 0;
1058
- if (requireTime && !hasTime) {
1059
- validationError("community_invalid_datetime", "The field must include a time component.", location);
1060
- }
1061
- const parts = {
1062
- year: Number(match[1]),
1063
- month: Number(match[2]),
1064
- day: Number(match[3]),
1065
- hour: Number(match[4] ?? 0),
1066
- minute: Number(match[5] ?? 0),
1067
- second: Number(match[6] ?? 0),
1068
- millisecond: Number(match[7] ?? 0),
1069
- hasTime
1070
- };
1071
- if (parts.year === 0 || parts.month < 1 || parts.month > 12 || parts.hour > 23 || parts.minute > 59 || parts.second > 59) {
1072
- validationError("community_invalid_datetime", "The field must use a supported Iris date-time format.", location);
1073
- }
1074
- const calendarCheck = /* @__PURE__ */ new Date(0);
1075
- calendarCheck.setUTCFullYear(parts.year, parts.month - 1, parts.day);
1076
- calendarCheck.setUTCHours(parts.hour, parts.minute, parts.second, parts.millisecond);
1077
- if (calendarCheck.getUTCFullYear() !== parts.year || calendarCheck.getUTCMonth() !== parts.month - 1 || calendarCheck.getUTCDate() !== parts.day || calendarCheck.getUTCHours() !== parts.hour || calendarCheck.getUTCMinutes() !== parts.minute || calendarCheck.getUTCSeconds() !== parts.second || calendarCheck.getUTCMilliseconds() !== parts.millisecond) {
1078
- validationError("community_invalid_datetime", "The field must use a supported Iris date-time format.", location);
1079
- }
1080
- return parts;
1081
- }
1082
- function normalizeTextField(input, output, field, context, options = {}) {
1083
- const path = options.path ?? field;
1084
- if (!hasOwn(input, field) || input[field] === null) {
1085
- if (options.required) {
1086
- validationError("community_required_field", "A required field is missing.", fieldLocation(context, path));
1087
- }
1088
- delete output[field];
1089
- return void 0;
1090
- }
1091
- const value = input[field];
1092
- if (typeof value !== "string") {
1093
- validationError("community_invalid_text", "The field must be a JSON string.", fieldLocation(context, path));
1094
- }
1095
- let normalized = value;
1096
- if (options.maxLength !== void 0 && normalized.length > options.maxLength) {
1097
- if (!options.truncate) {
1098
- validationError(
1099
- "community_text_too_long",
1100
- `The field must not exceed ${options.maxLength} UTF-16 code units.`,
1101
- fieldLocation(context, path)
1102
- );
1103
- }
1104
- normalized = normalized.slice(0, options.maxLength);
1105
- context.stats.truncated(statField(context, path));
1106
- }
1107
- if (options.dateTime) {
1108
- parseDateTime(normalized, fieldLocation(context, path), options.requireTime);
1109
- }
1110
- output[field] = normalized;
1111
- return normalized;
1112
- }
1113
- function parseEncodedObject(value) {
1114
- if (isObject(value)) return value;
1115
- if (typeof value !== "string") return void 0;
1116
- try {
1117
- const parsed = parseCommunityJson(value);
1118
- return isObject(parsed) ? parsed : void 0;
1119
- } catch {
1120
- return void 0;
1121
- }
1122
- }
1123
- function normalizeFallbackObjectField(input, output, field, context) {
1124
- const object = hasOwn(input, field) && input[field] !== null ? parseEncodedObject(input[field]) : void 0;
1125
- if (object === void 0) {
1126
- const fallback = {};
1127
- output[field] = fallback;
1128
- context.stats.defaulted(statField(context, field));
1129
- return fallback;
1130
- }
1131
- output[field] = object;
1132
- return object;
1133
- }
1134
- function normalizeRequiredObjectValue(value, context, field) {
1135
- const object = parseEncodedObject(value);
1136
- if (object === void 0) {
1137
- validationError("community_invalid_object", "The field must be a JSON object.", fieldLocation(context, field));
1138
- }
1139
- return object;
1140
- }
1141
- function normalizeSubtitle(input, output, context) {
1142
- if (!hasOwn(input, "subtitle") || input.subtitle === null) {
1143
- delete output.subtitle;
1144
- return;
1145
- }
1146
- let value = input.subtitle;
1147
- if (typeof value === "string") {
1148
- try {
1149
- value = parseCommunityJson(value);
1150
- } catch {
1151
- validationError("community_invalid_array", "The field must be an array of JSON objects.", fieldLocation(context, "subtitle"));
1152
- }
1153
- }
1154
- if (!Array.isArray(value) || value.some((element) => !isObject(element))) {
1155
- validationError("community_invalid_array", "The field must be an array of JSON objects.", fieldLocation(context, "subtitle"));
1156
- }
1157
- output.subtitle = value;
1158
- }
1159
- function metricNamesForContentType(contentType) {
1160
- if (contentType === 0 || contentType === 1) return POST_VIDEO_METRICS;
1161
- if (contentType === 7) return USER_METRICS;
1162
- return GENERAL_CONTENT_METRICS;
1163
- }
1164
- function normalizeMetrics(value, contentType, context, fieldPrefix) {
1165
- const metrics = normalizeRequiredObjectValue(value, context, fieldPrefix);
1166
- const names = Object.keys(metrics);
1167
- if (names.length === 0) {
1168
- validationError("community_empty_metrics", "The metrics object must not be empty.", fieldLocation(context, fieldPrefix));
1169
- }
1170
- const allowed = metricNamesForContentType(contentType);
1171
- const normalized = { ...metrics };
1172
- for (const name of names) {
1173
- const field = `${fieldPrefix}.${name}`;
1174
- if (!allowed.has(name)) {
1175
- validationError(
1176
- "community_invalid_metric",
1177
- "The metric name is not allowed for the selected content type.",
1178
- fieldLocation(context, field)
1179
- );
1180
- }
1181
- const parsed = parseInteger(metrics[name], fieldLocation(context, field), { nonnegative: true });
1182
- normalized[name] = parsed.value;
1183
- if (parsed.converted) {
1184
- context.stats.convertedInteger(statField(context, field));
1185
- }
1186
- }
1187
- return normalized;
1188
- }
1189
- function normalizeInteractionSidecar(input, output, context, contentType) {
1190
- if (!hasOwn(input, "interaction") || input.interaction === null) {
1191
- delete output.interaction;
1192
- return;
1193
- }
1194
- const interaction = normalizeRequiredObjectValue(input.interaction, context, "interaction");
1195
- const normalized = { ...interaction };
1196
- normalizeTextField(interaction, normalized, "collect_time", context, {
1197
- required: true,
1198
- dateTime: true,
1199
- path: "interaction.collect_time"
1200
- });
1201
- if (!hasOwn(interaction, "metrics") || interaction.metrics === null) {
1202
- validationError("community_required_field", "A required field is missing.", fieldLocation(context, "interaction.metrics"));
1203
- }
1204
- normalized.metrics = normalizeMetrics(interaction.metrics, contentType, context, "interaction.metrics");
1205
- output.interaction = normalized;
1206
- }
1207
- function normalizePost(input, context) {
1208
- const output = { ...input };
1209
- normalizeTextField(input, output, "post_uuid", context, { required: true, maxLength: 32 });
1210
- normalizeTextField(input, output, "user_id", context, { maxLength: 64, truncate: true });
1211
- normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
1212
- normalizeTextField(input, output, "title", context, { maxLength: 200, truncate: true });
1213
- normalizeTextField(input, output, "content", context, { maxLength: 65533, truncate: true });
1214
- normalizeTextField(input, output, "publish_time", context, { dateTime: true });
1215
- normalizeFallbackObjectField(input, output, "extras", context);
1216
- normalizeInteractionSidecar(input, output, context, 0);
1217
- return output;
1218
- }
1219
- function normalizeVideo(input, context) {
1220
- const output = { ...input };
1221
- normalizeTextField(input, output, "video_uuid", context, { required: true, maxLength: 32 });
1222
- normalizeTextField(input, output, "user_id", context, { maxLength: 64, truncate: true });
1223
- normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
1224
- normalizeTextField(input, output, "title", context, { maxLength: 1e3, truncate: true });
1225
- normalizeTextField(input, output, "description", context, { maxLength: 65533, truncate: true });
1226
- normalizeTextField(input, output, "publish_time", context, { dateTime: true });
1227
- normalizeSubtitle(input, output, context);
1228
- normalizeFallbackObjectField(input, output, "extras", context);
1229
- normalizeInteractionSidecar(input, output, context, 1);
1230
- return output;
1231
- }
1232
- function normalizeRootFields(input, output, context) {
1233
- normalizeTextField(input, output, "root_id", context, { required: true, maxLength: 32, truncate: true });
1234
- const rootType = normalizeTextField(input, output, "root_type", context, { required: true });
1235
- if (!ROOT_TYPES.has(rootType)) {
1236
- validationError("community_invalid_enum", "The field contains an unsupported enum value.", fieldLocation(context, "root_type"));
1237
- }
1238
- }
1239
- function normalizeReply(input, context) {
1240
- const output = { ...input };
1241
- normalizeTextField(input, output, "reply_uuid", context, { required: true, maxLength: 32 });
1242
- normalizeTextField(input, output, "user_id", context, { maxLength: 64, truncate: true });
1243
- normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
1244
- normalizeTextField(input, output, "content", context, { maxLength: 65533, truncate: true });
1245
- normalizeTextField(input, output, "publish_time", context, { dateTime: true });
1246
- normalizeTextField(input, output, "parent_id", context, { maxLength: 32, truncate: true });
1247
- normalizeRootFields(input, output, context);
1248
- normalizeFallbackObjectField(input, output, "extras", context);
1249
- return output;
1250
- }
1251
- function normalizeDanmu(input, context) {
1252
- const output = { ...input };
1253
- normalizeTextField(input, output, "danmu_uuid", context, { required: true, maxLength: 32 });
1254
- normalizeTextField(input, output, "user_id", context, { maxLength: 64, truncate: true });
1255
- normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
1256
- normalizeTextField(input, output, "content", context, { maxLength: 65533, truncate: true });
1257
- normalizeIntegerField(input, output, "timestamp", context, { required: true, nonnegative: true });
1258
- normalizeTextField(input, output, "publish_time", context, { dateTime: true });
1259
- normalizeRootFields(input, output, context);
1260
- return output;
1261
- }
1262
- function dateTimeToUtcMilliseconds(parts) {
1263
- const date = /* @__PURE__ */ new Date(0);
1264
- date.setUTCFullYear(parts.year, parts.month - 1, parts.day);
1265
- date.setUTCHours(parts.hour, parts.minute, parts.second, parts.millisecond);
1266
- return date.getTime();
1267
- }
1268
- function zonedDateTimeToEpochMilliseconds(parts, zoneId) {
1269
- const localAsUtc = dateTimeToUtcMilliseconds(parts);
1270
- const formatter = new Intl.DateTimeFormat("en-US-u-ca-iso8601-nu-latn", {
1271
- timeZone: zoneId,
1272
- year: "numeric",
1273
- month: "2-digit",
1274
- day: "2-digit",
1275
- hour: "2-digit",
1276
- minute: "2-digit",
1277
- second: "2-digit",
1278
- hourCycle: "h23"
1279
- });
1280
- let estimate = localAsUtc;
1281
- for (let attempt = 0; attempt < 4; attempt += 1) {
1282
- const formatted = Object.fromEntries(
1283
- formatter.formatToParts(new Date(estimate)).filter((part) => part.type !== "literal").map((part) => [part.type, Number(part.value)])
1284
- );
1285
- const represented = /* @__PURE__ */ new Date(0);
1286
- represented.setUTCFullYear(formatted.year, formatted.month - 1, formatted.day);
1287
- represented.setUTCHours(formatted.hour, formatted.minute, formatted.second, 0);
1288
- const offset = represented.getTime() - (estimate - estimate % 1e3);
1289
- const next = localAsUtc - offset;
1290
- if (next === estimate) break;
1291
- estimate = next;
1292
- }
1293
- return estimate;
1294
- }
1295
- function validateStreamId(roomId, streamStartTime, context) {
1296
- const parts = parseDateTime(streamStartTime, fieldLocation(context, "stream_start_time"), true);
1297
- const timestamp = zonedDateTimeToEpochMilliseconds(parts, context.zoneId);
1298
- const streamId = `${context.channelId}_${roomId}_${timestamp}`;
1299
- if (streamId.length > 64) {
1300
- validationError(
1301
- "community_derived_id_too_long",
1302
- "The derived stream identifier exceeds its supported length.",
1303
- fieldLocation(context, "stream_start_time")
1304
- );
1305
- }
1306
- }
1307
- function normalizeLiveRoom(input, context) {
1308
- const output = { ...input };
1309
- const uuid = normalizeIntegerField(input, output, "uuid", context, { required: true, nonnegative: true });
1310
- const roomId = normalizeTextField(input, output, "room_id", context, { required: true, maxLength: 32 });
1311
- normalizeTextField(input, output, "room_name", context, { required: true, maxLength: 80 });
1312
- normalizeTextField(input, output, "room_type", context, { maxLength: 128, truncate: true });
1313
- normalizeTextField(input, output, "room_avatar", context, { required: true, maxLength: 65533 });
1314
- normalizeIntegerField(input, output, "fans", context, { required: true, nonnegative: true });
1315
- normalizeTextField(input, output, "stream_cover", context, { maxLength: 65533, truncate: true });
1316
- normalizeTextField(input, output, "stream_title", context, { maxLength: 80, truncate: true });
1317
- const suppliedStreamStatus = hasOwn(input, "stream_status") && input.stream_status !== null;
1318
- if (!suppliedStreamStatus) {
1319
- output.stream_status = 0;
1320
- context.stats.defaulted(statField(context, "stream_status"));
1321
- } else {
1322
- const status = normalizeSmallIntegerField(input, output, "stream_status", context, { min: 0n, max: 2n });
1323
- if (status === 2) {
1324
- output.stream_status = 0;
1325
- context.stats.defaulted(statField(context, "stream_status"));
1326
- }
1327
- }
1328
- const streamStartTime = normalizeTextField(input, output, "stream_start_time", context, {
1329
- dateTime: true,
1330
- requireTime: true
1331
- });
1332
- normalizeTextField(input, output, "stream_end_time", context, { dateTime: true });
1333
- normalizeTextField(input, output, "stream_notice", context, { maxLength: 255 });
1334
- normalizeTextField(input, output, "timestamp", context, { required: true, dateTime: true });
1335
- for (const metric of ["online", "heat", "noble_count", "guardian_count", "diamond_fan_count", "dfans_count"]) {
1336
- normalizeIntegerField(input, output, metric, context, { nonnegative: true });
1337
- }
1338
- const guardianAliases = ["guardian_count", "diamond_fan_count", "dfans_count"].filter((field) => hasOwn(input, field) && input[field] !== null);
1339
- if (guardianAliases.length > 1) {
1340
- validationError(
1341
- "community_conflicting_fields",
1342
- "Only one guardian metric alias may be supplied.",
1343
- fieldLocation(context, guardianAliases[1])
1344
- );
1345
- }
1346
- const streamDependentFields = [
1347
- "stream_cover",
1348
- "stream_title",
1349
- "stream_status",
1350
- "stream_end_time",
1351
- "stream_notice",
1352
- "online",
1353
- "heat",
1354
- "noble_count",
1355
- "guardian_count",
1356
- "diamond_fan_count",
1357
- "dfans_count"
1358
- ];
1359
- const hasStreamDetails = streamDependentFields.some((field) => hasOwn(input, field) && input[field] !== null);
1360
- if (hasStreamDetails && streamStartTime === void 0) {
1361
- validationError(
1362
- "community_required_field",
1363
- "A stream start time is required when stream details or metrics are supplied.",
1364
- fieldLocation(context, "stream_start_time")
1365
- );
1366
- }
1367
- if (uuid * 5n + 4n > INT64_MAX) {
1368
- validationError(
1369
- "community_derived_id_overflow",
1370
- "A derived live metric identifier would exceed signed int64.",
1371
- fieldLocation(context, "uuid")
1372
- );
1373
- }
1374
- if (streamStartTime !== void 0) {
1375
- validateStreamId(roomId, streamStartTime, context);
1376
- }
1377
- return output;
1378
- }
1379
- function normalizeLiveInteraction(input, context) {
1380
- const output = { ...input };
1381
- normalizeIntegerField(input, output, "uuid", context, { required: true, nonnegative: true });
1382
- const activityType = normalizeTextField(input, output, "activity_type", context, { required: true });
1383
- if (!ACTIVITY_TYPES.has(activityType)) {
1384
- validationError("community_invalid_enum", "The field contains an unsupported enum value.", fieldLocation(context, "activity_type"));
1385
- }
1386
- normalizeTextField(input, output, "activity_content", context, { required: true, maxLength: 1024, truncate: true });
1387
- normalizeTextField(input, output, "user_id", context, { maxLength: 80, truncate: true });
1388
- normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
1389
- const userExtra = normalizeFallbackObjectField(input, output, "user_extra", context);
1390
- if (stringifyCommunityJson(userExtra).length > 1024) {
1391
- validationError(
1392
- "community_object_too_long",
1393
- "The compact user_extra object must not exceed 1024 UTF-16 code units.",
1394
- fieldLocation(context, "user_extra")
1395
- );
1396
- }
1397
- normalizeTextField(input, output, "timestamp", context, { required: true, dateTime: true });
1398
- const roomId = normalizeTextField(input, output, "room_id", context, { required: true, maxLength: 32 });
1399
- const streamStartTime = normalizeTextField(input, output, "stream_start_time", context, {
1400
- required: true,
1401
- dateTime: true,
1402
- requireTime: true
1403
- });
1404
- validateStreamId(roomId, streamStartTime, context);
1405
- return output;
1406
- }
1407
- function normalizeChat(input, context) {
1408
- const output = { ...input };
1409
- normalizeTextField(input, output, "chat_uuid", context, { required: true, maxLength: 36, truncate: true });
1410
- normalizeTextField(input, output, "user_id", context, { required: true, maxLength: 80, truncate: true });
1411
- normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
1412
- normalizeTextField(input, output, "chat_room_type", context, { required: true, maxLength: 16, truncate: true });
1413
- normalizeTextField(input, output, "chat_room_id", context, { required: true, maxLength: 80, truncate: true });
1414
- normalizeTextField(input, output, "chat_room", context, { maxLength: 80, truncate: true });
1415
- normalizeTextField(input, output, "chat_server", context, { maxLength: 80, truncate: true });
1416
- normalizeTextField(input, output, "chat_server_id", context, { maxLength: 80, truncate: true });
1417
- normalizeTextField(input, output, "content", context, { required: true, maxLength: 65533, truncate: true });
1418
- normalizeTextField(input, output, "publish_time", context, { required: true, dateTime: true });
1419
- normalizeFallbackObjectField(input, output, "extras", context);
1420
- return output;
1421
- }
1422
- function normalizeInteraction(input, context) {
1423
- const output = { ...input };
1424
- normalizeTextField(input, output, "content_uuid", context, { required: true, maxLength: 32, truncate: true });
1425
- const contentType = normalizeSmallIntegerField(input, output, "content_type", context, {
1426
- required: true,
1427
- min: 0n,
1428
- max: 7n
1429
- });
1430
- normalizeTextField(input, output, "collect_time", context, { required: true, dateTime: true });
1431
- if (!hasOwn(input, "metrics") || input.metrics === null) {
1432
- validationError("community_required_field", "A required field is missing.", fieldLocation(context, "metrics"));
1433
- }
1434
- output.metrics = normalizeMetrics(input.metrics, contentType, context, "metrics");
1435
- return output;
1436
- }
1437
- function normalizeRecord(input, context) {
1438
- if (!isObject(input)) {
1439
- validationError("community_invalid_record", "Each data record must be a JSON object.", context.location);
1440
- }
1441
- switch (context.type) {
1442
- case "post":
1443
- return normalizePost(input, context);
1444
- case "video":
1445
- return normalizeVideo(input, context);
1446
- case "reply":
1447
- return normalizeReply(input, context);
1448
- case "danmu":
1449
- return normalizeDanmu(input, context);
1450
- case "live_room":
1451
- return normalizeLiveRoom(input, context);
1452
- case "live_interaction":
1453
- return normalizeLiveInteraction(input, context);
1454
- case "chat":
1455
- return normalizeChat(input, context);
1456
- case "interaction":
1457
- return normalizeInteraction(input, context);
1458
- }
1459
- }
1460
- function parseDataType(value, segment) {
1461
- if (typeof value !== "string" || !ALL_DATA_TYPES.has(value)) {
1462
- validationError(
1463
- "community_invalid_data_type",
1464
- "The segment data_type must be one of the eight supported community data types.",
1465
- { segment, field: "data_type" }
1466
- );
1467
- }
1468
- return value;
1469
- }
1470
- function normalizeSegment(input, segment, channelId, zoneId, stats) {
1471
- if (!isObject(input)) {
1472
- validationError("community_invalid_segment", "Each payload segment must be a JSON object.", { segment });
1473
- }
1474
- if (!hasOwn(input, "data_type")) {
1475
- validationError("community_required_field", "A required field is missing.", { segment, field: "data_type" });
1476
- }
1477
- if (!hasOwn(input, "data") || input.data === null) {
1478
- validationError("community_required_field", "A required field is missing.", { segment, field: "data" });
1479
- }
1480
- const dataType = parseDataType(input.data_type, segment);
1481
- const records = Array.isArray(input.data) ? input.data : [input.data];
1482
- if (records.length === 0) {
1483
- validationError("community_empty_data", "A segment data array must not be empty.", { segment, field: "data" });
1484
- }
1485
- const normalized = records.map((record, index) => normalizeRecord(record, {
1486
- type: dataType,
1487
- channelId,
1488
- zoneId,
1489
- location: { segment, record: index },
1490
- stats
1491
- }));
1492
- return { ...input, data_type: dataType, data: normalized };
1493
- }
1494
- function validateZoneId(value) {
1495
- if (typeof value !== "string" || value.length === 0) {
1496
- validationError("community_invalid_zone_id", "zone_id must be a valid IANA time zone name.", { field: "zone_id" });
1497
- }
1498
- try {
1499
- new Intl.DateTimeFormat("en-US", { timeZone: value }).format(0);
1500
- } catch {
1501
- validationError("community_invalid_zone_id", "zone_id must be a valid IANA time zone name.", { field: "zone_id" });
1502
- }
1503
- return value;
1504
- }
1505
- function normalizeCommunityReportInput(input) {
1506
- const spaceId = parsePositiveId(input.spaceId, "space_id");
1507
- const channelId = parsePositiveId(input.channelId, "channel_id");
1508
- const sourceId = parsePositiveId(input.sourceId, "source_id");
1509
- const zoneId = validateZoneId(input.zoneId);
1510
- const hasDataMode = input.dataType !== void 0 || input.data !== void 0;
1511
- const hasPayloadMode = input.payload !== void 0;
1512
- if (hasDataMode === hasPayloadMode) {
1513
- validationError(
1514
- "community_input_mode",
1515
- "Use exactly one input mode: data_type with data, or payload.",
1516
- { field: "data/payload" }
1517
- );
1518
- }
1519
- let rawSegments;
1520
- if (hasDataMode) {
1521
- if (input.dataType === void 0 || input.data === void 0) {
1522
- validationError(
1523
- "community_input_mode",
1524
- "The data_type and data fields must be supplied together.",
1525
- { field: input.dataType === void 0 ? "data_type" : "data" }
1526
- );
1527
- }
1528
- rawSegments = [{ data_type: input.dataType, data: input.data }];
1529
- } else {
1530
- rawSegments = Array.isArray(input.payload) ? input.payload : [input.payload];
1531
- if (rawSegments.length === 0) {
1532
- validationError("community_empty_payload", "The payload array must not be empty.", { field: "payload" });
1533
- }
1534
- }
1535
- const stats = new StatsCollector();
1536
- const segments = rawSegments.map((segment, index) => normalizeSegment(
1537
- segment,
1538
- index,
1539
- channelId,
1540
- zoneId,
1541
- stats
1542
- ));
1543
- const dataTypes = [...new Set(segments.map((segment) => segment.data_type))];
1544
- const recordCount = segments.reduce((count, segment) => count + segment.data.length, 0);
1545
- const wirePayload = {
1546
- custom_data: {
1547
- channel_id: channelId,
1548
- game_id: spaceId,
1549
- source_id: sourceId,
1550
- source_type: "#standard",
1551
- version: "5.0.0"
1552
- },
1553
- zone_id: zoneId,
1554
- payload: segments
1555
- };
1556
- const wireBody = stringifyCommunityJson(wirePayload);
1557
- return {
1558
- wirePayload,
1559
- wireBody,
1560
- segmentCount: segments.length,
1561
- recordCount,
1562
- dataTypes,
1563
- byteLength: Buffer.byteLength(wireBody, "utf8"),
1564
- normalization: stats.value
1565
- };
1566
- }
1567
-
1568
- // src/commands/te-community/data/report.ts
1569
- var DEFAULT_ZONE_ID = "Asia/Shanghai";
1570
- var SCHEMA_HELP_COMMAND = "ae-cli community data report --help";
1571
- var SCHEMA_VALIDATION_HINT = `Run "${SCHEMA_HELP_COMMAND}" to review input modes and required fields for all eight data types. Use meta.location, when present, to correct the field without exposing its value.`;
1572
- var QUEUED_VERIFICATION_NEXT_STEP = "After asynchronous processing, verify the submitted record identifiers through an authorized downstream query or storage path before treating this submission as persisted.";
1573
- var COMMUNITY_REPORT_HELP = [
1574
- "Record schema summary (required fields):",
1575
- " post: post_uuid",
1576
- " video: video_uuid",
1577
- " reply: reply_uuid, root_id, root_type",
1578
- " danmu: danmu_uuid, timestamp, root_id, root_type",
1579
- " live_room: uuid, room_id, room_name, room_avatar, fans, timestamp",
1580
- " live_interaction: uuid, activity_type, activity_content, room_id, stream_start_time, timestamp",
1581
- " chat: chat_uuid, user_id, chat_room_type, chat_room_id, content, publish_time",
1582
- " interaction: content_uuid, content_type, collect_time, metrics",
1583
- "",
1584
- "Input modes: use --data-type <type> with --data, or --payload for mixed data types."
1585
- ].join("\n");
1586
- var communityDataReport = {
1587
- service: "community",
1588
- resource: "data",
1589
- command: "report",
1590
- usesAeHost: false,
1591
- description: "Validate and queue community data at an explicit Iris /sync_content endpoint.",
1592
- helpText: COMMUNITY_REPORT_HELP,
1593
- flags: [
1594
- {
1595
- name: "endpoint",
1596
- type: "string",
1597
- sensitive: true,
1598
- desc: "Complete Iris ingestion URL ending in /sync_content. Overrides AE_IRIS_SYNC_ENDPOINT; unrelated to --host."
1599
- },
1600
- {
1601
- name: "space-id",
1602
- type: "string",
1603
- required: true,
1604
- desc: "Positive int64 community space ID, sent to Iris as game_id."
1605
- },
1606
- {
1607
- name: "channel-id",
1608
- type: "string",
1609
- required: true,
1610
- desc: "Positive int64 Iris channel ID."
1611
- },
1612
- {
1613
- name: "source-id",
1614
- type: "string",
1615
- required: true,
1616
- desc: "Positive int64 Iris source ID."
1617
- },
1618
- {
1619
- name: "data-type",
1620
- type: "string",
1621
- desc: "Record type for --data: post, video, reply, danmu, live_room, live_interaction, chat, or interaction."
1622
- },
1623
- {
1624
- name: "data",
1625
- type: "string",
1626
- sensitive: true,
1627
- desc: "One record or a non-empty record array as inline JSON, path, @path, or - for stdin."
1628
- },
1629
- {
1630
- name: "payload",
1631
- type: "string",
1632
- sensitive: true,
1633
- desc: "One segment or a non-empty segment array as inline JSON, path, @path, or - for stdin."
1634
- },
1635
- {
1636
- name: "zone-id",
1637
- type: "string",
1638
- default: DEFAULT_ZONE_ID,
1639
- desc: `IANA time zone used by Iris. Default: ${DEFAULT_ZONE_ID}.`
1640
- }
1641
- ],
1642
- risk: "write",
1643
- dryRun: async (ctx) => {
1644
- const prepared = prepareCommunityReport(ctx);
1645
- return {
1646
- endpoint: prepared.endpoint,
1647
- data_types: prepared.normalized.dataTypes,
1648
- segment_count: prepared.normalized.segmentCount,
1649
- record_count: prepared.normalized.recordCount,
1650
- request_bytes: prepared.normalized.byteLength,
1651
- normalization: formatNormalization(prepared.normalized.normalization)
1652
- };
1653
- },
1654
- execute: async (ctx) => {
1655
- const prepared = prepareCommunityReport(ctx);
1656
- const transport = await ctx.communityReport(
1657
- prepared.endpoint,
1658
- prepared.normalized.wireBody
1659
- );
1660
- return {
1661
- status: "queued",
1662
- return_code: transport.return_code,
1663
- return_message: transport.return_message,
1664
- endpoint: prepared.endpoint,
1665
- space_id: prepared.ids.spaceId,
1666
- channel_id: prepared.ids.channelId,
1667
- source_id: prepared.ids.sourceId,
1668
- zone_id: prepared.zoneId,
1669
- data_types: prepared.normalized.dataTypes,
1670
- segment_count: prepared.normalized.segmentCount,
1671
- submitted_record_count: prepared.normalized.recordCount,
1672
- request_bytes: prepared.normalized.byteLength,
1673
- response_bytes: transport.response_bytes,
1674
- http_status: transport.http_status,
1675
- normalization: formatNormalization(prepared.normalized.normalization),
1676
- persistence_verified: false,
1677
- next_step: QUEUED_VERIFICATION_NEXT_STEP
1678
- };
1679
- }
1680
- };
1681
- function resolveCommunityReportEndpoint(flagValue, environment = process.env) {
1682
- const raw = flagValue?.trim() || environment.AE_IRIS_SYNC_ENDPOINT?.trim() || "";
1683
- if (!raw) {
1684
- throw new CliValidationError(
1685
- "A complete Iris sync endpoint is required.",
1686
- {
1687
- code: "community_missing_endpoint",
1688
- hint: "Pass --endpoint <http(s)://.../sync_content> or set AE_IRIS_SYNC_ENDPOINT.",
1689
- location: { field: "endpoint" }
1690
- }
1691
- );
1692
- }
1693
- let endpoint;
1694
- try {
1695
- endpoint = new URL(raw);
1696
- } catch {
1697
- throw endpointValidationError();
1698
- }
1699
- const usesHttp = endpoint.protocol === "http:" || endpoint.protocol === "https:";
1700
- const hasForbiddenComponents = endpoint.username !== "" || endpoint.password !== "" || endpoint.search !== "" || endpoint.hash !== "" || raw.includes("?") || raw.includes("#");
1701
- if (usesHttp && !hasForbiddenComponents && !endpoint.pathname.endsWith("/sync_content")) {
1702
- throw endpointMissingSyncContentError();
1703
- }
1704
- if (!usesHttp || hasForbiddenComponents) {
1705
- throw endpointValidationError();
1706
- }
1707
- return endpoint.toString();
1708
- }
1709
- function readCommunityReportInput(raw, field) {
1710
- const trimmed = raw.trim();
1711
- let text;
1712
- try {
1713
- if (trimmed === "-") {
1714
- text = readFileSync(0, "utf8");
1715
- } else if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
1716
- text = trimmed;
1717
- } else {
1718
- const filePath = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
1719
- if (!filePath) throw new Error("missing path");
1720
- text = readFileSync(filePath, "utf8");
1721
- }
1722
- } catch {
1723
- throw new CliValidationError(
1724
- "Unable to read the community JSON input.",
1725
- {
1726
- code: "community_input_read_failed",
1727
- hint: `Pass --${field} as inline JSON, an existing path, @path, or - for stdin.`,
1728
- location: { field }
1729
- }
1730
- );
1731
- }
1732
- try {
1733
- return parseCommunityJson(text);
1734
- } catch (error) {
1735
- if (error instanceof CommunityJsonParseError) {
1736
- throw new CliValidationError(
1737
- "Community input must be valid JSON.",
1738
- {
1739
- code: error.code,
1740
- hint: SCHEMA_VALIDATION_HINT,
1741
- location: { field }
1742
- }
1743
- );
1744
- }
1745
- throw error;
1746
- }
1747
- }
1748
- function prepareCommunityReport(ctx) {
1749
- const endpoint = resolveCommunityReportEndpoint(ctx.str("endpoint"));
1750
- const dataType = ctx.str("data-type");
1751
- const dataArgument = ctx.str("data");
1752
- const payloadArgument = ctx.str("payload");
1753
- const hasDataMode = dataType !== "" || dataArgument !== "";
1754
- const hasPayloadMode = payloadArgument !== "";
1755
- let data;
1756
- let payload;
1757
- if (hasDataMode && dataArgument !== "") {
1758
- data = readCommunityReportInput(dataArgument, "data");
1759
- }
1760
- if (hasPayloadMode) {
1761
- payload = readCommunityReportInput(payloadArgument, "payload");
1762
- }
1763
- const ids = {
1764
- spaceId: ctx.str("space-id"),
1765
- channelId: ctx.str("channel-id"),
1766
- sourceId: ctx.str("source-id")
1767
- };
1768
- const zoneId = ctx.str("zone-id") || DEFAULT_ZONE_ID;
1769
- try {
1770
- const normalized = normalizeCommunityReportInput({
1771
- spaceId: ids.spaceId,
1772
- channelId: ids.channelId,
1773
- sourceId: ids.sourceId,
1774
- zoneId,
1775
- dataType: hasDataMode ? dataType || void 0 : void 0,
1776
- data: hasDataMode ? data : void 0,
1777
- payload: hasPayloadMode ? payload : void 0
1778
- });
1779
- return { endpoint, normalized, ids, zoneId };
1780
- } catch (error) {
1781
- if (error instanceof CommunityDataValidationError) {
1782
- throw new CliValidationError(error.message, {
1783
- code: error.code,
1784
- hint: SCHEMA_VALIDATION_HINT,
1785
- location: {
1786
- segment: error.segment,
1787
- record: error.record,
1788
- field: error.field
1789
- }
1790
- });
1791
- }
1792
- throw error;
1793
- }
1794
- }
1795
- function endpointMissingSyncContentError() {
1796
- return new CliValidationError(
1797
- "The Iris sync endpoint URL is valid, but its path does not end with the required /sync_content suffix.",
1798
- {
1799
- code: "community_invalid_endpoint",
1800
- hint: "Provide the complete endpoint with /sync_content as the final path component and no trailing slash.",
1801
- location: { field: "endpoint" }
1802
- }
1803
- );
1804
- }
1805
- function endpointValidationError() {
1806
- return new CliValidationError(
1807
- "The Iris sync endpoint is invalid.",
1808
- {
1809
- code: "community_invalid_endpoint",
1810
- hint: "Use a complete http(s) URL ending in /sync_content, without credentials, query parameters, or a fragment.",
1811
- location: { field: "endpoint" }
1812
- }
1813
- );
1814
- }
1815
- function formatNormalization(stats) {
1816
- return {
1817
- truncated_fields: stats.truncatedFields,
1818
- defaulted_fields: stats.defaultedFields,
1819
- converted_integer_fields: stats.convertedIntegerFields,
1820
- fields: stats.fields
1821
- };
1822
- }
1823
-
1824
- // src/commands/te-community/index.ts
1825
- registerCapabilityGatewayRoute("community", { gatewayDomain: "community" });
1826
- registerMcpMappings({
1827
- "community_content": { componentName: "community", mappingPath: "content" },
1828
- "community_analysis": { componentName: "community", mappingPath: "analysis" },
1829
- "community_hot": { componentName: "community", mappingPath: "hot" }
1830
- });
1831
- var commands = [
1832
- communityDataReport,
1833
- // Content commands
1834
- getLivestreamList,
1835
- getLivestreamDetail,
1836
- getLivestreamAnalysis,
1837
- getPostDetail,
1838
- getCommentsSummary,
1839
- getCommentTagAnalysis,
1840
- getCorpusTags,
1841
- getRiskContent,
1842
- searchPosts,
1843
- getLivestreamOverview,
1844
- getLivestreamRooms,
1845
- getLivestreamRoomMetrics,
1846
- // Analysis commands
1847
- getChannelInfo,
1848
- getOverviewMetrics,
1849
- getSentimentOverview,
1850
- getTagTrends,
1851
- // Hot commands
1852
- getDailySummary,
1853
- getHotTopics,
1854
- getTopicDetail
1855
- ];
1856
- var te_community_default = commands;
1857
- export {
1858
- te_community_default as default
1859
- };