@bty/customer-service-cli 0.4.7 → 0.5.0
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 +286 -0
- package/README.md +137 -4
- package/dist/bin.js +1250 -46
- 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(
|
|
183
|
+
function writeCache(cache3) {
|
|
184
184
|
const dir = getConfigDir();
|
|
185
185
|
fs2.mkdirSync(dir, { recursive: true });
|
|
186
|
-
fs2.writeFileSync(getCachePath(), JSON.stringify(
|
|
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
|
|
238
|
+
const cache3 = readCache();
|
|
239
239
|
const now = Date.now();
|
|
240
|
-
if (
|
|
241
|
-
return compareSemver(currentVersion,
|
|
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 });
|
|
@@ -276,7 +276,7 @@ function toExitCode(err) {
|
|
|
276
276
|
return 1;
|
|
277
277
|
}
|
|
278
278
|
function createRequest(globalTimeout) {
|
|
279
|
-
return async function request(baseUrl,
|
|
279
|
+
return async function request(baseUrl, path5, options) {
|
|
280
280
|
const headers = {
|
|
281
281
|
"Content-Type": "application/json",
|
|
282
282
|
...options.headers
|
|
@@ -304,7 +304,7 @@ function createRequest(globalTimeout) {
|
|
|
304
304
|
if (workspaceId) {
|
|
305
305
|
headers["workspace-id"] = workspaceId;
|
|
306
306
|
}
|
|
307
|
-
let url = `${baseUrl}${
|
|
307
|
+
let url = `${baseUrl}${path5}`;
|
|
308
308
|
if (options.query) {
|
|
309
309
|
const params = new URLSearchParams();
|
|
310
310
|
for (const [key, value] of Object.entries(options.query)) {
|
|
@@ -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) {
|
|
@@ -1784,7 +1791,387 @@ async function getIssueStats(opts) {
|
|
|
1784
1791
|
return result.data ?? result;
|
|
1785
1792
|
}
|
|
1786
1793
|
|
|
1794
|
+
// src/client/workbench-issues-api.ts
|
|
1795
|
+
async function batchReassignOwner(params) {
|
|
1796
|
+
const request = createRequest();
|
|
1797
|
+
return request(getCsAdminUrl(), "/api/workbench/issues/batch-owner", {
|
|
1798
|
+
method: "POST",
|
|
1799
|
+
body: { issueIds: params.issueIds, ownerUserId: params.ownerUserId }
|
|
1800
|
+
});
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
// src/utils/batch-input.ts
|
|
1804
|
+
import fs5 from "fs";
|
|
1805
|
+
function parseIdsInput(opts) {
|
|
1806
|
+
const hasIds = opts.ids !== void 0 && opts.ids.trim() !== "";
|
|
1807
|
+
const hasFile = opts.idsFile !== void 0 && opts.idsFile.trim() !== "";
|
|
1808
|
+
if (hasIds && hasFile) {
|
|
1809
|
+
throw new Error("--ids \u4E0E --ids-file \u4E92\u65A5\uFF0C\u8BF7\u53EA\u63D0\u4F9B\u5176\u4E00");
|
|
1810
|
+
}
|
|
1811
|
+
if (!hasIds && !hasFile) {
|
|
1812
|
+
throw new Error("\u9700\u8981\u63D0\u4F9B --ids \u6216 --ids-file \u4E4B\u4E00");
|
|
1813
|
+
}
|
|
1814
|
+
let raw;
|
|
1815
|
+
if (hasIds) {
|
|
1816
|
+
raw = opts.ids.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1817
|
+
} else {
|
|
1818
|
+
const filePath = opts.idsFile;
|
|
1819
|
+
let content;
|
|
1820
|
+
try {
|
|
1821
|
+
content = fs5.readFileSync(filePath, "utf8");
|
|
1822
|
+
} catch (err) {
|
|
1823
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1824
|
+
throw new Error(`\u8BFB\u53D6 --ids-file \u5931\u8D25: ${msg}`);
|
|
1825
|
+
}
|
|
1826
|
+
raw = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
1827
|
+
}
|
|
1828
|
+
if (raw.length === 0) {
|
|
1829
|
+
throw new Error("--ids / --ids-file \u672A\u89E3\u6790\u5230\u4EFB\u4F55 id");
|
|
1830
|
+
}
|
|
1831
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1832
|
+
const ids = [];
|
|
1833
|
+
for (const id of raw) {
|
|
1834
|
+
if (!seen.has(id)) {
|
|
1835
|
+
seen.add(id);
|
|
1836
|
+
ids.push(id);
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return ids;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/utils/issue-batch-input.ts
|
|
1843
|
+
import fs6 from "fs";
|
|
1844
|
+
var PRIORITY_VALUES = ["critical", "high", "medium", "low"];
|
|
1845
|
+
function readSource(opts) {
|
|
1846
|
+
const hasFile = typeof opts.file === "string" && opts.file.trim() !== "";
|
|
1847
|
+
const hasStdin = typeof opts.stdin === "string";
|
|
1848
|
+
if (hasFile && hasStdin) {
|
|
1849
|
+
throw new Error("--file \u4E0E --stdin \u4E92\u65A5\uFF0C\u8BF7\u53EA\u63D0\u4F9B\u5176\u4E00");
|
|
1850
|
+
}
|
|
1851
|
+
if (!hasFile && !hasStdin) {
|
|
1852
|
+
throw new Error("\u9700\u8981\u63D0\u4F9B --file <path> \u6216\u901A\u8FC7 stdin \u4F20\u5165 JSON");
|
|
1853
|
+
}
|
|
1854
|
+
if (hasFile) {
|
|
1855
|
+
try {
|
|
1856
|
+
return fs6.readFileSync(opts.file, "utf8");
|
|
1857
|
+
} catch (err) {
|
|
1858
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1859
|
+
throw new Error(`\u8BFB\u53D6 --file \u5931\u8D25: ${msg}`);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
return opts.stdin;
|
|
1863
|
+
}
|
|
1864
|
+
function parseIssueBatchInput(opts) {
|
|
1865
|
+
const raw = readSource(opts).trim();
|
|
1866
|
+
if (!raw) {
|
|
1867
|
+
throw new Error("\u8F93\u5165 JSON \u4E3A\u7A7A");
|
|
1868
|
+
}
|
|
1869
|
+
let parsed;
|
|
1870
|
+
try {
|
|
1871
|
+
parsed = JSON.parse(raw);
|
|
1872
|
+
} catch (err) {
|
|
1873
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1874
|
+
throw new Error(`JSON \u89E3\u6790\u5931\u8D25: ${msg}`);
|
|
1875
|
+
}
|
|
1876
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1877
|
+
throw new Error("\u9876\u5C42\u5FC5\u987B\u662F JSON object\uFF08\u542B version + issues[]\uFF09");
|
|
1878
|
+
}
|
|
1879
|
+
const root = parsed;
|
|
1880
|
+
const version2 = typeof root.version === "string" ? root.version : "1";
|
|
1881
|
+
const issuesRaw = root.issues;
|
|
1882
|
+
if (!Array.isArray(issuesRaw)) {
|
|
1883
|
+
throw new Error("\u9876\u5C42\u7F3A\u5C11\u6570\u7EC4\u5B57\u6BB5 issues[]");
|
|
1884
|
+
}
|
|
1885
|
+
if (issuesRaw.length === 0) {
|
|
1886
|
+
throw new Error("issues[] \u4E3A\u7A7A\uFF0C\u65E0\u53EF\u521B\u5EFA");
|
|
1887
|
+
}
|
|
1888
|
+
const workspaceId = typeof root.workspace_id === "string" ? root.workspace_id : void 0;
|
|
1889
|
+
const issues = issuesRaw.map((it, idx) => validateIssue(it, idx));
|
|
1890
|
+
return { version: version2, workspace_id: workspaceId, issues };
|
|
1891
|
+
}
|
|
1892
|
+
function validateIssue(value, idx) {
|
|
1893
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1894
|
+
throw new Error(`issues[${idx}] \u4E0D\u662F object`);
|
|
1895
|
+
}
|
|
1896
|
+
const v = value;
|
|
1897
|
+
const where = `issues[${idx}]`;
|
|
1898
|
+
const title = requireString(v.title, `${where}.title`);
|
|
1899
|
+
const priority = requirePriority(v.priority, `${where}.priority`);
|
|
1900
|
+
const expected = requireString(v.expected, `${where}.expected`);
|
|
1901
|
+
const conversation_id = optionalString(v.conversation_id, `${where}.conversation_id`);
|
|
1902
|
+
const user_name = optionalString(v.user_name, `${where}.user_name`);
|
|
1903
|
+
if (!conversation_id && !user_name) {
|
|
1904
|
+
throw new Error(`${where} \u5FC5\u987B\u63D0\u4F9B conversation_id \u6216 user_name \u4E4B\u4E00`);
|
|
1905
|
+
}
|
|
1906
|
+
const description = optionalString(v.description, `${where}.description`);
|
|
1907
|
+
let tags;
|
|
1908
|
+
if (v.tags !== void 0) {
|
|
1909
|
+
if (!Array.isArray(v.tags) || !v.tags.every((t) => typeof t === "string")) {
|
|
1910
|
+
throw new Error(`${where}.tags \u5FC5\u987B\u662F string[]`);
|
|
1911
|
+
}
|
|
1912
|
+
tags = v.tags;
|
|
1913
|
+
}
|
|
1914
|
+
const source_type = optionalString(v.source_type, `${where}.source_type`);
|
|
1915
|
+
let source_data;
|
|
1916
|
+
if (v.source_data !== void 0) {
|
|
1917
|
+
if (!v.source_data || typeof v.source_data !== "object" || Array.isArray(v.source_data)) {
|
|
1918
|
+
throw new Error(`${where}.source_data \u5FC5\u987B\u662F object`);
|
|
1919
|
+
}
|
|
1920
|
+
source_data = v.source_data;
|
|
1921
|
+
}
|
|
1922
|
+
return {
|
|
1923
|
+
title,
|
|
1924
|
+
priority,
|
|
1925
|
+
expected,
|
|
1926
|
+
description,
|
|
1927
|
+
conversation_id,
|
|
1928
|
+
user_name,
|
|
1929
|
+
tags,
|
|
1930
|
+
source_type,
|
|
1931
|
+
source_data
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
function requireString(v, where) {
|
|
1935
|
+
if (typeof v !== "string" || v.trim() === "") {
|
|
1936
|
+
throw new Error(`${where} \u5FC5\u586B\uFF0C\u4E14\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
1937
|
+
}
|
|
1938
|
+
return v;
|
|
1939
|
+
}
|
|
1940
|
+
function optionalString(v, where) {
|
|
1941
|
+
if (v === void 0 || v === null) return void 0;
|
|
1942
|
+
if (typeof v !== "string") {
|
|
1943
|
+
throw new Error(`${where} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
1944
|
+
}
|
|
1945
|
+
return v.trim() === "" ? void 0 : v;
|
|
1946
|
+
}
|
|
1947
|
+
function requirePriority(v, where) {
|
|
1948
|
+
if (typeof v !== "string" || !PRIORITY_VALUES.includes(v)) {
|
|
1949
|
+
throw new Error(
|
|
1950
|
+
`${where} \u5FC5\u987B\u662F ${PRIORITY_VALUES.join("|")} \u4E4B\u4E00\uFF0C\u6536\u5230: ${JSON.stringify(v)}`
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1953
|
+
return v;
|
|
1954
|
+
}
|
|
1955
|
+
function buildContent(issue) {
|
|
1956
|
+
const head = issue.description?.trim();
|
|
1957
|
+
if (head) {
|
|
1958
|
+
return `${head}
|
|
1959
|
+
|
|
1960
|
+
## Expected
|
|
1961
|
+
${issue.expected}`;
|
|
1962
|
+
}
|
|
1963
|
+
return `## Expected
|
|
1964
|
+
${issue.expected}`;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
// src/client/account-roles-api.ts
|
|
1968
|
+
async function listAccountRoles(opts = {}) {
|
|
1969
|
+
const request = createRequest();
|
|
1970
|
+
const query = {};
|
|
1971
|
+
if (opts.role) query.role = opts.role;
|
|
1972
|
+
if (opts.includeInactive) query.includeInactive = "1";
|
|
1973
|
+
const result = await request(getCsAdminUrl(), "/api/account-roles", {
|
|
1974
|
+
method: "GET",
|
|
1975
|
+
query
|
|
1976
|
+
});
|
|
1977
|
+
return result.rows ?? [];
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// src/utils/resolve-owner.ts
|
|
1981
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1982
|
+
var cache = null;
|
|
1983
|
+
async function getEngineers() {
|
|
1984
|
+
const now = Date.now();
|
|
1985
|
+
if (cache && cache.expireAt > now) return cache.engineers;
|
|
1986
|
+
const [ka, smb] = await Promise.all([
|
|
1987
|
+
listAccountRoles({ role: "KA_ENGINEER" }),
|
|
1988
|
+
listAccountRoles({ role: "SMB_ENGINEER" })
|
|
1989
|
+
]);
|
|
1990
|
+
const merged = mergeAndDedupe(ka, smb);
|
|
1991
|
+
cache = { engineers: merged, expireAt: now + CACHE_TTL_MS };
|
|
1992
|
+
return merged;
|
|
1993
|
+
}
|
|
1994
|
+
function mergeAndDedupe(...lists) {
|
|
1995
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1996
|
+
for (const list of lists) {
|
|
1997
|
+
for (const row of list) {
|
|
1998
|
+
const existing = seen.get(row.csAdminUserId);
|
|
1999
|
+
if (!existing) {
|
|
2000
|
+
seen.set(row.csAdminUserId, { ...row });
|
|
2001
|
+
} else {
|
|
2002
|
+
const roles = /* @__PURE__ */ new Set([...existing.roles, ...row.roles]);
|
|
2003
|
+
existing.roles = [...roles];
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
return [...seen.values()].sort((a, b) => a.engineerName.localeCompare(b.engineerName));
|
|
2008
|
+
}
|
|
2009
|
+
async function resolveOwner(input) {
|
|
2010
|
+
const trimmed = input.trim();
|
|
2011
|
+
if (!trimmed) throw new Error("--owner \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2012
|
+
if (/^\d+$/.test(trimmed)) {
|
|
2013
|
+
if (trimmed.length === 11) {
|
|
2014
|
+
throw new Error("--owner \u4E0D\u518D\u63A5\u53D7\u624B\u673A\u53F7\uFF08\u5DF2\u5E9F\u5F03\uFF09\u3002\u8BF7\u4F20 user_id \u6216\u82B1\u540D");
|
|
2015
|
+
}
|
|
2016
|
+
const n = Number(trimmed);
|
|
2017
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
2018
|
+
throw new Error(`--owner \u6570\u5B57\u89E3\u6790\u5931\u8D25: ${input}`);
|
|
2019
|
+
}
|
|
2020
|
+
return n;
|
|
2021
|
+
}
|
|
2022
|
+
const engineers = await getEngineers();
|
|
2023
|
+
const exact = engineers.filter((e) => e.engineerName === trimmed);
|
|
2024
|
+
if (exact.length === 1) return exact[0].csAdminUserId;
|
|
2025
|
+
if (exact.length > 1) {
|
|
2026
|
+
const list = exact.map((e) => `${e.engineerName}(${e.csAdminUserId})`).join(", ");
|
|
2027
|
+
throw new Error(`--owner "${input}" \u540C\u540D\u591A\u4EBA\uFF1A${list}\u3002\u8BF7\u6539\u4F20 user_id`);
|
|
2028
|
+
}
|
|
2029
|
+
const partial = engineers.filter(
|
|
2030
|
+
(e) => e.engineerName.toLowerCase().includes(trimmed.toLowerCase())
|
|
2031
|
+
);
|
|
2032
|
+
if (partial.length === 1) return partial[0].csAdminUserId;
|
|
2033
|
+
if (partial.length > 1) {
|
|
2034
|
+
const list = partial.map((e) => `${e.engineerName}(${e.csAdminUserId})`).join(", ");
|
|
2035
|
+
throw new Error(`--owner "${input}" \u6A21\u7CCA\u5339\u914D\u591A\u4EBA\uFF1A${list}\u3002\u8BF7\u6539\u7528\u7CBE\u786E\u82B1\u540D\u6216 user_id`);
|
|
2036
|
+
}
|
|
2037
|
+
throw new Error(`--owner "${input}" \u672A\u627E\u5230\u5339\u914D\u7684\u5DE5\u7A0B\u5E08\uFF08\u5171 ${engineers.length} \u4EBA\u5728\u518C\uFF09`);
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
// src/utils/resolve-session.ts
|
|
2041
|
+
var CACHE_TTL_MS2 = 5 * 60 * 1e3;
|
|
2042
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
2043
|
+
async function resolveSession(opts) {
|
|
2044
|
+
const userName = opts.userName.trim();
|
|
2045
|
+
if (!userName) throw new Error("user_name \u4E0D\u80FD\u4E3A\u7A7A");
|
|
2046
|
+
const cacheKey = `${opts.workspaceId ?? ""}|${userName}`;
|
|
2047
|
+
const now = Date.now();
|
|
2048
|
+
const hit = cache2.get(cacheKey);
|
|
2049
|
+
if (hit && hit.expireAt > now) return hit.conversationId;
|
|
2050
|
+
const resp = await listConversations({
|
|
2051
|
+
userName,
|
|
2052
|
+
workspaceId: opts.workspaceId,
|
|
2053
|
+
pageSize: opts.pageSize ?? 5
|
|
2054
|
+
});
|
|
2055
|
+
const list = (resp.list ?? []).filter((c) => typeof c.conversation_id === "string");
|
|
2056
|
+
if (list.length === 0) {
|
|
2057
|
+
throw new Error(`user_name "${userName}" \u672A\u5339\u914D\u5230\u4EFB\u4F55\u4F1A\u8BDD\uFF08NOT_FOUND\uFF09`);
|
|
2058
|
+
}
|
|
2059
|
+
if (list.length > 1) {
|
|
2060
|
+
const preview = list.slice(0, 3).map((c) => c.conversation_id).join(", ");
|
|
2061
|
+
throw new Error(
|
|
2062
|
+
`user_name "${userName}" \u547D\u4E2D ${list.length} \u6761\u4F1A\u8BDD\uFF08AMBIGUOUS\uFF09\uFF1A${preview}${list.length > 3 ? " ..." : ""}\u3002\u8BF7\u6539\u4F20 conversation_id`
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
const conversationId = list[0].conversation_id;
|
|
2066
|
+
cache2.set(cacheKey, { conversationId, expireAt: now + CACHE_TTL_MS2 });
|
|
2067
|
+
return conversationId;
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
// src/utils/run-batch.ts
|
|
2071
|
+
import readline from "readline";
|
|
2072
|
+
var DEFAULT_CONCURRENCY = 5;
|
|
2073
|
+
async function runBatch(opts) {
|
|
2074
|
+
const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);
|
|
2075
|
+
const results = [];
|
|
2076
|
+
let aborted = false;
|
|
2077
|
+
const sigintHandler = () => {
|
|
2078
|
+
if (!aborted) {
|
|
2079
|
+
aborted = true;
|
|
2080
|
+
outputInfo("\u6536\u5230 SIGINT\uFF0C\u7B49\u5F85\u98DE\u884C\u4E2D\u8BF7\u6C42\u7ED3\u675F\u540E\u9000\u51FA...");
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
2083
|
+
process.on("SIGINT", sigintHandler);
|
|
2084
|
+
try {
|
|
2085
|
+
for (let i = 0; i < opts.ids.length; i += concurrency) {
|
|
2086
|
+
if (aborted) break;
|
|
2087
|
+
const chunk = opts.ids.slice(i, i + concurrency);
|
|
2088
|
+
const settled = await Promise.all(
|
|
2089
|
+
chunk.map(async (id) => {
|
|
2090
|
+
try {
|
|
2091
|
+
return await opts.perItem(id);
|
|
2092
|
+
} catch (err) {
|
|
2093
|
+
return mapErrorToFailure(id, err);
|
|
2094
|
+
}
|
|
2095
|
+
})
|
|
2096
|
+
);
|
|
2097
|
+
results.push(...settled);
|
|
2098
|
+
}
|
|
2099
|
+
} finally {
|
|
2100
|
+
process.off("SIGINT", sigintHandler);
|
|
2101
|
+
}
|
|
2102
|
+
return buildReport(results, aborted);
|
|
2103
|
+
}
|
|
2104
|
+
function mapErrorToFailure(id, err) {
|
|
2105
|
+
if (err instanceof APIError) {
|
|
2106
|
+
return { status: "failed", id, code: `HTTP_${err.statusCode}`, msg: err.message };
|
|
2107
|
+
}
|
|
2108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2109
|
+
return { status: "failed", id, code: "INTERNAL", msg };
|
|
2110
|
+
}
|
|
2111
|
+
function buildReport(results, aborted) {
|
|
2112
|
+
const report = {
|
|
2113
|
+
version: "1",
|
|
2114
|
+
total: results.length,
|
|
2115
|
+
success_count: 0,
|
|
2116
|
+
failed_count: 0,
|
|
2117
|
+
skipped_count: 0,
|
|
2118
|
+
aborted,
|
|
2119
|
+
success_ids: [],
|
|
2120
|
+
failed: [],
|
|
2121
|
+
skipped: []
|
|
2122
|
+
};
|
|
2123
|
+
for (const r of results) {
|
|
2124
|
+
if (r.status === "success") {
|
|
2125
|
+
report.success_count++;
|
|
2126
|
+
report.success_ids.push(r.id);
|
|
2127
|
+
} else if (r.status === "failed") {
|
|
2128
|
+
report.failed_count++;
|
|
2129
|
+
report.failed.push({ id: r.id, code: r.code, msg: r.msg });
|
|
2130
|
+
} else {
|
|
2131
|
+
report.skipped_count++;
|
|
2132
|
+
report.skipped.push({ id: r.id, reason: r.reason });
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
return report;
|
|
2136
|
+
}
|
|
2137
|
+
function reportToExitCode(report) {
|
|
2138
|
+
if (report.aborted) return 130;
|
|
2139
|
+
if (report.failed_count === 0) return 0;
|
|
2140
|
+
if (report.failed_count === report.total) return 1;
|
|
2141
|
+
return 2;
|
|
2142
|
+
}
|
|
2143
|
+
var DEFAULT_CONFIRM_THRESHOLD = 20;
|
|
2144
|
+
async function confirmBatch(opts) {
|
|
2145
|
+
const threshold = opts.threshold ?? DEFAULT_CONFIRM_THRESHOLD;
|
|
2146
|
+
if (opts.count < threshold) return;
|
|
2147
|
+
if (opts.yes) return;
|
|
2148
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
2149
|
+
throw new Error(`\u975E TTY \u73AF\u5883\u4E0B\u8981 ${opts.action} ${opts.count} \u6761\uFF0C\u9700\u663E\u5F0F --yes \u624D\u80FD\u6267\u884C`);
|
|
2150
|
+
}
|
|
2151
|
+
const rl = readline.createInterface({
|
|
2152
|
+
input: process.stdin,
|
|
2153
|
+
output: process.stderr
|
|
2154
|
+
});
|
|
2155
|
+
try {
|
|
2156
|
+
const answer = await new Promise((resolve) => {
|
|
2157
|
+
rl.question(`\u5373\u5C06${opts.action} ${opts.count} \u6761\u8BB0\u5F55\u3002\u7EE7\u7EED\uFF1F\u8F93\u5165 yes \u786E\u8BA4: `, resolve);
|
|
2158
|
+
});
|
|
2159
|
+
if (answer.trim().toLowerCase() !== "yes") {
|
|
2160
|
+
throw new Error("\u5DF2\u53D6\u6D88");
|
|
2161
|
+
}
|
|
2162
|
+
} finally {
|
|
2163
|
+
rl.close();
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
|
|
1787
2167
|
// src/commands/issue.ts
|
|
2168
|
+
async function readAllStdin() {
|
|
2169
|
+
const chunks = [];
|
|
2170
|
+
for await (const chunk of process.stdin) {
|
|
2171
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
2172
|
+
}
|
|
2173
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
2174
|
+
}
|
|
1788
2175
|
function registerIssueCommand(program2) {
|
|
1789
2176
|
const issue = program2.command("issue").description(
|
|
1790
2177
|
"\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 +2212,219 @@ function registerIssueCommand(program2) {
|
|
|
1825
2212
|
process.exit(toExitCode(err));
|
|
1826
2213
|
}
|
|
1827
2214
|
});
|
|
1828
|
-
issue.command("create").description(
|
|
2215
|
+
issue.command("create").description(
|
|
2216
|
+
"\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"
|
|
2217
|
+
).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) => {
|
|
2218
|
+
const hasBatch = Boolean(opts.file) || opts.stdin === true;
|
|
2219
|
+
const hasSingleLike = Boolean(opts.title || opts.content);
|
|
2220
|
+
if (hasBatch && hasSingleLike) {
|
|
2221
|
+
reportCaughtError(
|
|
2222
|
+
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")
|
|
2223
|
+
);
|
|
2224
|
+
process.exit(1);
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
if (!hasBatch) {
|
|
2228
|
+
try {
|
|
2229
|
+
if (!opts.title || !opts.content) {
|
|
2230
|
+
throw new Error(
|
|
2231
|
+
"\u5355\u6761\u521B\u5EFA\u9700\u63D0\u4F9B --title \u548C --content\uFF08\u6216\u6539\u7528 --file/--stdin \u6279\u91CF\u6A21\u5F0F\uFF09"
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
const data = await createIssue({
|
|
2235
|
+
title: opts.title,
|
|
2236
|
+
content: opts.content,
|
|
2237
|
+
priority: opts.priority,
|
|
2238
|
+
tags: opts.tags,
|
|
2239
|
+
conversation_id: opts.conversationId,
|
|
2240
|
+
record_id: opts.recordId,
|
|
2241
|
+
workspace_id: opts.workspaceId,
|
|
2242
|
+
source_type: opts.sourceType,
|
|
2243
|
+
source_data: opts.sourceData ? JSON.parse(opts.sourceData) : void 0
|
|
2244
|
+
});
|
|
2245
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
2246
|
+
} catch (err) {
|
|
2247
|
+
reportCaughtError(err);
|
|
2248
|
+
process.exit(toExitCode(err));
|
|
2249
|
+
}
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
let batchReport = null;
|
|
1829
2253
|
try {
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2254
|
+
const stdinContent = opts.stdin ? await readAllStdin() : void 0;
|
|
2255
|
+
const parsed = parseIssueBatchInput({ file: opts.file, stdin: stdinContent });
|
|
2256
|
+
const workspaceId = parsed.workspace_id;
|
|
2257
|
+
const resolved = await resolveSessionsForIssues(parsed.issues, workspaceId);
|
|
2258
|
+
if (opts.dryRun) {
|
|
2259
|
+
formatOutput(
|
|
2260
|
+
{
|
|
2261
|
+
success: true,
|
|
2262
|
+
data: {
|
|
2263
|
+
dry_run: true,
|
|
2264
|
+
version: parsed.version,
|
|
2265
|
+
workspace_id: workspaceId,
|
|
2266
|
+
total: parsed.issues.length,
|
|
2267
|
+
will_create: resolved.map((r) => ({
|
|
2268
|
+
title: r.issue.title,
|
|
2269
|
+
priority: r.issue.priority,
|
|
2270
|
+
conversation_id: r.conversationId,
|
|
2271
|
+
tags: r.issue.tags,
|
|
2272
|
+
resolved_from_user_name: r.issue.conversation_id ? void 0 : r.issue.user_name,
|
|
2273
|
+
preview_content: buildContent(r.issue)
|
|
2274
|
+
}))
|
|
2275
|
+
}
|
|
2276
|
+
},
|
|
2277
|
+
program2.opts().table
|
|
2278
|
+
);
|
|
2279
|
+
return;
|
|
2280
|
+
}
|
|
2281
|
+
await confirmBatch({ count: parsed.issues.length, action: "\u521B\u5EFA", yes: opts.yes });
|
|
2282
|
+
const concurrency = Number(opts.concurrency);
|
|
2283
|
+
if (!Number.isFinite(concurrency) || concurrency < 1) {
|
|
2284
|
+
throw new Error(`--concurrency \u5FC5\u987B\u4E3A\u6B63\u6574\u6570\uFF0C\u6536\u5230: ${opts.concurrency}`);
|
|
2285
|
+
}
|
|
2286
|
+
const ids = parsed.issues.map((_, idx) => `idx-${idx}`);
|
|
2287
|
+
const indexed = new Map(resolved.map((r, idx) => [`idx-${idx}`, r]));
|
|
2288
|
+
batchReport = await runBatch({
|
|
2289
|
+
ids,
|
|
2290
|
+
concurrency,
|
|
2291
|
+
perItem: async (id) => {
|
|
2292
|
+
const r = indexed.get(id);
|
|
2293
|
+
if (!r) throw new Error(`internal: missing resolved entry for ${id}`);
|
|
2294
|
+
const data = await createIssue({
|
|
2295
|
+
title: r.issue.title,
|
|
2296
|
+
content: buildContent(r.issue),
|
|
2297
|
+
priority: r.issue.priority,
|
|
2298
|
+
tags: r.issue.tags,
|
|
2299
|
+
conversation_id: r.conversationId,
|
|
2300
|
+
workspace_id: workspaceId,
|
|
2301
|
+
source_type: r.issue.source_type,
|
|
2302
|
+
source_data: r.issue.source_data
|
|
2303
|
+
});
|
|
2304
|
+
return { status: "success", id, data };
|
|
2305
|
+
}
|
|
1840
2306
|
});
|
|
1841
|
-
formatOutput({ success: true, data }, program2.opts().table);
|
|
2307
|
+
formatOutput({ success: true, data: batchReport }, program2.opts().table);
|
|
1842
2308
|
} catch (err) {
|
|
1843
2309
|
reportCaughtError(err);
|
|
1844
2310
|
process.exit(toExitCode(err));
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
if (batchReport !== null) {
|
|
2314
|
+
const code = reportToExitCode(batchReport);
|
|
2315
|
+
if (code !== 0) process.exit(code);
|
|
1845
2316
|
}
|
|
1846
2317
|
});
|
|
1847
|
-
issue.command("update").description(
|
|
2318
|
+
issue.command("update").description(
|
|
2319
|
+
"\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"
|
|
2320
|
+
).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
2321
|
"--data <json>",
|
|
1849
2322
|
"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) => {
|
|
2323
|
+
).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) => {
|
|
2324
|
+
const hasSingle = issueId !== void 0;
|
|
2325
|
+
const hasBatch = opts.ids !== void 0 || opts.idsFile !== void 0;
|
|
2326
|
+
if (hasSingle && hasBatch) {
|
|
2327
|
+
reportCaughtError(new Error("\u4E0D\u80FD\u540C\u65F6\u63D0\u4F9B <issue_id> \u548C --ids/--ids-file"));
|
|
2328
|
+
process.exit(1);
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
if (!hasSingle && !hasBatch) {
|
|
2332
|
+
reportCaughtError(new Error("\u9700\u8981\u63D0\u4F9B <issue_id> \u6216 --ids/--ids-file \u4E4B\u4E00"));
|
|
2333
|
+
process.exit(1);
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
let batchReport = null;
|
|
1851
2337
|
try {
|
|
1852
2338
|
const body = parseDataOption(opts.data);
|
|
1853
|
-
|
|
1854
|
-
|
|
2339
|
+
if (hasSingle) {
|
|
2340
|
+
const data = await updateIssue(issueId, body);
|
|
2341
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
2342
|
+
return;
|
|
2343
|
+
}
|
|
2344
|
+
const ids = parseIdsInput({ ids: opts.ids, idsFile: opts.idsFile });
|
|
2345
|
+
if (opts.dryRun) {
|
|
2346
|
+
formatOutput(
|
|
2347
|
+
{
|
|
2348
|
+
success: true,
|
|
2349
|
+
data: {
|
|
2350
|
+
dry_run: true,
|
|
2351
|
+
total: ids.length,
|
|
2352
|
+
ids,
|
|
2353
|
+
will_apply: body
|
|
2354
|
+
}
|
|
2355
|
+
},
|
|
2356
|
+
program2.opts().table
|
|
2357
|
+
);
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
await confirmBatch({ count: ids.length, action: "\u66F4\u65B0", yes: opts.yes });
|
|
2361
|
+
const concurrency = Number(opts.concurrency);
|
|
2362
|
+
if (!Number.isFinite(concurrency) || concurrency < 1) {
|
|
2363
|
+
throw new Error(`--concurrency \u5FC5\u987B\u4E3A\u6B63\u6574\u6570\uFF0C\u6536\u5230: ${opts.concurrency}`);
|
|
2364
|
+
}
|
|
2365
|
+
batchReport = await runBatch({
|
|
2366
|
+
ids,
|
|
2367
|
+
concurrency,
|
|
2368
|
+
perItem: async (id) => {
|
|
2369
|
+
const data = await updateIssue(id, body);
|
|
2370
|
+
return { status: "success", id, data };
|
|
2371
|
+
}
|
|
2372
|
+
});
|
|
2373
|
+
formatOutput({ success: true, data: batchReport }, program2.opts().table);
|
|
1855
2374
|
} catch (err) {
|
|
1856
2375
|
reportCaughtError(err);
|
|
1857
2376
|
process.exit(toExitCode(err));
|
|
2377
|
+
return;
|
|
2378
|
+
}
|
|
2379
|
+
if (batchReport !== null) {
|
|
2380
|
+
const code = reportToExitCode(batchReport);
|
|
2381
|
+
if (code !== 0) process.exit(code);
|
|
1858
2382
|
}
|
|
1859
2383
|
});
|
|
1860
2384
|
issue.command("update-owner").description(
|
|
1861
|
-
"\u66F4\u65B0\u5DE5\u5355\u8D1F\u8D23\u4EBA\
|
|
1862
|
-
).argument("
|
|
2385
|
+
"\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"
|
|
2386
|
+
).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) => {
|
|
2387
|
+
const hasSingle = issueId !== void 0;
|
|
2388
|
+
const hasBatch = opts.ids !== void 0 || opts.idsFile !== void 0;
|
|
2389
|
+
if (hasSingle && hasBatch) {
|
|
2390
|
+
reportCaughtError(new Error("\u4E0D\u80FD\u540C\u65F6\u63D0\u4F9B <issue_id> \u548C --ids/--ids-file"));
|
|
2391
|
+
process.exit(1);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
if (!hasSingle && !hasBatch) {
|
|
2395
|
+
reportCaughtError(new Error("\u9700\u8981\u63D0\u4F9B <issue_id> \u6216 --ids/--ids-file \u4E4B\u4E00"));
|
|
2396
|
+
process.exit(1);
|
|
2397
|
+
return;
|
|
2398
|
+
}
|
|
1863
2399
|
try {
|
|
1864
|
-
const
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2400
|
+
const ownerId = await resolveOwner(String(opts.owner));
|
|
2401
|
+
if (hasSingle) {
|
|
2402
|
+
const data = await updateIssueOwner(issueId, { owner: ownerId });
|
|
2403
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
2404
|
+
return;
|
|
1868
2405
|
}
|
|
1869
|
-
const
|
|
1870
|
-
if (
|
|
1871
|
-
|
|
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;
|
|
2406
|
+
const ids = parseIdsInput({ ids: opts.ids, idsFile: opts.idsFile });
|
|
2407
|
+
if (ids.length > 200) {
|
|
2408
|
+
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
2409
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
2410
|
+
if (opts.dryRun) {
|
|
2411
|
+
formatOutput(
|
|
2412
|
+
{
|
|
2413
|
+
success: true,
|
|
2414
|
+
data: {
|
|
2415
|
+
dry_run: true,
|
|
2416
|
+
total: ids.length,
|
|
2417
|
+
ids,
|
|
2418
|
+
resolved_owner_id: ownerId
|
|
2419
|
+
}
|
|
2420
|
+
},
|
|
2421
|
+
program2.opts().table
|
|
2422
|
+
);
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
await confirmBatch({ count: ids.length, action: "\u6539\u6D3E", yes: opts.yes });
|
|
2426
|
+
const result = await batchReassignOwner({ issueIds: ids, ownerUserId: ownerId });
|
|
2427
|
+
formatOutput({ success: true, data: result }, program2.opts().table);
|
|
1885
2428
|
} catch (err) {
|
|
1886
2429
|
reportCaughtError(err);
|
|
1887
2430
|
process.exit(toExitCode(err));
|
|
@@ -1919,9 +2462,30 @@ function registerIssueCommand(program2) {
|
|
|
1919
2462
|
}
|
|
1920
2463
|
});
|
|
1921
2464
|
}
|
|
2465
|
+
async function resolveSessionsForIssues(issues, workspaceId) {
|
|
2466
|
+
const out = [];
|
|
2467
|
+
for (let i = 0; i < issues.length; i++) {
|
|
2468
|
+
const it = issues[i];
|
|
2469
|
+
let conversationId;
|
|
2470
|
+
if (it.conversation_id) {
|
|
2471
|
+
conversationId = it.conversation_id;
|
|
2472
|
+
} else if (it.user_name) {
|
|
2473
|
+
try {
|
|
2474
|
+
conversationId = await resolveSession({ userName: it.user_name, workspaceId });
|
|
2475
|
+
} catch (err) {
|
|
2476
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2477
|
+
throw new Error(`issues[${i}] \u53CD\u67E5\u4F1A\u8BDD\u5931\u8D25\uFF1A${msg}`);
|
|
2478
|
+
}
|
|
2479
|
+
} else {
|
|
2480
|
+
throw new Error(`issues[${i}] \u65E2\u65E0 conversation_id \u4E5F\u65E0 user_name`);
|
|
2481
|
+
}
|
|
2482
|
+
out.push({ issue: it, conversationId });
|
|
2483
|
+
}
|
|
2484
|
+
return out;
|
|
2485
|
+
}
|
|
1922
2486
|
|
|
1923
2487
|
// src/commands/knowledge.ts
|
|
1924
|
-
import
|
|
2488
|
+
import fs7 from "fs";
|
|
1925
2489
|
|
|
1926
2490
|
// src/client/knowledge-api.ts
|
|
1927
2491
|
async function listExternalKnowledge(opts) {
|
|
@@ -2027,7 +2591,7 @@ async function deleteKnowledgeContent(opts) {
|
|
|
2027
2591
|
// src/commands/knowledge.ts
|
|
2028
2592
|
function readContentArg(value) {
|
|
2029
2593
|
if (value.startsWith("@")) {
|
|
2030
|
-
return
|
|
2594
|
+
return fs7.readFileSync(value.slice(1), "utf-8");
|
|
2031
2595
|
}
|
|
2032
2596
|
return value;
|
|
2033
2597
|
}
|
|
@@ -3447,6 +4011,645 @@ function registerSACommand(program2) {
|
|
|
3447
4011
|
});
|
|
3448
4012
|
}
|
|
3449
4013
|
|
|
4014
|
+
// src/commands/testset.ts
|
|
4015
|
+
import fs9 from "fs";
|
|
4016
|
+
|
|
4017
|
+
// src/utils/file-output.ts
|
|
4018
|
+
import fs8 from "fs";
|
|
4019
|
+
import path4 from "path";
|
|
4020
|
+
async function writeBinaryToFile(filePath, buffer) {
|
|
4021
|
+
try {
|
|
4022
|
+
const dir = path4.dirname(filePath);
|
|
4023
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
4024
|
+
fs8.writeFileSync(filePath, buffer);
|
|
4025
|
+
} catch (err) {
|
|
4026
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4027
|
+
throw new APIError(1, `\u6587\u4EF6\u5199\u5165\u5931\u8D25: ${msg}`);
|
|
4028
|
+
}
|
|
4029
|
+
return { path: filePath, bytes: buffer.length };
|
|
4030
|
+
}
|
|
4031
|
+
|
|
4032
|
+
// src/client/testset-api.ts
|
|
4033
|
+
function unwrapPaginated(raw, fallbackPageSize) {
|
|
4034
|
+
return {
|
|
4035
|
+
items: Array.isArray(raw?.data) ? raw?.data : [],
|
|
4036
|
+
total: typeof raw?.total === "number" ? raw.total : 0,
|
|
4037
|
+
page: typeof raw?.page_no === "number" ? raw.page_no : 1,
|
|
4038
|
+
pageSize: typeof raw?.page_size === "number" ? raw.page_size : fallbackPageSize
|
|
4039
|
+
};
|
|
4040
|
+
}
|
|
4041
|
+
async function listTestSets(query) {
|
|
4042
|
+
const request = createRequest();
|
|
4043
|
+
const raw = await request(
|
|
4044
|
+
getCustomerServiceUrl(),
|
|
4045
|
+
"/v1/test_sets",
|
|
4046
|
+
{
|
|
4047
|
+
method: "GET",
|
|
4048
|
+
query: {
|
|
4049
|
+
customer_agent_config_id: query.customerAgentConfigId,
|
|
4050
|
+
keyword: query.keyword,
|
|
4051
|
+
page: query.page,
|
|
4052
|
+
page_size: query.pageSize
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
);
|
|
4056
|
+
return unwrapPaginated(raw, query.pageSize ?? 20);
|
|
4057
|
+
}
|
|
4058
|
+
async function getTestSet(id) {
|
|
4059
|
+
const request = createRequest();
|
|
4060
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${id}`, {
|
|
4061
|
+
method: "GET"
|
|
4062
|
+
});
|
|
4063
|
+
}
|
|
4064
|
+
async function updateTestSet(id, body) {
|
|
4065
|
+
const request = createRequest();
|
|
4066
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${id}`, {
|
|
4067
|
+
method: "PUT",
|
|
4068
|
+
body
|
|
4069
|
+
});
|
|
4070
|
+
}
|
|
4071
|
+
async function deleteTestSet(id) {
|
|
4072
|
+
const request = createRequest();
|
|
4073
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${id}`, {
|
|
4074
|
+
method: "DELETE"
|
|
4075
|
+
});
|
|
4076
|
+
}
|
|
4077
|
+
async function duplicateTestSet(id, body) {
|
|
4078
|
+
const request = createRequest();
|
|
4079
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${id}/duplicate`, {
|
|
4080
|
+
method: "POST",
|
|
4081
|
+
body
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
async function getEvalPrompt(id) {
|
|
4085
|
+
const request = createRequest();
|
|
4086
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${id}/evaluate-prompt`, {
|
|
4087
|
+
method: "GET"
|
|
4088
|
+
});
|
|
4089
|
+
}
|
|
4090
|
+
async function setEvalPrompt(id, body) {
|
|
4091
|
+
const request = createRequest();
|
|
4092
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${id}/evaluate-prompt`, {
|
|
4093
|
+
method: "PUT",
|
|
4094
|
+
body
|
|
4095
|
+
});
|
|
4096
|
+
}
|
|
4097
|
+
async function listCases(testSetId, query = {}) {
|
|
4098
|
+
const request = createRequest();
|
|
4099
|
+
const raw = await request(
|
|
4100
|
+
getCustomerServiceUrl(),
|
|
4101
|
+
`/v1/test_sets/${testSetId}/cases`,
|
|
4102
|
+
{
|
|
4103
|
+
method: "GET",
|
|
4104
|
+
query: {
|
|
4105
|
+
page: query.page,
|
|
4106
|
+
page_size: query.pageSize
|
|
4107
|
+
}
|
|
4108
|
+
}
|
|
4109
|
+
);
|
|
4110
|
+
return unwrapPaginated(raw, query.pageSize ?? 20);
|
|
4111
|
+
}
|
|
4112
|
+
async function createCase(testSetId, body) {
|
|
4113
|
+
const request = createRequest();
|
|
4114
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${testSetId}/cases`, {
|
|
4115
|
+
method: "POST",
|
|
4116
|
+
body
|
|
4117
|
+
});
|
|
4118
|
+
}
|
|
4119
|
+
async function updateCase(testSetId, caseId, body) {
|
|
4120
|
+
const request = createRequest();
|
|
4121
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${testSetId}/cases/${caseId}`, {
|
|
4122
|
+
method: "PUT",
|
|
4123
|
+
body
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
async function deleteCase(testSetId, caseId) {
|
|
4127
|
+
const request = createRequest();
|
|
4128
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/${testSetId}/cases/${caseId}`, {
|
|
4129
|
+
method: "DELETE"
|
|
4130
|
+
});
|
|
4131
|
+
}
|
|
4132
|
+
async function listBatches(query) {
|
|
4133
|
+
const request = createRequest();
|
|
4134
|
+
const raw = await request(
|
|
4135
|
+
getCustomerServiceUrl(),
|
|
4136
|
+
"/v1/test_sets/execution_batches",
|
|
4137
|
+
{
|
|
4138
|
+
method: "GET",
|
|
4139
|
+
query: {
|
|
4140
|
+
test_set_id: query.testSetId,
|
|
4141
|
+
customer_agent_config_id: query.customerAgentConfigId,
|
|
4142
|
+
status: query.status,
|
|
4143
|
+
keyword: query.keyword,
|
|
4144
|
+
page: query.page,
|
|
4145
|
+
page_size: query.pageSize
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
);
|
|
4149
|
+
return unwrapPaginated(raw, query.pageSize ?? 20);
|
|
4150
|
+
}
|
|
4151
|
+
async function getBatch(batchId) {
|
|
4152
|
+
const request = createRequest();
|
|
4153
|
+
return request(getCustomerServiceUrl(), `/v1/test_sets/execution_batches/${batchId}`, {
|
|
4154
|
+
method: "GET"
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
async function getBatchStatus(batchId) {
|
|
4158
|
+
const request = createRequest();
|
|
4159
|
+
return request(
|
|
4160
|
+
getCustomerServiceUrl(),
|
|
4161
|
+
`/v1/test_sets/execution_batches/${batchId}/status`,
|
|
4162
|
+
{
|
|
4163
|
+
method: "GET"
|
|
4164
|
+
}
|
|
4165
|
+
);
|
|
4166
|
+
}
|
|
4167
|
+
async function executeBatch(testSetId, customerAgentConfigId) {
|
|
4168
|
+
const request = createRequest();
|
|
4169
|
+
return request(getCustomerServiceUrl(), "/v1/test_sets/execute", {
|
|
4170
|
+
method: "POST",
|
|
4171
|
+
body: {
|
|
4172
|
+
test_set_id: testSetId,
|
|
4173
|
+
customer_agent_config_id: customerAgentConfigId
|
|
4174
|
+
}
|
|
4175
|
+
});
|
|
4176
|
+
}
|
|
4177
|
+
async function rerunCase(batchId, caseId) {
|
|
4178
|
+
const request = createRequest();
|
|
4179
|
+
return request(
|
|
4180
|
+
getCustomerServiceUrl(),
|
|
4181
|
+
`/v1/test_sets/execution_batches/${batchId}/cases/${caseId}/rerun`,
|
|
4182
|
+
{
|
|
4183
|
+
method: "POST"
|
|
4184
|
+
}
|
|
4185
|
+
);
|
|
4186
|
+
}
|
|
4187
|
+
function buildAuthAndWorkspaceHeaders() {
|
|
4188
|
+
const creds = readCredentials();
|
|
4189
|
+
if (!creds) {
|
|
4190
|
+
throw new APIError(2, "\u672A\u767B\u5F55\uFF0C\u8BF7\u8FD0\u884C: cs-cli auth login");
|
|
4191
|
+
}
|
|
4192
|
+
if (isTokenExpired(creds.expiresAt)) {
|
|
4193
|
+
clearCredentials();
|
|
4194
|
+
throw new APIError(2, "Token \u5DF2\u8FC7\u671F\uFF0C\u8BF7\u8FD0\u884C: cs-cli auth login");
|
|
4195
|
+
}
|
|
4196
|
+
const envLock = readEnvLockState();
|
|
4197
|
+
const config = readConfig();
|
|
4198
|
+
let workspaceId;
|
|
4199
|
+
if (envLock.workspaceId) {
|
|
4200
|
+
assertNoWorkspaceOverride(getRuntimeWorkspaceId());
|
|
4201
|
+
workspaceId = envLock.workspaceId;
|
|
4202
|
+
} else {
|
|
4203
|
+
workspaceId = getRuntimeWorkspaceId() ?? config?.defaultWorkspaceId;
|
|
4204
|
+
}
|
|
4205
|
+
if (!workspaceId) {
|
|
4206
|
+
throw new APIError(1, "\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u7A7A\u95F4\uFF0C\u8BF7\u8FD0\u884C: cs-cli config set-workspace <id>");
|
|
4207
|
+
}
|
|
4208
|
+
return {
|
|
4209
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
4210
|
+
"workspace-id": workspaceId
|
|
4211
|
+
};
|
|
4212
|
+
}
|
|
4213
|
+
async function exportBatchToFile(testSetId, batchId, outputPath) {
|
|
4214
|
+
const headers = buildAuthAndWorkspaceHeaders();
|
|
4215
|
+
const baseUrl = getCustomerServiceUrl();
|
|
4216
|
+
const timeoutMs = getRuntimeRequestTimeoutMs() ?? 6e4;
|
|
4217
|
+
const url = `${baseUrl}/v1/test_sets/${testSetId}/export?batch_id=${encodeURIComponent(batchId)}`;
|
|
4218
|
+
const response = await fetch(url, {
|
|
4219
|
+
method: "GET",
|
|
4220
|
+
headers,
|
|
4221
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
4222
|
+
});
|
|
4223
|
+
if (!response.ok) {
|
|
4224
|
+
if (response.status === 401) clearCredentials();
|
|
4225
|
+
throw new APIError(response.status, `\u5BFC\u51FA\u5931\u8D25 HTTP ${response.status}`);
|
|
4226
|
+
}
|
|
4227
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
4228
|
+
if (!contentType.toLowerCase().includes("spreadsheetml")) {
|
|
4229
|
+
throw new APIError(1, `\u5BFC\u51FA\u54CD\u5E94 Content-Type \u975E xlsx: ${contentType || "<\u7A7A>"}`);
|
|
4230
|
+
}
|
|
4231
|
+
const arrayBuf = await response.arrayBuffer();
|
|
4232
|
+
const buf = Buffer.from(arrayBuf);
|
|
4233
|
+
await writeBinaryToFile(outputPath, buf);
|
|
4234
|
+
return { path: outputPath, bytes: buf.byteLength };
|
|
4235
|
+
}
|
|
4236
|
+
|
|
4237
|
+
// src/commands/batch.ts
|
|
4238
|
+
function registerBatchCommand(rootProgram, parent = rootProgram) {
|
|
4239
|
+
const batch = parent.command("batch").description(
|
|
4240
|
+
"\u6267\u884C\u6279\u6B21\uFF08ExecutionBatch\uFF09\u7BA1\u7406 \u2014\u2014 \u6D4B\u8BD5\u96C6\u56DE\u5F52\u6267\u884C\u540E\u7684\u6279\u6B21\u5217\u8868 / \u8BE6\u60C5 / \u72B6\u6001\u67E5\u8BE2\u3002"
|
|
4241
|
+
);
|
|
4242
|
+
batch.command("list").description("\u5217\u51FA\u6267\u884C\u6279\u6B21\uFF08--testset \u53EF\u9009\uFF1B\u7F3A\u7701\u5217\u5F53\u524D workspace \u5168\u90E8\u6279\u6B21\uFF09").option("--testset <id>", "\u6309\u6D4B\u8BD5\u96C6\u7B5B\u9009").option("--customer-agent-config-id <id>", "\u6309 Agent \u914D\u7F6E\u7B5B\u9009").option("--status <status>", "\u6309\u72B6\u6001\u7B5B\u9009\uFF08\u5982 running / finished / failed\uFF09").option("--keyword <text>", "\u6309\u5173\u952E\u8BCD\u6A21\u7CCA\u641C\u7D22").option("--page <number>", "\u9875\u7801", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF", "20").action(async (opts) => {
|
|
4243
|
+
try {
|
|
4244
|
+
const result = await listBatches({
|
|
4245
|
+
testSetId: opts.testset,
|
|
4246
|
+
customerAgentConfigId: opts.customerAgentConfigId,
|
|
4247
|
+
status: opts.status,
|
|
4248
|
+
keyword: opts.keyword,
|
|
4249
|
+
page: Number(opts.page),
|
|
4250
|
+
pageSize: Number(opts.pageSize)
|
|
4251
|
+
});
|
|
4252
|
+
formatOutput(
|
|
4253
|
+
{
|
|
4254
|
+
success: true,
|
|
4255
|
+
data: result.items,
|
|
4256
|
+
pagination: {
|
|
4257
|
+
page: result.page,
|
|
4258
|
+
pageSize: result.pageSize,
|
|
4259
|
+
total: result.total
|
|
4260
|
+
}
|
|
4261
|
+
},
|
|
4262
|
+
rootProgram.opts().table
|
|
4263
|
+
);
|
|
4264
|
+
} catch (err) {
|
|
4265
|
+
reportCaughtError(err);
|
|
4266
|
+
process.exit(toExitCode(err));
|
|
4267
|
+
}
|
|
4268
|
+
});
|
|
4269
|
+
batch.command("show").description("\u67E5\u770B\u6279\u6B21\u8BE6\u60C5\uFF08summary\uFF09").argument("<batch_id>", "\u6279\u6B21 ID").action(async (batchId) => {
|
|
4270
|
+
try {
|
|
4271
|
+
const data = await getBatch(batchId);
|
|
4272
|
+
formatOutput({ success: true, data }, rootProgram.opts().table);
|
|
4273
|
+
} catch (err) {
|
|
4274
|
+
reportCaughtError(err);
|
|
4275
|
+
process.exit(toExitCode(err));
|
|
4276
|
+
}
|
|
4277
|
+
});
|
|
4278
|
+
batch.command("status").description("\u67E5\u770B\u6279\u6B21\u5B9E\u65F6\u72B6\u6001\uFF08\u542B is_terminal \u5B57\u6BB5\uFF09").argument("<batch_id>", "\u6279\u6B21 ID").action(async (batchId) => {
|
|
4279
|
+
try {
|
|
4280
|
+
const data = await getBatchStatus(batchId);
|
|
4281
|
+
formatOutput({ success: true, data }, rootProgram.opts().table);
|
|
4282
|
+
} catch (err) {
|
|
4283
|
+
reportCaughtError(err);
|
|
4284
|
+
process.exit(toExitCode(err));
|
|
4285
|
+
}
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
// src/commands/case.ts
|
|
4290
|
+
var SHOW_PAGE_SIZE = 200;
|
|
4291
|
+
var SHOW_MAX_PAGES = 1e3;
|
|
4292
|
+
function registerCaseCommand(rootProgram, parent = rootProgram) {
|
|
4293
|
+
const caseCmd = parent.command("case").description(
|
|
4294
|
+
"\u6D4B\u8BD5\u7528\u4F8B\uFF08TestCase\uFF09\u7BA1\u7406 \u2014\u2014 \u5355\u6761 case \u7684 CRUD\uFF0C\u6240\u6709\u5B50\u547D\u4EE4\u9700\u8981 --testset \u951A\u5B9A\u5F52\u5C5E\u6D4B\u8BD5\u96C6\u3002"
|
|
4295
|
+
);
|
|
4296
|
+
caseCmd.command("list").description("\u5217\u51FA\u6307\u5B9A\u6D4B\u8BD5\u96C6\u4E0B\u7684\u7528\u4F8B").requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").option("--page <number>", "\u9875\u7801", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF", "20").action(async (opts) => {
|
|
4297
|
+
try {
|
|
4298
|
+
const result = await listCases(opts.testset, {
|
|
4299
|
+
page: Number(opts.page),
|
|
4300
|
+
pageSize: Number(opts.pageSize)
|
|
4301
|
+
});
|
|
4302
|
+
formatOutput(
|
|
4303
|
+
{
|
|
4304
|
+
success: true,
|
|
4305
|
+
data: result.items,
|
|
4306
|
+
pagination: {
|
|
4307
|
+
page: result.page,
|
|
4308
|
+
pageSize: result.pageSize,
|
|
4309
|
+
total: result.total
|
|
4310
|
+
}
|
|
4311
|
+
},
|
|
4312
|
+
rootProgram.opts().table
|
|
4313
|
+
);
|
|
4314
|
+
} catch (err) {
|
|
4315
|
+
reportCaughtError(err);
|
|
4316
|
+
process.exit(toExitCode(err));
|
|
4317
|
+
}
|
|
4318
|
+
});
|
|
4319
|
+
caseCmd.command("show").description("\u67E5\u770B\u5355\u6761\u7528\u4F8B\u8BE6\u60C5\uFF08\u5185\u90E8\u7528 list+filter \u5B9E\u73B0\uFF0C\u81EA\u52A8\u7FFB\u9875\uFF09").argument("<case_id>", "\u7528\u4F8B ID").requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").action(async (caseId, opts) => {
|
|
4320
|
+
try {
|
|
4321
|
+
let page = 1;
|
|
4322
|
+
let accumulated = 0;
|
|
4323
|
+
while (page <= SHOW_MAX_PAGES) {
|
|
4324
|
+
const result = await listCases(opts.testset, {
|
|
4325
|
+
page,
|
|
4326
|
+
pageSize: SHOW_PAGE_SIZE
|
|
4327
|
+
});
|
|
4328
|
+
const items = result.items ?? [];
|
|
4329
|
+
const hit = items.find((it) => it?.case_id === caseId);
|
|
4330
|
+
if (hit) {
|
|
4331
|
+
formatOutput({ success: true, data: hit }, rootProgram.opts().table);
|
|
4332
|
+
return;
|
|
4333
|
+
}
|
|
4334
|
+
accumulated += items.length;
|
|
4335
|
+
const isLastPage = items.length === 0 || accumulated >= result.total;
|
|
4336
|
+
if (isLastPage) break;
|
|
4337
|
+
page += 1;
|
|
4338
|
+
}
|
|
4339
|
+
outputError(1, `\u7528\u4F8B ${caseId} \u4E0D\u5B58\u5728`);
|
|
4340
|
+
process.exit(1);
|
|
4341
|
+
} catch (err) {
|
|
4342
|
+
reportCaughtError(err);
|
|
4343
|
+
process.exit(toExitCode(err));
|
|
4344
|
+
}
|
|
4345
|
+
});
|
|
4346
|
+
caseCmd.command("create").description("\u65B0\u5EFA\u7528\u4F8B").requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--data <json|@file>", "\u7528\u4F8B JSON body \u6216 @file \u8DEF\u5F84\uFF08\u5FC5\u586B\uFF09").action(async (opts) => {
|
|
4347
|
+
try {
|
|
4348
|
+
const body = parseDataOption(opts.data);
|
|
4349
|
+
const data = await createCase(opts.testset, body);
|
|
4350
|
+
formatOutput({ success: true, data }, rootProgram.opts().table);
|
|
4351
|
+
} catch (err) {
|
|
4352
|
+
reportCaughtError(err);
|
|
4353
|
+
process.exit(toExitCode(err));
|
|
4354
|
+
}
|
|
4355
|
+
});
|
|
4356
|
+
caseCmd.command("update").description("\u66F4\u65B0\u7528\u4F8B").argument("<case_id>", "\u7528\u4F8B ID").requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--data <json|@file>", "\u66F4\u65B0 JSON body \u6216 @file \u8DEF\u5F84\uFF08\u5FC5\u586B\uFF09").action(async (caseId, opts) => {
|
|
4357
|
+
try {
|
|
4358
|
+
const body = parseDataOption(opts.data);
|
|
4359
|
+
const data = await updateCase(opts.testset, caseId, body);
|
|
4360
|
+
formatOutput({ success: true, data }, rootProgram.opts().table);
|
|
4361
|
+
} catch (err) {
|
|
4362
|
+
reportCaughtError(err);
|
|
4363
|
+
process.exit(toExitCode(err));
|
|
4364
|
+
}
|
|
4365
|
+
});
|
|
4366
|
+
caseCmd.command("delete").description("\u5220\u9664\u7528\u4F8B\uFF08\u4E0D\u5F39\u4E8C\u6B21\u786E\u8BA4\uFF09").argument("<case_id>", "\u7528\u4F8B ID").requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").action(async (caseId, opts) => {
|
|
4367
|
+
try {
|
|
4368
|
+
const data = await deleteCase(opts.testset, caseId);
|
|
4369
|
+
formatOutput({ success: true, data }, rootProgram.opts().table);
|
|
4370
|
+
} catch (err) {
|
|
4371
|
+
reportCaughtError(err);
|
|
4372
|
+
process.exit(toExitCode(err));
|
|
4373
|
+
}
|
|
4374
|
+
});
|
|
4375
|
+
}
|
|
4376
|
+
|
|
4377
|
+
// src/commands/export.ts
|
|
4378
|
+
function registerExportCommand(rootProgram, parent = rootProgram) {
|
|
4379
|
+
parent.command("export").description(
|
|
4380
|
+
"\u5BFC\u51FA\u6279\u6B21\u7ED3\u679C\u4E3A xlsx \u6587\u4EF6\uFF08GET /test_sets/{id}/export?batch_id=<id>\uFF0C\u843D\u76D8\u5230 --output\uFF09"
|
|
4381
|
+
).requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--batch <id>", "\u6279\u6B21 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--output <path>", "\u843D\u76D8\u6587\u4EF6\u8DEF\u5F84\uFF08\u5FC5\u586B\uFF0C\u7236\u76EE\u5F55\u81EA\u52A8 mkdir -p\uFF09").action(async (opts) => {
|
|
4382
|
+
try {
|
|
4383
|
+
const result = await exportBatchToFile(opts.testset, opts.batch, opts.output);
|
|
4384
|
+
formatOutput({ success: true, data: result }, rootProgram.opts().table);
|
|
4385
|
+
} catch (err) {
|
|
4386
|
+
reportCaughtError(err);
|
|
4387
|
+
process.exit(toExitCode(err));
|
|
4388
|
+
}
|
|
4389
|
+
});
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
// src/commands/run-case.ts
|
|
4393
|
+
function registerRunCaseCommand(rootProgram, parent = rootProgram) {
|
|
4394
|
+
parent.command("run-case").description(
|
|
4395
|
+
"\u91CD\u8DD1\u6307\u5B9A\u6279\u6B21\u5185\u7684\u5355\u6761\u7528\u4F8B\uFF08POST /test_sets/execution_batches/{B}/cases/{C}/rerun\uFF09"
|
|
4396
|
+
).requiredOption("--batch <id>", "\u6279\u6B21 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--case <id>", "\u7528\u4F8B ID\uFF08\u5FC5\u586B\uFF09").action(async (opts) => {
|
|
4397
|
+
try {
|
|
4398
|
+
const data = await rerunCase(opts.batch, opts.case);
|
|
4399
|
+
formatOutput({ success: true, data }, rootProgram.opts().table);
|
|
4400
|
+
} catch (err) {
|
|
4401
|
+
reportCaughtError(err);
|
|
4402
|
+
process.exit(toExitCode(err));
|
|
4403
|
+
}
|
|
4404
|
+
});
|
|
4405
|
+
}
|
|
4406
|
+
|
|
4407
|
+
// src/utils/poll.ts
|
|
4408
|
+
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
4409
|
+
async function pollUntilDone(opts) {
|
|
4410
|
+
const {
|
|
4411
|
+
intervalMs,
|
|
4412
|
+
timeoutSec,
|
|
4413
|
+
fetch: fetchFn,
|
|
4414
|
+
isDone,
|
|
4415
|
+
maxConsecutiveFailures = 3,
|
|
4416
|
+
now = Date.now,
|
|
4417
|
+
sleep = defaultSleep
|
|
4418
|
+
} = opts;
|
|
4419
|
+
const start = now();
|
|
4420
|
+
let lastResult;
|
|
4421
|
+
let lastError;
|
|
4422
|
+
let consecutiveFailures = 0;
|
|
4423
|
+
while (true) {
|
|
4424
|
+
const elapsedMs = now() - start;
|
|
4425
|
+
if (elapsedMs >= timeoutSec * 1e3) {
|
|
4426
|
+
return {
|
|
4427
|
+
done: false,
|
|
4428
|
+
timedOut: true,
|
|
4429
|
+
lastResult,
|
|
4430
|
+
elapsedSec: Math.floor(elapsedMs / 1e3)
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
try {
|
|
4434
|
+
const result = await fetchFn();
|
|
4435
|
+
lastResult = result;
|
|
4436
|
+
consecutiveFailures = 0;
|
|
4437
|
+
if (isDone(result)) {
|
|
4438
|
+
return { done: true, result };
|
|
4439
|
+
}
|
|
4440
|
+
} catch (err) {
|
|
4441
|
+
lastError = err;
|
|
4442
|
+
consecutiveFailures += 1;
|
|
4443
|
+
if (consecutiveFailures >= maxConsecutiveFailures) {
|
|
4444
|
+
return { done: false, failed: true, lastError };
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
const afterFetchElapsedMs = now() - start;
|
|
4448
|
+
if (afterFetchElapsedMs >= timeoutSec * 1e3) {
|
|
4449
|
+
return {
|
|
4450
|
+
done: false,
|
|
4451
|
+
timedOut: true,
|
|
4452
|
+
lastResult,
|
|
4453
|
+
elapsedSec: Math.floor(afterFetchElapsedMs / 1e3)
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
await sleep(intervalMs);
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
// src/commands/run.ts
|
|
4461
|
+
var DEFAULT_POLL_INTERVAL_MS = 3e3;
|
|
4462
|
+
var DEFAULT_TIMEOUT_SEC = 600;
|
|
4463
|
+
function extractBatchId(resp) {
|
|
4464
|
+
if (resp && typeof resp === "object" && "batch_id" in resp) {
|
|
4465
|
+
const v = resp.batch_id;
|
|
4466
|
+
if (typeof v === "string") return v;
|
|
4467
|
+
}
|
|
4468
|
+
return "";
|
|
4469
|
+
}
|
|
4470
|
+
function registerRunCommand(rootProgram, parent = rootProgram) {
|
|
4471
|
+
parent.command("run").description("\u89E6\u53D1\u6D4B\u8BD5\u96C6\u56DE\u5F52\u6267\u884C\uFF08\u9ED8\u8BA4\u5F02\u6B65\uFF1B--wait \u5207\u540C\u6B65\u8F6E\u8BE2\uFF0C\u8D85\u65F6\u4ECD exit 0 / ADR-002\uFF09").requiredOption("--testset <id>", "\u6D4B\u8BD5\u96C6 ID\uFF08\u5FC5\u586B\uFF09").requiredOption("--agent <id>", "Agent \u914D\u7F6E ID\uFF08\u5FC5\u586B\uFF0C\u5BF9\u5E94\u540E\u7AEF customer_agent_config_id\uFF09").option("--wait", "\u540C\u6B65\u7B49\u5F85\u7EC8\u6001\uFF08poll /status\uFF0C3s \u95F4\u9694\uFF0C--timeout \u63A7\u5236\u4E0A\u9650\uFF0C\u5355\u4F4D\uFF1A\u79D2\uFF09", false).option(
|
|
4472
|
+
"--timeout <sec>",
|
|
4473
|
+
"\u540C\u6B65\u7B49\u5F85\u4E0A\u9650\uFF08\u5355\u4F4D\uFF1A\u79D2\uFF1B\u4E0E\u5168\u5C40 --request-timeout \u7684\u6BEB\u79D2\u4E0D\u540C\uFF09",
|
|
4474
|
+
String(DEFAULT_TIMEOUT_SEC)
|
|
4475
|
+
).action(async (opts) => {
|
|
4476
|
+
let exitCode = 0;
|
|
4477
|
+
try {
|
|
4478
|
+
const triggerResp = await executeBatch(opts.testset, opts.agent);
|
|
4479
|
+
const batchId = extractBatchId(triggerResp);
|
|
4480
|
+
if (!opts.wait) {
|
|
4481
|
+
formatOutput({ success: true, data: { batch_id: batchId } }, rootProgram.opts().table);
|
|
4482
|
+
} else {
|
|
4483
|
+
const timeoutSec = Number(opts.timeout) || DEFAULT_TIMEOUT_SEC;
|
|
4484
|
+
const startedAt = Date.now();
|
|
4485
|
+
const pollResult = await pollUntilDone({
|
|
4486
|
+
intervalMs: DEFAULT_POLL_INTERVAL_MS,
|
|
4487
|
+
timeoutSec,
|
|
4488
|
+
fetch: () => getBatchStatus(batchId),
|
|
4489
|
+
isDone: (r) => r?.is_terminal === true
|
|
4490
|
+
});
|
|
4491
|
+
if (pollResult.done) {
|
|
4492
|
+
const elapsedSec = Math.floor((Date.now() - startedAt) / 1e3);
|
|
4493
|
+
formatOutput(
|
|
4494
|
+
{
|
|
4495
|
+
success: true,
|
|
4496
|
+
data: {
|
|
4497
|
+
...pollResult.result,
|
|
4498
|
+
elapsed_sec: elapsedSec
|
|
4499
|
+
}
|
|
4500
|
+
},
|
|
4501
|
+
rootProgram.opts().table
|
|
4502
|
+
);
|
|
4503
|
+
} else if ("timedOut" in pollResult && pollResult.timedOut) {
|
|
4504
|
+
const last = pollResult.lastResult ?? {};
|
|
4505
|
+
formatOutput(
|
|
4506
|
+
{
|
|
4507
|
+
success: true,
|
|
4508
|
+
data: {
|
|
4509
|
+
batch_id: batchId,
|
|
4510
|
+
status: last.status,
|
|
4511
|
+
is_terminal: false,
|
|
4512
|
+
timeout: true,
|
|
4513
|
+
elapsed_sec: pollResult.elapsedSec,
|
|
4514
|
+
total_count: last.total_count,
|
|
4515
|
+
pass_count: last.pass_count,
|
|
4516
|
+
fail_count: last.fail_count
|
|
4517
|
+
}
|
|
4518
|
+
},
|
|
4519
|
+
rootProgram.opts().table
|
|
4520
|
+
);
|
|
4521
|
+
} else if ("failed" in pollResult && pollResult.failed) {
|
|
4522
|
+
reportCaughtError(pollResult.lastError);
|
|
4523
|
+
const mapped = toExitCode(pollResult.lastError);
|
|
4524
|
+
exitCode = mapped === 1 ? 3 : mapped;
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
} catch (err) {
|
|
4528
|
+
reportCaughtError(err);
|
|
4529
|
+
exitCode = toExitCode(err);
|
|
4530
|
+
}
|
|
4531
|
+
process.exit(exitCode);
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
// src/commands/testset.ts
|
|
4536
|
+
function readPromptInput(value) {
|
|
4537
|
+
if (value.startsWith("@")) {
|
|
4538
|
+
const filePath = value.slice(1);
|
|
4539
|
+
if (!filePath) {
|
|
4540
|
+
throw new Error("File path cannot be empty after @");
|
|
4541
|
+
}
|
|
4542
|
+
return fs9.readFileSync(filePath, "utf-8");
|
|
4543
|
+
}
|
|
4544
|
+
return value;
|
|
4545
|
+
}
|
|
4546
|
+
function registerTestsetCommand(program2) {
|
|
4547
|
+
const testset = program2.command("testset").description(
|
|
4548
|
+
"\u6D4B\u8BD5\u96C6\uFF08TestSet\uFF09\u7BA1\u7406 \u2014\u2014 \u5BF9\u8BDD\u56DE\u5F52\u6D4B\u8BD5\u7528\u4F8B\u96C6\u5408\uFF0C\u914D\u5408 batch / run / export \u5F62\u6210\u95ED\u73AF\u3002"
|
|
4549
|
+
);
|
|
4550
|
+
testset.command("list").description("\u5217\u51FA\u6D4B\u8BD5\u96C6").requiredOption("--customer-agent-config-id <id>", "Agent \u914D\u7F6E ID\uFF08\u5FC5\u586B\uFF0C\u4E0E\u540E\u7AEF\u5951\u7EA6\u5BF9\u9F50\uFF09").option("--keyword <text>", "\u6309\u540D\u79F0\u5173\u952E\u8BCD\u6A21\u7CCA\u641C\u7D22").option("--page <number>", "\u9875\u7801", "1").option("--page-size <number>", "\u6BCF\u9875\u6570\u91CF", "20").action(async (opts) => {
|
|
4551
|
+
try {
|
|
4552
|
+
const result = await listTestSets({
|
|
4553
|
+
customerAgentConfigId: opts.customerAgentConfigId,
|
|
4554
|
+
keyword: opts.keyword,
|
|
4555
|
+
page: Number(opts.page),
|
|
4556
|
+
pageSize: Number(opts.pageSize)
|
|
4557
|
+
});
|
|
4558
|
+
formatOutput(
|
|
4559
|
+
{
|
|
4560
|
+
success: true,
|
|
4561
|
+
data: result.items,
|
|
4562
|
+
pagination: {
|
|
4563
|
+
page: result.page,
|
|
4564
|
+
pageSize: result.pageSize,
|
|
4565
|
+
total: result.total
|
|
4566
|
+
}
|
|
4567
|
+
},
|
|
4568
|
+
program2.opts().table
|
|
4569
|
+
);
|
|
4570
|
+
} catch (err) {
|
|
4571
|
+
reportCaughtError(err);
|
|
4572
|
+
process.exit(toExitCode(err));
|
|
4573
|
+
}
|
|
4574
|
+
});
|
|
4575
|
+
testset.command("show").description("\u67E5\u770B\u5355\u4E2A\u6D4B\u8BD5\u96C6\u8BE6\u60C5").argument("<id>", "\u6D4B\u8BD5\u96C6 ID").action(async (id) => {
|
|
4576
|
+
try {
|
|
4577
|
+
const data = await getTestSet(id);
|
|
4578
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
4579
|
+
} catch (err) {
|
|
4580
|
+
reportCaughtError(err);
|
|
4581
|
+
process.exit(toExitCode(err));
|
|
4582
|
+
}
|
|
4583
|
+
});
|
|
4584
|
+
testset.command("update").description("\u66F4\u65B0\u6D4B\u8BD5\u96C6\uFF08\u81F3\u5C11\u63D0\u4F9B --name / --description / --data \u4E4B\u4E00\uFF1B--data \u4F18\u5148\u5408\u5E76\uFF09").argument("<id>", "\u6D4B\u8BD5\u96C6 ID").option("--name <text>", "\u6D4B\u8BD5\u96C6\u540D\u79F0").option("--description <text>", "\u63CF\u8FF0").option("--data <json|@file>", "\u5B8C\u6574 JSON body\uFF08\u8986\u76D6 --name / --description\uFF09").action(async (id, opts) => {
|
|
4585
|
+
try {
|
|
4586
|
+
let body = {};
|
|
4587
|
+
if (opts.name !== void 0) body.test_set_name = opts.name;
|
|
4588
|
+
if (opts.description !== void 0) body.description = opts.description;
|
|
4589
|
+
if (opts.data !== void 0) {
|
|
4590
|
+
const parsed = parseDataOption(opts.data);
|
|
4591
|
+
body = { ...body, ...parsed };
|
|
4592
|
+
}
|
|
4593
|
+
if (Object.keys(body).length === 0) {
|
|
4594
|
+
outputError(1, "\u81F3\u5C11\u63D0\u4F9B --name/--description/--data \u4E4B\u4E00");
|
|
4595
|
+
process.exit(1);
|
|
4596
|
+
}
|
|
4597
|
+
const data = await updateTestSet(id, body);
|
|
4598
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
4599
|
+
} catch (err) {
|
|
4600
|
+
reportCaughtError(err);
|
|
4601
|
+
process.exit(toExitCode(err));
|
|
4602
|
+
}
|
|
4603
|
+
});
|
|
4604
|
+
testset.command("delete").description("\u5220\u9664\u6D4B\u8BD5\u96C6\uFF08\u76F4\u63A5\u6267\u884C\uFF0C\u4E0D\u5F39\u4E8C\u6B21\u786E\u8BA4\uFF09").argument("<id>", "\u6D4B\u8BD5\u96C6 ID").action(async (id) => {
|
|
4605
|
+
try {
|
|
4606
|
+
const data = await deleteTestSet(id);
|
|
4607
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
4608
|
+
} catch (err) {
|
|
4609
|
+
reportCaughtError(err);
|
|
4610
|
+
process.exit(toExitCode(err));
|
|
4611
|
+
}
|
|
4612
|
+
});
|
|
4613
|
+
testset.command("copy").description("\u590D\u5236\u6D4B\u8BD5\u96C6").argument("<id>", "\u6E90\u6D4B\u8BD5\u96C6 ID").option("--name <text>", "\u526F\u672C\u540D\u79F0").action(async (id, opts) => {
|
|
4614
|
+
try {
|
|
4615
|
+
const data = await duplicateTestSet(id, { test_set_name: opts.name });
|
|
4616
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
4617
|
+
} catch (err) {
|
|
4618
|
+
reportCaughtError(err);
|
|
4619
|
+
process.exit(toExitCode(err));
|
|
4620
|
+
}
|
|
4621
|
+
});
|
|
4622
|
+
testset.command("get-eval-prompt").description("\u67E5\u770B\u6D4B\u8BD5\u96C6\u7684\u8BC4\u4EF7\u63D0\u793A\u8BCD").argument("<id>", "\u6D4B\u8BD5\u96C6 ID").option("--raw", "\u88F8\u6587\u672C\u8F93\u51FA\u5230 stdout\uFF08\u4E0D\u5E26 JSON \u5305\u88C5\uFF09", false).action(async (id, opts) => {
|
|
4623
|
+
try {
|
|
4624
|
+
const data = await getEvalPrompt(id);
|
|
4625
|
+
if (opts.raw) {
|
|
4626
|
+
const text = data?.prompt_template ?? "";
|
|
4627
|
+
process.stdout.write(text);
|
|
4628
|
+
return;
|
|
4629
|
+
}
|
|
4630
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
4631
|
+
} catch (err) {
|
|
4632
|
+
reportCaughtError(err);
|
|
4633
|
+
process.exit(toExitCode(err));
|
|
4634
|
+
}
|
|
4635
|
+
});
|
|
4636
|
+
testset.command("set-eval-prompt").description("\u8BBE\u7F6E\u6D4B\u8BD5\u96C6\u7684\u8BC4\u4EF7\u63D0\u793A\u8BCD\uFF08\u652F\u6301 @file \u8BFB\u7EAF\u6587\u672C\uFF09").argument("<id>", "\u6D4B\u8BD5\u96C6 ID").requiredOption("--prompt <text|@file>", "\u63D0\u793A\u8BCD\u5B57\u9762\u91CF\u6216 @file \u8DEF\u5F84").action(async (id, opts) => {
|
|
4637
|
+
try {
|
|
4638
|
+
const prompt = readPromptInput(opts.prompt);
|
|
4639
|
+
const data = await setEvalPrompt(id, { prompt });
|
|
4640
|
+
formatOutput({ success: true, data }, program2.opts().table);
|
|
4641
|
+
} catch (err) {
|
|
4642
|
+
reportCaughtError(err);
|
|
4643
|
+
process.exit(toExitCode(err));
|
|
4644
|
+
}
|
|
4645
|
+
});
|
|
4646
|
+
registerCaseCommand(program2, testset);
|
|
4647
|
+
registerBatchCommand(program2, testset);
|
|
4648
|
+
registerRunCommand(program2, testset);
|
|
4649
|
+
registerRunCaseCommand(program2, testset);
|
|
4650
|
+
registerExportCommand(program2, testset);
|
|
4651
|
+
}
|
|
4652
|
+
|
|
3450
4653
|
// src/commands/workspace.ts
|
|
3451
4654
|
var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
3452
4655
|
function toISODate(d) {
|
|
@@ -3533,6 +4736,7 @@ registerMonitorCommand(program);
|
|
|
3533
4736
|
registerRepairRecordCommand(program);
|
|
3534
4737
|
registerOperationsRecordCommand(program);
|
|
3535
4738
|
registerChangeConsumerCommand(program);
|
|
4739
|
+
registerTestsetCommand(program);
|
|
3536
4740
|
process.on("uncaughtException", (err) => {
|
|
3537
4741
|
outputError(3, err.message);
|
|
3538
4742
|
process.exit(3);
|