@thinkingai/ae-cli 1.0.27 → 1.0.30
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 +1 -16
- package/README.zh.md +1 -16
- package/dist/auth-H3GJD55A.js +457 -0
- package/dist/{auth-TXFJHPXU.js → auth-U25QJU5D.js} +3 -3
- package/dist/{chunk-B4UL2VIJ.js → chunk-CLJF7MQA.js} +1 -1
- package/dist/{chunk-6T7245YE.js → chunk-EGEIXA2Z.js} +90 -18
- package/dist/{chunk-2NGWYMLB.js → chunk-GLNZSDKO.js} +88 -50
- package/dist/chunk-GSJGPNKK.js +49 -0
- package/dist/{chunk-VT6VC4PA.js → chunk-I42JGQ2J.js} +26 -11
- package/dist/chunk-JM34JPCO.js +239 -0
- package/dist/chunk-NOO24N7W.js +11 -0
- package/dist/chunk-SF3KTPIC.js +98 -0
- package/dist/chunk-SRJIAOBN.js +244 -0
- package/dist/chunk-U6TKB3IV.js +171 -0
- package/dist/{chunk-TG64AQV4.js → chunk-VJFUB3H3.js} +99 -56
- package/dist/{client-IU2E5IOL.js → client-IN42F223.js} +4 -4
- package/dist/config-64G36TJL.js +130 -0
- package/dist/index.js +52 -25
- package/dist/{model-CC7U5XOJ.js → model-VZCYNIOG.js} +11 -10
- package/dist/{raw-TUHZQ4UB.js → raw-WJIWC6YP.js} +13 -7
- package/dist/{sync-EVEDS7YE.js → sync-NVASAISS.js} +146 -47
- package/dist/{te-agent-FLTDYV3L.js → te-agent-YV4DXEXK.js} +24 -23
- package/dist/{te-analysis-CMXADIJM.js → te-analysis-CB3DA5I5.js} +61 -13
- package/dist/{te-audience-LPZFFSXM.js → te-audience-W7SCQFC7.js} +7 -5
- package/dist/{te-common-BXQFKTB3.js → te-common-FBXVWBSI.js} +21 -9
- package/dist/{te-community-K5S3VOZY.js → te-community-LNVDZSGW.js} +6 -4
- package/dist/{te-dataops-567IMCQG.js → te-dataops-IPCSSWYH.js} +29 -27
- package/dist/{te-engage-TBUTGW5F.js → te-engage-KARARMIR.js} +6 -4
- package/dist/{te-kb-XHVBWP5E.js → te-kb-2Z2CARN3.js} +7 -5
- package/dist/{te-meta-NQ4U3VJW.js → te-meta-PDKLMWXN.js} +6 -4
- package/dist/{te-team-556APRIU.js → te-team-SW5B7DHO.js} +17 -12
- package/package.json +6 -2
- package/skills/ae-agent/SKILL.md +2 -1
- package/skills/ae-analysis/SKILL.md +1 -1
- package/skills/ae-analysis/references/list_clusters.md +4 -2
- package/skills/ae-analysis-global/SKILL.md +51 -0
- package/skills/ae-analysis-global/references/list_query_clusters.md +67 -0
- package/skills/ae-community/SKILL.md +2 -3
- package/skills/ae-dataops/SKILL.md +1 -2
- package/skills/ae-engage/SKILL.md +1 -0
- package/skills/ae-engage/references/add-approver.md +0 -1
- package/skills/ae-engage/references/add-channel.md +0 -1
- package/skills/ae-engage/references/approver-list.md +0 -1
- package/skills/ae-engage/references/cancel-query-by-request-id.md +0 -1
- package/skills/ae-engage/references/channel-detail.md +0 -1
- package/skills/ae-engage/references/channel-list.md +0 -1
- package/skills/ae-engage/references/config-channel-detail.md +0 -1
- package/skills/ae-engage/references/config-channel-list.md +0 -1
- package/skills/ae-engage/references/config-item-analysis-report.md +0 -1
- package/skills/ae-engage/references/config-item-detail.md +0 -1
- package/skills/ae-engage/references/config-item-list.md +0 -1
- package/skills/ae-engage/references/config-item-strategy-comparison.md +0 -1
- package/skills/ae-engage/references/config-item-trigger-report.md +0 -1
- package/skills/ae-engage/references/copy-config-template.md +0 -1
- package/skills/ae-engage/references/delete-channel.md +0 -1
- package/skills/ae-engage/references/delete-config-channel.md +0 -1
- package/skills/ae-engage/references/delete-config-item.md +0 -1
- package/skills/ae-engage/references/delete-flow.md +0 -1
- package/skills/ae-engage/references/flow-ab-split-node-report.md +0 -1
- package/skills/ae-engage/references/flow-detail.md +0 -1
- package/skills/ae-engage/references/flow-list.md +0 -1
- package/skills/ae-engage/references/flow-node-config-schema.md +0 -1
- package/skills/ae-engage/references/flow-node-detail-report.md +0 -1
- package/skills/ae-engage/references/flow-node-overview-report.md +0 -1
- package/skills/ae-engage/references/flow-process-report.md +0 -1
- package/skills/ae-engage/references/manage-flow.md +0 -1
- package/skills/ae-engage/references/manage-strategy.md +0 -1
- package/skills/ae-engage/references/manage-task.md +0 -1
- package/skills/ae-engage/references/modify-flow-base-info.md +0 -1
- package/skills/ae-engage/references/strategy-detail.md +0 -1
- package/skills/ae-engage/references/strategy-list.md +0 -1
- package/skills/ae-engage/references/task-data-detail.md +0 -1
- package/skills/ae-engage/references/task-data-overview.md +0 -1
- package/skills/ae-engage/references/task-detail.md +0 -1
- package/skills/ae-engage/references/task-experiment-report.md +0 -1
- package/skills/ae-engage/references/task-list.md +0 -1
- package/skills/ae-engage/references/task-metric-detail.md +0 -1
- package/skills/ae-engage/references/task-stats.md +0 -1
- package/skills/ae-engage/references/update-channel-status.md +0 -1
- package/skills/ae-engage/references/update-config-channel-status.md +0 -1
- package/skills/ae-engage/references/validate-flow-node-config.md +0 -1
- package/skills/ae-engage/references/whitelist-list.md +0 -1
- package/skills/ae-kb/SKILL.md +1 -0
- package/skills/ae-team/SKILL.md +1 -1
- package/dist/auth-W2JZ3DKA.js +0 -170
- package/dist/chunk-BI5ZVD6T.js +0 -186
- package/dist/chunk-MNHE6SSI.js +0 -79
- package/dist/chunk-MWWYGZ76.js +0 -130
- package/dist/chunk-SRRUNQIM.js +0 -312
- package/dist/config-WH3R3IRY.js +0 -258
|
@@ -2,10 +2,14 @@ import {
|
|
|
2
2
|
callMcpTool,
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
resolveMcpUrl
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
} from "./chunk-GLNZSDKO.js";
|
|
6
|
+
import {
|
|
7
|
+
isGlobalQueryModeEnabled
|
|
8
|
+
} from "./chunk-GSJGPNKK.js";
|
|
9
|
+
import "./chunk-NOO24N7W.js";
|
|
10
|
+
import "./chunk-U6TKB3IV.js";
|
|
11
|
+
import "./chunk-JM34JPCO.js";
|
|
12
|
+
import "./chunk-SRJIAOBN.js";
|
|
9
13
|
|
|
10
14
|
// src/commands/te-analysis/shared.ts
|
|
11
15
|
function createMcpCommand(config) {
|
|
@@ -56,6 +60,24 @@ function optionalJsonString(ctx, name) {
|
|
|
56
60
|
function requiredJsonString(ctx, name) {
|
|
57
61
|
return JSON.stringify(ctx.json(name));
|
|
58
62
|
}
|
|
63
|
+
function clusterQueryFlags(scopeDesc) {
|
|
64
|
+
if (!isGlobalQueryModeEnabled()) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return [
|
|
68
|
+
{ name: "cluster_query_scope", type: "string", required: false, desc: scopeDesc },
|
|
69
|
+
{ name: "slave_cluster_id", type: "string", required: false, desc: "Slave cluster ID. Required when cluster_query_scope=SLAVE. Use +list_query_clusters first to choose this value." }
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
function optionalClusterQueryArgs(ctx) {
|
|
73
|
+
if (!isGlobalQueryModeEnabled()) {
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
clusterQueryScope: optionalString(ctx, "cluster_query_scope"),
|
|
78
|
+
slaveClusterId: optionalString(ctx, "slave_cluster_id")
|
|
79
|
+
};
|
|
80
|
+
}
|
|
59
81
|
var DEFAULT_QUERY_LIMIT = 1e3;
|
|
60
82
|
var MAX_QUERY_LIMIT = 1e5;
|
|
61
83
|
function assertLimitWithinCap(value, flagName) {
|
|
@@ -249,7 +271,8 @@ var queryReportData = createMcpCommand({
|
|
|
249
271
|
{ name: "start_date", type: "string", required: false, desc: "Optional start date in yyyy-MM-dd format" },
|
|
250
272
|
{ name: "end_date", type: "string", required: false, desc: "Optional end date in yyyy-MM-dd format" },
|
|
251
273
|
{ name: "time_granularity", type: "string", required: false, desc: "Optional time granularity used to override the report default. Supported values: minute, minute5, minute10, hour, day, week, month, quarter, year, total." },
|
|
252
|
-
{ name: "timeout_minutes", type: "number", required: false, desc: "Query timeout in minutes. If omitted, 30 minutes is used." }
|
|
274
|
+
{ name: "timeout_minutes", type: "number", required: false, desc: "Query timeout in minutes. If omitted, 30 minutes is used." },
|
|
275
|
+
...clusterQueryFlags("Optional cluster query scope. Supported values: GLOBAL, SLAVE. Omit to query the current self cluster.")
|
|
253
276
|
],
|
|
254
277
|
risk: "read",
|
|
255
278
|
buildArgs: (ctx) => ({
|
|
@@ -262,7 +285,8 @@ var queryReportData = createMcpCommand({
|
|
|
262
285
|
startDate: optionalString(ctx, "start_date"),
|
|
263
286
|
endDate: optionalString(ctx, "end_date"),
|
|
264
287
|
timeGranularity: optionalString(ctx, "time_granularity"),
|
|
265
|
-
timeoutMinutes: optionalNumber(ctx, "timeout_minutes")
|
|
288
|
+
timeoutMinutes: optionalNumber(ctx, "timeout_minutes"),
|
|
289
|
+
...optionalClusterQueryArgs(ctx)
|
|
266
290
|
})
|
|
267
291
|
});
|
|
268
292
|
|
|
@@ -350,7 +374,8 @@ var queryDashboardReportData = createMcpCommand({
|
|
|
350
374
|
{ name: "end_date", type: "string", required: false, desc: "Optional end date in yyyy-MM-dd format" },
|
|
351
375
|
{ name: "time_granularity", type: "string", required: false, desc: "Optional time granularity used to override the report default. Supported values: minute, minute5, minute10, hour, day, week, month, quarter, year, total." },
|
|
352
376
|
{ name: "use_cache", type: "boolean", required: false, desc: "Whether to use cache. Default: true" },
|
|
353
|
-
{ name: "report_ids", type: "json", required: false, desc: "Optional report IDs JSON array" }
|
|
377
|
+
{ name: "report_ids", type: "json", required: false, desc: "Optional report IDs JSON array" },
|
|
378
|
+
...clusterQueryFlags("Optional cluster query scope. Supported values: GLOBAL, SLAVE. Omit to follow dashboard configuration.")
|
|
354
379
|
],
|
|
355
380
|
risk: "read",
|
|
356
381
|
buildArgs: (ctx) => ({
|
|
@@ -361,7 +386,8 @@ var queryDashboardReportData = createMcpCommand({
|
|
|
361
386
|
endDate: optionalString(ctx, "end_date"),
|
|
362
387
|
timeGranularity: optionalString(ctx, "time_granularity"),
|
|
363
388
|
useCache: optionalBoolean(ctx, "use_cache"),
|
|
364
|
-
reportIds: optionalJson(ctx, "report_ids")
|
|
389
|
+
reportIds: optionalJson(ctx, "report_ids"),
|
|
390
|
+
...optionalClusterQueryArgs(ctx)
|
|
365
391
|
})
|
|
366
392
|
});
|
|
367
393
|
|
|
@@ -562,7 +588,8 @@ var queryAdhoc = createMcpCommand({
|
|
|
562
588
|
{ name: "fields", type: "json", required: false, desc: "Optional fields to return. Must match column names in result." },
|
|
563
589
|
{ name: "limit", type: "number", required: false, desc: "Optional limit. Default: 1000, maximum: 100000. For model_type=sql, either omit LIMIT in the SQL or keep it equal to --limit (the two must not differ)." },
|
|
564
590
|
{ name: "offset", type: "number", required: false, desc: "Optional offset. Default: 0." },
|
|
565
|
-
{ name: "timeout_minutes", type: "number", required: false, desc: "Query timeout in minutes. If omitted, 30 minutes is used." }
|
|
591
|
+
{ name: "timeout_minutes", type: "number", required: false, desc: "Query timeout in minutes. If omitted, 30 minutes is used." },
|
|
592
|
+
...clusterQueryFlags("Optional cluster query scope. Supported values: GLOBAL, SLAVE. Omit to query the current self cluster. SQL model analysis does not support this parameter.")
|
|
566
593
|
],
|
|
567
594
|
risk: "read",
|
|
568
595
|
validate: (ctx) => {
|
|
@@ -584,7 +611,8 @@ var queryAdhoc = createMcpCommand({
|
|
|
584
611
|
fields: optionalJson(ctx, "fields"),
|
|
585
612
|
limit: resolveSqlAwareLimit(optionalNumber(ctx, "limit"), ctx.str("model_type"), ctx.json("qp")),
|
|
586
613
|
offset: optionalNumber(ctx, "offset"),
|
|
587
|
-
timeoutMinutes: optionalNumber(ctx, "timeout_minutes")
|
|
614
|
+
timeoutMinutes: optionalNumber(ctx, "timeout_minutes"),
|
|
615
|
+
...optionalClusterQueryArgs(ctx)
|
|
588
616
|
})
|
|
589
617
|
});
|
|
590
618
|
|
|
@@ -1069,16 +1097,36 @@ var commands6 = [
|
|
|
1069
1097
|
];
|
|
1070
1098
|
var schema_default = commands6;
|
|
1071
1099
|
|
|
1072
|
-
// src/commands/te-analysis/
|
|
1100
|
+
// src/commands/te-analysis/global/list-query-clusters.ts
|
|
1101
|
+
var listQueryClusters = createMcpCommand({
|
|
1102
|
+
command: "+list_query_clusters",
|
|
1103
|
+
description: "List query cluster options accessible to the current account in the project. Use this to decide whether to query the current self cluster, global aggregated data, or one specific slave cluster.",
|
|
1104
|
+
flags: [
|
|
1105
|
+
{ name: "project_id", type: "number", required: true, desc: "Project ID used to resolve cluster query permissions", alias: "p" }
|
|
1106
|
+
],
|
|
1107
|
+
risk: "read",
|
|
1108
|
+
buildArgs: (ctx) => ({
|
|
1109
|
+
projectId: ctx.num("project_id")
|
|
1110
|
+
})
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1113
|
+
// src/commands/te-analysis/global/index.ts
|
|
1073
1114
|
var commands7 = [
|
|
1115
|
+
listQueryClusters
|
|
1116
|
+
];
|
|
1117
|
+
var global_default = commands7;
|
|
1118
|
+
|
|
1119
|
+
// src/commands/te-analysis/index.ts
|
|
1120
|
+
var commands8 = [
|
|
1074
1121
|
...meta_default,
|
|
1075
1122
|
...report_default,
|
|
1076
1123
|
...dashboard_default,
|
|
1077
1124
|
...model_default,
|
|
1078
1125
|
...entity_default,
|
|
1079
|
-
...schema_default
|
|
1126
|
+
...schema_default,
|
|
1127
|
+
...isGlobalQueryModeEnabled() ? global_default : []
|
|
1080
1128
|
];
|
|
1081
|
-
var te_analysis_default =
|
|
1129
|
+
var te_analysis_default = commands8;
|
|
1082
1130
|
export {
|
|
1083
1131
|
te_analysis_default as default
|
|
1084
1132
|
};
|
|
@@ -2,10 +2,12 @@ import {
|
|
|
2
2
|
callMcpTool,
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
resolveMcpUrl
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-GLNZSDKO.js";
|
|
6
|
+
import "./chunk-GSJGPNKK.js";
|
|
7
|
+
import "./chunk-NOO24N7W.js";
|
|
8
|
+
import "./chunk-U6TKB3IV.js";
|
|
9
|
+
import "./chunk-JM34JPCO.js";
|
|
10
|
+
import "./chunk-SRJIAOBN.js";
|
|
9
11
|
|
|
10
12
|
// src/commands/te-audience/shared.ts
|
|
11
13
|
function createMcpCommand(config) {
|
|
@@ -59,7 +61,7 @@ function requiredJsonString(ctx, name) {
|
|
|
59
61
|
// src/commands/te-audience/cluster/list-clusters.ts
|
|
60
62
|
var listClusters = createMcpCommand({
|
|
61
63
|
command: "+list_clusters",
|
|
62
|
-
description: "List cluster metadata with optional query/fields/limit/offset",
|
|
64
|
+
description: "List user cohort/segment cluster metadata with optional query/fields/limit/offset.",
|
|
63
65
|
flags: [
|
|
64
66
|
{ name: "project_id", type: "number", required: true, desc: "Project ID", alias: "p" },
|
|
65
67
|
{ name: "query", type: "string", required: false, desc: "Keyword filter", alias: "q" },
|
|
@@ -2,10 +2,12 @@ import {
|
|
|
2
2
|
callMcpTool,
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
resolveMcpUrl
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-GLNZSDKO.js";
|
|
6
|
+
import "./chunk-GSJGPNKK.js";
|
|
7
|
+
import "./chunk-NOO24N7W.js";
|
|
8
|
+
import "./chunk-U6TKB3IV.js";
|
|
9
|
+
import "./chunk-JM34JPCO.js";
|
|
10
|
+
import "./chunk-SRJIAOBN.js";
|
|
9
11
|
|
|
10
12
|
// src/commands/te-common/shared.ts
|
|
11
13
|
function createMcpCommand(config) {
|
|
@@ -49,15 +51,25 @@ var commands = [
|
|
|
49
51
|
var project_default = commands;
|
|
50
52
|
|
|
51
53
|
// src/commands/te-common/resource/get-resource-url.ts
|
|
52
|
-
function normalizeResourceUrlFields(node) {
|
|
54
|
+
function normalizeResourceUrlFields(node, host) {
|
|
53
55
|
if (!node || typeof node !== "object") return;
|
|
54
56
|
const obj = node;
|
|
55
|
-
for (const value of Object.
|
|
56
|
-
if (
|
|
57
|
-
|
|
57
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
58
|
+
if (typeof value === "string") {
|
|
59
|
+
if (/url|link/i.test(key) || value.includes("/#/")) {
|
|
60
|
+
obj[key] = absolutizeRelativeUrls(value, host);
|
|
61
|
+
}
|
|
62
|
+
} else if (value && typeof value === "object") {
|
|
63
|
+
normalizeResourceUrlFields(value, host);
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
}
|
|
67
|
+
function absolutizeRelativeUrls(s, host) {
|
|
68
|
+
const base = host.replace(/\/+$/, "");
|
|
69
|
+
s = s.replace(/\]\((\/(?!\/)[^)]*)\)/g, `](${base}$1)`);
|
|
70
|
+
if (/^\/(?!\/)/.test(s)) s = base + s;
|
|
71
|
+
return s;
|
|
72
|
+
}
|
|
61
73
|
var getResourceUrl = {
|
|
62
74
|
service: "analysis_common",
|
|
63
75
|
command: "+get_resource_url",
|
|
@@ -96,7 +108,7 @@ var getResourceUrl = {
|
|
|
96
108
|
if (typeof parsed !== "object" || parsed === null) {
|
|
97
109
|
return parsed;
|
|
98
110
|
}
|
|
99
|
-
normalizeResourceUrlFields(parsed);
|
|
111
|
+
normalizeResourceUrlFields(parsed, ctx.host());
|
|
100
112
|
return parsed;
|
|
101
113
|
}
|
|
102
114
|
};
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
registerMcpMappings,
|
|
5
5
|
resolveMcpUrl
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-GLNZSDKO.js";
|
|
7
|
+
import "./chunk-GSJGPNKK.js";
|
|
8
|
+
import "./chunk-NOO24N7W.js";
|
|
9
|
+
import "./chunk-U6TKB3IV.js";
|
|
10
|
+
import "./chunk-JM34JPCO.js";
|
|
11
|
+
import "./chunk-SRJIAOBN.js";
|
|
10
12
|
|
|
11
13
|
// src/commands/te-community/get_channel_info.ts
|
|
12
14
|
var getChannelInfo = {
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
registerMcpMappings,
|
|
5
5
|
resolveMcpUrl
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-GLNZSDKO.js";
|
|
7
|
+
import "./chunk-GSJGPNKK.js";
|
|
8
|
+
import "./chunk-NOO24N7W.js";
|
|
9
|
+
import "./chunk-U6TKB3IV.js";
|
|
10
|
+
import "./chunk-JM34JPCO.js";
|
|
11
|
+
import "./chunk-SRJIAOBN.js";
|
|
10
12
|
|
|
11
13
|
// src/commands/te-dataops/datatable/list-tables-by-page.ts
|
|
12
14
|
var listTablesByPage = {
|
|
@@ -1534,7 +1536,7 @@ var executeSql2 = {
|
|
|
1534
1536
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1535
1537
|
{ name: "repoCode", type: "string", required: true, desc: "Repository code" },
|
|
1536
1538
|
{ name: "sql", type: "string", required: true, desc: "SQL statement" },
|
|
1537
|
-
{ name: "engineType", type: "string", required:
|
|
1539
|
+
{ name: "engineType", type: "string", required: false, default: "TASK_ENGINE_TRINO", desc: "SQL execution engine: TASK_ENGINE_TRINO(default, suitable for interactive queries), TASK_ENGINE_STARROCKS(suitable for real-time analytics and high-concurrency queries)" },
|
|
1538
1540
|
{ name: "confirmed", type: "boolean", required: true, desc: "Whether confirmed to execute. First call: omit or pass false for preview, then pass true to execute" }
|
|
1539
1541
|
],
|
|
1540
1542
|
risk: "write",
|
|
@@ -1558,8 +1560,8 @@ var listCatalogs2 = {
|
|
|
1558
1560
|
description: "Lists all catalogs and their schemas in the repository. Returns: catalogs(list containing catalogName and schemas sub-list). Requires repoCode (obtainable via ide_list_repos)",
|
|
1559
1561
|
flags: [
|
|
1560
1562
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1561
|
-
{ name: "connType", type: "string", required:
|
|
1562
|
-
{ name: "repoCode", type: "string", required:
|
|
1563
|
+
{ name: "connType", type: "string", required: false, default: "SPACE", desc: "Connection type: SPACE(data warehouse for daily queries, default), ETL(ETL engine for data processing), APP(app warehouse for external services)" },
|
|
1564
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" }
|
|
1563
1565
|
],
|
|
1564
1566
|
risk: "read",
|
|
1565
1567
|
execute: async (ctx) => {
|
|
@@ -1580,8 +1582,8 @@ var getSchemaInfo = {
|
|
|
1580
1582
|
description: "Gets schema statistics, including table count and view count. Returns: tableCount, viewCount",
|
|
1581
1583
|
flags: [
|
|
1582
1584
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1583
|
-
{ name: "connType", type: "string", required:
|
|
1584
|
-
{ name: "repoCode", type: "string", required:
|
|
1585
|
+
{ name: "connType", type: "string", required: false, default: "SPACE", desc: "Connection type: SPACE(data warehouse for daily queries, default), ETL(ETL engine for data processing), APP(app warehouse for external services)" },
|
|
1586
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1585
1587
|
{ name: "catalog", type: "string", required: true, desc: "Catalog name" },
|
|
1586
1588
|
{ name: "schema", type: "string", required: true, desc: "Schema name" }
|
|
1587
1589
|
],
|
|
@@ -1606,12 +1608,12 @@ var searchColumns = {
|
|
|
1606
1608
|
description: "Fuzzy search column names across tables. Returns: columnName, columnType, tableName, catalogName, schemaName. Useful for finding which tables contain a specific field",
|
|
1607
1609
|
flags: [
|
|
1608
1610
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1609
|
-
{ name: "repoCode", type: "string", required:
|
|
1611
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1610
1612
|
{ name: "searchKey", type: "string", required: true, desc: "Column name search keyword" },
|
|
1611
1613
|
{ name: "tables", type: "string", required: true, desc: "List of tables to search (JSON array format, each item contains catalog, schema, tableName fields)" },
|
|
1612
|
-
{ name: "engineType", type: "string", required:
|
|
1613
|
-
{ name: "pageNum", type: "number", required:
|
|
1614
|
-
{ name: "pageSize", type: "number", required:
|
|
1614
|
+
{ name: "engineType", type: "string", required: false, default: "TASK_ENGINE_TRINO", desc: "SQL execution engine: TASK_ENGINE_TRINO(default), TASK_ENGINE_STARROCKS" },
|
|
1615
|
+
{ name: "pageNum", type: "number", required: false, default: 1, desc: "Page number, default 1" },
|
|
1616
|
+
{ name: "pageSize", type: "number", required: false, default: 100, desc: "Page size, default 100" }
|
|
1615
1617
|
],
|
|
1616
1618
|
risk: "read",
|
|
1617
1619
|
execute: async (ctx) => {
|
|
@@ -1636,12 +1638,12 @@ var getTableDetail2 = {
|
|
|
1636
1638
|
description: "Gets real-time table metadata from the data warehouse engine, including column definitions, DDL, partition information. Returns: columns(name/type/comment), ddl, partitions, rowCount, dataSize. Difference from datatable_get_table_detail: This tool directly queries real-time metadata from the data warehouse engine, datatable queries dataops platform registered information(including data lineage). Difference from integration_get_table_structure: integration queries external data source metadata",
|
|
1637
1639
|
flags: [
|
|
1638
1640
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1639
|
-
{ name: "connType", type: "string", required:
|
|
1640
|
-
{ name: "repoCode", type: "string", required:
|
|
1641
|
+
{ name: "connType", type: "string", required: false, default: "SPACE", desc: "Connection type: SPACE(data warehouse for daily queries, default), ETL(ETL engine for data processing), APP(app warehouse for external services)" },
|
|
1642
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1641
1643
|
{ name: "catalog", type: "string", required: true, desc: "Catalog name" },
|
|
1642
1644
|
{ name: "schema", type: "string", required: true, desc: "Schema name" },
|
|
1643
1645
|
{ name: "tableName", type: "string", required: true, desc: "Table name" },
|
|
1644
|
-
{ name: "engineType", type: "string", required:
|
|
1646
|
+
{ name: "engineType", type: "string", required: false, default: "TASK_ENGINE_TRINO", desc: "SQL execution engine: TASK_ENGINE_TRINO(default), TASK_ENGINE_STARROCKS" },
|
|
1645
1647
|
{ name: "isView", type: "boolean", required: true, desc: "Whether it is a view, auto-detect if not provided" }
|
|
1646
1648
|
],
|
|
1647
1649
|
risk: "read",
|
|
@@ -1668,11 +1670,11 @@ var generateSql = {
|
|
|
1668
1670
|
description: "Generates SELECT SQL statement based on table name and column names. Returns: Executable SELECT SQL string. Related tools: ide_execute_sql (execute the generated SQL)",
|
|
1669
1671
|
flags: [
|
|
1670
1672
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1671
|
-
{ name: "repoCode", type: "string", required:
|
|
1673
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1672
1674
|
{ name: "catalog", type: "string", required: true, desc: "Catalog name" },
|
|
1673
1675
|
{ name: "schema", type: "string", required: true, desc: "Schema name" },
|
|
1674
1676
|
{ name: "tableName", type: "string", required: true, desc: "Table name" },
|
|
1675
|
-
{ name: "engineType", type: "string", required:
|
|
1677
|
+
{ name: "engineType", type: "string", required: false, default: "TASK_ENGINE_TRINO", desc: "SQL execution engine: TASK_ENGINE_TRINO(default), TASK_ENGINE_STARROCKS" },
|
|
1676
1678
|
{ name: "selectColumns", type: "string", required: true, desc: "List of column names to query, queries all columns if not provided" }
|
|
1677
1679
|
],
|
|
1678
1680
|
risk: "read",
|
|
@@ -1698,8 +1700,8 @@ var searchTables = {
|
|
|
1698
1700
|
description: "Fuzzy search tables by keyword across catalog/schema. Returns: tableName, catalogName, schemaName, tableType. Difference from datatable_search_tables: This tool queries real-time metadata from the data warehouse engine, datatable queries dataops platform registered metadata(including business descriptions)",
|
|
1699
1701
|
flags: [
|
|
1700
1702
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1701
|
-
{ name: "connType", type: "string", required:
|
|
1702
|
-
{ name: "repoCode", type: "string", required:
|
|
1703
|
+
{ name: "connType", type: "string", required: false, default: "SPACE", desc: "Connection type: SPACE(data warehouse for daily queries, default), ETL(ETL engine for data processing), APP(app warehouse for external services)" },
|
|
1704
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1703
1705
|
{ name: "searchKey", type: "string", required: true, desc: "Search keyword" },
|
|
1704
1706
|
{ name: "size", type: "number", required: true, desc: "Maximum number of results to return" }
|
|
1705
1707
|
],
|
|
@@ -1742,13 +1744,13 @@ var listTables = {
|
|
|
1742
1744
|
description: "Paginated list of tables/views under a schema. Returns: tableName, tableType. Defaults to physical tables, set isView=true to return views",
|
|
1743
1745
|
flags: [
|
|
1744
1746
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1745
|
-
{ name: "connType", type: "string", required:
|
|
1746
|
-
{ name: "repoCode", type: "string", required:
|
|
1747
|
+
{ name: "connType", type: "string", required: false, default: "SPACE", desc: "Connection type: SPACE(data warehouse for daily queries, default), ETL(ETL engine for data processing), APP(app warehouse for external services)" },
|
|
1748
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1747
1749
|
{ name: "catalog", type: "string", required: true, desc: "Catalog name" },
|
|
1748
1750
|
{ name: "schema", type: "string", required: true, desc: "Schema name" },
|
|
1749
|
-
{ name: "isView", type: "boolean", required:
|
|
1750
|
-
{ name: "pageNum", type: "number", required:
|
|
1751
|
-
{ name: "pageSize", type: "number", required:
|
|
1751
|
+
{ name: "isView", type: "boolean", required: false, desc: "Whether to query views, default false returns physical tables, set true to return views" },
|
|
1752
|
+
{ name: "pageNum", type: "number", required: false, default: 1, desc: "Page number, default 1" },
|
|
1753
|
+
{ name: "pageSize", type: "number", required: false, default: 100, desc: "Page size, default 100" }
|
|
1752
1754
|
],
|
|
1753
1755
|
risk: "read",
|
|
1754
1756
|
execute: async (ctx) => {
|
|
@@ -1774,8 +1776,8 @@ var getQueryResult = {
|
|
|
1774
1776
|
description: "Gets result data details (column names + row data) for a specific query record. Returns: columns(column definitions), rows(data rows). Requires recordId (obtainable via ide_list_query_history). Note: Only queries with SUCCESS status have result data",
|
|
1775
1777
|
flags: [
|
|
1776
1778
|
{ name: "spaceCode", type: "string", required: true, desc: "Space code" },
|
|
1777
|
-
{ name: "connType", type: "string", required:
|
|
1778
|
-
{ name: "repoCode", type: "string", required:
|
|
1779
|
+
{ name: "connType", type: "string", required: false, default: "SPACE", desc: "Connection type: SPACE(data warehouse for daily queries, default), ETL(ETL engine for data processing), APP(app warehouse for external services)" },
|
|
1780
|
+
{ name: "repoCode", type: "string", required: false, default: "te_etl", desc: "Repository code, defaults to te_etl if not provided" },
|
|
1779
1781
|
{ name: "recordId", type: "number", required: true, desc: "Query record ID (obtainable via ide_list_query_history)" }
|
|
1780
1782
|
],
|
|
1781
1783
|
risk: "read",
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
registerMcpMappings,
|
|
5
5
|
resolveMcpUrl
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-GLNZSDKO.js";
|
|
7
|
+
import "./chunk-GSJGPNKK.js";
|
|
8
|
+
import "./chunk-NOO24N7W.js";
|
|
9
|
+
import "./chunk-U6TKB3IV.js";
|
|
10
|
+
import "./chunk-JM34JPCO.js";
|
|
11
|
+
import "./chunk-SRJIAOBN.js";
|
|
10
12
|
|
|
11
13
|
// src/commands/te-engage/utils.ts
|
|
12
14
|
function buildMcpDryRun(ctx, serviceName46, toolName46, args) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
kbApi,
|
|
3
3
|
kbUpload
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-SF3KTPIC.js";
|
|
5
|
+
import "./chunk-GLNZSDKO.js";
|
|
6
|
+
import "./chunk-GSJGPNKK.js";
|
|
7
|
+
import "./chunk-NOO24N7W.js";
|
|
8
|
+
import "./chunk-U6TKB3IV.js";
|
|
9
|
+
import "./chunk-JM34JPCO.js";
|
|
10
|
+
import "./chunk-SRJIAOBN.js";
|
|
9
11
|
|
|
10
12
|
// src/commands/te-kb/query.ts
|
|
11
13
|
var API_PATH = "/agent/api/external/knowledge-bases/query";
|
|
@@ -2,10 +2,12 @@ import {
|
|
|
2
2
|
callMcpTool,
|
|
3
3
|
parseMcpResult,
|
|
4
4
|
resolveMcpUrl
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-GLNZSDKO.js";
|
|
6
|
+
import "./chunk-GSJGPNKK.js";
|
|
7
|
+
import "./chunk-NOO24N7W.js";
|
|
8
|
+
import "./chunk-U6TKB3IV.js";
|
|
9
|
+
import "./chunk-JM34JPCO.js";
|
|
10
|
+
import "./chunk-SRJIAOBN.js";
|
|
9
11
|
|
|
10
12
|
// src/commands/te-meta/shared.ts
|
|
11
13
|
function createMcpCommand(config) {
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
getAuthHeaders,
|
|
2
3
|
kbApi
|
|
3
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SF3KTPIC.js";
|
|
4
5
|
import {
|
|
5
6
|
printError,
|
|
6
7
|
printOutput
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-CLJF7MQA.js";
|
|
9
|
+
import "./chunk-GLNZSDKO.js";
|
|
10
|
+
import "./chunk-GSJGPNKK.js";
|
|
11
|
+
import "./chunk-NOO24N7W.js";
|
|
12
|
+
import "./chunk-U6TKB3IV.js";
|
|
13
|
+
import "./chunk-JM34JPCO.js";
|
|
14
|
+
import "./chunk-SRJIAOBN.js";
|
|
12
15
|
|
|
13
16
|
// src/commands/te-team/shared.ts
|
|
14
17
|
var API_PREFIX = process.env.AE_API_PREFIX ?? "/agent";
|
|
@@ -176,17 +179,19 @@ var listTemplates = {
|
|
|
176
179
|
};
|
|
177
180
|
|
|
178
181
|
// src/commands/te-team/team/list-projects.ts
|
|
179
|
-
var OAUTH_CHECK_PATH = "/api/oauth/check";
|
|
182
|
+
var OAUTH_CHECK_PATH = "/agent/api/external/team/oauth/check";
|
|
180
183
|
var listProjects = {
|
|
181
184
|
service: "team",
|
|
182
185
|
command: "+list-projects",
|
|
183
186
|
description: "List all projects available to the current user (from /api/oauth/check).",
|
|
184
187
|
flags: [],
|
|
185
188
|
risk: "read",
|
|
186
|
-
dryRun: (ctx) => ({
|
|
189
|
+
dryRun: (ctx) => ({
|
|
190
|
+
method: "POST",
|
|
191
|
+
url: `${ctx.host().replace(/\/$/, "")}${OAUTH_CHECK_PATH}`
|
|
192
|
+
}),
|
|
187
193
|
execute: async (ctx) => {
|
|
188
|
-
const
|
|
189
|
-
const data = await kbApi(ctx, "POST", OAUTH_CHECK_PATH, {}, { accessToken });
|
|
194
|
+
const data = await kbApi(ctx, "POST", OAUTH_CHECK_PATH, {});
|
|
190
195
|
return data?.projectInfoList ?? data;
|
|
191
196
|
}
|
|
192
197
|
};
|
|
@@ -393,7 +398,7 @@ var watchRun = {
|
|
|
393
398
|
execute: async (ctx) => {
|
|
394
399
|
const runId = ctx.str("id");
|
|
395
400
|
const quiet = ctx.bool("quiet");
|
|
396
|
-
const
|
|
401
|
+
const authHeaders = await getAuthHeaders(ctx);
|
|
397
402
|
const host = ctx.host().replace(/\/$/, "");
|
|
398
403
|
let lastLogTs = ctx.num("after-log") || void 0;
|
|
399
404
|
let reconnects = 0;
|
|
@@ -404,7 +409,7 @@ var watchRun = {
|
|
|
404
409
|
try {
|
|
405
410
|
resp = await fetch(url.toString(), {
|
|
406
411
|
headers: {
|
|
407
|
-
|
|
412
|
+
...authHeaders,
|
|
408
413
|
Accept: "text/event-stream",
|
|
409
414
|
"Cache-Control": "no-cache"
|
|
410
415
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thinkingai/ae-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.30",
|
|
4
4
|
"description": "CLI tool for ThinkingAI (AE) analytics platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,13 +16,17 @@
|
|
|
16
16
|
"scripts": {
|
|
17
17
|
"dev": "tsx src/index.ts",
|
|
18
18
|
"build": "tsup src/index.ts --format esm --outDir dist",
|
|
19
|
-
"verify:
|
|
19
|
+
"verify:config": "node test/config-commands.test.mjs",
|
|
20
|
+
"verify:analysis-tools": "tsx scripts/verify-te-analysis-tools.mjs",
|
|
21
|
+
"verify:analysis-cluster-routing": "node scripts/verify-analysis-cluster-routing.mjs",
|
|
22
|
+
"verify:multi-cluster-mode": "node test/multi-cluster-mode.test.mjs",
|
|
20
23
|
"verify:analysis-builder-dry-run": "node scripts/verify-te-analysis-builder-dry-run.mjs",
|
|
21
24
|
"verify:analysis-audience-tools": "node scripts/verify-te-audience-tools.mjs",
|
|
22
25
|
"verify:analysis-meta-tools": "node scripts/verify-te-meta-tools.mjs",
|
|
23
26
|
"verify:analysis-common-tools": "node scripts/verify-te-common-tools.mjs",
|
|
24
27
|
"verify:team-tools": "node scripts/verify-te-team-tools.mjs",
|
|
25
28
|
"verify:agent-tools": "node scripts/verify-agent-tools.mjs",
|
|
29
|
+
"verify:self-check-overlay": "node test/self-check-overlay.test.mjs",
|
|
26
30
|
"verify:update-check": "npx tsx test/update-check.test.mjs",
|
|
27
31
|
"self-check": "node self-check/scan.mjs",
|
|
28
32
|
"check:agents-docs": "node scripts/check-agents-docs-sync.mjs",
|
package/skills/ae-agent/SKILL.md
CHANGED
|
@@ -21,6 +21,7 @@ ae-cli agent +<command> [options]
|
|
|
21
21
|
- Toggle operations on company/system resources only affect the current user's preference, not the global state.
|
|
22
22
|
- JSON flags must be valid JSON strings, usually wrapped in single quotes in shell.
|
|
23
23
|
- Successful commands return JSON by default. Use `--format table` when a table is easier to scan.
|
|
24
|
+
- `--host <url>` overrides the active AE host. It is available on every command and may be placed after the subcommand, e.g. `ae-cli agent +<command> --host <url>`.
|
|
24
25
|
|
|
25
26
|
## Commands
|
|
26
27
|
|
|
@@ -127,7 +128,7 @@ ae-cli agent +toggle-model --id <model-cuid> --enabled false
|
|
|
127
128
|
## Notes
|
|
128
129
|
|
|
129
130
|
- Only `personal` scope resources can be created or deleted via CLI.
|
|
130
|
-
- MCP creation
|
|
131
|
+
- MCP creation does NOT validate server connectivity — an unreachable URL is accepted at create time and only fails when the agent actually calls the MCP at runtime. Double-check the URL.
|
|
131
132
|
- Attachment upload supports files up to 50MB each, with a 1GB user quota.
|
|
132
133
|
- Batch attachment uploads support partial success — individual file failures don't affect others.
|
|
133
134
|
- Skill `--instructions @-` reads from stdin, useful for piping long instruction text.
|
|
@@ -6,7 +6,6 @@ description: "AE/TE/ThinkingEngine/ThinkingAI ae-cli manual for analysis-side ta
|
|
|
6
6
|
|
|
7
7
|
# ae-analysis
|
|
8
8
|
|
|
9
|
-
> **CRITICAL - This skill is self-contained.** Use the Global AE CLI Rules below; do not require a separate shared skill for analysis-side tasks.
|
|
10
9
|
> **CRITICAL - For all commands that require `project_id`, you MUST satisfy `PROJECT_ID_GATE` first (no guessing): verify the project by ID/name with `analysis_common +list_projects` only when there is no valid project context yet, when the user switches project/host/environment, or when the supplied project is ambiguous. Reuse a project already verified in the same continuous conversation and same host/environment.**
|
|
11
10
|
> **CRITICAL - For write operations in this skill, you MUST complete the post-write link loop when applicable:** after success and extractable `resource_id`, call `analysis_common +get_resource_url` and return the main result + resource link (or explicit link-failure reason).
|
|
12
11
|
> **CRITICAL - Before running any `+<tool_name>` command, you MUST first read the corresponding `references/<tool_name>.md`.** The reference filename always equals the command name without the leading `+`, for example `+query_adhoc` -> `references/query_adhoc.md`.
|
|
@@ -21,6 +20,7 @@ Global parameters:
|
|
|
21
20
|
|---|---|
|
|
22
21
|
| `--format <json\|table>` | Output format. Default is JSON. |
|
|
23
22
|
| `--jq <expr>` | jq filter expression for JSON output. |
|
|
23
|
+
| `--host <url>` | Override the active AE host. Available on every command and may be placed after the subcommand, e.g. `ae-cli analysis +list_dashboards --host <url>`. |
|
|
24
24
|
|
|
25
25
|
Output and errors:
|
|
26
26
|
- Successful commands return machine-readable JSON by default.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# analysis_audience +list_clusters (Cluster Search)
|
|
1
|
+
# analysis_audience +list_clusters (Audience/User Segment Cluster Search)
|
|
2
2
|
|
|
3
3
|
> **Prerequisite:** Follow the Global AE CLI Rules in [`../SKILL.md`](../SKILL.md).
|
|
4
4
|
|
|
@@ -8,8 +8,10 @@ Domain: **Cluster Management**
|
|
|
8
8
|
|
|
9
9
|
**Fuzzy Search Fallback:** If `--query` returns no results, retry with broader keywords (max 3 attempts), then fall back to full list. See [SKILL.md § C. FUZZY_SEARCH_FALLBACK](../SKILL.md#c-fuzzy_search_fallback).
|
|
10
10
|
|
|
11
|
+
**Term boundary:** This command lists audience/user segment clusters only. It is not a server, region, or deployment inventory tool.
|
|
12
|
+
|
|
11
13
|
## Use Cases
|
|
12
|
-
- List cluster metadata accessible to the current user in the project.
|
|
14
|
+
- List audience/user segment cluster metadata accessible to the current user in the project.
|
|
13
15
|
- Supports payload governance parameters: `query`, `fields`, `limit`, `offset`.
|
|
14
16
|
- Returns a paginated envelope (items + total + limit + offset + hasMore), not cluster members.
|
|
15
17
|
|