@thinkingai/ae-cli 6.0.56 → 6.0.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/README.zh.md +12 -4
- package/bin/ae-cli.js +5 -0
- package/bin/runtime-capabilities.json +5 -0
- package/cli-installation-guide.md +24 -3
- package/dist/{auth-U7MNUUEF.js → auth-A5OO4VS4.js} +30 -12
- package/dist/{automation-K32V2NKP.js → automation-FD2QZUP6.js} +6 -6
- package/dist/{capability-VV7RHB3B.js → capability-SKI4GHF4.js} +48 -20
- package/dist/{chunk-P6VFCH3P.js → chunk-2F64AZYH.js} +2 -2
- package/dist/{chunk-LMCYSVVA.js → chunk-4PSTYEG5.js} +50 -41
- package/dist/{chunk-KHS64JZQ.js → chunk-72NJKS7O.js} +4 -4
- package/dist/{chunk-GI5BMEZO.js → chunk-AWVZTN2B.js} +1 -1
- package/dist/{chunk-UUD5RB36.js → chunk-E25OSZLJ.js} +3 -3
- package/dist/{chunk-W2PTY5NN.js → chunk-EEARZNHE.js} +3 -3
- package/dist/chunk-GBSACKFB.js +2283 -0
- package/dist/{chunk-3TQA5RXE.js → chunk-HQCY3QCA.js} +6 -4
- package/dist/{chunk-T44M3PLZ.js → chunk-JEUXDY3B.js} +1 -1
- package/dist/{chunk-2UWS5CUP.js → chunk-JSBLCLFE.js} +3 -3
- package/dist/chunk-K2Z7QHDR.js +132 -0
- package/dist/{chunk-QSAR3GHY.js → chunk-OAWPT7VP.js} +2 -2
- package/dist/{chunk-2IYIYSUO.js → chunk-QVRIDN42.js} +2 -2
- package/dist/{chunk-F6NIPIT5.js → chunk-QWSA3EOD.js} +35 -14
- package/dist/{chunk-P7KL7IOH.js → chunk-SOSFRU3O.js} +2 -2
- package/dist/{chunk-MXV3PYC3.js → chunk-UYJMEHAF.js} +50 -12
- package/dist/{chunk-JJJIP2XQ.js → chunk-VDICGXCY.js} +2 -2
- package/dist/chunk-WMDO53YK.js +222 -0
- package/dist/{chunk-X7DRLE62.js → chunk-YVG6QD3V.js} +1 -1
- package/dist/{chunk-ZW4VMY5H.js → chunk-Z3Q2AEIN.js} +152 -98
- package/dist/{community-report-client-S2K3A3Q6.js → community-report-client-TJVCLZ3K.js} +1 -1
- package/dist/compat-check-WFEKVLTH.js +32 -0
- package/dist/{config-ASSAAIOI.js → config-U4I7X25P.js} +38 -6
- package/dist/{context-6RWLVOM5.js → context-CDB5H5PD.js} +4 -4
- package/dist/{data-integration-EXSUPUDZ.js → data-integration-QE37EQ3A.js} +207 -24
- package/dist/{evaluation-5YT4QLCL.js → evaluation-LR4YDYWL.js} +4 -4
- package/dist/index.js +291 -71
- package/dist/{local-data-upload-client-7HCPGIS3.js → local-data-upload-client-KCMG6J62.js} +1 -1
- package/dist/{memory-VCYLVPMI.js → memory-SNFR5M23.js} +6 -6
- package/dist/{metadata-TIWIOFTU.js → metadata-6QM6EED5.js} +16 -8
- package/dist/{model-DPJHSD73.js → model-KEVCEXWD.js} +4 -4
- package/dist/{personal-semantic-preference-SBKU2LOK.js → personal-semantic-preference-7VXQPP2N.js} +7 -6
- package/dist/{project-semantic-SKNWLH3I.js → project-semantic-PSEPYH2L.js} +7 -6
- package/dist/{sync-7PP6KQ75.js → sync-22ACDK5Z.js} +8 -7
- package/dist/{te-agent-L2BYNPHM.js → te-agent-BJ36B4QI.js} +11 -7
- package/dist/{te-analysis-RJB6VJC4.js → te-analysis-WQ6D6YG4.js} +179 -27
- package/dist/{te-community-QN3WMPNT.js → te-community-VRWMFGI6.js} +720 -228
- package/dist/{te-dataops-UKD2XN3E.js → te-dataops-YTLFTTW7.js} +6 -5
- package/dist/{te-engage-KPTN4N2U.js → te-engage-V2QW7TIC.js} +7 -6
- package/dist/{te-kb-SFZI3W5T.js → te-kb-4AQLISDR.js} +407 -83
- package/dist/{te-system-IE7NCKC3.js → te-system-CYWSK3UN.js} +6 -6
- package/dist/{te-team-4KHCFM2I.js → te-team-6ELNGBWT.js} +7 -6
- package/dist/{things-7JJUVAWL.js → things-HFWLGCUH.js} +7 -7
- package/dist/{update-AX66IFMJ.js → update-3CRVVHV5.js} +11 -7
- package/dist/{workspace-Q5NMEKEN.js → workspace-2CKQX2XL.js} +6 -6
- package/package.json +17 -7
- package/skills/ae-agent/SKILL.md +1 -1
- package/skills/ae-agent/references/resource-package.md +4 -0
- package/skills/ae-analysis/SKILL.md +14 -10
- package/skills/ae-analysis/references/agent_review_preflight.md +52 -11
- package/skills/ae-analysis/references/agent_review_priorities_comparisons.md +5 -3
- package/skills/ae-analysis/references/agent_review_submit_to_page.md +57 -25
- package/skills/ae-analysis/references/command_index.md +7 -3
- package/skills/ae-analysis/references/governance_recommendation_export.md +31 -17
- package/skills/ae-analysis/references/governance_recommendation_run_update.md +13 -0
- package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +9 -1
- package/skills/ae-analysis/references/scenario_append.md +6 -0
- package/skills/ae-analysis/references/scenario_begin.md +11 -0
- package/skills/ae-analysis/references/scenario_collect.md +10 -2
- package/skills/ae-analysis/references/scenario_evaluation.md +6 -18
- package/skills/ae-analysis/references/scenario_get.md +15 -0
- package/skills/ae-analysis/references/scenario_list.md +64 -0
- package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +14 -1
- package/skills/ae-community/SKILL.md +77 -27
- package/skills/ae-community/references/community-analyzing-theme-comment.md +10 -8
- package/skills/ae-community/references/community-data-quality.md +68 -0
- package/skills/ae-community/references/community-query-evidence.md +85 -0
- package/skills/ae-community/references/community-scenario-capabilities.md +137 -0
- package/skills/ae-community/references/community-weekly-report.md +6 -3
- package/skills/ae-community/references/get_channel_info.md +7 -4
- package/skills/ae-community/references/get_comment_tag_analysis.md +3 -3
- package/skills/ae-community/references/get_comments_summary.md +4 -4
- package/skills/ae-community/references/get_corpus_tags.md +3 -3
- package/skills/ae-community/references/get_daily_summary.md +3 -3
- package/skills/ae-community/references/get_hot_topics.md +3 -3
- package/skills/ae-community/references/get_livestream_analysis.md +3 -3
- package/skills/ae-community/references/get_livestream_detail.md +4 -4
- package/skills/ae-community/references/get_livestream_list.md +3 -3
- package/skills/ae-community/references/get_livestream_overview.md +3 -3
- package/skills/ae-community/references/get_livestream_room_metrics.md +3 -3
- package/skills/ae-community/references/get_livestream_rooms.md +3 -3
- package/skills/ae-community/references/get_overview_metrics.md +3 -3
- package/skills/ae-community/references/get_post_detail.md +4 -4
- package/skills/ae-community/references/get_risk_content.md +5 -5
- package/skills/ae-community/references/get_sentiment_overview.md +4 -4
- package/skills/ae-community/references/get_tag_trends.md +3 -3
- package/skills/ae-community/references/get_topic_detail.md +3 -3
- package/skills/ae-community/references/list_sources.md +28 -0
- package/skills/ae-community/references/list_spaces.md +39 -0
- package/skills/ae-community/references/search_posts.md +21 -7
- package/skills/ae-data-integration/SKILL.md +6 -4
- package/skills/ae-data-integration/references/error-handling.md +1 -0
- package/skills/ae-data-integration/references/handoff.md +1 -1
- package/skills/ae-data-integration/references/sink-upload.md +11 -0
- package/skills/ae-data-integration/references/source-inspect.md +2 -1
- package/skills/ae-data-integration/references/transform.md +1 -1
- package/skills/ae-data-integration/references/ue-mapping.md +1 -1
- package/skills/ae-kb/SKILL.md +37 -8
- package/skills/ae-kb/references/publication.md +81 -0
- package/skills/ae-kb/references/query-workflow.md +20 -6
- package/skills/ae-kb-discovery/SKILL.md +15 -6
- package/skills/ae-metadata/SKILL.md +2 -2
- package/skills/ae-metadata/references/metadata_data_table_get.md +35 -0
- package/dist/chunk-ELCR235M.js +0 -1429
|
@@ -1,24 +1,40 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
createCapabilityCommand
|
|
4
|
+
} from "./chunk-HQCY3QCA.js";
|
|
5
|
+
import {
|
|
6
|
+
registerCapabilityGatewayRoute,
|
|
7
|
+
resolveGatewayDomain
|
|
4
8
|
} from "./chunk-KPQY44JQ.js";
|
|
9
|
+
import {
|
|
10
|
+
CapabilityGatewayError,
|
|
11
|
+
buildCapabilityGatewayUrl,
|
|
12
|
+
requestCapabilityGatewayWithEnvelope
|
|
13
|
+
} from "./chunk-UYJMEHAF.js";
|
|
14
|
+
import {
|
|
15
|
+
unwrapOutputData,
|
|
16
|
+
withOutputMetadata
|
|
17
|
+
} from "./chunk-WMDO53YK.js";
|
|
5
18
|
import {
|
|
6
19
|
internalCallSourceHeaders
|
|
7
20
|
} from "./chunk-Y74WTIKJ.js";
|
|
21
|
+
import "./chunk-GBSACKFB.js";
|
|
8
22
|
import {
|
|
9
23
|
SecureStoreAuthError,
|
|
10
24
|
getCliToken
|
|
11
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-SOSFRU3O.js";
|
|
12
26
|
import {
|
|
13
27
|
getActiveHost,
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
parseJsonRequest,
|
|
29
|
+
safeJsonParse,
|
|
30
|
+
stringifyJsonRequest
|
|
31
|
+
} from "./chunk-4PSTYEG5.js";
|
|
16
32
|
import {
|
|
17
33
|
CliValidationError,
|
|
18
34
|
PermissionError,
|
|
19
35
|
externalCliTokenRequestOptions,
|
|
20
36
|
logger
|
|
21
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-Z3Q2AEIN.js";
|
|
22
38
|
|
|
23
39
|
// src/core/mcp.ts
|
|
24
40
|
async function permissionDetails(resp) {
|
|
@@ -96,7 +112,7 @@ async function mcpRequest(url, method, params = {}, hostOverride) {
|
|
|
96
112
|
const resp = await fetch(url, {
|
|
97
113
|
method: "POST",
|
|
98
114
|
headers,
|
|
99
|
-
body:
|
|
115
|
+
body: stringifyJsonRequest(body),
|
|
100
116
|
...externalCliTokenRequestOptions(url)
|
|
101
117
|
});
|
|
102
118
|
if (!resp.ok) {
|
|
@@ -202,13 +218,13 @@ var getChannelInfo = {
|
|
|
202
218
|
command: "+get_channel_info",
|
|
203
219
|
description: "Valid channel list for a community space (channel id and name).",
|
|
204
220
|
flags: [
|
|
205
|
-
{ name: "space-id", type: "
|
|
206
|
-
{ name: "game-id", type: "
|
|
221
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
222
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." }
|
|
207
223
|
],
|
|
208
224
|
risk: "read",
|
|
209
225
|
execute: async (ctx) => {
|
|
210
|
-
const spaceId = ctx.
|
|
211
|
-
const gameId = ctx.
|
|
226
|
+
const spaceId = ctx.int64("space-id");
|
|
227
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
212
228
|
const host = ctx.host();
|
|
213
229
|
const param = { gameId };
|
|
214
230
|
const args = { spaceId, param };
|
|
@@ -218,14 +234,219 @@ var getChannelInfo = {
|
|
|
218
234
|
}
|
|
219
235
|
};
|
|
220
236
|
|
|
237
|
+
// src/commands/te-community/support.ts
|
|
238
|
+
var INTEGER_ENUMS = {
|
|
239
|
+
"+get_comment_tag_analysis:resource-type": [0, 1],
|
|
240
|
+
"+get_post_detail:resource-type": [0, 1],
|
|
241
|
+
"+get_post_detail:danmu-order-by": [0, 1],
|
|
242
|
+
"+get_post_detail:reply-order-by": [0, 1],
|
|
243
|
+
"+get_livestream_detail:order-by": [0, 1],
|
|
244
|
+
"+get_hot_topics:order-by": [0, 1],
|
|
245
|
+
"+get_risk_content:order-by": [0],
|
|
246
|
+
"+search_posts:search-mode": [0, 1, 2],
|
|
247
|
+
"+search_posts:order-by": [0, 1, 2, 3, 4]
|
|
248
|
+
};
|
|
249
|
+
var INTEGER_LIST_ENUMS = {
|
|
250
|
+
"+get_livestream_detail:activity-types": [0, 1, 2, 3],
|
|
251
|
+
"+search_posts:resource-type": [0, 1],
|
|
252
|
+
"+search_posts:sentiment-types": [0, 1, 2]
|
|
253
|
+
};
|
|
254
|
+
function validateCommunityCommand(ctx, command) {
|
|
255
|
+
const gameId = ctx.optionalInt64("game-id");
|
|
256
|
+
const spaceId = command.flags.some((flag) => flag.name === "space-id") ? ctx.int64("space-id") : void 0;
|
|
257
|
+
if (spaceId !== void 0 && gameId !== void 0 && gameId !== spaceId) {
|
|
258
|
+
throw validationError(
|
|
259
|
+
"--game-id must match --space-id",
|
|
260
|
+
"Omit --game-id; it is a deprecated compatibility alias and defaults to --space-id."
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
validateTemporalFlag(ctx, command, "date", "date");
|
|
264
|
+
validateTemporalFlag(ctx, command, "start-time", "date-time");
|
|
265
|
+
validateTemporalFlag(ctx, command, "end-time", "date-time");
|
|
266
|
+
validateTemporalFlag(ctx, command, "anchor-time", "minute");
|
|
267
|
+
validateTemporalFlag(ctx, command, "year", "year");
|
|
268
|
+
validateRange(ctx, command);
|
|
269
|
+
validateInt64Csv(ctx, command, "channel-id-list");
|
|
270
|
+
validateInt64Csv(ctx, command, "risk-codes");
|
|
271
|
+
if (command.flags.some((flag) => flag.name === "tag-filters") && ctx.str("tag-filters").trim()) {
|
|
272
|
+
validateTagFilters(ctx.json("tag-filters"));
|
|
273
|
+
}
|
|
274
|
+
for (const [key, choices] of Object.entries(INTEGER_ENUMS)) {
|
|
275
|
+
const [commandName, flagName] = key.split(":");
|
|
276
|
+
if (command.command === commandName) validateIntegerEnum(ctx, command, flagName, choices);
|
|
277
|
+
}
|
|
278
|
+
for (const [key, choices] of Object.entries(INTEGER_LIST_ENUMS)) {
|
|
279
|
+
const [commandName, flagName] = key.split(":");
|
|
280
|
+
if (command.command === commandName) validateIntegerCsv(ctx, command, flagName, choices);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function stringCsv(ctx, name) {
|
|
284
|
+
const raw = ctx.str(name).trim();
|
|
285
|
+
if (!raw) return void 0;
|
|
286
|
+
const values = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
287
|
+
return values.length > 0 ? values : void 0;
|
|
288
|
+
}
|
|
289
|
+
function tagFilters(ctx) {
|
|
290
|
+
const raw = ctx.str("tag-filters").trim();
|
|
291
|
+
if (!raw) return void 0;
|
|
292
|
+
const value = ctx.json("tag-filters");
|
|
293
|
+
validateTagFilters(value);
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
function validateTemporalFlag(ctx, command, name, format) {
|
|
297
|
+
if (!command.flags.some((flag) => flag.name === name)) return;
|
|
298
|
+
const value = ctx.str(name).trim();
|
|
299
|
+
if (!value) return;
|
|
300
|
+
if (format === "year") {
|
|
301
|
+
if (!/^\d{4}$/.test(value)) {
|
|
302
|
+
throw validationError(`--${name} must use yyyy format`);
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (format === "minute") {
|
|
307
|
+
if (!parseDateTime(value, true)) {
|
|
308
|
+
throw validationError(`--${name} must use yyyy-MM-dd HH:mm format`);
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (format === "date") {
|
|
313
|
+
if (!parseDate(value)) {
|
|
314
|
+
throw validationError(`--${name} must be a valid date in yyyy-MM-dd format`);
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!parseDate(value) && !parseDateTime(value, false)) {
|
|
319
|
+
throw validationError(
|
|
320
|
+
`--${name} must be a valid date or timestamp`,
|
|
321
|
+
"Use yyyy-MM-dd, yyyy-MM-dd HH:mm, or yyyy-MM-dd HH:mm:ss."
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function validateRange(ctx, command) {
|
|
326
|
+
if (!command.flags.some((flag) => flag.name === "start-time") || !command.flags.some((flag) => flag.name === "end-time")) return;
|
|
327
|
+
const start = temporalValue(ctx.str("start-time"));
|
|
328
|
+
const end = temporalValue(ctx.str("end-time"));
|
|
329
|
+
if (start !== void 0 && end !== void 0 && end < start) {
|
|
330
|
+
throw validationError("--end-time must not be earlier than --start-time");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
function validateInt64Csv(ctx, command, name) {
|
|
334
|
+
if (!command.flags.some((flag) => flag.name === name)) return;
|
|
335
|
+
const raw = ctx.str(name).trim();
|
|
336
|
+
if (!raw) return;
|
|
337
|
+
const values = raw.split(",");
|
|
338
|
+
const max = (1n << 63n) - 1n;
|
|
339
|
+
for (const value of values) {
|
|
340
|
+
const item = value.trim();
|
|
341
|
+
if (!/^[1-9]\d*$/.test(item) || BigInt(item) > max) {
|
|
342
|
+
throw validationError(`--${name} must be a comma-separated list of positive int64 values`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function validateIntegerEnum(ctx, command, name, choices) {
|
|
347
|
+
if (!command.flags.some((flag) => flag.name === name)) return;
|
|
348
|
+
const raw = ctx.str(name).trim();
|
|
349
|
+
if (!raw) return;
|
|
350
|
+
const value = Number(raw);
|
|
351
|
+
if (!Number.isInteger(value) || !choices.includes(value)) {
|
|
352
|
+
throw validationError(`--${name} must be one of: ${choices.join(", ")}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function validateIntegerCsv(ctx, command, name, choices) {
|
|
356
|
+
if (!command.flags.some((flag) => flag.name === name)) return;
|
|
357
|
+
const raw = ctx.str(name).trim();
|
|
358
|
+
if (!raw) return;
|
|
359
|
+
const values = raw.split(",").map((value) => Number(value.trim()));
|
|
360
|
+
if (values.some((value) => !Number.isInteger(value) || !choices.includes(value))) {
|
|
361
|
+
throw validationError(
|
|
362
|
+
`--${name} must be a comma-separated list containing only: ${choices.join(", ")}`
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function validateTagFilters(value) {
|
|
367
|
+
if (!Array.isArray(value)) {
|
|
368
|
+
throw validationError("--tag-filters must be a JSON array");
|
|
369
|
+
}
|
|
370
|
+
const required = ["tagName", "tagCode", "inferenceType", "calcuSymbol"];
|
|
371
|
+
value.forEach((item, index) => {
|
|
372
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
373
|
+
throw validationError(`--tag-filters item ${index + 1} must be an object`);
|
|
374
|
+
}
|
|
375
|
+
const record2 = item;
|
|
376
|
+
for (const field2 of required) {
|
|
377
|
+
if (typeof record2[field2] !== "string" || !record2[field2].trim()) {
|
|
378
|
+
throw validationError(`--tag-filters item ${index + 1} requires non-empty ${field2}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const inferenceType = String(record2.inferenceType);
|
|
382
|
+
const calcuSymbol = String(record2.calcuSymbol);
|
|
383
|
+
if (inferenceType !== "label" && inferenceType !== "score") {
|
|
384
|
+
throw validationError(
|
|
385
|
+
`--tag-filters item ${index + 1} inferenceType must be label or score`,
|
|
386
|
+
"Use inferenceType from +get_corpus_tags; extract tags are not filterable by search_posts."
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const allowedSymbols = inferenceType === "label" ? ["C04", "C05"] : ["C00", "C01", "C02", "C020", "C03", "C030", "C04", "C05", "C06"];
|
|
390
|
+
if (!allowedSymbols.includes(calcuSymbol)) {
|
|
391
|
+
throw validationError(
|
|
392
|
+
`--tag-filters item ${index + 1} calcuSymbol must be one of: ${allowedSymbols.join(", ")}`
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
if (inferenceType === "score" && !["C04", "C05"].includes(calcuSymbol)) {
|
|
396
|
+
if (!Array.isArray(record2.ftv)) {
|
|
397
|
+
throw validationError(
|
|
398
|
+
`--tag-filters item ${index + 1} ftv must be an array for score comparison ${calcuSymbol}`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
const requiredValues = calcuSymbol === "C06" ? 2 : 1;
|
|
402
|
+
if (record2.ftv.length < requiredValues) {
|
|
403
|
+
throw validationError(
|
|
404
|
+
`--tag-filters item ${index + 1} ftv requires at least ${requiredValues} value${requiredValues === 1 ? "" : "s"}`
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
function parseDate(value) {
|
|
411
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
|
|
412
|
+
if (!match) return void 0;
|
|
413
|
+
return validatedUtc(Number(match[1]), Number(match[2]), Number(match[3]), 0, 0, 0);
|
|
414
|
+
}
|
|
415
|
+
function parseDateTime(value, minuteOnly) {
|
|
416
|
+
const pattern = minuteOnly ? /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})$/ : /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2})(?::(\d{2}))?$/;
|
|
417
|
+
const match = pattern.exec(value);
|
|
418
|
+
if (!match) return void 0;
|
|
419
|
+
return validatedUtc(
|
|
420
|
+
Number(match[1]),
|
|
421
|
+
Number(match[2]),
|
|
422
|
+
Number(match[3]),
|
|
423
|
+
Number(match[4]),
|
|
424
|
+
Number(match[5]),
|
|
425
|
+
Number(match[6] ?? 0)
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
function validatedUtc(year, month, day, hour, minute, second) {
|
|
429
|
+
const value = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
|
|
430
|
+
if (value.getUTCFullYear() !== year || value.getUTCMonth() !== month - 1 || value.getUTCDate() !== day || value.getUTCHours() !== hour || value.getUTCMinutes() !== minute || value.getUTCSeconds() !== second) return void 0;
|
|
431
|
+
return value.getTime();
|
|
432
|
+
}
|
|
433
|
+
function temporalValue(value) {
|
|
434
|
+
const trimmed = value.trim();
|
|
435
|
+
if (!trimmed) return void 0;
|
|
436
|
+
return parseDate(trimmed) ?? parseDateTime(trimmed, false);
|
|
437
|
+
}
|
|
438
|
+
function validationError(message, hint) {
|
|
439
|
+
return new CliValidationError(message, hint ? { hint } : void 0);
|
|
440
|
+
}
|
|
441
|
+
|
|
221
442
|
// src/commands/te-community/search_posts.ts
|
|
222
443
|
var searchPosts = {
|
|
223
444
|
service: "community",
|
|
224
445
|
command: "+search_posts",
|
|
225
446
|
description: "Search social posts/videos/feeds in a community space.",
|
|
226
447
|
flags: [
|
|
227
|
-
{ name: "space-id", type: "
|
|
228
|
-
{ name: "game-id", type: "
|
|
448
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
449
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
229
450
|
{ name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info (channelId). Comma-separated." },
|
|
230
451
|
{ name: "keywords", type: "string", required: false, desc: "Keyword filter list. Comma-separated." },
|
|
231
452
|
{ name: "start-time", type: "string", required: true, desc: "Content publish time lower bound. Format: yyyy-MM-dd." },
|
|
@@ -235,40 +456,46 @@ var searchPosts = {
|
|
|
235
456
|
{ 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.' },
|
|
236
457
|
{ name: "search-word", type: "string", required: false, desc: "Full-text query; used together with searchMode." },
|
|
237
458
|
{ name: "sentiment-types", type: "string", required: false, desc: "Sentiment filter: 0 negative, 1 positive, 2 neutral. Comma-separated." },
|
|
459
|
+
{ name: "topic-id-list", type: "string", required: false, desc: "Topic IDs from +get_hot_topics (topicId). Comma-separated." },
|
|
460
|
+
{ name: "tag-filters", type: "json", required: false, desc: "Corpus tag filter JSON array using tagName, tagCode, inferenceType, calcuSymbol, and optional ftv." },
|
|
238
461
|
{ 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." },
|
|
239
462
|
{ name: "page-num", type: "number", required: false, desc: "Page number, starting at 1." },
|
|
240
463
|
{ name: "page-size", type: "number", required: false, desc: "Page size. Max 10000." }
|
|
241
464
|
],
|
|
242
465
|
risk: "read",
|
|
243
466
|
execute: async (ctx) => {
|
|
244
|
-
const spaceId = ctx.
|
|
245
|
-
const gameId = ctx.
|
|
467
|
+
const spaceId = ctx.int64("space-id");
|
|
468
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
246
469
|
const startTime = ctx.str("start-time");
|
|
247
470
|
const endTime = ctx.str("end-time");
|
|
248
471
|
const host = ctx.host();
|
|
249
472
|
const param = { gameId, startTime, endTime };
|
|
250
473
|
const channelIdList = ctx.str("channel-id-list");
|
|
251
|
-
if (channelIdList) param.channelIdList = channelIdList.split(",").map(
|
|
474
|
+
if (channelIdList) param.channelIdList = channelIdList.split(",").map((value) => BigInt(value.trim()));
|
|
252
475
|
const keywords = ctx.str("keywords");
|
|
253
476
|
if (keywords) param.keywords = keywords.split(",");
|
|
254
|
-
const official = ctx.
|
|
477
|
+
const official = ctx.optionalBool("official");
|
|
255
478
|
if (official !== void 0) param.official = official;
|
|
256
479
|
const resourceType = ctx.str("resource-type");
|
|
257
480
|
if (resourceType) param.resourceType = resourceType.split(",").map(Number);
|
|
258
|
-
const searchMode = ctx.
|
|
481
|
+
const searchMode = ctx.optionalNum("search-mode");
|
|
259
482
|
if (searchMode !== void 0) param.searchMode = searchMode;
|
|
260
483
|
const searchWord = ctx.str("search-word");
|
|
261
484
|
if (searchWord) param.searchWord = searchWord;
|
|
262
485
|
const sentimentTypes = ctx.str("sentiment-types");
|
|
263
486
|
if (sentimentTypes) param.sentimentTypes = sentimentTypes.split(",").map(Number);
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
if (
|
|
487
|
+
const topicIdList = stringCsv(ctx, "topic-id-list");
|
|
488
|
+
if (topicIdList) param.topicIdList = topicIdList;
|
|
489
|
+
const parsedTagFilters = tagFilters(ctx);
|
|
490
|
+
if (parsedTagFilters) param.tagFilters = parsedTagFilters;
|
|
491
|
+
const orderBy = ctx.optionalNum("order-by");
|
|
492
|
+
const pageNum = ctx.optionalNum("page-num");
|
|
493
|
+
const pageSize = ctx.optionalNum("page-size");
|
|
494
|
+
if (orderBy !== void 0 || pageNum !== void 0 || pageSize !== void 0) {
|
|
268
495
|
param.pagerHeader = {};
|
|
269
496
|
if (orderBy !== void 0) param.pagerHeader.orderBy = orderBy;
|
|
270
|
-
if (pageNum) param.pagerHeader.pageNum = pageNum;
|
|
271
|
-
if (pageSize) param.pagerHeader.pageSize = pageSize;
|
|
497
|
+
if (pageNum !== void 0) param.pagerHeader.pageNum = pageNum;
|
|
498
|
+
if (pageSize !== void 0) param.pagerHeader.pageSize = pageSize;
|
|
272
499
|
}
|
|
273
500
|
const args = { spaceId, param };
|
|
274
501
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
|
|
@@ -283,9 +510,9 @@ var getPostDetail = {
|
|
|
283
510
|
command: "+get_post_detail",
|
|
284
511
|
description: "Full detail for one post/video: body, media, subtitles, danmu, sentiment, keywords, topics, tag values, and comment pager.",
|
|
285
512
|
flags: [
|
|
286
|
-
{ name: "space-id", type: "
|
|
287
|
-
{ name: "game-id", type: "
|
|
288
|
-
{ name: "channel-id", type: "
|
|
513
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
514
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
515
|
+
{ name: "channel-id", type: "int64", required: true, min: 1, desc: "Channel ID." },
|
|
289
516
|
{ name: "uuid", type: "string", required: true, desc: "Content UUID (unique id)." },
|
|
290
517
|
{ name: "resource-type", type: "number", required: true, desc: "Resource type: 0 post, 1 video." },
|
|
291
518
|
{ name: "danmu-order-by", type: "number", required: false, desc: "Sort for danmu: 0 video timestamp desc, 1 publish time desc." },
|
|
@@ -297,30 +524,30 @@ var getPostDetail = {
|
|
|
297
524
|
],
|
|
298
525
|
risk: "read",
|
|
299
526
|
execute: async (ctx) => {
|
|
300
|
-
const spaceId = ctx.
|
|
301
|
-
const gameId = ctx.
|
|
302
|
-
const channelId = ctx.
|
|
527
|
+
const spaceId = ctx.int64("space-id");
|
|
528
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
529
|
+
const channelId = ctx.int64("channel-id");
|
|
303
530
|
const uuid = ctx.str("uuid");
|
|
304
531
|
const resourceType = ctx.num("resource-type");
|
|
305
532
|
const host = ctx.host();
|
|
306
533
|
const param = { gameId, channelId, uuid, resourceType };
|
|
307
|
-
const danmuOrderBy = ctx.
|
|
308
|
-
const danmuPageNum = ctx.
|
|
309
|
-
const danmuPageSize = ctx.
|
|
310
|
-
if (danmuOrderBy !== void 0 || danmuPageNum || danmuPageSize) {
|
|
534
|
+
const danmuOrderBy = ctx.optionalNum("danmu-order-by");
|
|
535
|
+
const danmuPageNum = ctx.optionalNum("danmu-page-num");
|
|
536
|
+
const danmuPageSize = ctx.optionalNum("danmu-page-size");
|
|
537
|
+
if (danmuOrderBy !== void 0 || danmuPageNum !== void 0 || danmuPageSize !== void 0) {
|
|
311
538
|
param.danmuPagerHeader = {};
|
|
312
539
|
if (danmuOrderBy !== void 0) param.danmuPagerHeader.orderBy = danmuOrderBy;
|
|
313
|
-
if (danmuPageNum) param.danmuPagerHeader.pageNum = danmuPageNum;
|
|
314
|
-
if (danmuPageSize) param.danmuPagerHeader.pageSize = danmuPageSize;
|
|
540
|
+
if (danmuPageNum !== void 0) param.danmuPagerHeader.pageNum = danmuPageNum;
|
|
541
|
+
if (danmuPageSize !== void 0) param.danmuPagerHeader.pageSize = danmuPageSize;
|
|
315
542
|
}
|
|
316
|
-
const replyOrderBy = ctx.
|
|
317
|
-
const replyPageNum = ctx.
|
|
318
|
-
const replyPageSize = ctx.
|
|
319
|
-
if (replyOrderBy !== void 0 || replyPageNum || replyPageSize) {
|
|
543
|
+
const replyOrderBy = ctx.optionalNum("reply-order-by");
|
|
544
|
+
const replyPageNum = ctx.optionalNum("reply-page-num");
|
|
545
|
+
const replyPageSize = ctx.optionalNum("reply-page-size");
|
|
546
|
+
if (replyOrderBy !== void 0 || replyPageNum !== void 0 || replyPageSize !== void 0) {
|
|
320
547
|
param.replyPagerHeader = {};
|
|
321
548
|
if (replyOrderBy !== void 0) param.replyPagerHeader.orderBy = replyOrderBy;
|
|
322
|
-
if (replyPageNum) param.replyPagerHeader.pageNum = replyPageNum;
|
|
323
|
-
if (replyPageSize) param.replyPagerHeader.pageSize = replyPageSize;
|
|
549
|
+
if (replyPageNum !== void 0) param.replyPagerHeader.pageNum = replyPageNum;
|
|
550
|
+
if (replyPageSize !== void 0) param.replyPagerHeader.pageSize = replyPageSize;
|
|
324
551
|
}
|
|
325
552
|
const args = { spaceId, param };
|
|
326
553
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
|
|
@@ -335,22 +562,22 @@ var getOverviewMetrics = {
|
|
|
335
562
|
command: "+get_overview_metrics",
|
|
336
563
|
description: "Overall community metrics in a date range: post/reply counts, sentiment counts, per-channel breakdown.",
|
|
337
564
|
flags: [
|
|
338
|
-
{ name: "space-id", type: "
|
|
339
|
-
{ name: "game-id", type: "
|
|
565
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
566
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
340
567
|
{ name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info (channelId)." },
|
|
341
568
|
{ name: "start-time", type: "string", required: true, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
342
569
|
{ name: "end-time", type: "string", required: true, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
|
|
343
570
|
],
|
|
344
571
|
risk: "read",
|
|
345
572
|
execute: async (ctx) => {
|
|
346
|
-
const spaceId = ctx.
|
|
347
|
-
const gameId = ctx.
|
|
573
|
+
const spaceId = ctx.int64("space-id");
|
|
574
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
348
575
|
const startTime = ctx.str("start-time");
|
|
349
576
|
const endTime = ctx.str("end-time");
|
|
350
577
|
const host = ctx.host();
|
|
351
578
|
const param = { gameId, startTime, endTime };
|
|
352
579
|
const channelIdList = ctx.str("channel-id-list");
|
|
353
|
-
if (channelIdList) param.channelIdList = channelIdList.split(",").map(
|
|
580
|
+
if (channelIdList) param.channelIdList = channelIdList.split(",").map((value) => BigInt(value.trim()));
|
|
354
581
|
const args = { spaceId, param };
|
|
355
582
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_analysis");
|
|
356
583
|
const result = await callMcpTool(mcpUrl, "get_overview_metrics", args, host);
|
|
@@ -364,8 +591,8 @@ var getSentimentOverview = {
|
|
|
364
591
|
command: "+get_sentiment_overview",
|
|
365
592
|
description: "Sentiment distribution and hot-keyword overview: top keywords per sentiment with trends, plus overall keyword ranking.",
|
|
366
593
|
flags: [
|
|
367
|
-
{ name: "space-id", type: "
|
|
368
|
-
{ name: "game-id", type: "
|
|
594
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
595
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
369
596
|
{ name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info (channelId)." },
|
|
370
597
|
{ name: "start-time", type: "string", required: true, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
371
598
|
{ name: "end-time", type: "string", required: true, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." },
|
|
@@ -374,18 +601,18 @@ var getSentimentOverview = {
|
|
|
374
601
|
],
|
|
375
602
|
risk: "read",
|
|
376
603
|
execute: async (ctx) => {
|
|
377
|
-
const spaceId = ctx.
|
|
378
|
-
const gameId = ctx.
|
|
604
|
+
const spaceId = ctx.int64("space-id");
|
|
605
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
379
606
|
const startTime = ctx.str("start-time");
|
|
380
607
|
const endTime = ctx.str("end-time");
|
|
381
608
|
const host = ctx.host();
|
|
382
609
|
const param = { gameId, startTime, endTime };
|
|
383
610
|
const channelIdList = ctx.str("channel-id-list");
|
|
384
|
-
if (channelIdList) param.channelIdList = channelIdList.split(",").map(
|
|
385
|
-
const isMerged = ctx.
|
|
611
|
+
if (channelIdList) param.channelIdList = channelIdList.split(",").map((value) => BigInt(value.trim()));
|
|
612
|
+
const isMerged = ctx.optionalBool("is-merged");
|
|
386
613
|
if (isMerged !== void 0) param.isMerged = isMerged;
|
|
387
|
-
const limit = ctx.
|
|
388
|
-
if (limit) param.limit = limit;
|
|
614
|
+
const limit = ctx.optionalNum("limit");
|
|
615
|
+
if (limit !== void 0) param.limit = limit;
|
|
389
616
|
const args = { spaceId, param };
|
|
390
617
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_analysis");
|
|
391
618
|
const result = await callMcpTool(mcpUrl, "get_sentiment_overview", args, host);
|
|
@@ -399,8 +626,8 @@ var getTagTrends = {
|
|
|
399
626
|
command: "+get_tag_trends",
|
|
400
627
|
description: "Daily tag trends: given tagCode and date range, returns counts and time series per tag value.",
|
|
401
628
|
flags: [
|
|
402
|
-
{ name: "space-id", type: "
|
|
403
|
-
{ name: "game-id", type: "
|
|
629
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
630
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
404
631
|
{ name: "tag-code", type: "string", required: true, desc: "Tag code (tagCode) from +get_corpus_tags." },
|
|
405
632
|
{ name: "channel-id-list", type: "string", required: false, desc: "Channel IDs from +get_channel_info." },
|
|
406
633
|
{ name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
@@ -408,13 +635,13 @@ var getTagTrends = {
|
|
|
408
635
|
],
|
|
409
636
|
risk: "read",
|
|
410
637
|
execute: async (ctx) => {
|
|
411
|
-
const spaceId = ctx.
|
|
412
|
-
const gameId = ctx.
|
|
638
|
+
const spaceId = ctx.int64("space-id");
|
|
639
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
413
640
|
const tagCode = ctx.str("tag-code");
|
|
414
641
|
const host = ctx.host();
|
|
415
642
|
const param = { gameId, tagCode };
|
|
416
643
|
const channelIdList = ctx.str("channel-id-list");
|
|
417
|
-
if (channelIdList) param.channelIdList = channelIdList.split(",").map(
|
|
644
|
+
if (channelIdList) param.channelIdList = channelIdList.split(",").map((value) => BigInt(value.trim()));
|
|
418
645
|
const startTime = ctx.str("start-time");
|
|
419
646
|
if (startTime) param.startTime = startTime;
|
|
420
647
|
const endTime = ctx.str("end-time");
|
|
@@ -432,14 +659,14 @@ var getDailySummary = {
|
|
|
432
659
|
command: "+get_daily_summary",
|
|
433
660
|
description: "Daily community summary for a given date: hot topic rankings and highlighted opinions (positive/negative).",
|
|
434
661
|
flags: [
|
|
435
|
-
{ name: "space-id", type: "
|
|
436
|
-
{ name: "game-id", type: "
|
|
662
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
663
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
437
664
|
{ name: "date", type: "string", required: true, desc: "Summary date. Format: yyyy-MM-dd." }
|
|
438
665
|
],
|
|
439
666
|
risk: "read",
|
|
440
667
|
execute: async (ctx) => {
|
|
441
|
-
const spaceId = ctx.
|
|
442
|
-
const gameId = ctx.
|
|
668
|
+
const spaceId = ctx.int64("space-id");
|
|
669
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
443
670
|
const date = ctx.str("date");
|
|
444
671
|
const host = ctx.host();
|
|
445
672
|
const param = { gameId, date };
|
|
@@ -456,23 +683,23 @@ var getHotTopics = {
|
|
|
456
683
|
command: "+get_hot_topics",
|
|
457
684
|
description: "Hot topics leaderboard over a time range.",
|
|
458
685
|
flags: [
|
|
459
|
-
{ name: "space-id", type: "
|
|
460
|
-
{ name: "game-id", type: "
|
|
686
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
687
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
461
688
|
{ name: "start-time", type: "string", required: false, desc: "Topic statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
462
689
|
{ name: "end-time", type: "string", required: false, desc: "Topic statistics range end (inclusive). Format: yyyy-MM-dd." },
|
|
463
690
|
{ name: "order-by", type: "number", required: false, desc: "Sort: 0 by topic heat desc, 1 by topic start time desc." }
|
|
464
691
|
],
|
|
465
692
|
risk: "read",
|
|
466
693
|
execute: async (ctx) => {
|
|
467
|
-
const spaceId = ctx.
|
|
468
|
-
const gameId = ctx.
|
|
694
|
+
const spaceId = ctx.int64("space-id");
|
|
695
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
469
696
|
const host = ctx.host();
|
|
470
697
|
const param = { gameId };
|
|
471
698
|
const startTime = ctx.str("start-time");
|
|
472
699
|
if (startTime) param.startTime = startTime;
|
|
473
700
|
const endTime = ctx.str("end-time");
|
|
474
701
|
if (endTime) param.endTime = endTime;
|
|
475
|
-
const orderBy = ctx.
|
|
702
|
+
const orderBy = ctx.optionalNum("order-by");
|
|
476
703
|
if (orderBy !== void 0) param.orderBy = orderBy;
|
|
477
704
|
const args = { spaceId, param };
|
|
478
705
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_hot");
|
|
@@ -487,16 +714,16 @@ var getTopicDetail = {
|
|
|
487
714
|
command: "+get_topic_detail",
|
|
488
715
|
description: "Deep stats for one topic in a range: content/reply totals, sentiment, per-channel daily trends, hot keywords.",
|
|
489
716
|
flags: [
|
|
490
|
-
{ name: "space-id", type: "
|
|
491
|
-
{ name: "game-id", type: "
|
|
717
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
718
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
492
719
|
{ name: "topic-id", type: "string", required: true, desc: "Topic ID from +get_hot_topics (topicId)." },
|
|
493
720
|
{ name: "start-time", type: "string", required: true, desc: "Statistics start date. Format: yyyy-MM-dd. Prefer topic startTime from +get_hot_topics." },
|
|
494
721
|
{ name: "end-time", type: "string", required: true, desc: "Statistics end date. Format: yyyy-MM-dd. Prefer topic endTime from +get_hot_topics." }
|
|
495
722
|
],
|
|
496
723
|
risk: "read",
|
|
497
724
|
execute: async (ctx) => {
|
|
498
|
-
const spaceId = ctx.
|
|
499
|
-
const gameId = ctx.
|
|
725
|
+
const spaceId = ctx.int64("space-id");
|
|
726
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
500
727
|
const topicId = ctx.str("topic-id");
|
|
501
728
|
const startTime = ctx.str("start-time");
|
|
502
729
|
const endTime = ctx.str("end-time");
|
|
@@ -515,15 +742,15 @@ var getLivestreamOverview = {
|
|
|
515
742
|
command: "+get_livestream_overview",
|
|
516
743
|
description: "Livestream overview trends: active rooms, session count, interactions, and streamed duration.",
|
|
517
744
|
flags: [
|
|
518
|
-
{ name: "space-id", type: "
|
|
519
|
-
{ name: "game-id", type: "
|
|
745
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
746
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
520
747
|
{ name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
521
748
|
{ name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
|
|
522
749
|
],
|
|
523
750
|
risk: "read",
|
|
524
751
|
execute: async (ctx) => {
|
|
525
|
-
const spaceId = ctx.
|
|
526
|
-
const gameId = ctx.
|
|
752
|
+
const spaceId = ctx.int64("space-id");
|
|
753
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
527
754
|
const host = ctx.host();
|
|
528
755
|
const param = { gameId };
|
|
529
756
|
const startTime = ctx.str("start-time");
|
|
@@ -543,20 +770,20 @@ var getLivestreamRooms = {
|
|
|
543
770
|
command: "+get_livestream_rooms",
|
|
544
771
|
description: "Search live rooms by time range and channels; returns room list with stats.",
|
|
545
772
|
flags: [
|
|
546
|
-
{ name: "space-id", type: "
|
|
547
|
-
{ name: "game-id", type: "
|
|
773
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
774
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
548
775
|
{ name: "channel-id-list", type: "string", required: false, desc: "Channel ID list." },
|
|
549
776
|
{ name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
550
777
|
{ name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
|
|
551
778
|
],
|
|
552
779
|
risk: "read",
|
|
553
780
|
execute: async (ctx) => {
|
|
554
|
-
const spaceId = ctx.
|
|
555
|
-
const gameId = ctx.
|
|
781
|
+
const spaceId = ctx.int64("space-id");
|
|
782
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
556
783
|
const host = ctx.host();
|
|
557
784
|
const param = { gameId };
|
|
558
785
|
const channelIdList = ctx.str("channel-id-list");
|
|
559
|
-
if (channelIdList) param.channelIdList = channelIdList.split(",").map(
|
|
786
|
+
if (channelIdList) param.channelIdList = channelIdList.split(",").map((value) => BigInt(value.trim()));
|
|
560
787
|
const startTime = ctx.str("start-time");
|
|
561
788
|
if (startTime) param.startTime = startTime;
|
|
562
789
|
const endTime = ctx.str("end-time");
|
|
@@ -574,17 +801,17 @@ var getLivestreamRoomMetrics = {
|
|
|
574
801
|
command: "+get_livestream_room_metrics",
|
|
575
802
|
description: "Core metrics and yearly calendar for one room: overview KPIs plus daily streamed-duration calendar.",
|
|
576
803
|
flags: [
|
|
577
|
-
{ name: "space-id", type: "
|
|
578
|
-
{ name: "game-id", type: "
|
|
579
|
-
{ name: "channel-id", type: "
|
|
804
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
805
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
806
|
+
{ name: "channel-id", type: "int64", required: true, min: 1, desc: "Channel ID from +get_livestream_rooms." },
|
|
580
807
|
{ name: "room-id", type: "string", required: true, desc: "Live room ID (roomId) from +get_livestream_rooms." },
|
|
581
808
|
{ name: "year", type: "string", required: false, desc: "Calendar year (yyyy)." }
|
|
582
809
|
],
|
|
583
810
|
risk: "read",
|
|
584
811
|
execute: async (ctx) => {
|
|
585
|
-
const spaceId = ctx.
|
|
586
|
-
const gameId = ctx.
|
|
587
|
-
const channelId = ctx.
|
|
812
|
+
const spaceId = ctx.int64("space-id");
|
|
813
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
814
|
+
const channelId = ctx.int64("channel-id");
|
|
588
815
|
const roomId = ctx.str("room-id");
|
|
589
816
|
const host = ctx.host();
|
|
590
817
|
const param = { gameId, channelId, roomId };
|
|
@@ -603,20 +830,20 @@ var getLivestreamList = {
|
|
|
603
830
|
command: "+get_livestream_list",
|
|
604
831
|
description: "List live sessions for a room in a time range (stream records).",
|
|
605
832
|
flags: [
|
|
606
|
-
{ name: "space-id", type: "
|
|
607
|
-
{ name: "game-id", type: "
|
|
608
|
-
{ name: "channel-id", type: "
|
|
833
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
834
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
835
|
+
{ name: "channel-id", type: "int64", required: false, min: 1, desc: "Channel ID from +get_livestream_rooms." },
|
|
609
836
|
{ name: "room-id", type: "string", required: false, desc: "Live room ID (roomId) from +get_livestream_rooms." },
|
|
610
837
|
{ name: "start-time", type: "string", required: false, desc: "Statistics range start (inclusive). Format: yyyy-MM-dd." },
|
|
611
838
|
{ name: "end-time", type: "string", required: false, desc: "Statistics range end (inclusive). Format: yyyy-MM-dd." }
|
|
612
839
|
],
|
|
613
840
|
risk: "read",
|
|
614
841
|
execute: async (ctx) => {
|
|
615
|
-
const spaceId = ctx.
|
|
616
|
-
const gameId = ctx.
|
|
842
|
+
const spaceId = ctx.int64("space-id");
|
|
843
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
617
844
|
const host = ctx.host();
|
|
618
845
|
const param = { gameId };
|
|
619
|
-
const channelId = ctx.
|
|
846
|
+
const channelId = ctx.int64("channel-id");
|
|
620
847
|
if (channelId) param.channelId = channelId;
|
|
621
848
|
const roomId = ctx.str("room-id");
|
|
622
849
|
if (roomId) param.roomId = roomId;
|
|
@@ -637,8 +864,8 @@ var getLivestreamDetail = {
|
|
|
637
864
|
command: "+get_livestream_detail",
|
|
638
865
|
description: "One live session detail: interaction leaderboards and paginated interaction feed.",
|
|
639
866
|
flags: [
|
|
640
|
-
{ name: "space-id", type: "
|
|
641
|
-
{ name: "game-id", type: "
|
|
867
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
868
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
642
869
|
{ name: "stream-id", type: "string", required: true, desc: "Live session ID (streamId) from +get_livestream_list." },
|
|
643
870
|
{ name: "activity-types", type: "string", required: false, desc: "Activity type codes to filter details: 0 danmu, 1 gift, 2 superchat, 3 premium. Comma-separated." },
|
|
644
871
|
{ name: "anchor-time", type: "string", required: false, desc: "Anchor time for interactions. Format: yyyy-MM-dd HH:mm." },
|
|
@@ -649,8 +876,8 @@ var getLivestreamDetail = {
|
|
|
649
876
|
],
|
|
650
877
|
risk: "read",
|
|
651
878
|
execute: async (ctx) => {
|
|
652
|
-
const spaceId = ctx.
|
|
653
|
-
const gameId = ctx.
|
|
879
|
+
const spaceId = ctx.int64("space-id");
|
|
880
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
654
881
|
const streamId = ctx.str("stream-id");
|
|
655
882
|
const host = ctx.host();
|
|
656
883
|
const param = { gameId, streamId };
|
|
@@ -660,14 +887,14 @@ var getLivestreamDetail = {
|
|
|
660
887
|
if (anchorTime) param.anchorTime = anchorTime;
|
|
661
888
|
const searchWord = ctx.str("search-word");
|
|
662
889
|
if (searchWord) param.searchWord = searchWord;
|
|
663
|
-
const orderBy = ctx.
|
|
664
|
-
const pageNum = ctx.
|
|
665
|
-
const pageSize = ctx.
|
|
666
|
-
if (orderBy !== void 0 || pageNum || pageSize) {
|
|
890
|
+
const orderBy = ctx.optionalNum("order-by");
|
|
891
|
+
const pageNum = ctx.optionalNum("page-num");
|
|
892
|
+
const pageSize = ctx.optionalNum("page-size");
|
|
893
|
+
if (orderBy !== void 0 || pageNum !== void 0 || pageSize !== void 0) {
|
|
667
894
|
param.pagerHeader = {};
|
|
668
895
|
if (orderBy !== void 0) param.pagerHeader.orderBy = orderBy;
|
|
669
|
-
if (pageNum) param.pagerHeader.pageNum = pageNum;
|
|
670
|
-
if (pageSize) param.pagerHeader.pageSize = pageSize;
|
|
896
|
+
if (pageNum !== void 0) param.pagerHeader.pageNum = pageNum;
|
|
897
|
+
if (pageSize !== void 0) param.pagerHeader.pageSize = pageSize;
|
|
671
898
|
}
|
|
672
899
|
const args = { spaceId, param };
|
|
673
900
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
|
|
@@ -682,14 +909,14 @@ var getLivestreamAnalysis = {
|
|
|
682
909
|
command: "+get_livestream_analysis",
|
|
683
910
|
description: "AI-generated live session analysis report: highlight moments (time slices with metrics and AI insights) and sentiment opinions.",
|
|
684
911
|
flags: [
|
|
685
|
-
{ name: "space-id", type: "
|
|
686
|
-
{ name: "game-id", type: "
|
|
912
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
913
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
687
914
|
{ name: "stream-id", type: "string", required: true, desc: "Live session ID (streamId) from +get_livestream_list." }
|
|
688
915
|
],
|
|
689
916
|
risk: "read",
|
|
690
917
|
execute: async (ctx) => {
|
|
691
|
-
const spaceId = ctx.
|
|
692
|
-
const gameId = ctx.
|
|
918
|
+
const spaceId = ctx.int64("space-id");
|
|
919
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
693
920
|
const streamId = ctx.str("stream-id");
|
|
694
921
|
const host = ctx.host();
|
|
695
922
|
const param = { gameId, streamId };
|
|
@@ -706,18 +933,18 @@ var getCommentsSummary = {
|
|
|
706
933
|
command: "+get_comments_summary",
|
|
707
934
|
description: "Comment analytics summary for a post: sentiment mix and time trends. Aggregate stats only\u2014no raw comment list.",
|
|
708
935
|
flags: [
|
|
709
|
-
{ name: "space-id", type: "
|
|
710
|
-
{ name: "game-id", type: "
|
|
711
|
-
{ name: "channel-id", type: "
|
|
936
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
937
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
938
|
+
{ name: "channel-id", type: "int64", required: true, min: 1, desc: "Channel ID from search_posts results." },
|
|
712
939
|
{ name: "uuid", type: "string", required: true, desc: "Content UUID from search_posts results." },
|
|
713
940
|
{ 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." },
|
|
714
941
|
{ name: "end-time", type: "string", required: false, desc: "Comment publish time upper bound. Format: yyyy-MM-dd." }
|
|
715
942
|
],
|
|
716
943
|
risk: "read",
|
|
717
944
|
execute: async (ctx) => {
|
|
718
|
-
const spaceId = ctx.
|
|
719
|
-
const gameId = ctx.
|
|
720
|
-
const channelId = ctx.
|
|
945
|
+
const spaceId = ctx.int64("space-id");
|
|
946
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
947
|
+
const channelId = ctx.int64("channel-id");
|
|
721
948
|
const uuid = ctx.str("uuid");
|
|
722
949
|
const host = ctx.host();
|
|
723
950
|
const param = { gameId, channelId, uuid };
|
|
@@ -738,9 +965,9 @@ var getCommentTagAnalysis = {
|
|
|
738
965
|
command: "+get_comment_tag_analysis",
|
|
739
966
|
description: "Comment tag analysis: for a tagCode, counts and trends per tag value over time.",
|
|
740
967
|
flags: [
|
|
741
|
-
{ name: "space-id", type: "
|
|
742
|
-
{ name: "game-id", type: "
|
|
743
|
-
{ name: "channel-id", type: "
|
|
968
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
969
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
970
|
+
{ name: "channel-id", type: "int64", required: true, min: 1, desc: "Channel ID from search_posts results." },
|
|
744
971
|
{ name: "uuid", type: "string", required: true, desc: "Content UUID from search_posts results." },
|
|
745
972
|
{ name: "resource-type", type: "number", required: true, desc: "Resource type: 0 post, 1 video." },
|
|
746
973
|
{ name: "tag-code", type: "string", required: true, desc: "Tag code (tagCode) from +get_corpus_tags." },
|
|
@@ -749,9 +976,9 @@ var getCommentTagAnalysis = {
|
|
|
749
976
|
],
|
|
750
977
|
risk: "read",
|
|
751
978
|
execute: async (ctx) => {
|
|
752
|
-
const spaceId = ctx.
|
|
753
|
-
const gameId = ctx.
|
|
754
|
-
const channelId = ctx.
|
|
979
|
+
const spaceId = ctx.int64("space-id");
|
|
980
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
981
|
+
const channelId = ctx.int64("channel-id");
|
|
755
982
|
const uuid = ctx.str("uuid");
|
|
756
983
|
const resourceType = ctx.num("resource-type");
|
|
757
984
|
const tagCode = ctx.str("tag-code");
|
|
@@ -774,13 +1001,13 @@ var getCorpusTags = {
|
|
|
774
1001
|
command: "+get_corpus_tags",
|
|
775
1002
|
description: "List corpus tags (tagId, tagCode, tagName, inferenceType) to pick tagCode for filters.",
|
|
776
1003
|
flags: [
|
|
777
|
-
{ name: "space-id", type: "
|
|
778
|
-
{ name: "game-id", type: "
|
|
1004
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
1005
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." }
|
|
779
1006
|
],
|
|
780
1007
|
risk: "read",
|
|
781
1008
|
execute: async (ctx) => {
|
|
782
|
-
const spaceId = ctx.
|
|
783
|
-
const gameId = ctx.
|
|
1009
|
+
const spaceId = ctx.int64("space-id");
|
|
1010
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
784
1011
|
const host = ctx.host();
|
|
785
1012
|
const param = { gameId };
|
|
786
1013
|
const args = { spaceId, param };
|
|
@@ -796,8 +1023,8 @@ var getRiskContent = {
|
|
|
796
1023
|
command: "+get_risk_content",
|
|
797
1024
|
description: "List content flagged as risk/moderation items; filter by risk type and time.",
|
|
798
1025
|
flags: [
|
|
799
|
-
{ name: "space-id", type: "
|
|
800
|
-
{ name: "game-id", type: "
|
|
1026
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID. Discover it with: ae-cli community spaces list" },
|
|
1027
|
+
{ name: "game-id", type: "int64", required: false, min: 1, desc: "Deprecated compatibility alias; defaults to --space-id and must match it." },
|
|
801
1028
|
{ name: "start-time", type: "string", required: true, desc: "Risk content publish time lower bound. Format: yyyy-MM-dd." },
|
|
802
1029
|
{ name: "end-time", type: "string", required: true, desc: "Risk content publish time upper bound. Format: yyyy-MM-dd." },
|
|
803
1030
|
{ name: "risk-codes", type: "string", required: false, desc: "Risk type code list." },
|
|
@@ -809,26 +1036,26 @@ var getRiskContent = {
|
|
|
809
1036
|
],
|
|
810
1037
|
risk: "read",
|
|
811
1038
|
execute: async (ctx) => {
|
|
812
|
-
const spaceId = ctx.
|
|
813
|
-
const gameId = ctx.
|
|
1039
|
+
const spaceId = ctx.int64("space-id");
|
|
1040
|
+
const gameId = ctx.optionalInt64("game-id") ?? spaceId;
|
|
814
1041
|
const startTime = ctx.str("start-time");
|
|
815
1042
|
const endTime = ctx.str("end-time");
|
|
816
1043
|
const host = ctx.host();
|
|
817
1044
|
const param = { gameId, startTime, endTime };
|
|
818
1045
|
const riskCodes = ctx.str("risk-codes");
|
|
819
|
-
if (riskCodes) param.riskCodes = riskCodes.split(",").map(
|
|
1046
|
+
if (riskCodes) param.riskCodes = riskCodes.split(",").map((value) => BigInt(value.trim()));
|
|
820
1047
|
const searchWord = ctx.str("search-word");
|
|
821
1048
|
if (searchWord) param.searchWord = searchWord;
|
|
822
|
-
const includeTrends = ctx.
|
|
1049
|
+
const includeTrends = ctx.optionalBool("include-trends");
|
|
823
1050
|
if (includeTrends !== void 0) param.includeTrends = includeTrends;
|
|
824
|
-
const orderBy = ctx.
|
|
825
|
-
const pageNum = ctx.
|
|
826
|
-
const pageSize = ctx.
|
|
827
|
-
if (orderBy !== void 0 || pageNum || pageSize) {
|
|
1051
|
+
const orderBy = ctx.optionalNum("order-by");
|
|
1052
|
+
const pageNum = ctx.optionalNum("page-num");
|
|
1053
|
+
const pageSize = ctx.optionalNum("page-size");
|
|
1054
|
+
if (orderBy !== void 0 || pageNum !== void 0 || pageSize !== void 0) {
|
|
828
1055
|
param.pagerHeader = {};
|
|
829
1056
|
if (orderBy !== void 0) param.pagerHeader.orderBy = orderBy;
|
|
830
|
-
if (pageNum) param.pagerHeader.pageNum = pageNum;
|
|
831
|
-
if (pageSize) param.pagerHeader.pageSize = pageSize;
|
|
1057
|
+
if (pageNum !== void 0) param.pagerHeader.pageNum = pageNum;
|
|
1058
|
+
if (pageSize !== void 0) param.pagerHeader.pageSize = pageSize;
|
|
832
1059
|
}
|
|
833
1060
|
const args = { spaceId, param };
|
|
834
1061
|
const mcpUrl = resolveMcpUrl(ctx.mcpUrl(), host, "community_content");
|
|
@@ -929,36 +1156,36 @@ var StatsCollector = class {
|
|
|
929
1156
|
convertedIntegerFields: 0,
|
|
930
1157
|
fields: {}
|
|
931
1158
|
};
|
|
932
|
-
truncated(
|
|
1159
|
+
truncated(field2) {
|
|
933
1160
|
this.value.truncatedFields += 1;
|
|
934
|
-
this.increment(
|
|
1161
|
+
this.increment(field2);
|
|
935
1162
|
}
|
|
936
|
-
defaulted(
|
|
1163
|
+
defaulted(field2) {
|
|
937
1164
|
this.value.defaultedFields += 1;
|
|
938
|
-
this.increment(
|
|
1165
|
+
this.increment(field2);
|
|
939
1166
|
}
|
|
940
|
-
convertedInteger(
|
|
1167
|
+
convertedInteger(field2) {
|
|
941
1168
|
this.value.convertedIntegerFields += 1;
|
|
942
|
-
this.increment(
|
|
1169
|
+
this.increment(field2);
|
|
943
1170
|
}
|
|
944
|
-
increment(
|
|
945
|
-
this.value.fields[
|
|
1171
|
+
increment(field2) {
|
|
1172
|
+
this.value.fields[field2] = (this.value.fields[field2] ?? 0) + 1;
|
|
946
1173
|
}
|
|
947
1174
|
};
|
|
948
|
-
function
|
|
1175
|
+
function validationError2(code, message, location = {}) {
|
|
949
1176
|
throw new CommunityDataValidationError(code, message, location);
|
|
950
1177
|
}
|
|
951
1178
|
function isObject(value) {
|
|
952
1179
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
953
1180
|
}
|
|
954
|
-
function hasOwn(object,
|
|
955
|
-
return Object.prototype.hasOwnProperty.call(object,
|
|
1181
|
+
function hasOwn(object, field2) {
|
|
1182
|
+
return Object.prototype.hasOwnProperty.call(object, field2);
|
|
956
1183
|
}
|
|
957
|
-
function fieldLocation(context,
|
|
958
|
-
return { ...context.location, field };
|
|
1184
|
+
function fieldLocation(context, field2) {
|
|
1185
|
+
return { ...context.location, field: field2 };
|
|
959
1186
|
}
|
|
960
|
-
function statField(context,
|
|
961
|
-
return `${context.type}.${
|
|
1187
|
+
function statField(context, field2) {
|
|
1188
|
+
return `${context.type}.${field2}`;
|
|
962
1189
|
}
|
|
963
1190
|
function parseInteger(value, location, options = {}) {
|
|
964
1191
|
let integer;
|
|
@@ -967,7 +1194,7 @@ function parseInteger(value, location, options = {}) {
|
|
|
967
1194
|
integer = value;
|
|
968
1195
|
} else if (typeof value === "number") {
|
|
969
1196
|
if (!Number.isSafeInteger(value)) {
|
|
970
|
-
|
|
1197
|
+
validationError2(
|
|
971
1198
|
"community_invalid_integer",
|
|
972
1199
|
"The field must be a lossless integer number or a canonical decimal string.",
|
|
973
1200
|
location
|
|
@@ -976,7 +1203,7 @@ function parseInteger(value, location, options = {}) {
|
|
|
976
1203
|
integer = BigInt(value);
|
|
977
1204
|
} else if (typeof value === "string") {
|
|
978
1205
|
if (!CANONICAL_INTEGER.test(value) || value === "-0") {
|
|
979
|
-
|
|
1206
|
+
validationError2(
|
|
980
1207
|
"community_invalid_integer",
|
|
981
1208
|
"The field must be a lossless integer number or a canonical decimal string.",
|
|
982
1209
|
location
|
|
@@ -985,7 +1212,7 @@ function parseInteger(value, location, options = {}) {
|
|
|
985
1212
|
integer = BigInt(value);
|
|
986
1213
|
converted = true;
|
|
987
1214
|
} else {
|
|
988
|
-
|
|
1215
|
+
validationError2(
|
|
989
1216
|
"community_invalid_integer",
|
|
990
1217
|
"The field must be a lossless integer number or a canonical decimal string.",
|
|
991
1218
|
location
|
|
@@ -994,49 +1221,49 @@ function parseInteger(value, location, options = {}) {
|
|
|
994
1221
|
const minimum = options.min ?? INT64_MIN;
|
|
995
1222
|
const maximum = options.max ?? INT64_MAX;
|
|
996
1223
|
if (integer < minimum || integer > maximum) {
|
|
997
|
-
|
|
1224
|
+
validationError2("community_integer_out_of_range", "The field is outside its supported integer range.", location);
|
|
998
1225
|
}
|
|
999
1226
|
if (options.positive && integer <= 0n) {
|
|
1000
|
-
|
|
1227
|
+
validationError2("community_integer_out_of_range", "The field must be a positive integer.", location);
|
|
1001
1228
|
}
|
|
1002
1229
|
if (options.nonnegative && integer < 0n) {
|
|
1003
|
-
|
|
1230
|
+
validationError2("community_integer_out_of_range", "The field must be a nonnegative integer.", location);
|
|
1004
1231
|
}
|
|
1005
1232
|
return { value: integer, converted };
|
|
1006
1233
|
}
|
|
1007
|
-
function parsePositiveId(value,
|
|
1008
|
-
return parseInteger(value, { field }, { positive: true }).value;
|
|
1234
|
+
function parsePositiveId(value, field2) {
|
|
1235
|
+
return parseInteger(value, { field: field2 }, { positive: true }).value;
|
|
1009
1236
|
}
|
|
1010
|
-
function normalizeIntegerField(input, output,
|
|
1011
|
-
if (!hasOwn(input,
|
|
1237
|
+
function normalizeIntegerField(input, output, field2, context, options = {}) {
|
|
1238
|
+
if (!hasOwn(input, field2) || input[field2] === null) {
|
|
1012
1239
|
if (options.required) {
|
|
1013
|
-
|
|
1240
|
+
validationError2("community_required_field", "A required field is missing.", fieldLocation(context, field2));
|
|
1014
1241
|
}
|
|
1015
|
-
delete output[
|
|
1242
|
+
delete output[field2];
|
|
1016
1243
|
return void 0;
|
|
1017
1244
|
}
|
|
1018
|
-
const parsed = parseInteger(input[
|
|
1019
|
-
output[
|
|
1245
|
+
const parsed = parseInteger(input[field2], fieldLocation(context, field2), options);
|
|
1246
|
+
output[field2] = parsed.value;
|
|
1020
1247
|
if (parsed.converted) {
|
|
1021
|
-
context.stats.convertedInteger(statField(context,
|
|
1248
|
+
context.stats.convertedInteger(statField(context, field2));
|
|
1022
1249
|
}
|
|
1023
1250
|
return parsed.value;
|
|
1024
1251
|
}
|
|
1025
|
-
function normalizeSmallIntegerField(input, output,
|
|
1026
|
-
const integer = normalizeIntegerField(input, output,
|
|
1252
|
+
function normalizeSmallIntegerField(input, output, field2, context, options = {}) {
|
|
1253
|
+
const integer = normalizeIntegerField(input, output, field2, context, options);
|
|
1027
1254
|
if (integer === void 0) return void 0;
|
|
1028
1255
|
const result = Number(integer);
|
|
1029
|
-
output[
|
|
1256
|
+
output[field2] = result;
|
|
1030
1257
|
return result;
|
|
1031
1258
|
}
|
|
1032
|
-
function
|
|
1259
|
+
function parseDateTime2(value, location, requireTime = false) {
|
|
1033
1260
|
const match = /^(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?)?$/.exec(value);
|
|
1034
1261
|
if (!match) {
|
|
1035
|
-
|
|
1262
|
+
validationError2("community_invalid_datetime", "The field must use a supported Iris date-time format.", location);
|
|
1036
1263
|
}
|
|
1037
1264
|
const hasTime = match[4] !== void 0;
|
|
1038
1265
|
if (requireTime && !hasTime) {
|
|
1039
|
-
|
|
1266
|
+
validationError2("community_invalid_datetime", "The field must include a time component.", location);
|
|
1040
1267
|
}
|
|
1041
1268
|
const parts = {
|
|
1042
1269
|
year: Number(match[1]),
|
|
@@ -1049,33 +1276,33 @@ function parseDateTime(value, location, requireTime = false) {
|
|
|
1049
1276
|
hasTime
|
|
1050
1277
|
};
|
|
1051
1278
|
if (parts.year === 0 || parts.month < 1 || parts.month > 12 || parts.hour > 23 || parts.minute > 59 || parts.second > 59) {
|
|
1052
|
-
|
|
1279
|
+
validationError2("community_invalid_datetime", "The field must use a supported Iris date-time format.", location);
|
|
1053
1280
|
}
|
|
1054
1281
|
const calendarCheck = /* @__PURE__ */ new Date(0);
|
|
1055
1282
|
calendarCheck.setUTCFullYear(parts.year, parts.month - 1, parts.day);
|
|
1056
1283
|
calendarCheck.setUTCHours(parts.hour, parts.minute, parts.second, parts.millisecond);
|
|
1057
1284
|
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) {
|
|
1058
|
-
|
|
1285
|
+
validationError2("community_invalid_datetime", "The field must use a supported Iris date-time format.", location);
|
|
1059
1286
|
}
|
|
1060
1287
|
return parts;
|
|
1061
1288
|
}
|
|
1062
|
-
function normalizeTextField(input, output,
|
|
1063
|
-
const path = options.path ??
|
|
1064
|
-
if (!hasOwn(input,
|
|
1289
|
+
function normalizeTextField(input, output, field2, context, options = {}) {
|
|
1290
|
+
const path = options.path ?? field2;
|
|
1291
|
+
if (!hasOwn(input, field2) || input[field2] === null) {
|
|
1065
1292
|
if (options.required) {
|
|
1066
|
-
|
|
1293
|
+
validationError2("community_required_field", "A required field is missing.", fieldLocation(context, path));
|
|
1067
1294
|
}
|
|
1068
|
-
delete output[
|
|
1295
|
+
delete output[field2];
|
|
1069
1296
|
return void 0;
|
|
1070
1297
|
}
|
|
1071
|
-
const value = input[
|
|
1298
|
+
const value = input[field2];
|
|
1072
1299
|
if (typeof value !== "string") {
|
|
1073
|
-
|
|
1300
|
+
validationError2("community_invalid_text", "The field must be a JSON string.", fieldLocation(context, path));
|
|
1074
1301
|
}
|
|
1075
1302
|
let normalized = value;
|
|
1076
1303
|
if (options.maxLength !== void 0 && normalized.length > options.maxLength) {
|
|
1077
1304
|
if (!options.truncate) {
|
|
1078
|
-
|
|
1305
|
+
validationError2(
|
|
1079
1306
|
"community_text_too_long",
|
|
1080
1307
|
`The field must not exceed ${options.maxLength} UTF-16 code units.`,
|
|
1081
1308
|
fieldLocation(context, path)
|
|
@@ -1085,9 +1312,9 @@ function normalizeTextField(input, output, field, context, options = {}) {
|
|
|
1085
1312
|
context.stats.truncated(statField(context, path));
|
|
1086
1313
|
}
|
|
1087
1314
|
if (options.dateTime) {
|
|
1088
|
-
|
|
1315
|
+
parseDateTime2(normalized, fieldLocation(context, path), options.requireTime);
|
|
1089
1316
|
}
|
|
1090
|
-
output[
|
|
1317
|
+
output[field2] = normalized;
|
|
1091
1318
|
return normalized;
|
|
1092
1319
|
}
|
|
1093
1320
|
function parseEncodedObject(value) {
|
|
@@ -1100,21 +1327,21 @@ function parseEncodedObject(value) {
|
|
|
1100
1327
|
return void 0;
|
|
1101
1328
|
}
|
|
1102
1329
|
}
|
|
1103
|
-
function normalizeFallbackObjectField(input, output,
|
|
1104
|
-
const object = hasOwn(input,
|
|
1330
|
+
function normalizeFallbackObjectField(input, output, field2, context) {
|
|
1331
|
+
const object = hasOwn(input, field2) && input[field2] !== null ? parseEncodedObject(input[field2]) : void 0;
|
|
1105
1332
|
if (object === void 0) {
|
|
1106
1333
|
const fallback = {};
|
|
1107
|
-
output[
|
|
1108
|
-
context.stats.defaulted(statField(context,
|
|
1334
|
+
output[field2] = fallback;
|
|
1335
|
+
context.stats.defaulted(statField(context, field2));
|
|
1109
1336
|
return fallback;
|
|
1110
1337
|
}
|
|
1111
|
-
output[
|
|
1338
|
+
output[field2] = object;
|
|
1112
1339
|
return object;
|
|
1113
1340
|
}
|
|
1114
|
-
function normalizeRequiredObjectValue(value, context,
|
|
1341
|
+
function normalizeRequiredObjectValue(value, context, field2) {
|
|
1115
1342
|
const object = parseEncodedObject(value);
|
|
1116
1343
|
if (object === void 0) {
|
|
1117
|
-
|
|
1344
|
+
validationError2("community_invalid_object", "The field must be a JSON object.", fieldLocation(context, field2));
|
|
1118
1345
|
}
|
|
1119
1346
|
return object;
|
|
1120
1347
|
}
|
|
@@ -1128,11 +1355,11 @@ function normalizeSubtitle(input, output, context) {
|
|
|
1128
1355
|
try {
|
|
1129
1356
|
value = parseCommunityJson(value);
|
|
1130
1357
|
} catch {
|
|
1131
|
-
|
|
1358
|
+
validationError2("community_invalid_array", "The field must be an array of JSON objects.", fieldLocation(context, "subtitle"));
|
|
1132
1359
|
}
|
|
1133
1360
|
}
|
|
1134
1361
|
if (!Array.isArray(value) || value.some((element) => !isObject(element))) {
|
|
1135
|
-
|
|
1362
|
+
validationError2("community_invalid_array", "The field must be an array of JSON objects.", fieldLocation(context, "subtitle"));
|
|
1136
1363
|
}
|
|
1137
1364
|
output.subtitle = value;
|
|
1138
1365
|
}
|
|
@@ -1145,23 +1372,23 @@ function normalizeMetrics(value, contentType, context, fieldPrefix) {
|
|
|
1145
1372
|
const metrics = normalizeRequiredObjectValue(value, context, fieldPrefix);
|
|
1146
1373
|
const names = Object.keys(metrics);
|
|
1147
1374
|
if (names.length === 0) {
|
|
1148
|
-
|
|
1375
|
+
validationError2("community_empty_metrics", "The metrics object must not be empty.", fieldLocation(context, fieldPrefix));
|
|
1149
1376
|
}
|
|
1150
1377
|
const allowed = metricNamesForContentType(contentType);
|
|
1151
1378
|
const normalized = { ...metrics };
|
|
1152
1379
|
for (const name of names) {
|
|
1153
|
-
const
|
|
1380
|
+
const field2 = `${fieldPrefix}.${name}`;
|
|
1154
1381
|
if (!allowed.has(name)) {
|
|
1155
|
-
|
|
1382
|
+
validationError2(
|
|
1156
1383
|
"community_invalid_metric",
|
|
1157
1384
|
"The metric name is not allowed for the selected content type.",
|
|
1158
|
-
fieldLocation(context,
|
|
1385
|
+
fieldLocation(context, field2)
|
|
1159
1386
|
);
|
|
1160
1387
|
}
|
|
1161
|
-
const parsed = parseInteger(metrics[name], fieldLocation(context,
|
|
1388
|
+
const parsed = parseInteger(metrics[name], fieldLocation(context, field2), { nonnegative: true });
|
|
1162
1389
|
normalized[name] = parsed.value;
|
|
1163
1390
|
if (parsed.converted) {
|
|
1164
|
-
context.stats.convertedInteger(statField(context,
|
|
1391
|
+
context.stats.convertedInteger(statField(context, field2));
|
|
1165
1392
|
}
|
|
1166
1393
|
}
|
|
1167
1394
|
return normalized;
|
|
@@ -1179,7 +1406,7 @@ function normalizeInteractionSidecar(input, output, context, contentType) {
|
|
|
1179
1406
|
path: "interaction.collect_time"
|
|
1180
1407
|
});
|
|
1181
1408
|
if (!hasOwn(interaction, "metrics") || interaction.metrics === null) {
|
|
1182
|
-
|
|
1409
|
+
validationError2("community_required_field", "A required field is missing.", fieldLocation(context, "interaction.metrics"));
|
|
1183
1410
|
}
|
|
1184
1411
|
normalized.metrics = normalizeMetrics(interaction.metrics, contentType, context, "interaction.metrics");
|
|
1185
1412
|
output.interaction = normalized;
|
|
@@ -1213,7 +1440,7 @@ function normalizeRootFields(input, output, context) {
|
|
|
1213
1440
|
normalizeTextField(input, output, "root_id", context, { required: true, maxLength: 32, truncate: true });
|
|
1214
1441
|
const rootType = normalizeTextField(input, output, "root_type", context, { required: true });
|
|
1215
1442
|
if (!ROOT_TYPES.has(rootType)) {
|
|
1216
|
-
|
|
1443
|
+
validationError2("community_invalid_enum", "The field contains an unsupported enum value.", fieldLocation(context, "root_type"));
|
|
1217
1444
|
}
|
|
1218
1445
|
}
|
|
1219
1446
|
function normalizeReply(input, context) {
|
|
@@ -1273,11 +1500,11 @@ function zonedDateTimeToEpochMilliseconds(parts, zoneId) {
|
|
|
1273
1500
|
return estimate;
|
|
1274
1501
|
}
|
|
1275
1502
|
function validateStreamId(roomId, streamStartTime, context) {
|
|
1276
|
-
const parts =
|
|
1503
|
+
const parts = parseDateTime2(streamStartTime, fieldLocation(context, "stream_start_time"), true);
|
|
1277
1504
|
const timestamp = zonedDateTimeToEpochMilliseconds(parts, context.zoneId);
|
|
1278
1505
|
const streamId = `${context.channelId}_${roomId}_${timestamp}`;
|
|
1279
1506
|
if (streamId.length > 64) {
|
|
1280
|
-
|
|
1507
|
+
validationError2(
|
|
1281
1508
|
"community_derived_id_too_long",
|
|
1282
1509
|
"The derived stream identifier exceeds its supported length.",
|
|
1283
1510
|
fieldLocation(context, "stream_start_time")
|
|
@@ -1315,9 +1542,9 @@ function normalizeLiveRoom(input, context) {
|
|
|
1315
1542
|
for (const metric of ["online", "heat", "noble_count", "guardian_count", "diamond_fan_count", "dfans_count"]) {
|
|
1316
1543
|
normalizeIntegerField(input, output, metric, context, { nonnegative: true });
|
|
1317
1544
|
}
|
|
1318
|
-
const guardianAliases = ["guardian_count", "diamond_fan_count", "dfans_count"].filter((
|
|
1545
|
+
const guardianAliases = ["guardian_count", "diamond_fan_count", "dfans_count"].filter((field2) => hasOwn(input, field2) && input[field2] !== null);
|
|
1319
1546
|
if (guardianAliases.length > 1) {
|
|
1320
|
-
|
|
1547
|
+
validationError2(
|
|
1321
1548
|
"community_conflicting_fields",
|
|
1322
1549
|
"Only one guardian metric alias may be supplied.",
|
|
1323
1550
|
fieldLocation(context, guardianAliases[1])
|
|
@@ -1336,16 +1563,16 @@ function normalizeLiveRoom(input, context) {
|
|
|
1336
1563
|
"diamond_fan_count",
|
|
1337
1564
|
"dfans_count"
|
|
1338
1565
|
];
|
|
1339
|
-
const hasStreamDetails = streamDependentFields.some((
|
|
1566
|
+
const hasStreamDetails = streamDependentFields.some((field2) => hasOwn(input, field2) && input[field2] !== null);
|
|
1340
1567
|
if (hasStreamDetails && streamStartTime === void 0) {
|
|
1341
|
-
|
|
1568
|
+
validationError2(
|
|
1342
1569
|
"community_required_field",
|
|
1343
1570
|
"A stream start time is required when stream details or metrics are supplied.",
|
|
1344
1571
|
fieldLocation(context, "stream_start_time")
|
|
1345
1572
|
);
|
|
1346
1573
|
}
|
|
1347
1574
|
if (uuid * 5n + 4n > INT64_MAX) {
|
|
1348
|
-
|
|
1575
|
+
validationError2(
|
|
1349
1576
|
"community_derived_id_overflow",
|
|
1350
1577
|
"A derived live metric identifier would exceed signed int64.",
|
|
1351
1578
|
fieldLocation(context, "uuid")
|
|
@@ -1361,14 +1588,14 @@ function normalizeLiveInteraction(input, context) {
|
|
|
1361
1588
|
normalizeIntegerField(input, output, "uuid", context, { required: true, nonnegative: true });
|
|
1362
1589
|
const activityType = normalizeTextField(input, output, "activity_type", context, { required: true });
|
|
1363
1590
|
if (!ACTIVITY_TYPES.has(activityType)) {
|
|
1364
|
-
|
|
1591
|
+
validationError2("community_invalid_enum", "The field contains an unsupported enum value.", fieldLocation(context, "activity_type"));
|
|
1365
1592
|
}
|
|
1366
1593
|
normalizeTextField(input, output, "activity_content", context, { required: true, maxLength: 1024, truncate: true });
|
|
1367
1594
|
normalizeTextField(input, output, "user_id", context, { maxLength: 80, truncate: true });
|
|
1368
1595
|
normalizeTextField(input, output, "user_name", context, { maxLength: 80, truncate: true });
|
|
1369
1596
|
const userExtra = normalizeFallbackObjectField(input, output, "user_extra", context);
|
|
1370
1597
|
if (stringifyCommunityJson(userExtra).length > 1024) {
|
|
1371
|
-
|
|
1598
|
+
validationError2(
|
|
1372
1599
|
"community_object_too_long",
|
|
1373
1600
|
"The compact user_extra object must not exceed 1024 UTF-16 code units.",
|
|
1374
1601
|
fieldLocation(context, "user_extra")
|
|
@@ -1409,14 +1636,14 @@ function normalizeInteraction(input, context) {
|
|
|
1409
1636
|
});
|
|
1410
1637
|
normalizeTextField(input, output, "collect_time", context, { required: true, dateTime: true });
|
|
1411
1638
|
if (!hasOwn(input, "metrics") || input.metrics === null) {
|
|
1412
|
-
|
|
1639
|
+
validationError2("community_required_field", "A required field is missing.", fieldLocation(context, "metrics"));
|
|
1413
1640
|
}
|
|
1414
1641
|
output.metrics = normalizeMetrics(input.metrics, contentType, context, "metrics");
|
|
1415
1642
|
return output;
|
|
1416
1643
|
}
|
|
1417
1644
|
function normalizeRecord(input, context) {
|
|
1418
1645
|
if (!isObject(input)) {
|
|
1419
|
-
|
|
1646
|
+
validationError2("community_invalid_record", "Each data record must be a JSON object.", context.location);
|
|
1420
1647
|
}
|
|
1421
1648
|
switch (context.type) {
|
|
1422
1649
|
case "post":
|
|
@@ -1439,7 +1666,7 @@ function normalizeRecord(input, context) {
|
|
|
1439
1666
|
}
|
|
1440
1667
|
function parseDataType(value, segment) {
|
|
1441
1668
|
if (typeof value !== "string" || !ALL_DATA_TYPES.has(value)) {
|
|
1442
|
-
|
|
1669
|
+
validationError2(
|
|
1443
1670
|
"community_invalid_data_type",
|
|
1444
1671
|
"The segment data_type must be one of the eight supported community data types.",
|
|
1445
1672
|
{ segment, field: "data_type" }
|
|
@@ -1449,20 +1676,20 @@ function parseDataType(value, segment) {
|
|
|
1449
1676
|
}
|
|
1450
1677
|
function normalizeSegment(input, segment, channelId, zoneId, stats) {
|
|
1451
1678
|
if (!isObject(input)) {
|
|
1452
|
-
|
|
1679
|
+
validationError2("community_invalid_segment", "Each payload segment must be a JSON object.", { segment });
|
|
1453
1680
|
}
|
|
1454
1681
|
if (!hasOwn(input, "data_type")) {
|
|
1455
|
-
|
|
1682
|
+
validationError2("community_required_field", "A required field is missing.", { segment, field: "data_type" });
|
|
1456
1683
|
}
|
|
1457
1684
|
if (!hasOwn(input, "data") || input.data === null) {
|
|
1458
|
-
|
|
1685
|
+
validationError2("community_required_field", "A required field is missing.", { segment, field: "data" });
|
|
1459
1686
|
}
|
|
1460
1687
|
const dataType = parseDataType(input.data_type, segment);
|
|
1461
1688
|
const records = Array.isArray(input.data) ? input.data : [input.data];
|
|
1462
1689
|
if (records.length === 0) {
|
|
1463
|
-
|
|
1690
|
+
validationError2("community_empty_data", "A segment data array must not be empty.", { segment, field: "data" });
|
|
1464
1691
|
}
|
|
1465
|
-
const normalized = records.map((
|
|
1692
|
+
const normalized = records.map((record2, index) => normalizeRecord(record2, {
|
|
1466
1693
|
type: dataType,
|
|
1467
1694
|
channelId,
|
|
1468
1695
|
zoneId,
|
|
@@ -1473,12 +1700,12 @@ function normalizeSegment(input, segment, channelId, zoneId, stats) {
|
|
|
1473
1700
|
}
|
|
1474
1701
|
function validateZoneId(value) {
|
|
1475
1702
|
if (typeof value !== "string" || value.length === 0) {
|
|
1476
|
-
|
|
1703
|
+
validationError2("community_invalid_zone_id", "zone_id must be a valid IANA time zone name.", { field: "zone_id" });
|
|
1477
1704
|
}
|
|
1478
1705
|
try {
|
|
1479
1706
|
new Intl.DateTimeFormat("en-US", { timeZone: value }).format(0);
|
|
1480
1707
|
} catch {
|
|
1481
|
-
|
|
1708
|
+
validationError2("community_invalid_zone_id", "zone_id must be a valid IANA time zone name.", { field: "zone_id" });
|
|
1482
1709
|
}
|
|
1483
1710
|
return value;
|
|
1484
1711
|
}
|
|
@@ -1490,7 +1717,7 @@ function normalizeCommunityReportInput(input) {
|
|
|
1490
1717
|
const hasDataMode = input.dataType !== void 0 || input.data !== void 0;
|
|
1491
1718
|
const hasPayloadMode = input.payload !== void 0;
|
|
1492
1719
|
if (hasDataMode === hasPayloadMode) {
|
|
1493
|
-
|
|
1720
|
+
validationError2(
|
|
1494
1721
|
"community_input_mode",
|
|
1495
1722
|
"Use exactly one input mode: data_type with data, or payload.",
|
|
1496
1723
|
{ field: "data/payload" }
|
|
@@ -1499,7 +1726,7 @@ function normalizeCommunityReportInput(input) {
|
|
|
1499
1726
|
let rawSegments;
|
|
1500
1727
|
if (hasDataMode) {
|
|
1501
1728
|
if (input.dataType === void 0 || input.data === void 0) {
|
|
1502
|
-
|
|
1729
|
+
validationError2(
|
|
1503
1730
|
"community_input_mode",
|
|
1504
1731
|
"The data_type and data fields must be supplied together.",
|
|
1505
1732
|
{ field: input.dataType === void 0 ? "data_type" : "data" }
|
|
@@ -1509,7 +1736,7 @@ function normalizeCommunityReportInput(input) {
|
|
|
1509
1736
|
} else {
|
|
1510
1737
|
rawSegments = Array.isArray(input.payload) ? input.payload : [input.payload];
|
|
1511
1738
|
if (rawSegments.length === 0) {
|
|
1512
|
-
|
|
1739
|
+
validationError2("community_empty_payload", "The payload array must not be empty.", { field: "payload" });
|
|
1513
1740
|
}
|
|
1514
1741
|
}
|
|
1515
1742
|
const stats = new StatsCollector();
|
|
@@ -1686,7 +1913,7 @@ function resolveCommunityReportEndpoint(flagValue, environment = process.env) {
|
|
|
1686
1913
|
}
|
|
1687
1914
|
return endpoint.toString();
|
|
1688
1915
|
}
|
|
1689
|
-
function readCommunityReportInput(raw,
|
|
1916
|
+
function readCommunityReportInput(raw, field2) {
|
|
1690
1917
|
const trimmed = raw.trim();
|
|
1691
1918
|
let text;
|
|
1692
1919
|
try {
|
|
@@ -1704,8 +1931,8 @@ function readCommunityReportInput(raw, field) {
|
|
|
1704
1931
|
"Unable to read the community JSON input.",
|
|
1705
1932
|
{
|
|
1706
1933
|
code: "community_input_read_failed",
|
|
1707
|
-
hint: `Pass --${
|
|
1708
|
-
location: { field }
|
|
1934
|
+
hint: `Pass --${field2} as inline JSON, an existing path, @path, or - for stdin.`,
|
|
1935
|
+
location: { field: field2 }
|
|
1709
1936
|
}
|
|
1710
1937
|
);
|
|
1711
1938
|
}
|
|
@@ -1718,7 +1945,7 @@ function readCommunityReportInput(raw, field) {
|
|
|
1718
1945
|
{
|
|
1719
1946
|
code: error.code,
|
|
1720
1947
|
hint: SCHEMA_VALIDATION_HINT,
|
|
1721
|
-
location: { field }
|
|
1948
|
+
location: { field: field2 }
|
|
1722
1949
|
}
|
|
1723
1950
|
);
|
|
1724
1951
|
}
|
|
@@ -1801,6 +2028,258 @@ function formatNormalization(stats) {
|
|
|
1801
2028
|
};
|
|
1802
2029
|
}
|
|
1803
2030
|
|
|
2031
|
+
// src/commands/te-community/spaces-list.ts
|
|
2032
|
+
var spacesList = createCapabilityCommand({
|
|
2033
|
+
cliService: "community",
|
|
2034
|
+
resource: "spaces",
|
|
2035
|
+
command: "list",
|
|
2036
|
+
capabilityId: "community.space.list",
|
|
2037
|
+
description: "List community spaces available to the current user.",
|
|
2038
|
+
flags: [],
|
|
2039
|
+
risk: "read",
|
|
2040
|
+
buildInput: () => ({})
|
|
2041
|
+
});
|
|
2042
|
+
var communitySpacesList = {
|
|
2043
|
+
...spacesList,
|
|
2044
|
+
async execute(ctx) {
|
|
2045
|
+
const result = await spacesList.execute(ctx);
|
|
2046
|
+
const data = unwrapOutputData(result);
|
|
2047
|
+
return Array.isArray(data) ? withOutputMetadata(result, { returned_count: data.length }) : result;
|
|
2048
|
+
}
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
// src/commands/te-community/sources-list.ts
|
|
2052
|
+
var communitySourcesList = createCapabilityCommand({
|
|
2053
|
+
cliService: "community",
|
|
2054
|
+
resource: "sources",
|
|
2055
|
+
command: "list",
|
|
2056
|
+
capabilityId: "community.source.list",
|
|
2057
|
+
description: "List configured community ingestion sources and their channel/source IDs.",
|
|
2058
|
+
flags: [
|
|
2059
|
+
{
|
|
2060
|
+
name: "space-id",
|
|
2061
|
+
type: "int64",
|
|
2062
|
+
required: true,
|
|
2063
|
+
min: 1,
|
|
2064
|
+
desc: "Community space ID returned by `ae-cli community spaces list`."
|
|
2065
|
+
}
|
|
2066
|
+
],
|
|
2067
|
+
risk: "read",
|
|
2068
|
+
buildInput: (ctx) => ({
|
|
2069
|
+
game_id: ctx.int64("space-id")
|
|
2070
|
+
})
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
// src/commands/te-community/evidence-fetch.ts
|
|
2074
|
+
import { createHash } from "crypto";
|
|
2075
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
2076
|
+
var CAPABILITY = "community.content.post_detail";
|
|
2077
|
+
var MAX_ITEMS = 20;
|
|
2078
|
+
var INT64_MAX2 = 9223372036854775807n;
|
|
2079
|
+
function invalid(message) {
|
|
2080
|
+
throw new CliValidationError(message, { hint: 'Use --input @file.json with {"items":[{"channel_id":"<ID>","uuid":"<UUID>","resource_type":0}]}.' });
|
|
2081
|
+
}
|
|
2082
|
+
function record(value) {
|
|
2083
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2084
|
+
}
|
|
2085
|
+
function int64(value) {
|
|
2086
|
+
if (typeof value === "number" && !Number.isSafeInteger(value)) invalid("Evidence IDs must be exact integers; use quoted int64 IDs.");
|
|
2087
|
+
if (!["string", "number", "bigint"].includes(typeof value) || !/^[1-9]\d*$/.test(String(value))) invalid("Evidence channel_id must be a positive int64.");
|
|
2088
|
+
if (BigInt(String(value)) > INT64_MAX2) invalid("Evidence channel_id exceeds signed int64.");
|
|
2089
|
+
return String(value);
|
|
2090
|
+
}
|
|
2091
|
+
function prepare(ctx) {
|
|
2092
|
+
let input;
|
|
2093
|
+
try {
|
|
2094
|
+
const raw = ctx.str("input").trim();
|
|
2095
|
+
input = parseJsonRequest(raw === "-" ? readFileSync2(0, "utf8") : raw.startsWith("@") ? readFileSync2(raw.slice(1), "utf8") : raw);
|
|
2096
|
+
} catch {
|
|
2097
|
+
invalid("Cannot read evidence input as JSON.");
|
|
2098
|
+
}
|
|
2099
|
+
if (!record(input) || Object.keys(input).some((k) => k !== "items") || !Array.isArray(input.items) || input.items.length < 1 || input.items.length > MAX_ITEMS) invalid("Evidence input must contain only items, with 1 to 20 entries.");
|
|
2100
|
+
const locators = input.items.map((value) => {
|
|
2101
|
+
if (!record(value) || Object.keys(value).some((k) => !["channel_id", "uuid", "resource_type"].includes(k))) invalid("Each evidence item must contain only channel_id, uuid, and resource_type.");
|
|
2102
|
+
const channelId = int64(value.channel_id);
|
|
2103
|
+
if (typeof value.uuid !== "string" || !value.uuid.trim() || value.uuid.length > 512 || /[\u0000-\u001f\u007f]/.test(value.uuid)) invalid("Evidence uuid must be a non-empty opaque ID of at most 512 characters without control characters.");
|
|
2104
|
+
if (value.resource_type !== 0 && value.resource_type !== 1) invalid("Evidence resource_type must be 0 (post) or 1 (video).");
|
|
2105
|
+
return { channel_id: channelId, uuid: value.uuid, resource_type: value.resource_type };
|
|
2106
|
+
});
|
|
2107
|
+
const spaceId = ctx.int64("space-id");
|
|
2108
|
+
if (spaceId < 1n || spaceId > INT64_MAX2) invalid("Evidence space-id must be a positive int64.");
|
|
2109
|
+
const maxChars = ctx.optionalNum("max-chars") ?? 1e5;
|
|
2110
|
+
if (!Number.isInteger(maxChars) || maxChars < 1 || maxChars > 1e6) invalid("--max-chars must be an integer between 1 and 1000000.");
|
|
2111
|
+
const unique = [...new Map(locators.map((item) => [JSON.stringify(item), item])).values()];
|
|
2112
|
+
return { spaceId, requested: locators.length, locators: unique, maxChars };
|
|
2113
|
+
}
|
|
2114
|
+
function request(spaceId, item) {
|
|
2115
|
+
return {
|
|
2116
|
+
game_id: spaceId,
|
|
2117
|
+
channel_id: BigInt(item.channel_id),
|
|
2118
|
+
uuid: item.uuid,
|
|
2119
|
+
resource_type: item.resource_type,
|
|
2120
|
+
reply_pager_header: { page_num: 1, page_size: 1 },
|
|
2121
|
+
danmu_pager_header: { page_num: 1, page_size: 1 }
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
function identity(host, spaceId, item) {
|
|
2125
|
+
return `community:${createHash("sha256").update(JSON.stringify([host.replace(/\/+$/, ""), String(spaceId), item.channel_id, item.resource_type, item.uuid])).digest("hex")}`;
|
|
2126
|
+
}
|
|
2127
|
+
function field(data, snake, camel = snake) {
|
|
2128
|
+
return data[snake] ?? data[camel];
|
|
2129
|
+
}
|
|
2130
|
+
function matches(data, item) {
|
|
2131
|
+
if (!record(data)) return false;
|
|
2132
|
+
const type = field(data, "resource_type", "resourceType");
|
|
2133
|
+
return String(field(data, "channel_id", "channelId")) === item.channel_id && data.uuid === item.uuid && (type === item.resource_type || type === (item.resource_type === 0 ? "POST" : "VIDEO"));
|
|
2134
|
+
}
|
|
2135
|
+
function evidenceText(data, remaining) {
|
|
2136
|
+
const text = {};
|
|
2137
|
+
const textFields = {};
|
|
2138
|
+
let used = 0;
|
|
2139
|
+
let truncated = false;
|
|
2140
|
+
for (const name of ["title", "content", "description", "subtitle"]) {
|
|
2141
|
+
const value = data[name];
|
|
2142
|
+
const parts = typeof value === "string" ? [value] : Array.isArray(value) && value.every((v) => typeof v === "string") ? value : null;
|
|
2143
|
+
if (parts === null) {
|
|
2144
|
+
text[name] = null;
|
|
2145
|
+
textFields[name] = { status: value == null ? "missing" : "unsupported_format" };
|
|
2146
|
+
continue;
|
|
2147
|
+
}
|
|
2148
|
+
let original = 0;
|
|
2149
|
+
let returned = 0;
|
|
2150
|
+
const clipped = [];
|
|
2151
|
+
for (const part of parts) {
|
|
2152
|
+
const chars = Array.from(part);
|
|
2153
|
+
original += chars.length;
|
|
2154
|
+
const kept = chars.slice(0, Math.max(0, remaining - used));
|
|
2155
|
+
returned += kept.length;
|
|
2156
|
+
used += kept.length;
|
|
2157
|
+
if (kept.length || chars.length === 0) clipped.push(kept.join(""));
|
|
2158
|
+
}
|
|
2159
|
+
text[name] = typeof value === "string" ? clipped[0] ?? "" : clipped;
|
|
2160
|
+
const cut = returned < original;
|
|
2161
|
+
truncated ||= cut;
|
|
2162
|
+
textFields[name] = { status: cut ? "truncated" : "available", original_chars: original, returned_chars: returned };
|
|
2163
|
+
}
|
|
2164
|
+
return { text, textFields, used, truncated };
|
|
2165
|
+
}
|
|
2166
|
+
function safeSource(data) {
|
|
2167
|
+
const value = field(data, "red_note_url", "redNoteUrl");
|
|
2168
|
+
if (typeof value !== "string" || value.length > 4096) return null;
|
|
2169
|
+
try {
|
|
2170
|
+
return ["https:", "http:"].includes(new URL(value).protocol) ? value : null;
|
|
2171
|
+
} catch {
|
|
2172
|
+
return null;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
function safeError(error) {
|
|
2176
|
+
const code = (value, fallback) => typeof value === "string" && /^[A-Za-z0-9_-]{1,80}$/.test(value) ? value : fallback;
|
|
2177
|
+
if (error instanceof SecureStoreAuthError) return { type: "auth", code: "AUTH_REQUIRED", message: "Authentication is required to read evidence." };
|
|
2178
|
+
if (error instanceof PermissionError) return { type: "permission", code: code(error.code, "PERMISSION_DENIED"), message: "Access to this evidence was denied." };
|
|
2179
|
+
if (error instanceof CapabilityGatewayError) {
|
|
2180
|
+
const type = error.httpStatus === 401 ? "auth" : error.httpStatus === 403 ? "permission" : "api";
|
|
2181
|
+
return { type, code: code(error.code, "CAPABILITY_FAILED"), message: "Evidence could not be read from the capability service." };
|
|
2182
|
+
}
|
|
2183
|
+
return { type: "api", code: "REQUEST_FAILED", message: "Evidence request failed." };
|
|
2184
|
+
}
|
|
2185
|
+
var communityEvidenceFetch = {
|
|
2186
|
+
service: "community",
|
|
2187
|
+
resource: "evidence",
|
|
2188
|
+
command: "fetch",
|
|
2189
|
+
description: "Read up to 20 unique post/video sources with verified IDs, complete text, and explicit partial-result status.",
|
|
2190
|
+
flags: [
|
|
2191
|
+
{ name: "space-id", type: "int64", required: true, min: 1, desc: "Community space ID from spaces list." },
|
|
2192
|
+
{ name: "input", type: "string", required: true, sensitive: true, desc: "JSON object, @file.json, or - for stdin; items contain channel_id, uuid, resource_type." },
|
|
2193
|
+
{ name: "max-chars", type: "number", min: 1, max: 1e6, desc: "Combined returned text budget in Unicode code points (default 100000)." }
|
|
2194
|
+
],
|
|
2195
|
+
risk: "read",
|
|
2196
|
+
dryRun: (ctx) => {
|
|
2197
|
+
const plan = prepare(ctx);
|
|
2198
|
+
return {
|
|
2199
|
+
method: "POST",
|
|
2200
|
+
url: buildCapabilityGatewayUrl(ctx.host(), resolveGatewayDomain("community", "community"), `capabilities/${CAPABILITY}/execute`),
|
|
2201
|
+
body: {
|
|
2202
|
+
preview_only: true,
|
|
2203
|
+
requested: plan.requested,
|
|
2204
|
+
unique: plan.locators.length,
|
|
2205
|
+
max_chars: plan.maxChars,
|
|
2206
|
+
inputs: plan.locators.map((item) => ({ ...request(plan.spaceId, item), game_id: String(plan.spaceId), channel_id: item.channel_id }))
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
},
|
|
2210
|
+
execute: async (ctx) => {
|
|
2211
|
+
const plan = prepare(ctx);
|
|
2212
|
+
const gatewayDomain = resolveGatewayDomain("community", "community");
|
|
2213
|
+
const items = [];
|
|
2214
|
+
let returnedChars = 0;
|
|
2215
|
+
let accessStopped = false;
|
|
2216
|
+
for (const item of plan.locators) {
|
|
2217
|
+
const base = { evidence_id: identity(ctx.host(), plan.spaceId, item), locator: { space_id: String(plan.spaceId), ...item } };
|
|
2218
|
+
if (returnedChars >= plan.maxChars || accessStopped) {
|
|
2219
|
+
items.push({ ...base, status: "not_fetched", reason: accessStopped ? "access_denied" : "text_budget_exhausted" });
|
|
2220
|
+
continue;
|
|
2221
|
+
}
|
|
2222
|
+
try {
|
|
2223
|
+
const response = await requestCapabilityGatewayWithEnvelope(ctx.host(), gatewayDomain, `capabilities/${CAPABILITY}/execute`, "POST", { input: request(plan.spaceId, item) });
|
|
2224
|
+
if (response.data == null) {
|
|
2225
|
+
items.push({ ...base, status: "missing", reason: "no_detail_returned" });
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
if (!matches(response.data, item)) {
|
|
2229
|
+
items.push({ ...base, status: "invalid_response", reason: "evidence_identity_mismatch" });
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
const excerpt = evidenceText(response.data, plan.maxChars - returnedChars);
|
|
2233
|
+
returnedChars += excerpt.used;
|
|
2234
|
+
items.push({
|
|
2235
|
+
...base,
|
|
2236
|
+
status: "ok",
|
|
2237
|
+
text: excerpt.text,
|
|
2238
|
+
author: { user_id: field(response.data, "user_id", "userId") ?? null, user_name: field(response.data, "user_name", "userName") ?? null },
|
|
2239
|
+
publish_time: field(response.data, "publish_time", "publishTime") ?? null,
|
|
2240
|
+
last_update_time: field(response.data, "last_update_time", "lastUpdateTime") ?? null,
|
|
2241
|
+
source_url: safeSource(response.data),
|
|
2242
|
+
quality: {
|
|
2243
|
+
identity_verified: true,
|
|
2244
|
+
text_truncated: excerpt.truncated,
|
|
2245
|
+
source_completeness: "unknown",
|
|
2246
|
+
text_fields: excerpt.textFields,
|
|
2247
|
+
related_comments_included: false,
|
|
2248
|
+
related_media_included: false
|
|
2249
|
+
},
|
|
2250
|
+
...response.meta ? { meta: response.meta } : {}
|
|
2251
|
+
});
|
|
2252
|
+
} catch (error) {
|
|
2253
|
+
const failure = safeError(error);
|
|
2254
|
+
accessStopped = failure.type === "auth" || failure.type === "permission";
|
|
2255
|
+
items.push({ ...base, status: "error", error: failure });
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
return {
|
|
2259
|
+
contract_version: "community-evidence-v1",
|
|
2260
|
+
capability_id: CAPABILITY,
|
|
2261
|
+
host: ctx.host(),
|
|
2262
|
+
space_id: String(plan.spaceId),
|
|
2263
|
+
requested: plan.requested,
|
|
2264
|
+
unique: items.length,
|
|
2265
|
+
items,
|
|
2266
|
+
quality: {
|
|
2267
|
+
dataset_complete: false,
|
|
2268
|
+
batch_complete: items.every((item) => item.status === "ok" && !item.quality.text_truncated),
|
|
2269
|
+
returned_chars: returnedChars,
|
|
2270
|
+
max_chars: plan.maxChars,
|
|
2271
|
+
snapshot: false
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
var communityEvidenceFetchAlias = {
|
|
2277
|
+
...communityEvidenceFetch,
|
|
2278
|
+
resource: void 0,
|
|
2279
|
+
command: "+fetch-evidence",
|
|
2280
|
+
description: "Compatibility alias for community evidence fetch."
|
|
2281
|
+
};
|
|
2282
|
+
|
|
1804
2283
|
// src/commands/te-community/index.ts
|
|
1805
2284
|
registerCapabilityGatewayRoute("community", { gatewayDomain: "community" });
|
|
1806
2285
|
registerMcpMappings({
|
|
@@ -1808,8 +2287,7 @@ registerMcpMappings({
|
|
|
1808
2287
|
"community_analysis": { componentName: "community", mappingPath: "analysis" },
|
|
1809
2288
|
"community_hot": { componentName: "community", mappingPath: "hot" }
|
|
1810
2289
|
});
|
|
1811
|
-
var
|
|
1812
|
-
communityDataReport,
|
|
2290
|
+
var communityReadCommands = [
|
|
1813
2291
|
// Content commands
|
|
1814
2292
|
getLivestreamList,
|
|
1815
2293
|
getLivestreamDetail,
|
|
@@ -1833,6 +2311,20 @@ var commands = [
|
|
|
1833
2311
|
getHotTopics,
|
|
1834
2312
|
getTopicDetail
|
|
1835
2313
|
];
|
|
2314
|
+
var commands = [
|
|
2315
|
+
communityDataReport,
|
|
2316
|
+
communityEvidenceFetch,
|
|
2317
|
+
communityEvidenceFetchAlias,
|
|
2318
|
+
communitySpacesList,
|
|
2319
|
+
communitySourcesList,
|
|
2320
|
+
...communityReadCommands.map((command) => ({
|
|
2321
|
+
...command,
|
|
2322
|
+
validate: (ctx) => {
|
|
2323
|
+
validateCommunityCommand(ctx, command);
|
|
2324
|
+
command.validate?.(ctx);
|
|
2325
|
+
}
|
|
2326
|
+
}))
|
|
2327
|
+
];
|
|
1836
2328
|
var te_community_default = commands;
|
|
1837
2329
|
export {
|
|
1838
2330
|
te_community_default as default
|