@bty/customer-service-cli 0.4.8 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/README.md +103 -4
  3. package/dist/bin.js +905 -74
  4. package/package.json +3 -2
package/dist/bin.js CHANGED
@@ -180,10 +180,10 @@ function readCache() {
180
180
  return null;
181
181
  }
182
182
  }
183
- function writeCache(cache) {
183
+ function writeCache(cache3) {
184
184
  const dir = getConfigDir();
185
185
  fs2.mkdirSync(dir, { recursive: true });
186
- fs2.writeFileSync(getCachePath(), JSON.stringify(cache, null, 2));
186
+ fs2.writeFileSync(getCachePath(), JSON.stringify(cache3, null, 2));
187
187
  }
188
188
  function compareSemver(current, latest) {
189
189
  const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
@@ -235,10 +235,10 @@ async function checkForUpdate(currentVersion) {
235
235
  return null;
236
236
  }
237
237
  try {
238
- const cache = readCache();
238
+ const cache3 = readCache();
239
239
  const now = Date.now();
240
- if (cache && now - cache.lastCheckedAt < CHECK_INTERVAL_MS) {
241
- return compareSemver(currentVersion, cache.latestVersion) < 0 ? formatUpdateMessage(currentVersion, cache.latestVersion) : null;
240
+ if (cache3 && now - cache3.lastCheckedAt < CHECK_INTERVAL_MS) {
241
+ return compareSemver(currentVersion, cache3.latestVersion) < 0 ? formatUpdateMessage(currentVersion, cache3.latestVersion) : null;
242
242
  }
243
243
  const latestVersion = await fetchLatestVersion();
244
244
  writeCache({ latestVersion, lastCheckedAt: now });
@@ -437,6 +437,7 @@ function isTokenExpired(expiresAt) {
437
437
  var DEFAULT_CS_API_URL = "https://customer-servhub-api.betteryeah.com";
438
438
  var DEFAULT_AI_API_URL = "https://ai-api.betteryeah.com";
439
439
  var DEFAULT_CUSTOMER_AGENT_API_URL = "https://customer-agent.bantouyan.com";
440
+ var DEFAULT_CS_ADMIN_URL = "https://customer-service-admin.betteryeah.com";
440
441
  function getCustomerServiceUrl() {
441
442
  const envUrl = process.env.CS_CS_API_URL;
442
443
  if (envUrl) return envUrl;
@@ -455,6 +456,12 @@ function getCustomerAgentUrl() {
455
456
  const config = readConfig();
456
457
  return config?.customerAgentApiUrl ?? DEFAULT_CUSTOMER_AGENT_API_URL;
457
458
  }
459
+ function getCsAdminUrl() {
460
+ const envUrl = process.env.CS_CS_ADMIN_URL;
461
+ if (envUrl) return envUrl;
462
+ const config = readConfig();
463
+ return config?.csAdminUrl ?? DEFAULT_CS_ADMIN_URL;
464
+ }
458
465
  function getWorkspaceId(overrideWorkspaceId) {
459
466
  const lockState = readEnvLockState();
460
467
  if (lockState.workspaceId) {
@@ -1157,6 +1164,293 @@ function registerConversationCommand(program2) {
1157
1164
  });
1158
1165
  }
1159
1166
 
1167
+ // src/client/dashboard-api.ts
1168
+ var DASHBOARD_PREFIX = "/v1/dashboard";
1169
+ async function getDashboardSummary(opts) {
1170
+ const request = createRequest();
1171
+ const path5 = opts.configId ? `${DASHBOARD_PREFIX}/${opts.configId}/summary` : `${DASHBOARD_PREFIX}/summary`;
1172
+ const query = {};
1173
+ if (opts.startDate) query.start_date = opts.startDate;
1174
+ if (opts.endDate) query.end_date = opts.endDate;
1175
+ if (opts.channel) query.channel = opts.channel;
1176
+ return request(getCustomerServiceUrl(), path5, { method: "GET", query });
1177
+ }
1178
+ async function getDashboardTrend(opts) {
1179
+ const request = createRequest();
1180
+ const path5 = opts.configId ? `${DASHBOARD_PREFIX}/${opts.configId}/trend` : `${DASHBOARD_PREFIX}/trend`;
1181
+ const query = {};
1182
+ if (opts.startDate) query.start_date = opts.startDate;
1183
+ if (opts.endDate) query.end_date = opts.endDate;
1184
+ if (opts.granularity) query.granularity = opts.granularity;
1185
+ if (opts.channel) query.channel = opts.channel;
1186
+ return request(getCustomerServiceUrl(), path5, { method: "GET", query });
1187
+ }
1188
+ async function listShopStatistics(opts) {
1189
+ const request = createRequest();
1190
+ const query = {};
1191
+ if (opts.startDate) query.start_date = opts.startDate;
1192
+ if (opts.endDate) query.end_date = opts.endDate;
1193
+ if (opts.channel) query.channel = opts.channel;
1194
+ return request(getCustomerServiceUrl(), `${DASHBOARD_PREFIX}/shop-statistics`, {
1195
+ method: "GET",
1196
+ query
1197
+ });
1198
+ }
1199
+ async function getIntentStatistics(opts) {
1200
+ const request = createRequest();
1201
+ const query = {};
1202
+ if (opts.startDate) query.start_date = opts.startDate;
1203
+ if (opts.endDate) query.end_date = opts.endDate;
1204
+ return request(
1205
+ getCustomerServiceUrl(),
1206
+ `${DASHBOARD_PREFIX}/${opts.configId}/intent-statistics`,
1207
+ { method: "GET", query }
1208
+ );
1209
+ }
1210
+ async function listIntentConversations(opts) {
1211
+ const request = createRequest();
1212
+ const query = {
1213
+ event_one: opts.eventOne,
1214
+ event_two: opts.eventTwo
1215
+ };
1216
+ if (opts.startDate) query.start_date = opts.startDate;
1217
+ if (opts.endDate) query.end_date = opts.endDate;
1218
+ if (opts.page) query.page = opts.page;
1219
+ if (opts.pageSize) query.page_size = opts.pageSize;
1220
+ return request(
1221
+ getCustomerServiceUrl(),
1222
+ `${DASHBOARD_PREFIX}/${opts.configId}/intent-conversations`,
1223
+ { method: "GET", query }
1224
+ );
1225
+ }
1226
+ var EXPORT_HEADER_MAP = {
1227
+ \u65E5\u671F: "date",
1228
+ \u5E97\u94FA\u540D\u79F0: "shop_name",
1229
+ \u6E20\u9053: "channel",
1230
+ \u63A5\u5F85\u5BA2\u6237\u6570: "customers",
1231
+ AI\u72EC\u7ACB\u627F\u63A5\u5BA2\u6237\u6570: "ai_independent_customers",
1232
+ AI\u72EC\u7ACB\u627F\u63A5\u7387: "ai_independent_rate",
1233
+ \u8F6C\u4EBA\u5DE5\u5BA2\u6237\u6570: "transfer_customers",
1234
+ \u8F6C\u4EBA\u5DE5\u7387: "transfer_rate",
1235
+ AI\u56DE\u590D\u6D88\u606F\u6570: "ai_reply_messages"
1236
+ };
1237
+ var EXPORT_NUMERIC_KEYS = /* @__PURE__ */ new Set([
1238
+ "customers",
1239
+ "ai_independent_customers",
1240
+ "transfer_customers",
1241
+ "ai_reply_messages"
1242
+ ]);
1243
+ function parseCsv(text) {
1244
+ const s = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
1245
+ const rows = [];
1246
+ let row = [];
1247
+ let field = "";
1248
+ let inQuotes = false;
1249
+ for (let i = 0; i < s.length; i++) {
1250
+ const c = s[i];
1251
+ if (inQuotes) {
1252
+ if (c === '"') {
1253
+ if (s[i + 1] === '"') {
1254
+ field += '"';
1255
+ i++;
1256
+ } else {
1257
+ inQuotes = false;
1258
+ }
1259
+ } else {
1260
+ field += c;
1261
+ }
1262
+ } else if (c === '"') {
1263
+ inQuotes = true;
1264
+ } else if (c === ",") {
1265
+ row.push(field);
1266
+ field = "";
1267
+ } else if (c === "\n") {
1268
+ row.push(field);
1269
+ rows.push(row);
1270
+ row = [];
1271
+ field = "";
1272
+ } else if (c !== "\r") {
1273
+ field += c;
1274
+ }
1275
+ }
1276
+ if (field.length > 0 || row.length > 0) {
1277
+ row.push(field);
1278
+ rows.push(row);
1279
+ }
1280
+ return rows;
1281
+ }
1282
+ function csvToRows(csv) {
1283
+ const table = parseCsv(csv).filter((r) => r.some((cell) => cell.trim() !== ""));
1284
+ if (table.length === 0) return [];
1285
+ const headers = table[0].map((h) => EXPORT_HEADER_MAP[h.trim()] ?? h.trim());
1286
+ return table.slice(1).map((cells) => {
1287
+ const obj = {};
1288
+ headers.forEach((key, idx) => {
1289
+ const raw = cells[idx] ?? "";
1290
+ if (EXPORT_NUMERIC_KEYS.has(key)) {
1291
+ const n = Number(raw);
1292
+ obj[key] = Number.isNaN(n) ? raw : n;
1293
+ } else {
1294
+ obj[key] = raw;
1295
+ }
1296
+ });
1297
+ return obj;
1298
+ });
1299
+ }
1300
+ async function exportShopData(opts) {
1301
+ const request = createRequest();
1302
+ const body = {
1303
+ start_date: opts.startDate,
1304
+ end_date: opts.endDate
1305
+ };
1306
+ if (opts.configIds && opts.configIds.length > 0) body.config_ids = opts.configIds;
1307
+ if (opts.channel) body.channel = opts.channel;
1308
+ const data = await request(
1309
+ getCustomerServiceUrl(),
1310
+ `${DASHBOARD_PREFIX}/export`,
1311
+ { method: "POST", body }
1312
+ );
1313
+ const downloadUrl = data?.download_url;
1314
+ if (!downloadUrl) {
1315
+ throw new APIError(1, "\u5BFC\u51FA\u63A5\u53E3\u672A\u8FD4\u56DE download_url");
1316
+ }
1317
+ const timeoutMs = getRuntimeRequestTimeoutMs() ?? 6e4;
1318
+ const resp = await fetch(downloadUrl, { signal: AbortSignal.timeout(timeoutMs) });
1319
+ if (!resp.ok) {
1320
+ throw new APIError(resp.status, `\u4E0B\u8F7D\u5BFC\u51FA CSV \u5931\u8D25 HTTP ${resp.status}`);
1321
+ }
1322
+ const csv = await resp.text();
1323
+ return { downloadUrl, csv, rows: csvToRows(csv) };
1324
+ }
1325
+
1326
+ // src/utils/file-output.ts
1327
+ import fs5 from "fs";
1328
+ import path4 from "path";
1329
+ async function writeBinaryToFile(filePath, buffer) {
1330
+ try {
1331
+ const dir = path4.dirname(filePath);
1332
+ fs5.mkdirSync(dir, { recursive: true });
1333
+ fs5.writeFileSync(filePath, buffer);
1334
+ } catch (err) {
1335
+ const msg = err instanceof Error ? err.message : String(err);
1336
+ throw new APIError(1, `\u6587\u4EF6\u5199\u5165\u5931\u8D25: ${msg}`);
1337
+ }
1338
+ return { path: filePath, bytes: buffer.length };
1339
+ }
1340
+
1341
+ // src/commands/dashboard.ts
1342
+ function collect(value, previous) {
1343
+ return previous.concat([value]);
1344
+ }
1345
+ function registerDashboardCommand(program2) {
1346
+ const dashboard = program2.command("dashboard").description(
1347
+ "\u6570\u636E\u770B\u677F \u2014\u2014 \u8F6C\u4EBA\u5DE5\u7387/\u63A5\u5F85\u91CF/AI\u56DE\u590D\u91CF\u7B49\u8FD0\u8425\u6307\u6807\uFF08\u6570\u636E T+1\uFF0Cend \u2264 \u6628\u5929\uFF1Bstart/end \u4E3A\u95ED\u533A\u95F4\uFF09\u3002\u53EA\u4EA7\u7ED3\u6784\u5316\u6570\u636E\uFF0C\u62A5\u544A\u7531\u4E0A\u5C42\u6309\u573A\u666F\u52A0\u5DE5"
1348
+ );
1349
+ dashboard.command("summary").description(
1350
+ "\u5168\u5C40\u6216\u5355\u5E97\u94FA KPI \u6C47\u603B + \u73AF\u6BD4\uFF08\u63A5\u5F85\u5BA2\u6237\u6570\u3001\u8F6C\u4EBA\u5DE5\u7387\u3001AI\u72EC\u7ACB\u627F\u63A5\u7387\uFF09\u3002\u4F20 --config-id \u67E5\u5355\u5E97"
1351
+ ).option("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u4E0D\u4F20=\u5168\u5E97\u94FA\u805A\u5408\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5").option("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--channel <channel>", "\u6E20\u9053\u7B5B\u9009: qianniu | jingdong | pinduoduo | douyin").action(async (opts) => {
1352
+ try {
1353
+ const data = await getDashboardSummary({
1354
+ configId: opts.configId,
1355
+ startDate: opts.start,
1356
+ endDate: opts.end,
1357
+ channel: opts.channel
1358
+ });
1359
+ formatOutput({ success: true, data }, program2.opts().table);
1360
+ } catch (err) {
1361
+ reportCaughtError(err);
1362
+ process.exit(toExitCode(err));
1363
+ }
1364
+ });
1365
+ dashboard.command("trend").description(
1366
+ "\u5168\u5C40\u6216\u5355\u5E97\u94FA\u8D8B\u52BF\uFF08\u6BCF\u65E5/\u5468/\u6708\u63A5\u5F85\u5BA2\u6237\u6570\u3001\u8F6C\u4EBA\u5DE5\u6570\u3001\u8F6C\u4EBA\u5DE5\u7387\uFF09\u3002\u4F20 --config-id \u67E5\u5355\u5E97"
1367
+ ).option("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u4E0D\u4F20=\u5168\u5E97\u94FA\u805A\u5408\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5").option("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--granularity <g>", "\u7C92\u5EA6: day | week | month", "day").option("--channel <channel>", "\u6E20\u9053\u7B5B\u9009: qianniu | jingdong | pinduoduo | douyin").action(async (opts) => {
1368
+ try {
1369
+ const data = await getDashboardTrend({
1370
+ configId: opts.configId,
1371
+ startDate: opts.start,
1372
+ endDate: opts.end,
1373
+ granularity: opts.granularity,
1374
+ channel: opts.channel
1375
+ });
1376
+ formatOutput({ success: true, data }, program2.opts().table);
1377
+ } catch (err) {
1378
+ reportCaughtError(err);
1379
+ process.exit(toExitCode(err));
1380
+ }
1381
+ });
1382
+ dashboard.command("shops").description("\u5168\u90E8\u5E97\u94FA\u7EDF\u8BA1\u5217\u8868\uFF08\u9ED8\u8BA4\u6309\u8F6C\u4EBA\u5DE5\u5BA2\u6237\u6570\u964D\u5E8F\uFF09\uFF0C\u7528\u4E8E\u6A2A\u5411\u5BF9\u6BD4").option("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5").option("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").option("--channel <channel>", "\u6E20\u9053\u7B5B\u9009: qianniu | jingdong | pinduoduo | douyin").action(async (opts) => {
1383
+ try {
1384
+ const data = await listShopStatistics({
1385
+ startDate: opts.start,
1386
+ endDate: opts.end,
1387
+ channel: opts.channel
1388
+ });
1389
+ formatOutput({ success: true, data }, program2.opts().table);
1390
+ } catch (err) {
1391
+ reportCaughtError(err);
1392
+ process.exit(toExitCode(err));
1393
+ }
1394
+ });
1395
+ dashboard.command("export").description(
1396
+ "\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"
1397
+ ).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", collect, []).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) => {
1398
+ try {
1399
+ const result = await exportShopData({
1400
+ startDate: opts.start,
1401
+ endDate: opts.end,
1402
+ configIds: opts.configId,
1403
+ channel: opts.channel
1404
+ });
1405
+ if (opts.out) {
1406
+ const { path: path5, bytes } = await writeBinaryToFile(
1407
+ opts.out,
1408
+ Buffer.from(result.csv, "utf-8")
1409
+ );
1410
+ formatOutput(
1411
+ { success: true, data: { path: path5, bytes, count: result.rows.length } },
1412
+ program2.opts().table
1413
+ );
1414
+ } else {
1415
+ formatOutput({ success: true, data: result.rows }, program2.opts().table);
1416
+ }
1417
+ } catch (err) {
1418
+ reportCaughtError(err);
1419
+ process.exit(toExitCode(err));
1420
+ }
1421
+ });
1422
+ dashboard.command("intent").description("\u5355\u5E97\u94FA\u5404\u610F\u56FE\uFF08\u4E00\u7EA7/\u4E8C\u7EA7\u6807\u7B7E\uFF09\u7684\u6D88\u606F\u7EA7\u8F6C\u4EBA\u5DE5\u7EDF\u8BA1\uFF0C\u9ED8\u8BA4\u6309\u8F6C\u4EBA\u5DE5\u6D88\u606F\u6570\u964D\u5E8F").requiredOption("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u5FC5\u586B\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5").option("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)\uFF0C\u9ED8\u8BA4\u6628\u65E5\uFF0C\u4E0D\u80FD\u665A\u4E8E\u6628\u5929").action(async (opts) => {
1423
+ try {
1424
+ const data = await getIntentStatistics({
1425
+ configId: opts.configId,
1426
+ startDate: opts.start,
1427
+ endDate: opts.end
1428
+ });
1429
+ formatOutput({ success: true, data }, program2.opts().table);
1430
+ } catch (err) {
1431
+ reportCaughtError(err);
1432
+ process.exit(toExitCode(err));
1433
+ }
1434
+ });
1435
+ dashboard.command("intent-conversations").description("\u67D0\u610F\u56FE\u4E0B\u7684\u4F1A\u8BDD\u5217\u8868\u4E0B\u94BB\uFF08\u542B\u8BE5\u610F\u56FE\u5728\u4F1A\u8BDD\u4E2D\u7684\u8F6C\u4EBA\u5DE5\u6B21\u6570\uFF09").requiredOption("--config-id <id>", "\u5E97\u94FA\u914D\u7F6E ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--event-one <label>", "\u4E00\u7EA7\u610F\u56FE\uFF08\u5FC5\u586B\uFF09").requiredOption("--event-two <label>", "\u4E8C\u7EA7\u610F\u56FE\uFF08\u5FC5\u586B\uFF09").option("--start <date>", "\u5F00\u59CB\u65E5\u671F (YYYY-MM-DD)").option("--end <date>", "\u7ED3\u675F\u65E5\u671F (YYYY-MM-DD)").option("--page <number>", "\u9875\u7801", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF\uFF08\u6700\u5927 100\uFF09", "20").action(async (opts) => {
1436
+ try {
1437
+ const data = await listIntentConversations({
1438
+ configId: opts.configId,
1439
+ eventOne: opts.eventOne,
1440
+ eventTwo: opts.eventTwo,
1441
+ startDate: opts.start,
1442
+ endDate: opts.end,
1443
+ page: Number(opts.page),
1444
+ pageSize: Number(opts.pageSize)
1445
+ });
1446
+ formatOutput({ success: true, data }, program2.opts().table);
1447
+ } catch (err) {
1448
+ reportCaughtError(err);
1449
+ process.exit(toExitCode(err));
1450
+ }
1451
+ });
1452
+ }
1453
+
1160
1454
  // src/client/debug-api.ts
1161
1455
  var DEFAULT_FLOW_WORKSPACE_ID = "531c14d1ece047cbaec22914e1f1364d";
1162
1456
  async function createDebugConversation(opts) {
@@ -1784,7 +2078,387 @@ async function getIssueStats(opts) {
1784
2078
  return result.data ?? result;
1785
2079
  }
1786
2080
 
2081
+ // src/client/workbench-issues-api.ts
2082
+ async function batchReassignOwner(params) {
2083
+ const request = createRequest();
2084
+ return request(getCsAdminUrl(), "/api/workbench/issues/batch-owner", {
2085
+ method: "POST",
2086
+ body: { issueIds: params.issueIds, ownerUserId: params.ownerUserId }
2087
+ });
2088
+ }
2089
+
2090
+ // src/utils/batch-input.ts
2091
+ import fs6 from "fs";
2092
+ function parseIdsInput(opts) {
2093
+ const hasIds = opts.ids !== void 0 && opts.ids.trim() !== "";
2094
+ const hasFile = opts.idsFile !== void 0 && opts.idsFile.trim() !== "";
2095
+ if (hasIds && hasFile) {
2096
+ throw new Error("--ids \u4E0E --ids-file \u4E92\u65A5\uFF0C\u8BF7\u53EA\u63D0\u4F9B\u5176\u4E00");
2097
+ }
2098
+ if (!hasIds && !hasFile) {
2099
+ throw new Error("\u9700\u8981\u63D0\u4F9B --ids \u6216 --ids-file \u4E4B\u4E00");
2100
+ }
2101
+ let raw;
2102
+ if (hasIds) {
2103
+ raw = opts.ids.split(",").map((s) => s.trim()).filter(Boolean);
2104
+ } else {
2105
+ const filePath = opts.idsFile;
2106
+ let content;
2107
+ try {
2108
+ content = fs6.readFileSync(filePath, "utf8");
2109
+ } catch (err) {
2110
+ const msg = err instanceof Error ? err.message : String(err);
2111
+ throw new Error(`\u8BFB\u53D6 --ids-file \u5931\u8D25: ${msg}`);
2112
+ }
2113
+ raw = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
2114
+ }
2115
+ if (raw.length === 0) {
2116
+ throw new Error("--ids / --ids-file \u672A\u89E3\u6790\u5230\u4EFB\u4F55 id");
2117
+ }
2118
+ const seen = /* @__PURE__ */ new Set();
2119
+ const ids = [];
2120
+ for (const id of raw) {
2121
+ if (!seen.has(id)) {
2122
+ seen.add(id);
2123
+ ids.push(id);
2124
+ }
2125
+ }
2126
+ return ids;
2127
+ }
2128
+
2129
+ // src/utils/issue-batch-input.ts
2130
+ import fs7 from "fs";
2131
+ var PRIORITY_VALUES = ["critical", "high", "medium", "low"];
2132
+ function readSource(opts) {
2133
+ const hasFile = typeof opts.file === "string" && opts.file.trim() !== "";
2134
+ const hasStdin = typeof opts.stdin === "string";
2135
+ if (hasFile && hasStdin) {
2136
+ throw new Error("--file \u4E0E --stdin \u4E92\u65A5\uFF0C\u8BF7\u53EA\u63D0\u4F9B\u5176\u4E00");
2137
+ }
2138
+ if (!hasFile && !hasStdin) {
2139
+ throw new Error("\u9700\u8981\u63D0\u4F9B --file <path> \u6216\u901A\u8FC7 stdin \u4F20\u5165 JSON");
2140
+ }
2141
+ if (hasFile) {
2142
+ try {
2143
+ return fs7.readFileSync(opts.file, "utf8");
2144
+ } catch (err) {
2145
+ const msg = err instanceof Error ? err.message : String(err);
2146
+ throw new Error(`\u8BFB\u53D6 --file \u5931\u8D25: ${msg}`);
2147
+ }
2148
+ }
2149
+ return opts.stdin;
2150
+ }
2151
+ function parseIssueBatchInput(opts) {
2152
+ const raw = readSource(opts).trim();
2153
+ if (!raw) {
2154
+ throw new Error("\u8F93\u5165 JSON \u4E3A\u7A7A");
2155
+ }
2156
+ let parsed;
2157
+ try {
2158
+ parsed = JSON.parse(raw);
2159
+ } catch (err) {
2160
+ const msg = err instanceof Error ? err.message : String(err);
2161
+ throw new Error(`JSON \u89E3\u6790\u5931\u8D25: ${msg}`);
2162
+ }
2163
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
2164
+ throw new Error("\u9876\u5C42\u5FC5\u987B\u662F JSON object\uFF08\u542B version + issues[]\uFF09");
2165
+ }
2166
+ const root = parsed;
2167
+ const version2 = typeof root.version === "string" ? root.version : "1";
2168
+ const issuesRaw = root.issues;
2169
+ if (!Array.isArray(issuesRaw)) {
2170
+ throw new Error("\u9876\u5C42\u7F3A\u5C11\u6570\u7EC4\u5B57\u6BB5 issues[]");
2171
+ }
2172
+ if (issuesRaw.length === 0) {
2173
+ throw new Error("issues[] \u4E3A\u7A7A\uFF0C\u65E0\u53EF\u521B\u5EFA");
2174
+ }
2175
+ const workspaceId = typeof root.workspace_id === "string" ? root.workspace_id : void 0;
2176
+ const issues = issuesRaw.map((it, idx) => validateIssue(it, idx));
2177
+ return { version: version2, workspace_id: workspaceId, issues };
2178
+ }
2179
+ function validateIssue(value, idx) {
2180
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2181
+ throw new Error(`issues[${idx}] \u4E0D\u662F object`);
2182
+ }
2183
+ const v = value;
2184
+ const where = `issues[${idx}]`;
2185
+ const title = requireString(v.title, `${where}.title`);
2186
+ const priority = requirePriority(v.priority, `${where}.priority`);
2187
+ const expected = requireString(v.expected, `${where}.expected`);
2188
+ const conversation_id = optionalString(v.conversation_id, `${where}.conversation_id`);
2189
+ const user_name = optionalString(v.user_name, `${where}.user_name`);
2190
+ if (!conversation_id && !user_name) {
2191
+ throw new Error(`${where} \u5FC5\u987B\u63D0\u4F9B conversation_id \u6216 user_name \u4E4B\u4E00`);
2192
+ }
2193
+ const description = optionalString(v.description, `${where}.description`);
2194
+ let tags;
2195
+ if (v.tags !== void 0) {
2196
+ if (!Array.isArray(v.tags) || !v.tags.every((t) => typeof t === "string")) {
2197
+ throw new Error(`${where}.tags \u5FC5\u987B\u662F string[]`);
2198
+ }
2199
+ tags = v.tags;
2200
+ }
2201
+ const source_type = optionalString(v.source_type, `${where}.source_type`);
2202
+ let source_data;
2203
+ if (v.source_data !== void 0) {
2204
+ if (!v.source_data || typeof v.source_data !== "object" || Array.isArray(v.source_data)) {
2205
+ throw new Error(`${where}.source_data \u5FC5\u987B\u662F object`);
2206
+ }
2207
+ source_data = v.source_data;
2208
+ }
2209
+ return {
2210
+ title,
2211
+ priority,
2212
+ expected,
2213
+ description,
2214
+ conversation_id,
2215
+ user_name,
2216
+ tags,
2217
+ source_type,
2218
+ source_data
2219
+ };
2220
+ }
2221
+ function requireString(v, where) {
2222
+ if (typeof v !== "string" || v.trim() === "") {
2223
+ throw new Error(`${where} \u5FC5\u586B\uFF0C\u4E14\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
2224
+ }
2225
+ return v;
2226
+ }
2227
+ function optionalString(v, where) {
2228
+ if (v === void 0 || v === null) return void 0;
2229
+ if (typeof v !== "string") {
2230
+ throw new Error(`${where} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
2231
+ }
2232
+ return v.trim() === "" ? void 0 : v;
2233
+ }
2234
+ function requirePriority(v, where) {
2235
+ if (typeof v !== "string" || !PRIORITY_VALUES.includes(v)) {
2236
+ throw new Error(
2237
+ `${where} \u5FC5\u987B\u662F ${PRIORITY_VALUES.join("|")} \u4E4B\u4E00\uFF0C\u6536\u5230: ${JSON.stringify(v)}`
2238
+ );
2239
+ }
2240
+ return v;
2241
+ }
2242
+ function buildContent(issue) {
2243
+ const head = issue.description?.trim();
2244
+ if (head) {
2245
+ return `${head}
2246
+
2247
+ ## Expected
2248
+ ${issue.expected}`;
2249
+ }
2250
+ return `## Expected
2251
+ ${issue.expected}`;
2252
+ }
2253
+
2254
+ // src/client/account-roles-api.ts
2255
+ async function listAccountRoles(opts = {}) {
2256
+ const request = createRequest();
2257
+ const query = {};
2258
+ if (opts.role) query.role = opts.role;
2259
+ if (opts.includeInactive) query.includeInactive = "1";
2260
+ const result = await request(getCsAdminUrl(), "/api/account-roles", {
2261
+ method: "GET",
2262
+ query
2263
+ });
2264
+ return result.rows ?? [];
2265
+ }
2266
+
2267
+ // src/utils/resolve-owner.ts
2268
+ var CACHE_TTL_MS = 5 * 60 * 1e3;
2269
+ var cache = null;
2270
+ async function getEngineers() {
2271
+ const now = Date.now();
2272
+ if (cache && cache.expireAt > now) return cache.engineers;
2273
+ const [ka, smb] = await Promise.all([
2274
+ listAccountRoles({ role: "KA_ENGINEER" }),
2275
+ listAccountRoles({ role: "SMB_ENGINEER" })
2276
+ ]);
2277
+ const merged = mergeAndDedupe(ka, smb);
2278
+ cache = { engineers: merged, expireAt: now + CACHE_TTL_MS };
2279
+ return merged;
2280
+ }
2281
+ function mergeAndDedupe(...lists) {
2282
+ const seen = /* @__PURE__ */ new Map();
2283
+ for (const list of lists) {
2284
+ for (const row of list) {
2285
+ const existing = seen.get(row.csAdminUserId);
2286
+ if (!existing) {
2287
+ seen.set(row.csAdminUserId, { ...row });
2288
+ } else {
2289
+ const roles = /* @__PURE__ */ new Set([...existing.roles, ...row.roles]);
2290
+ existing.roles = [...roles];
2291
+ }
2292
+ }
2293
+ }
2294
+ return [...seen.values()].sort((a, b) => a.engineerName.localeCompare(b.engineerName));
2295
+ }
2296
+ async function resolveOwner(input) {
2297
+ const trimmed = input.trim();
2298
+ if (!trimmed) throw new Error("--owner \u4E0D\u80FD\u4E3A\u7A7A");
2299
+ if (/^\d+$/.test(trimmed)) {
2300
+ if (trimmed.length === 11) {
2301
+ throw new Error("--owner \u4E0D\u518D\u63A5\u53D7\u624B\u673A\u53F7\uFF08\u5DF2\u5E9F\u5F03\uFF09\u3002\u8BF7\u4F20 user_id \u6216\u82B1\u540D");
2302
+ }
2303
+ const n = Number(trimmed);
2304
+ if (!Number.isInteger(n) || n <= 0) {
2305
+ throw new Error(`--owner \u6570\u5B57\u89E3\u6790\u5931\u8D25: ${input}`);
2306
+ }
2307
+ return n;
2308
+ }
2309
+ const engineers = await getEngineers();
2310
+ const exact = engineers.filter((e) => e.engineerName === trimmed);
2311
+ if (exact.length === 1) return exact[0].csAdminUserId;
2312
+ if (exact.length > 1) {
2313
+ const list = exact.map((e) => `${e.engineerName}(${e.csAdminUserId})`).join(", ");
2314
+ throw new Error(`--owner "${input}" \u540C\u540D\u591A\u4EBA\uFF1A${list}\u3002\u8BF7\u6539\u4F20 user_id`);
2315
+ }
2316
+ const partial = engineers.filter(
2317
+ (e) => e.engineerName.toLowerCase().includes(trimmed.toLowerCase())
2318
+ );
2319
+ if (partial.length === 1) return partial[0].csAdminUserId;
2320
+ if (partial.length > 1) {
2321
+ const list = partial.map((e) => `${e.engineerName}(${e.csAdminUserId})`).join(", ");
2322
+ throw new Error(`--owner "${input}" \u6A21\u7CCA\u5339\u914D\u591A\u4EBA\uFF1A${list}\u3002\u8BF7\u6539\u7528\u7CBE\u786E\u82B1\u540D\u6216 user_id`);
2323
+ }
2324
+ throw new Error(`--owner "${input}" \u672A\u627E\u5230\u5339\u914D\u7684\u5DE5\u7A0B\u5E08\uFF08\u5171 ${engineers.length} \u4EBA\u5728\u518C\uFF09`);
2325
+ }
2326
+
2327
+ // src/utils/resolve-session.ts
2328
+ var CACHE_TTL_MS2 = 5 * 60 * 1e3;
2329
+ var cache2 = /* @__PURE__ */ new Map();
2330
+ async function resolveSession(opts) {
2331
+ const userName = opts.userName.trim();
2332
+ if (!userName) throw new Error("user_name \u4E0D\u80FD\u4E3A\u7A7A");
2333
+ const cacheKey = `${opts.workspaceId ?? ""}|${userName}`;
2334
+ const now = Date.now();
2335
+ const hit = cache2.get(cacheKey);
2336
+ if (hit && hit.expireAt > now) return hit.conversationId;
2337
+ const resp = await listConversations({
2338
+ userName,
2339
+ workspaceId: opts.workspaceId,
2340
+ pageSize: opts.pageSize ?? 5
2341
+ });
2342
+ const list = (resp.list ?? []).filter((c) => typeof c.conversation_id === "string");
2343
+ if (list.length === 0) {
2344
+ throw new Error(`user_name "${userName}" \u672A\u5339\u914D\u5230\u4EFB\u4F55\u4F1A\u8BDD\uFF08NOT_FOUND\uFF09`);
2345
+ }
2346
+ if (list.length > 1) {
2347
+ const preview = list.slice(0, 3).map((c) => c.conversation_id).join(", ");
2348
+ throw new Error(
2349
+ `user_name "${userName}" \u547D\u4E2D ${list.length} \u6761\u4F1A\u8BDD\uFF08AMBIGUOUS\uFF09\uFF1A${preview}${list.length > 3 ? " ..." : ""}\u3002\u8BF7\u6539\u4F20 conversation_id`
2350
+ );
2351
+ }
2352
+ const conversationId = list[0].conversation_id;
2353
+ cache2.set(cacheKey, { conversationId, expireAt: now + CACHE_TTL_MS2 });
2354
+ return conversationId;
2355
+ }
2356
+
2357
+ // src/utils/run-batch.ts
2358
+ import readline from "readline";
2359
+ var DEFAULT_CONCURRENCY = 5;
2360
+ async function runBatch(opts) {
2361
+ const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);
2362
+ const results = [];
2363
+ let aborted = false;
2364
+ const sigintHandler = () => {
2365
+ if (!aborted) {
2366
+ aborted = true;
2367
+ outputInfo("\u6536\u5230 SIGINT\uFF0C\u7B49\u5F85\u98DE\u884C\u4E2D\u8BF7\u6C42\u7ED3\u675F\u540E\u9000\u51FA...");
2368
+ }
2369
+ };
2370
+ process.on("SIGINT", sigintHandler);
2371
+ try {
2372
+ for (let i = 0; i < opts.ids.length; i += concurrency) {
2373
+ if (aborted) break;
2374
+ const chunk = opts.ids.slice(i, i + concurrency);
2375
+ const settled = await Promise.all(
2376
+ chunk.map(async (id) => {
2377
+ try {
2378
+ return await opts.perItem(id);
2379
+ } catch (err) {
2380
+ return mapErrorToFailure(id, err);
2381
+ }
2382
+ })
2383
+ );
2384
+ results.push(...settled);
2385
+ }
2386
+ } finally {
2387
+ process.off("SIGINT", sigintHandler);
2388
+ }
2389
+ return buildReport(results, aborted);
2390
+ }
2391
+ function mapErrorToFailure(id, err) {
2392
+ if (err instanceof APIError) {
2393
+ return { status: "failed", id, code: `HTTP_${err.statusCode}`, msg: err.message };
2394
+ }
2395
+ const msg = err instanceof Error ? err.message : String(err);
2396
+ return { status: "failed", id, code: "INTERNAL", msg };
2397
+ }
2398
+ function buildReport(results, aborted) {
2399
+ const report = {
2400
+ version: "1",
2401
+ total: results.length,
2402
+ success_count: 0,
2403
+ failed_count: 0,
2404
+ skipped_count: 0,
2405
+ aborted,
2406
+ success_ids: [],
2407
+ failed: [],
2408
+ skipped: []
2409
+ };
2410
+ for (const r of results) {
2411
+ if (r.status === "success") {
2412
+ report.success_count++;
2413
+ report.success_ids.push(r.id);
2414
+ } else if (r.status === "failed") {
2415
+ report.failed_count++;
2416
+ report.failed.push({ id: r.id, code: r.code, msg: r.msg });
2417
+ } else {
2418
+ report.skipped_count++;
2419
+ report.skipped.push({ id: r.id, reason: r.reason });
2420
+ }
2421
+ }
2422
+ return report;
2423
+ }
2424
+ function reportToExitCode(report) {
2425
+ if (report.aborted) return 130;
2426
+ if (report.failed_count === 0) return 0;
2427
+ if (report.failed_count === report.total) return 1;
2428
+ return 2;
2429
+ }
2430
+ var DEFAULT_CONFIRM_THRESHOLD = 20;
2431
+ async function confirmBatch(opts) {
2432
+ const threshold = opts.threshold ?? DEFAULT_CONFIRM_THRESHOLD;
2433
+ if (opts.count < threshold) return;
2434
+ if (opts.yes) return;
2435
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
2436
+ throw new Error(`\u975E TTY \u73AF\u5883\u4E0B\u8981 ${opts.action} ${opts.count} \u6761\uFF0C\u9700\u663E\u5F0F --yes \u624D\u80FD\u6267\u884C`);
2437
+ }
2438
+ const rl = readline.createInterface({
2439
+ input: process.stdin,
2440
+ output: process.stderr
2441
+ });
2442
+ try {
2443
+ const answer = await new Promise((resolve) => {
2444
+ rl.question(`\u5373\u5C06${opts.action} ${opts.count} \u6761\u8BB0\u5F55\u3002\u7EE7\u7EED\uFF1F\u8F93\u5165 yes \u786E\u8BA4: `, resolve);
2445
+ });
2446
+ if (answer.trim().toLowerCase() !== "yes") {
2447
+ throw new Error("\u5DF2\u53D6\u6D88");
2448
+ }
2449
+ } finally {
2450
+ rl.close();
2451
+ }
2452
+ }
2453
+
1787
2454
  // src/commands/issue.ts
2455
+ async function readAllStdin() {
2456
+ const chunks = [];
2457
+ for await (const chunk of process.stdin) {
2458
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
2459
+ }
2460
+ return Buffer.concat(chunks).toString("utf8");
2461
+ }
1788
2462
  function registerIssueCommand(program2) {
1789
2463
  const issue = program2.command("issue").description(
1790
2464
  "\u5DE5\u5355\uFF08Issue\uFF09\u7BA1\u7406 \u2014\u2014 \u8BB0\u5F55 Agent \u56DE\u590D\u5F02\u5E38\u3001\u5BA2\u6237\u6295\u8BC9\u7B49\u5F85\u5904\u7406\u95EE\u9898\u3002\u5DE5\u5355\u662F AI \u4FEE\u590D\u6D41\u7A0B\uFF08issue-repair\uFF09\u7684\u8F93\u5165\u6E90"
@@ -1825,63 +2499,219 @@ function registerIssueCommand(program2) {
1825
2499
  process.exit(toExitCode(err));
1826
2500
  }
1827
2501
  });
1828
- issue.command("create").description("\u624B\u52A8\u521B\u5EFA\u5DE5\u5355").requiredOption("--title <title>", "\u5DE5\u5355\u6807\u9898\uFF08\u7B80\u8981\u63CF\u8FF0\u95EE\u9898\uFF09").requiredOption("--content <content>", "\u5DE5\u5355\u5185\u5BB9\uFF08\u8BE6\u7EC6\u63CF\u8FF0\u95EE\u9898\u73B0\u8C61\u548C\u671F\u671B\u7ED3\u679C\uFF09").option("--priority <priority>", "\u4F18\u5148\u7EA7: critical | high | medium | low").option("--tags <tags...>", "\u6807\u7B7E\uFF08\u591A\u4E2A\u7A7A\u683C\u5206\u9694\uFF0C\u7528\u4E8E\u5206\u7C7B\u7B5B\u9009\uFF09").option("--conversation-id <id>", "\u5173\u8054\u7684\u4F1A\u8BDD ID").option("--record-id <id>", "\u5173\u8054\u7684\u6D88\u606F\u8BB0\u5F55 ID").option("--workspace-id <id>", "\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u9ED8\u8BA4\u4ECE\u73AF\u5883\u53D8\u91CF\u8BFB\u53D6\uFF09").option("--source-type <type>", "\u6765\u6E90\u7C7B\u578B").option("--source-data <json>", "\u6765\u6E90\u6570\u636E\uFF08JSON \u5B57\u7B26\u4E32\uFF09").action(async (opts) => {
2502
+ issue.command("create").description(
2503
+ "\u521B\u5EFA\u5DE5\u5355\u3002\u5355\u6761\uFF1A--title + --content\uFF08+ \u53EF\u9009\u5B57\u6BB5\uFF09\u3002\u6279\u91CF\uFF1A--file <path> \u6216 --stdin \u8BFB JSON\uFF08\u542B workspace_id + issues[]\uFF0C\u6BCF\u6761\u5FC5\u586B title/priority/expected \u548C conversation_id|user_name \u4E8C\u9009\u4E00\uFF09\u3002\u6279\u91CF\u6A21\u5F0F N>20 \u4E14 TTY \u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF1B\u975E TTY \u5FC5\u987B\u663E\u5F0F --yes"
2504
+ ).option("--title <title>", "[\u5355\u6761] \u5DE5\u5355\u6807\u9898\uFF08\u7B80\u8981\u63CF\u8FF0\u95EE\u9898\uFF09").option("--content <content>", "[\u5355\u6761] \u5DE5\u5355\u5185\u5BB9\uFF08\u8BE6\u7EC6\u63CF\u8FF0\u95EE\u9898\u73B0\u8C61\u548C\u671F\u671B\u7ED3\u679C\uFF09").option("--priority <priority>", "[\u5355\u6761] \u4F18\u5148\u7EA7: critical | high | medium | low").option("--tags <tags...>", "[\u5355\u6761] \u6807\u7B7E\uFF08\u591A\u4E2A\u7A7A\u683C\u5206\u9694\uFF0C\u7528\u4E8E\u5206\u7C7B\u7B5B\u9009\uFF09").option("--conversation-id <id>", "[\u5355\u6761] \u5173\u8054\u7684\u4F1A\u8BDD ID").option("--record-id <id>", "[\u5355\u6761] \u5173\u8054\u7684\u6D88\u606F\u8BB0\u5F55 ID").option("--workspace-id <id>", "[\u5355\u6761] \u5DE5\u4F5C\u7A7A\u95F4 ID\uFF08\u9ED8\u8BA4\u4ECE\u73AF\u5883\u53D8\u91CF\u8BFB\u53D6\uFF09").option("--source-type <type>", "[\u5355\u6761] \u6765\u6E90\u7C7B\u578B").option("--source-data <json>", "[\u5355\u6761] \u6765\u6E90\u6570\u636E\uFF08JSON \u5B57\u7B26\u4E32\uFF09").option("--file <path>", "[\u6279\u91CF] \u4ECE JSON \u6587\u4EF6\u8BFB\u8F93\u5165\uFF08\u4E0E --stdin \u4E92\u65A5\uFF09").option("--stdin", "[\u6279\u91CF] \u4ECE stdin \u8BFB JSON\uFF08\u4E0E --file \u4E92\u65A5\uFF09", false).option("--dry-run", "[\u6279\u91CF] \u9884\u89C8\uFF1A\u5B8C\u6210 user_name \u53CD\u67E5 + content \u62FC\u88C5\uFF0C\u4F46\u4E0D\u8C03\u521B\u5EFA\u63A5\u53E3", false).option("--concurrency <n>", "[\u6279\u91CF] \u5E76\u53D1\u6570\uFF08\u9ED8\u8BA4 5\uFF09", "5").option("--yes", "[\u6279\u91CF] \u8DF3\u8FC7 N>=20 \u4E8C\u6B21\u786E\u8BA4\uFF08\u975E TTY \u5FC5\u987B\u663E\u5F0F\u4F20\uFF09", false).action(async (opts) => {
2505
+ const hasBatch = Boolean(opts.file) || opts.stdin === true;
2506
+ const hasSingleLike = Boolean(opts.title || opts.content);
2507
+ if (hasBatch && hasSingleLike) {
2508
+ reportCaughtError(
2509
+ new Error("\u4E0D\u80FD\u540C\u65F6\u63D0\u4F9B\u5355\u6761\u5B57\u6BB5\uFF08--title/--content\uFF09\u548C\u6279\u91CF\u5B57\u6BB5\uFF08--file/--stdin\uFF09")
2510
+ );
2511
+ process.exit(1);
2512
+ return;
2513
+ }
2514
+ if (!hasBatch) {
2515
+ try {
2516
+ if (!opts.title || !opts.content) {
2517
+ throw new Error(
2518
+ "\u5355\u6761\u521B\u5EFA\u9700\u63D0\u4F9B --title \u548C --content\uFF08\u6216\u6539\u7528 --file/--stdin \u6279\u91CF\u6A21\u5F0F\uFF09"
2519
+ );
2520
+ }
2521
+ const data = await createIssue({
2522
+ title: opts.title,
2523
+ content: opts.content,
2524
+ priority: opts.priority,
2525
+ tags: opts.tags,
2526
+ conversation_id: opts.conversationId,
2527
+ record_id: opts.recordId,
2528
+ workspace_id: opts.workspaceId,
2529
+ source_type: opts.sourceType,
2530
+ source_data: opts.sourceData ? JSON.parse(opts.sourceData) : void 0
2531
+ });
2532
+ formatOutput({ success: true, data }, program2.opts().table);
2533
+ } catch (err) {
2534
+ reportCaughtError(err);
2535
+ process.exit(toExitCode(err));
2536
+ }
2537
+ return;
2538
+ }
2539
+ let batchReport = null;
1829
2540
  try {
1830
- const data = await createIssue({
1831
- title: opts.title,
1832
- content: opts.content,
1833
- priority: opts.priority,
1834
- tags: opts.tags,
1835
- conversation_id: opts.conversationId,
1836
- record_id: opts.recordId,
1837
- workspace_id: opts.workspaceId,
1838
- source_type: opts.sourceType,
1839
- source_data: opts.sourceData ? JSON.parse(opts.sourceData) : void 0
2541
+ const stdinContent = opts.stdin ? await readAllStdin() : void 0;
2542
+ const parsed = parseIssueBatchInput({ file: opts.file, stdin: stdinContent });
2543
+ const workspaceId = parsed.workspace_id;
2544
+ const resolved = await resolveSessionsForIssues(parsed.issues, workspaceId);
2545
+ if (opts.dryRun) {
2546
+ formatOutput(
2547
+ {
2548
+ success: true,
2549
+ data: {
2550
+ dry_run: true,
2551
+ version: parsed.version,
2552
+ workspace_id: workspaceId,
2553
+ total: parsed.issues.length,
2554
+ will_create: resolved.map((r) => ({
2555
+ title: r.issue.title,
2556
+ priority: r.issue.priority,
2557
+ conversation_id: r.conversationId,
2558
+ tags: r.issue.tags,
2559
+ resolved_from_user_name: r.issue.conversation_id ? void 0 : r.issue.user_name,
2560
+ preview_content: buildContent(r.issue)
2561
+ }))
2562
+ }
2563
+ },
2564
+ program2.opts().table
2565
+ );
2566
+ return;
2567
+ }
2568
+ await confirmBatch({ count: parsed.issues.length, action: "\u521B\u5EFA", yes: opts.yes });
2569
+ const concurrency = Number(opts.concurrency);
2570
+ if (!Number.isFinite(concurrency) || concurrency < 1) {
2571
+ throw new Error(`--concurrency \u5FC5\u987B\u4E3A\u6B63\u6574\u6570\uFF0C\u6536\u5230: ${opts.concurrency}`);
2572
+ }
2573
+ const ids = parsed.issues.map((_, idx) => `idx-${idx}`);
2574
+ const indexed = new Map(resolved.map((r, idx) => [`idx-${idx}`, r]));
2575
+ batchReport = await runBatch({
2576
+ ids,
2577
+ concurrency,
2578
+ perItem: async (id) => {
2579
+ const r = indexed.get(id);
2580
+ if (!r) throw new Error(`internal: missing resolved entry for ${id}`);
2581
+ const data = await createIssue({
2582
+ title: r.issue.title,
2583
+ content: buildContent(r.issue),
2584
+ priority: r.issue.priority,
2585
+ tags: r.issue.tags,
2586
+ conversation_id: r.conversationId,
2587
+ workspace_id: workspaceId,
2588
+ source_type: r.issue.source_type,
2589
+ source_data: r.issue.source_data
2590
+ });
2591
+ return { status: "success", id, data };
2592
+ }
1840
2593
  });
1841
- formatOutput({ success: true, data }, program2.opts().table);
2594
+ formatOutput({ success: true, data: batchReport }, program2.opts().table);
1842
2595
  } catch (err) {
1843
2596
  reportCaughtError(err);
1844
2597
  process.exit(toExitCode(err));
2598
+ return;
2599
+ }
2600
+ if (batchReport !== null) {
2601
+ const code = reportToExitCode(batchReport);
2602
+ if (code !== 0) process.exit(code);
1845
2603
  }
1846
2604
  });
1847
- issue.command("update").description("\u66F4\u65B0\u5DE5\u5355\u5B57\u6BB5\uFF08\u5982 status\u3001priority\u3001tags\u3001title\u3001content\uFF09").argument("<issue_id>", "\u5DE5\u5355 ID\uFF08\u4ECE issue list \u83B7\u53D6\uFF09").requiredOption(
2605
+ issue.command("update").description(
2606
+ "\u66F4\u65B0\u5DE5\u5355\u5B57\u6BB5\uFF08\u5982 status\u3001priority\u3001tags\u3001title\u3001content\uFF09\u3002\u5355\u6761\uFF1A\u4F20 <issue_id>\u3002\u6279\u91CF\uFF1A\u4F20 --ids \u6216 --ids-file\uFF0C\u6240\u6709 ID \u5E94\u7528\u540C\u4E00 --data\u3002\u6279\u91CF\u6A21\u5F0F\u4E0B N>20 \u4E14 TTY \u9700\u8981\u4E8C\u6B21\u786E\u8BA4\uFF1B\u975E TTY \u5FC5\u987B\u663E\u5F0F --yes"
2607
+ ).argument("[issue_id]", "\u5DE5\u5355 ID\uFF08\u5355\u6761\u66F4\u65B0\u65F6\u5FC5\u586B\uFF1B\u6279\u91CF\u66F4\u65B0\u8D70 --ids / --ids-file\uFF09").requiredOption(
1848
2608
  "--data <json>",
1849
2609
  "JSON \u6570\u636E\u6216 @\u6587\u4EF6\u8DEF\u5F84\u3002\u53EF\u66F4\u65B0: status\uFF08open|closed\uFF09\u3001priority\u3001tags\u3001title\u3001content"
1850
- ).action(async (issueId, opts) => {
2610
+ ).option("--ids <ids>", "\u6279\u91CF\u66F4\u65B0\uFF1A\u9017\u53F7\u5206\u9694\u7684\u5DE5\u5355 ID\uFF08\u4E0E <issue_id> \u4E92\u65A5\uFF09").option("--ids-file <path>", "\u6279\u91CF\u66F4\u65B0\uFF1A\u4ECE\u6587\u4EF6\u8BFB\u53D6 ID\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF1B# \u5F00\u5934\u6CE8\u91CA\uFF09").option("--dry-run", "\u6279\u91CF\u6A21\u5F0F\u9884\u89C8\uFF1A\u53EA\u6253\u5370 will-update \u5217\u8868\uFF0C\u4E0D\u8C03\u5199\u63A5\u53E3", false).option("--concurrency <n>", "\u6279\u91CF\u5E76\u53D1\u6570\uFF08\u9ED8\u8BA4 5\uFF09", "5").option("--yes", "\u6279\u91CF\u6A21\u5F0F\u8DF3\u8FC7\u4E8C\u6B21\u786E\u8BA4\uFF08\u975E TTY \u5FC5\u987B\u663E\u5F0F\u4F20\uFF09", false).action(async (issueId, opts) => {
2611
+ const hasSingle = issueId !== void 0;
2612
+ const hasBatch = opts.ids !== void 0 || opts.idsFile !== void 0;
2613
+ if (hasSingle && hasBatch) {
2614
+ reportCaughtError(new Error("\u4E0D\u80FD\u540C\u65F6\u63D0\u4F9B <issue_id> \u548C --ids/--ids-file"));
2615
+ process.exit(1);
2616
+ return;
2617
+ }
2618
+ if (!hasSingle && !hasBatch) {
2619
+ reportCaughtError(new Error("\u9700\u8981\u63D0\u4F9B <issue_id> \u6216 --ids/--ids-file \u4E4B\u4E00"));
2620
+ process.exit(1);
2621
+ return;
2622
+ }
2623
+ let batchReport = null;
1851
2624
  try {
1852
2625
  const body = parseDataOption(opts.data);
1853
- const data = await updateIssue(issueId, body);
1854
- formatOutput({ success: true, data }, program2.opts().table);
2626
+ if (hasSingle) {
2627
+ const data = await updateIssue(issueId, body);
2628
+ formatOutput({ success: true, data }, program2.opts().table);
2629
+ return;
2630
+ }
2631
+ const ids = parseIdsInput({ ids: opts.ids, idsFile: opts.idsFile });
2632
+ if (opts.dryRun) {
2633
+ formatOutput(
2634
+ {
2635
+ success: true,
2636
+ data: {
2637
+ dry_run: true,
2638
+ total: ids.length,
2639
+ ids,
2640
+ will_apply: body
2641
+ }
2642
+ },
2643
+ program2.opts().table
2644
+ );
2645
+ return;
2646
+ }
2647
+ await confirmBatch({ count: ids.length, action: "\u66F4\u65B0", yes: opts.yes });
2648
+ const concurrency = Number(opts.concurrency);
2649
+ if (!Number.isFinite(concurrency) || concurrency < 1) {
2650
+ throw new Error(`--concurrency \u5FC5\u987B\u4E3A\u6B63\u6574\u6570\uFF0C\u6536\u5230: ${opts.concurrency}`);
2651
+ }
2652
+ batchReport = await runBatch({
2653
+ ids,
2654
+ concurrency,
2655
+ perItem: async (id) => {
2656
+ const data = await updateIssue(id, body);
2657
+ return { status: "success", id, data };
2658
+ }
2659
+ });
2660
+ formatOutput({ success: true, data: batchReport }, program2.opts().table);
1855
2661
  } catch (err) {
1856
2662
  reportCaughtError(err);
1857
2663
  process.exit(toExitCode(err));
2664
+ return;
2665
+ }
2666
+ if (batchReport !== null) {
2667
+ const code = reportToExitCode(batchReport);
2668
+ if (code !== 0) process.exit(code);
1858
2669
  }
1859
2670
  });
1860
2671
  issue.command("update-owner").description(
1861
- "\u66F4\u65B0\u5DE5\u5355\u8D1F\u8D23\u4EBA\uFF08owner\uFF09\u3002\u4EC5\u4E8C\u9009\u4E00\uFF1A--owner <user_id> \u6216 --owner-phone <\u624B\u673A\u53F7>\uFF0C\u540E\u8005\u7531\u670D\u52A1\u7AEF\u89E3\u6790\u4E3A user_id\u3002\u6CE8\u610F\uFF1A\u540E\u7AEF\u9700\u5F00\u542F\u5BF9 owner / owner_phone \u5B57\u6BB5\u7684\u652F\u6301\u540E\u8BE5\u547D\u4EE4\u624D\u80FD\u751F\u6548"
1862
- ).argument("<issue_id>", "\u5DE5\u5355 ID\uFF08\u4ECE issue list \u83B7\u53D6\uFF09").option("--owner <user_id>", "\u8D1F\u8D23\u4EBA user_id\uFF08\u6B63\u6574\u6570\uFF09").option("--owner-phone <phone>", "\u8D1F\u8D23\u4EBA\u624B\u673A\u53F7\uFF08\u7531\u670D\u52A1\u7AEF\u89E3\u6790\u4E3A user_id\uFF09").action(async (issueId, opts) => {
2672
+ "\u66F4\u65B0\u5DE5\u5355\u8D1F\u8D23\u4EBA\u3002--owner \u63A5\u53D7 user_id\uFF08\u22647 \u4F4D\u7EAF\u6570\u5B57\uFF09\u6216\u82B1\u540D\uFF08\u81EA\u52A8\u901A\u8FC7 cs-admin /api/account-roles \u89E3\u6790\uFF09\u3002\u5355\u6761\uFF1A\u4F20 <issue_id>\u3002\u6279\u91CF\uFF1A\u4F20 --ids \u6216 --ids-file\uFF0C\u8D70 cs-admin BFF \u7684 batch-owner\uFF08\u5355\u6B21\u8C03\u7528\u3001\u539F\u5B50\u6539\u6D3E\u3001\u81EA\u52A8 Feishu/DingTalk \u901A\u77E5\uFF09"
2673
+ ).argument("[issue_id]", "\u5DE5\u5355 ID\uFF08\u5355\u6761\u66F4\u65B0\u65F6\u5FC5\u586B\uFF1B\u6279\u91CF\u8D70 --ids / --ids-file\uFF09").requiredOption("--owner <user_id_or_name>", "\u8D1F\u8D23\u4EBA user_id \u6216\u82B1\u540D").option("--ids <ids>", "\u6279\u91CF\u6539\u6D3E\uFF1A\u9017\u53F7\u5206\u9694\u7684\u5DE5\u5355 ID\uFF08\u4E0E <issue_id> \u4E92\u65A5\uFF09").option("--ids-file <path>", "\u6279\u91CF\u6539\u6D3E\uFF1A\u4ECE\u6587\u4EF6\u8BFB\u53D6 ID\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF1B# \u5F00\u5934\u6CE8\u91CA\uFF09").option("--dry-run", "\u6279\u91CF\u6A21\u5F0F\u9884\u89C8\uFF1A\u4EC5\u89E3\u6790 owner + \u5217\u51FA ID\uFF0C\u4E0D\u8C03\u5199\u63A5\u53E3", false).option("--yes", "\u6279\u91CF\u6A21\u5F0F\u8DF3\u8FC7 N>=20 \u4E8C\u6B21\u786E\u8BA4\uFF08\u975E TTY \u5FC5\u987B\u663E\u5F0F\u4F20\uFF09", false).action(async (issueId, opts) => {
2674
+ const hasSingle = issueId !== void 0;
2675
+ const hasBatch = opts.ids !== void 0 || opts.idsFile !== void 0;
2676
+ if (hasSingle && hasBatch) {
2677
+ reportCaughtError(new Error("\u4E0D\u80FD\u540C\u65F6\u63D0\u4F9B <issue_id> \u548C --ids/--ids-file"));
2678
+ process.exit(1);
2679
+ return;
2680
+ }
2681
+ if (!hasSingle && !hasBatch) {
2682
+ reportCaughtError(new Error("\u9700\u8981\u63D0\u4F9B <issue_id> \u6216 --ids/--ids-file \u4E4B\u4E00"));
2683
+ process.exit(1);
2684
+ return;
2685
+ }
1863
2686
  try {
1864
- const hasOwner = opts.owner !== void 0;
1865
- const hasPhone = opts.ownerPhone !== void 0;
1866
- if (hasOwner === hasPhone) {
1867
- throw new Error("\u5FC5\u987B\u4E14\u4EC5\u80FD\u63D0\u4F9B --owner \u6216 --owner-phone \u5176\u4E2D\u4E00\u4E2A");
2687
+ const ownerId = await resolveOwner(String(opts.owner));
2688
+ if (hasSingle) {
2689
+ const data = await updateIssueOwner(issueId, { owner: ownerId });
2690
+ formatOutput({ success: true, data }, program2.opts().table);
2691
+ return;
1868
2692
  }
1869
- const body = {};
1870
- if (hasOwner) {
1871
- const ownerId = Number(opts.owner);
1872
- if (!Number.isInteger(ownerId) || ownerId <= 0) {
1873
- throw new Error(`--owner \u5FC5\u987B\u4E3A\u6B63\u6574\u6570\uFF0C\u6536\u5230: ${opts.owner}`);
1874
- }
1875
- body.owner = ownerId;
1876
- } else {
1877
- const phone = String(opts.ownerPhone).trim();
1878
- if (!phone) {
1879
- throw new Error("--owner-phone \u4E0D\u80FD\u4E3A\u7A7A");
1880
- }
1881
- body.owner_phone = phone;
2693
+ const ids = parseIdsInput({ ids: opts.ids, idsFile: opts.idsFile });
2694
+ if (ids.length > 200) {
2695
+ throw new Error(`\u6279\u91CF\u6539\u6D3E\u5355\u6279\u6700\u591A 200 \u6761\uFF0C\u5F53\u524D ${ids.length}\u3002\u8BF7\u62C6\u5206\u8F93\u5165`);
1882
2696
  }
1883
- const data = await updateIssueOwner(issueId, body);
1884
- formatOutput({ success: true, data }, program2.opts().table);
2697
+ if (opts.dryRun) {
2698
+ formatOutput(
2699
+ {
2700
+ success: true,
2701
+ data: {
2702
+ dry_run: true,
2703
+ total: ids.length,
2704
+ ids,
2705
+ resolved_owner_id: ownerId
2706
+ }
2707
+ },
2708
+ program2.opts().table
2709
+ );
2710
+ return;
2711
+ }
2712
+ await confirmBatch({ count: ids.length, action: "\u6539\u6D3E", yes: opts.yes });
2713
+ const result = await batchReassignOwner({ issueIds: ids, ownerUserId: ownerId });
2714
+ formatOutput({ success: true, data: result }, program2.opts().table);
1885
2715
  } catch (err) {
1886
2716
  reportCaughtError(err);
1887
2717
  process.exit(toExitCode(err));
@@ -1919,9 +2749,30 @@ function registerIssueCommand(program2) {
1919
2749
  }
1920
2750
  });
1921
2751
  }
2752
+ async function resolveSessionsForIssues(issues, workspaceId) {
2753
+ const out = [];
2754
+ for (let i = 0; i < issues.length; i++) {
2755
+ const it = issues[i];
2756
+ let conversationId;
2757
+ if (it.conversation_id) {
2758
+ conversationId = it.conversation_id;
2759
+ } else if (it.user_name) {
2760
+ try {
2761
+ conversationId = await resolveSession({ userName: it.user_name, workspaceId });
2762
+ } catch (err) {
2763
+ const msg = err instanceof Error ? err.message : String(err);
2764
+ throw new Error(`issues[${i}] \u53CD\u67E5\u4F1A\u8BDD\u5931\u8D25\uFF1A${msg}`);
2765
+ }
2766
+ } else {
2767
+ throw new Error(`issues[${i}] \u65E2\u65E0 conversation_id \u4E5F\u65E0 user_name`);
2768
+ }
2769
+ out.push({ issue: it, conversationId });
2770
+ }
2771
+ return out;
2772
+ }
1922
2773
 
1923
2774
  // src/commands/knowledge.ts
1924
- import fs5 from "fs";
2775
+ import fs8 from "fs";
1925
2776
 
1926
2777
  // src/client/knowledge-api.ts
1927
2778
  async function listExternalKnowledge(opts) {
@@ -2027,7 +2878,7 @@ async function deleteKnowledgeContent(opts) {
2027
2878
  // src/commands/knowledge.ts
2028
2879
  function readContentArg(value) {
2029
2880
  if (value.startsWith("@")) {
2030
- return fs5.readFileSync(value.slice(1), "utf-8");
2881
+ return fs8.readFileSync(value.slice(1), "utf-8");
2031
2882
  }
2032
2883
  return value;
2033
2884
  }
@@ -3156,29 +4007,23 @@ async function listRepairRecords(opts) {
3156
4007
  if (opts.workspaceId) query.workspace_id = opts.workspaceId;
3157
4008
  if (opts.page) query.page = opts.page;
3158
4009
  if (opts.pageSize) query.page_size = opts.pageSize;
3159
- return request(getCustomerAgentUrl(), PATH_PREFIX2, {
4010
+ return request(getCsAdminUrl(), PATH_PREFIX2, {
3160
4011
  method: "GET",
3161
- query,
3162
- headers: buildCookieHeaders(),
3163
- skipAuth: true
4012
+ query
3164
4013
  });
3165
4014
  }
3166
4015
  async function createRepairRecord(data) {
3167
4016
  const request = createRequest();
3168
- return request(getCustomerAgentUrl(), PATH_PREFIX2, {
4017
+ return request(getCsAdminUrl(), PATH_PREFIX2, {
3169
4018
  method: "POST",
3170
- body: data,
3171
- headers: buildCookieHeaders(),
3172
- skipAuth: true
4019
+ body: data
3173
4020
  });
3174
4021
  }
3175
4022
  async function updateRepairRecord(recordId, data) {
3176
4023
  const request = createRequest();
3177
- return request(getCustomerAgentUrl(), `${PATH_PREFIX2}/${recordId}`, {
4024
+ return request(getCsAdminUrl(), `${PATH_PREFIX2}/${recordId}`, {
3178
4025
  method: "PUT",
3179
- body: data,
3180
- headers: buildCookieHeaders(),
3181
- skipAuth: true
4026
+ body: data
3182
4027
  });
3183
4028
  }
3184
4029
 
@@ -3448,22 +4293,7 @@ function registerSACommand(program2) {
3448
4293
  }
3449
4294
 
3450
4295
  // src/commands/testset.ts
3451
- import fs7 from "fs";
3452
-
3453
- // src/utils/file-output.ts
3454
- import fs6 from "fs";
3455
- import path4 from "path";
3456
- async function writeBinaryToFile(filePath, buffer) {
3457
- try {
3458
- const dir = path4.dirname(filePath);
3459
- fs6.mkdirSync(dir, { recursive: true });
3460
- fs6.writeFileSync(filePath, buffer);
3461
- } catch (err) {
3462
- const msg = err instanceof Error ? err.message : String(err);
3463
- throw new APIError(1, `\u6587\u4EF6\u5199\u5165\u5931\u8D25: ${msg}`);
3464
- }
3465
- return { path: filePath, bytes: buffer.length };
3466
- }
4296
+ import fs9 from "fs";
3467
4297
 
3468
4298
  // src/client/testset-api.ts
3469
4299
  function unwrapPaginated(raw, fallbackPageSize) {
@@ -3975,7 +4805,7 @@ function readPromptInput(value) {
3975
4805
  if (!filePath) {
3976
4806
  throw new Error("File path cannot be empty after @");
3977
4807
  }
3978
- return fs7.readFileSync(filePath, "utf-8");
4808
+ return fs9.readFileSync(filePath, "utf-8");
3979
4809
  }
3980
4810
  return value;
3981
4811
  }
@@ -4134,7 +4964,7 @@ var require2 = createRequire(import.meta.url);
4134
4964
  var { version } = require2("../package.json");
4135
4965
  var program = new Command();
4136
4966
  program.name("cs-cli").description(
4137
- "BetterYeah AI \u5BA2\u670D\u5E73\u53F0 CLI\u3002\u6838\u5FC3\u6982\u5FF5\uFF1Aworkspace\uFF08\u5DE5\u4F5C\u7A7A\u95F4\uFF09\u2192 agent\uFF08AI \u5BA2\u670D\u673A\u5668\u4EBA\uFF09\u2192 SA/product/FAQ\uFF08\u77E5\u8BC6\u914D\u7F6E\uFF09\u2192 issue\uFF08\u5DE5\u5355\uFF09\u2192 debug\uFF08\u8C03\u8BD5\u9A8C\u8BC1\uFF09\u2192 monitor\uFF08\u8FD0\u8425\u76D1\u63A7\uFF09\u2192 repair-record\uFF08\u4FEE\u590D\u5BA1\u8BA1\uFF09\u2192 change-consumer\uFF08\u5546\u54C1\u53D8\u66F4\u4E8B\u4EF6\u6D88\u8D39\uFF09\u3002\u6240\u6709\u547D\u4EE4\u9ED8\u8BA4\u8F93\u51FA JSON\uFF0C\u8FFD\u52A0 --table \u53EF\u5207\u6362\u4E3A\u4EBA\u7C7B\u53EF\u8BFB\u8868\u683C"
4967
+ "BetterYeah AI \u5BA2\u670D\u5E73\u53F0 CLI\u3002\u6838\u5FC3\u6982\u5FF5\uFF1Aworkspace\uFF08\u5DE5\u4F5C\u7A7A\u95F4\uFF09\u2192 agent\uFF08AI \u5BA2\u670D\u673A\u5668\u4EBA\uFF09\u2192 SA/product/FAQ\uFF08\u77E5\u8BC6\u914D\u7F6E\uFF09\u2192 issue\uFF08\u5DE5\u5355\uFF09\u2192 debug\uFF08\u8C03\u8BD5\u9A8C\u8BC1\uFF09\u2192 monitor\uFF08\u8FD0\u8425\u76D1\u63A7\uFF09\u2192 dashboard\uFF08\u6570\u636E\u770B\u677F\uFF09\u2192 repair-record\uFF08\u4FEE\u590D\u5BA1\u8BA1\uFF09\u2192 change-consumer\uFF08\u5546\u54C1\u53D8\u66F4\u4E8B\u4EF6\u6D88\u8D39\uFF09\u3002\u6240\u6709\u547D\u4EE4\u9ED8\u8BA4\u8F93\u51FA JSON\uFF0C\u8FFD\u52A0 --table \u53EF\u5207\u6362\u4E3A\u4EBA\u7C7B\u53EF\u8BFB\u8868\u683C"
4138
4968
  ).version(version).option("--table", "\u4EE5\u8868\u683C\u5F62\u5F0F\u8F93\u51FA\uFF08\u9ED8\u8BA4 JSON\uFF09\u3002\u4EBA\u5DE5\u67E5\u770B\u65F6\u4F7F\u7528", false).option("--workspace <id>", "\u4E34\u65F6\u8986\u76D6\u9ED8\u8BA4\u5DE5\u4F5C\u7A7A\u95F4 ID\uFF0C\u4E0D\u4FEE\u6539\u6301\u4E45\u5316\u914D\u7F6E").option(
4139
4969
  "--request-timeout <ms>",
4140
4970
  "\u5355\u4E2A HTTP \u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09\uFF0C\u590D\u6742 Agent \u56DE\u590D\u5EFA\u8BAE\u8C03\u9AD8\u3002\u4E0D\u8981\u5199 --timeout\uFF0C\u90A3\u662F\u5B50\u547D\u4EE4\u7684\u8F6E\u8BE2\u7A97\u53E3\uFF08\u79D2\uFF09",
@@ -4169,6 +4999,7 @@ registerFaqCommand(program);
4169
4999
  registerConversationCommand(program);
4170
5000
  registerDebugCommand(program);
4171
5001
  registerMonitorCommand(program);
5002
+ registerDashboardCommand(program);
4172
5003
  registerRepairRecordCommand(program);
4173
5004
  registerOperationsRecordCommand(program);
4174
5005
  registerChangeConsumerCommand(program);