@bty/customer-service-cli 0.6.8 → 0.6.10
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/CHANGELOG.md +9 -1
- package/dist/bin.js +124 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.6.10 (2026-08-26)
|
|
4
4
|
|
|
5
|
+
- `context-search`:`query_content` 增加最少 2 个字符的本地校验,避免过短关键词触发服务端全量扫描。
|
|
6
|
+
- `channel-metric backfill`:对齐服务端权限收紧——`--workspace-id` 逐个鉴权,缺省不再采集所有 workspace,改为只采集当前登录 workspace;同时修复不传 `--workspace-id` 时 commander 解析崩溃的问题。
|
|
7
|
+
|
|
8
|
+
## 0.6.9 (2026-08-24)
|
|
9
|
+
|
|
10
|
+
- 新增 `channel-metric` 命令组,查询千牛等渠道店铺客服的每日排行指标(`channel_metric_snapshot`):
|
|
11
|
+
- `channel-metric query --workspace-id <ws> --start <date> --end <date> [--agent <id>] [--platform <channel>]`:按 workspace + 日期区间(+ 可选 agent / 渠道)查询指标,按日期×渠道分组输出。
|
|
12
|
+
- `channel-metric backfill --channel <channel> --start <date> --end <date> [--workspace-id <ws>...]`:手动补数据,采集指定日期区间的渠道指标;`--workspace-id` 可重复传,缺省采集所有已开启配置的 workspace。
|
|
5
13
|
- 新增 `media upload` 命令与 `product supplement` 子命令组,支持商品「补充知识」图片/视频维护,与前端上传行为一致:
|
|
6
14
|
- `media upload --file <path> --type image|video [--path-prefix /ai/knowledge/supplement] [--dry-run]`:本地图片/视频先经 `/v1/upload_sts` 取 STS 凭证上传到公开 OSS(`resource_bucket`),返回公网 URL;已是 http(s) URL 直接拒绝(无需上传)。上传 key 与前端一致:`${prefix}/${timestamp}_${filename}`。图片上限 10MB、视频上限 200MB,超限/类型不符在发起上传请求前直接失败。
|
|
7
15
|
- `product supplement list --agent <id> --product-id <id>`:读取并解析商品「补充知识」条目(容错解析数组 / JSON 字符串 / 多层编码)。
|
package/dist/bin.js
CHANGED
|
@@ -1095,6 +1095,121 @@ function registerChangeConsumerCommand(program2) {
|
|
|
1095
1095
|
});
|
|
1096
1096
|
}
|
|
1097
1097
|
|
|
1098
|
+
// src/commands/channel-metric.ts
|
|
1099
|
+
import Table2 from "cli-table3";
|
|
1100
|
+
|
|
1101
|
+
// src/client/channel-metric-api.ts
|
|
1102
|
+
var CHANNEL_METRIC_PREFIX = "/v1/channel-metric";
|
|
1103
|
+
async function backfillChannelMetrics(opts) {
|
|
1104
|
+
const request = createRequest();
|
|
1105
|
+
const body = {
|
|
1106
|
+
channel: opts.channel,
|
|
1107
|
+
start_date: opts.startDate,
|
|
1108
|
+
end_date: opts.endDate
|
|
1109
|
+
};
|
|
1110
|
+
if (opts.workspaceIds && opts.workspaceIds.length > 0) {
|
|
1111
|
+
body.workspace_ids = opts.workspaceIds;
|
|
1112
|
+
}
|
|
1113
|
+
return request(getObservabilityUrl(), `${CHANNEL_METRIC_PREFIX}/backfill`, {
|
|
1114
|
+
method: "POST",
|
|
1115
|
+
body
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
async function queryChannelMetrics(opts) {
|
|
1119
|
+
const request = createRequest();
|
|
1120
|
+
const query = {
|
|
1121
|
+
workspace_id: opts.workspaceId,
|
|
1122
|
+
start_date: opts.startDate,
|
|
1123
|
+
end_date: opts.endDate
|
|
1124
|
+
};
|
|
1125
|
+
if (opts.configId) query.config_id = opts.configId;
|
|
1126
|
+
if (opts.channel) query.channel = opts.channel;
|
|
1127
|
+
return request(getObservabilityUrl(), `${CHANNEL_METRIC_PREFIX}/metrics`, {
|
|
1128
|
+
method: "GET",
|
|
1129
|
+
query
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// src/commands/channel-metric.ts
|
|
1134
|
+
function collect(value, previous) {
|
|
1135
|
+
return previous.concat([value]);
|
|
1136
|
+
}
|
|
1137
|
+
function renderGroupedTable(groups) {
|
|
1138
|
+
let total = 0;
|
|
1139
|
+
for (const group of groups) {
|
|
1140
|
+
process.stdout.write(`
|
|
1141
|
+
=== ${group.stat_date} \xB7 ${group.channel}\uFF08${group.items.length} \u4E2A\u8D26\u53F7\uFF09===
|
|
1142
|
+
`);
|
|
1143
|
+
const first = group.items[0];
|
|
1144
|
+
const headers = (first?.metrics_json ?? []).map((field) => field.label);
|
|
1145
|
+
const table = new Table2({ head: ["account_id", ...headers] });
|
|
1146
|
+
for (const row of group.items) {
|
|
1147
|
+
table.push([
|
|
1148
|
+
String(row.account_id),
|
|
1149
|
+
...row.metrics_json.map(
|
|
1150
|
+
(field) => field.value === void 0 || field.value === null ? "-" : String(field.value)
|
|
1151
|
+
)
|
|
1152
|
+
]);
|
|
1153
|
+
}
|
|
1154
|
+
process.stdout.write(`${table.toString()}
|
|
1155
|
+
`);
|
|
1156
|
+
total += group.items.length;
|
|
1157
|
+
}
|
|
1158
|
+
process.stdout.write(`
|
|
1159
|
+
\u5171 ${total} \u6761\u8BB0\u5F55
|
|
1160
|
+
`);
|
|
1161
|
+
}
|
|
1162
|
+
function registerChannelMetricCommand(program2) {
|
|
1163
|
+
const channelMetric = program2.command("channel-metric").description("\u6E20\u9053\u76D1\u63A7\u6307\u6807\u67E5\u8BE2 \u2014\u2014 \u67E5\u8BE2\u5343\u725B\u7B49\u6E20\u9053\u5E97\u94FA\u5BA2\u670D\u7684\u6BCF\u65E5\u6392\u884C\u6307\u6807\uFF08channel_metric_snapshot\uFF09");
|
|
1164
|
+
channelMetric.command("query").description(
|
|
1165
|
+
"\u6309 workspace + \u65E5\u671F\u533A\u95F4\uFF08+ \u53EF\u9009 agent / \u6E20\u9053\uFF09\u67E5\u8BE2\u6307\u6807\uFF0C\u6309\u65E5\u671F\xD7\u6E20\u9053\u5206\u7EC4\u8F93\u51FA\u3002\u4E0D\u4F20 --agent \u8F93\u51FA\u8BE5 workspace \u4E0B\u5168\u90E8 agent\uFF0C\u4E0D\u4F20 --platform \u8F93\u51FA\u5168\u90E8\u6E20\u9053"
|
|
1166
|
+
).requiredOption("--workspace-id <workspace_id>", "\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u5FC5\u4F20\uFF0C\u4E0E\u670D\u52A1\u7AEF\u5F52\u5C5E\u6821\u9A8C\u4E00\u81F4\uFF09").option("--agent <agent_id>", "Agent \u914D\u7F6E ID\uFF1B\u7F3A\u7701\u67E5\u8BE2\u8BE5 workspace \u4E0B\u5168\u90E8 agent").requiredOption("--start <date>", "\u8D77\u59CB\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").option("--platform <channel>", "\u6E20\u9053\uFF08platform\uFF09\uFF0C\u5982 qianniu\uFF1B\u7F3A\u7701\u67E5\u8BE2\u5168\u90E8\u6E20\u9053").action(async (opts) => {
|
|
1167
|
+
try {
|
|
1168
|
+
setRuntimeWorkspaceId(opts.workspaceId);
|
|
1169
|
+
const data = await queryChannelMetrics({
|
|
1170
|
+
workspaceId: opts.workspaceId,
|
|
1171
|
+
configId: opts.agent,
|
|
1172
|
+
startDate: opts.start,
|
|
1173
|
+
endDate: opts.end,
|
|
1174
|
+
channel: opts.platform
|
|
1175
|
+
});
|
|
1176
|
+
const groups = data?.groups ?? [];
|
|
1177
|
+
if (groups.length === 0) {
|
|
1178
|
+
process.stdout.write("(empty)\n");
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
renderGroupedTable(groups);
|
|
1182
|
+
} catch (err) {
|
|
1183
|
+
reportCaughtError(err);
|
|
1184
|
+
process.exit(toExitCode(err));
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
channelMetric.command("backfill").description(
|
|
1188
|
+
"\u624B\u52A8\u8865\u6570\u636E\uFF1A\u91C7\u96C6\u6307\u5B9A\u65E5\u671F\u533A\u95F4\u7684\u6E20\u9053\u6307\u6807\u3002\u53EF\u6307\u5B9A workspace\uFF08\u53EF\u91CD\u590D\u4F20\uFF0C\u9010\u4E2A\u9274\u6743\uFF09\uFF1B\u4E0D\u4F20 --workspace-id = \u53EA\u91C7\u96C6\u5F53\u524D workspace\uFF08\u767B\u5F55\u6001\u4E0B\uFF0C\u670D\u52A1\u7AEF\u9650\u5236\uFF09"
|
|
1189
|
+
).requiredOption("--channel <channel>", "\u6E20\u9053\uFF08platform\uFF09\uFF0C\u5982 qianniu").requiredOption("--start <date>", "\u8D77\u59CB\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F\uFF08YYYY-MM-DD\uFF09").option(
|
|
1190
|
+
"--workspace-id <workspace_id>",
|
|
1191
|
+
"\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u7F3A\u7701 = \u53EA\u91C7\u96C6\u5F53\u524D workspace\uFF09",
|
|
1192
|
+
collect,
|
|
1193
|
+
[]
|
|
1194
|
+
// 默认空数组:commander 首次解析时 previous 为 undefined,无默认值会崩
|
|
1195
|
+
).action(async (opts) => {
|
|
1196
|
+
try {
|
|
1197
|
+
const workspaceIds = opts.workspaceId?.length ? opts.workspaceId : void 0;
|
|
1198
|
+
setRuntimeWorkspaceId(workspaceIds?.[0]);
|
|
1199
|
+
const data = await backfillChannelMetrics({
|
|
1200
|
+
channel: opts.channel,
|
|
1201
|
+
startDate: opts.start,
|
|
1202
|
+
endDate: opts.end,
|
|
1203
|
+
workspaceIds
|
|
1204
|
+
});
|
|
1205
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
1206
|
+
} catch (err) {
|
|
1207
|
+
reportCaughtError(err);
|
|
1208
|
+
process.exit(toExitCode(err));
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1098
1213
|
// src/client/chat-history-api.ts
|
|
1099
1214
|
async function listChatHistory(opts) {
|
|
1100
1215
|
const request = createRequest();
|
|
@@ -1250,6 +1365,11 @@ function parseSearchTime(value, fieldName) {
|
|
|
1250
1365
|
}
|
|
1251
1366
|
return parsed;
|
|
1252
1367
|
}
|
|
1368
|
+
function validateContextSearchQuery(queryContent) {
|
|
1369
|
+
if (queryContent.trim().length < 2) {
|
|
1370
|
+
throw new Error("query_content \u81F3\u5C11\u9700\u8981 2 \u4E2A\u5B57\u7B26");
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1253
1373
|
function validateContextSearchRange(startTime, endTime) {
|
|
1254
1374
|
if (!startTime || !endTime) return;
|
|
1255
1375
|
const start = parseSearchTime(startTime, "\u5F00\u59CB\u65F6\u95F4");
|
|
@@ -1288,6 +1408,7 @@ function shouldKeepTransferHumanConversation(item, onlyAbnormal) {
|
|
|
1288
1408
|
});
|
|
1289
1409
|
}
|
|
1290
1410
|
async function contextSearchRecords(opts) {
|
|
1411
|
+
validateContextSearchQuery(opts.queryContent);
|
|
1291
1412
|
validateContextSearchRange(opts.startTime, opts.endTime);
|
|
1292
1413
|
const wsId = getWorkspaceId(opts.workspaceId);
|
|
1293
1414
|
const request = createRequest();
|
|
@@ -1608,7 +1729,7 @@ async function writeBinaryToFile(filePath, buffer) {
|
|
|
1608
1729
|
}
|
|
1609
1730
|
|
|
1610
1731
|
// src/commands/dashboard.ts
|
|
1611
|
-
function
|
|
1732
|
+
function collect2(value, previous) {
|
|
1612
1733
|
return previous.concat([value]);
|
|
1613
1734
|
}
|
|
1614
1735
|
function registerDashboardCommand(program2) {
|
|
@@ -1663,7 +1784,7 @@ function registerDashboardCommand(program2) {
|
|
|
1663
1784
|
});
|
|
1664
1785
|
dashboard.command("export").description(
|
|
1665
1786
|
"\u5BFC\u51FA\u5E97\u94FA\u7EF4\u5EA6\u6BCF\u65E5\u660E\u7EC6\uFF08\u63A5\u5F85\u5BA2\u6237\u6570/AI\u72EC\u7ACB\u627F\u63A5/\u8F6C\u4EBA\u5DE5/AI\u56DE\u590D\u6D88\u606F\u6570\uFF09\u3002\u9ED8\u8BA4\u89E3\u6790\u4E3A JSON \u884C\uFF1B--out \u843D\u76D8\u539F\u59CB CSV\u3002\u53EF\u91CD\u590D --config-id \u9650\u5B9A\u5E97\u94FA"
|
|
1666
|
-
).requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u4E0D\u4F20=\u5168\u90E8\u5E97\u94FA\uFF09",
|
|
1787
|
+
).requiredOption("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B").requiredOption("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u5FC5\u586B\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u53EF\u91CD\u590D\u4F20\uFF1B\u4E0D\u4F20=\u5168\u90E8\u5E97\u94FA\uFF09", collect2, []).option("--channel <channel>", "\u6E20\u9053\u7B5B\u9009: qianniu | jingdong | pinduoduo | douyin").option("--out <path>", "\u843D\u76D8\u539F\u59CB CSV \u8DEF\u5F84\uFF08\u7236\u76EE\u5F55\u81EA\u52A8 mkdir -p\uFF09").action(async (opts) => {
|
|
1667
1788
|
try {
|
|
1668
1789
|
const result = await exportShopData({
|
|
1669
1790
|
startDate: opts.start,
|
|
@@ -7470,6 +7591,7 @@ registerMediaCommand(program);
|
|
|
7470
7591
|
registerKnowledgeCommand(program);
|
|
7471
7592
|
registerActivityCommand(program);
|
|
7472
7593
|
registerIssueCommand(program);
|
|
7594
|
+
registerChannelMetricCommand(program);
|
|
7473
7595
|
registerChatHistoryCommand(program);
|
|
7474
7596
|
registerFaqCommand(program);
|
|
7475
7597
|
registerConversationCommand(program);
|