bfi-cli 0.1.107 → 0.1.109
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/dist/cli.mjs +55 -28
- package/dist/mcp-pm/main.mjs +88 -25
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -8714,6 +8714,31 @@ async function runInstall(args, globalOpts) {
|
|
|
8714
8714
|
console.log();
|
|
8715
8715
|
}
|
|
8716
8716
|
const YMC_DEFAULT_BASE = process.env.BFI_YMC_BASE || "https://bfi.yyuap.com/bfi-api/bficli/api/v2";
|
|
8717
|
+
function messageOf(parsed) {
|
|
8718
|
+
const raw = parsed.msg ?? parsed.message;
|
|
8719
|
+
if (typeof raw === "string") return raw;
|
|
8720
|
+
if (raw === void 0 || raw === null) return "";
|
|
8721
|
+
try {
|
|
8722
|
+
return JSON.stringify(raw);
|
|
8723
|
+
} catch {
|
|
8724
|
+
return String(raw);
|
|
8725
|
+
}
|
|
8726
|
+
}
|
|
8727
|
+
function describeApiError(parsed, status, raw = "") {
|
|
8728
|
+
const prefix = status >= 400 ? `HTTP ${status} ` : "";
|
|
8729
|
+
const detail = parsed.detail;
|
|
8730
|
+
if (Array.isArray(detail) && detail.length > 0) return `${prefix}${detail.map((item) => {
|
|
8731
|
+
if (!item || typeof item !== "object") return String(item);
|
|
8732
|
+
const o = item;
|
|
8733
|
+
const loc = Array.isArray(o.loc) ? o.loc.map(String).join(".") : "";
|
|
8734
|
+
const msg = o.msg ?? o.message ?? o.type ?? "参数错误";
|
|
8735
|
+
return loc ? `${loc}: ${String(msg)}` : String(msg);
|
|
8736
|
+
}).join("; ")}`;
|
|
8737
|
+
if (detail !== void 0 && detail !== null) return `${prefix}${typeof detail === "string" ? detail : JSON.stringify(detail)}`;
|
|
8738
|
+
const msg = messageOf(parsed);
|
|
8739
|
+
if (msg.trim()) return `${prefix}${msg}`;
|
|
8740
|
+
return `${prefix}${raw.trim().slice(0, 200) || "服务端未返回错误信息"}`;
|
|
8741
|
+
}
|
|
8717
8742
|
var YmcApi = class {
|
|
8718
8743
|
baseUrl;
|
|
8719
8744
|
token;
|
|
@@ -8732,12 +8757,12 @@ var YmcApi = class {
|
|
|
8732
8757
|
if (body !== void 0 && Object.keys(body).length > 0) console.log(JSON.stringify(body, null, 2));
|
|
8733
8758
|
process.exit(0);
|
|
8734
8759
|
}
|
|
8735
|
-
async call(path, body = {}, timeoutMs =
|
|
8760
|
+
async call(path, body = {}, timeoutMs = 12e4) {
|
|
8736
8761
|
if (this.dryRun) this._dryRunExit("POST", path, body);
|
|
8737
8762
|
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8738
8763
|
const payload = JSON.stringify(body);
|
|
8739
8764
|
const mod = url.startsWith("https://") ? https : http;
|
|
8740
|
-
const raw = await new Promise((resolve, reject) => {
|
|
8765
|
+
const { status, text: raw } = await new Promise((resolve, reject) => {
|
|
8741
8766
|
const req = mod.request(url, {
|
|
8742
8767
|
method: "POST",
|
|
8743
8768
|
headers: {
|
|
@@ -8750,7 +8775,10 @@ var YmcApi = class {
|
|
|
8750
8775
|
}, (res) => {
|
|
8751
8776
|
const chunks = [];
|
|
8752
8777
|
res.on("data", (c) => chunks.push(c));
|
|
8753
|
-
res.on("end", () => resolve(
|
|
8778
|
+
res.on("end", () => resolve({
|
|
8779
|
+
status: res.statusCode ?? 0,
|
|
8780
|
+
text: Buffer.concat(chunks).toString("utf-8")
|
|
8781
|
+
}));
|
|
8754
8782
|
});
|
|
8755
8783
|
req.on("timeout", () => {
|
|
8756
8784
|
req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`));
|
|
@@ -8763,12 +8791,13 @@ var YmcApi = class {
|
|
|
8763
8791
|
try {
|
|
8764
8792
|
parsed = JSON.parse(raw);
|
|
8765
8793
|
} catch {
|
|
8766
|
-
throw new Error(
|
|
8794
|
+
throw new Error(`HTTP ${status} 非 JSON 响应:${raw.slice(0, 200)}`);
|
|
8767
8795
|
}
|
|
8768
|
-
if (parsed.detail && parsed.code === void 0) throw new Error(
|
|
8796
|
+
if (parsed.detail !== void 0 && parsed.code === void 0) throw new Error(describeApiError(parsed, status, raw));
|
|
8797
|
+
if (status >= 400 && parsed.code === void 0 && parsed.msg === void 0) throw new Error(describeApiError(parsed, status, raw));
|
|
8769
8798
|
return {
|
|
8770
8799
|
code: typeof parsed.code === "number" ? parsed.code : -1,
|
|
8771
|
-
msg:
|
|
8800
|
+
msg: messageOf(parsed),
|
|
8772
8801
|
data: parsed.data ?? null,
|
|
8773
8802
|
raw: parsed
|
|
8774
8803
|
};
|
|
@@ -8816,7 +8845,7 @@ var YmcApi = class {
|
|
|
8816
8845
|
if (this.dryRun) this._dryRunExit("GET", path);
|
|
8817
8846
|
const url = `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
8818
8847
|
const mod = url.startsWith("https://") ? https : http;
|
|
8819
|
-
const raw = await new Promise((resolve, reject) => {
|
|
8848
|
+
const { status, text: raw } = await new Promise((resolve, reject) => {
|
|
8820
8849
|
const req = mod.request(url, {
|
|
8821
8850
|
method: "GET",
|
|
8822
8851
|
headers: {
|
|
@@ -8827,7 +8856,10 @@ var YmcApi = class {
|
|
|
8827
8856
|
}, (res) => {
|
|
8828
8857
|
const chunks = [];
|
|
8829
8858
|
res.on("data", (c) => chunks.push(c));
|
|
8830
|
-
res.on("end", () => resolve(
|
|
8859
|
+
res.on("end", () => resolve({
|
|
8860
|
+
status: res.statusCode ?? 0,
|
|
8861
|
+
text: Buffer.concat(chunks).toString("utf-8")
|
|
8862
|
+
}));
|
|
8831
8863
|
});
|
|
8832
8864
|
req.on("timeout", () => {
|
|
8833
8865
|
req.destroy(/* @__PURE__ */ new Error(`请求超时(${timeoutMs}ms)`));
|
|
@@ -8835,11 +8867,16 @@ var YmcApi = class {
|
|
|
8835
8867
|
req.on("error", reject);
|
|
8836
8868
|
req.end();
|
|
8837
8869
|
});
|
|
8838
|
-
|
|
8839
|
-
|
|
8870
|
+
let parsed;
|
|
8871
|
+
try {
|
|
8872
|
+
parsed = JSON.parse(raw);
|
|
8873
|
+
} catch {
|
|
8874
|
+
throw new Error(`HTTP ${status} 非 JSON 响应:${raw.slice(0, 200)}`);
|
|
8875
|
+
}
|
|
8876
|
+
if (parsed.detail !== void 0 && parsed.code === void 0) throw new Error(describeApiError(parsed, status, raw));
|
|
8840
8877
|
return {
|
|
8841
8878
|
code: typeof parsed.code === "number" ? parsed.code : -1,
|
|
8842
|
-
msg:
|
|
8879
|
+
msg: messageOf(parsed),
|
|
8843
8880
|
data: parsed.data ?? null,
|
|
8844
8881
|
raw: parsed
|
|
8845
8882
|
};
|
|
@@ -8921,7 +8958,6 @@ const F_ENV = [
|
|
|
8921
8958
|
"--env",
|
|
8922
8959
|
"--env-name"
|
|
8923
8960
|
];
|
|
8924
|
-
const F_TIME = ["-t", "--time"];
|
|
8925
8961
|
const F_RANGE = [
|
|
8926
8962
|
"-t",
|
|
8927
8963
|
"--time",
|
|
@@ -8941,7 +8977,7 @@ const ALLOWED_FLAGS = {
|
|
|
8941
8977
|
],
|
|
8942
8978
|
"sls-grep": [
|
|
8943
8979
|
...F_ENV,
|
|
8944
|
-
...
|
|
8980
|
+
...F_RANGE,
|
|
8945
8981
|
"--group-by",
|
|
8946
8982
|
"--line",
|
|
8947
8983
|
"--app"
|
|
@@ -9091,15 +9127,6 @@ function requireEnv(args) {
|
|
|
9091
9127
|
}
|
|
9092
9128
|
return { env_name: env };
|
|
9093
9129
|
}
|
|
9094
|
-
function timeOf(args, required = true) {
|
|
9095
|
-
const t = opt(args, "-t", "--time");
|
|
9096
|
-
const err = timeError(t);
|
|
9097
|
-
if (err && required) {
|
|
9098
|
-
console.error(err);
|
|
9099
|
-
process.exit(1);
|
|
9100
|
-
}
|
|
9101
|
-
return t ? { time: t } : {};
|
|
9102
|
-
}
|
|
9103
9130
|
function timeOpts(args) {
|
|
9104
9131
|
const out = {};
|
|
9105
9132
|
const t = opt(args, "-t", "--time");
|
|
@@ -9192,7 +9219,7 @@ async function runYmc(args, globalOpts) {
|
|
|
9192
9219
|
trace_id: id,
|
|
9193
9220
|
...requireEnv(rest),
|
|
9194
9221
|
...opt(rest, "--ts") ? { ts: Number(opt(rest, "--ts")) } : {}
|
|
9195
|
-
}), jsonMode, (data) => {
|
|
9222
|
+
}, 18e4), jsonMode, (data) => {
|
|
9196
9223
|
const d = data;
|
|
9197
9224
|
const lines = [];
|
|
9198
9225
|
const wc = d.warning_chain;
|
|
@@ -9277,7 +9304,7 @@ async function runYmc(args, globalOpts) {
|
|
|
9277
9304
|
case "sls-grep": {
|
|
9278
9305
|
const kw = positionals(rest)[0];
|
|
9279
9306
|
if (!kw) {
|
|
9280
|
-
console.error("用法:bfi ymc sls-grep \"<关键词>\" -e <env> [-t time] [--app <appCode>] [--group-by applicationName|message|requestURL]");
|
|
9307
|
+
console.error("用法:bfi ymc sls-grep \"<关键词>\" -e <env> [-t <time> | --start-time <s> --end-time <e>] [--app <appCode>] [--group-by applicationName|message|requestURL]");
|
|
9281
9308
|
console.error("例:bfi ymc sls-grep \"ClassCastException\" -e daily -t 3d");
|
|
9282
9309
|
console.error("⚠️ 关键词按「整值」匹配:类名/全限定异常名必须整串加引号");
|
|
9283
9310
|
console.error(" (\"java.lang.OutOfMemoryError\" 命中;简写 \"OutOfMemoryError\" 恒 0 且不报错)——sls-discipline §3");
|
|
@@ -9292,7 +9319,7 @@ async function runYmc(args, globalOpts) {
|
|
|
9292
9319
|
emit(await api.call("sls/raw", {
|
|
9293
9320
|
query,
|
|
9294
9321
|
...requireEnv(rest),
|
|
9295
|
-
...
|
|
9322
|
+
...timeOpts(rest),
|
|
9296
9323
|
line: Number(opt(rest, "--line") || 50)
|
|
9297
9324
|
}), jsonMode, aggSummary(`SLS 全文检索 "${kw}"`));
|
|
9298
9325
|
break;
|
|
@@ -9404,7 +9431,7 @@ async function runYmc(args, globalOpts) {
|
|
|
9404
9431
|
};
|
|
9405
9432
|
const app = opt(rest, "--app");
|
|
9406
9433
|
if (app) body.filters = { service_name: app };
|
|
9407
|
-
emit(await api.call("kibana/stats", body), jsonMode, (data) => {
|
|
9434
|
+
emit(await api.call("kibana/stats", body, 18e4), jsonMode, (data) => {
|
|
9408
9435
|
const d = data;
|
|
9409
9436
|
const a = d.aggs;
|
|
9410
9437
|
return `✓ URL 统计${app ? ` [${app}]` : "(⚠️ 未限定服务,共享路径为全平台混合)"}: QPS=${a.qps} avg=${a.avg_rt?.value ?? "-"} 慢请求Top=${Array.isArray(d.slow_top) ? d.slow_top.length : 0}`;
|
|
@@ -9429,7 +9456,7 @@ async function runYmc(args, globalOpts) {
|
|
|
9429
9456
|
console.error("用法:bfi ymc ingress --trace-id <id> | --uri </path> [-app <service_name>] -e <env> [-t time]");
|
|
9430
9457
|
process.exit(1);
|
|
9431
9458
|
}
|
|
9432
|
-
emit(await api.call("kibana/search", body), jsonMode, rowsSummary("ingress 日志"));
|
|
9459
|
+
emit(await api.call("kibana/search", body, 18e4), jsonMode, rowsSummary("ingress 日志"));
|
|
9433
9460
|
break;
|
|
9434
9461
|
}
|
|
9435
9462
|
case "dump-list": {
|
|
@@ -9721,7 +9748,7 @@ const HELP = {
|
|
|
9721
9748
|
bfi ymc sls-raw 'applicationname: yonbip-scm-scmmp AND "unaudit" | SELECT traceid, time LIMIT 10' -e core3 -t 1d
|
|
9722
9749
|
注意:业务关键词检索必须 applicationname: 限定目标服务(共享路径 URL 全平台都有);
|
|
9723
9750
|
traceid/spanid 检索例外(全局唯一)。非索引字段(logger/requesturl)不能进检索语法,只能在 SELECT 里引用。`,
|
|
9724
|
-
"sls-grep": `bfi ymc sls-grep "<关键词>" -e <env> -t <time> [--app <appCode>] [--group-by <字段>] [--json]
|
|
9751
|
+
"sls-grep": `bfi ymc sls-grep "<关键词>" -e <env> [-t <time> | --start-time <s> --end-time <e>] [--app <appCode>] [--group-by <字段>] [--json]
|
|
9725
9752
|
SLS 全文检索聚合(每组计数精确不封顶;组数上限 500——实测 2026-09-17:计数不受限,仅组数受 SLS 默认 LIMIT 约束,服务端已放宽)。
|
|
9726
9753
|
⚠️ 关键词按「整值」匹配:类名/全限定异常名必须整串加引号
|
|
9727
9754
|
("java.lang.OutOfMemoryError" 命中;简写 "OutOfMemoryError" 恒 0 命中且不报错)——sls-discipline §3
|
package/dist/mcp-pm/main.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { CallToolRequestSchema, CallToolResultSchema, ListToolsRequestSchema, ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
4
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
5
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
+
import { openAsBlob } from "node:fs";
|
|
6
7
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
const PM_ORIGIN = "https://bfi.yyuap.com";
|
|
@@ -42,27 +43,50 @@ var PmRemote = class {
|
|
|
42
43
|
}
|
|
43
44
|
async listTools() {
|
|
44
45
|
try {
|
|
45
|
-
return
|
|
46
|
+
return await this.listToolsOnce();
|
|
46
47
|
} catch (e) {
|
|
48
|
+
if (isSessionInvalid(e)) {
|
|
49
|
+
this.client = null;
|
|
50
|
+
try {
|
|
51
|
+
return await this.listToolsOnce();
|
|
52
|
+
} catch (e2) {
|
|
53
|
+
console.error(`[bfi-pm-mcp] 远端工具列表获取失败: ${messageOf(e2)}`);
|
|
54
|
+
this.client = null;
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
47
58
|
console.error(`[bfi-pm-mcp] 远端工具列表获取失败: ${messageOf(e)}`);
|
|
48
59
|
this.client = null;
|
|
49
60
|
return [];
|
|
50
61
|
}
|
|
51
62
|
}
|
|
52
63
|
async callTool(name, args) {
|
|
53
|
-
const client = await this.ensure();
|
|
54
64
|
try {
|
|
55
|
-
return await
|
|
56
|
-
method: "tools/call",
|
|
57
|
-
params: {
|
|
58
|
-
name,
|
|
59
|
-
arguments: args ?? {}
|
|
60
|
-
}
|
|
61
|
-
}, CallToolResultSchema);
|
|
65
|
+
return await this.callToolOnce(name, args);
|
|
62
66
|
} catch (e) {
|
|
67
|
+
if (isSessionInvalid(e)) {
|
|
68
|
+
this.client = null;
|
|
69
|
+
try {
|
|
70
|
+
return await this.callToolOnce(name, args);
|
|
71
|
+
} catch (e2) {
|
|
72
|
+
return this.handleRemoteFailure(e2);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
63
75
|
return this.handleRemoteFailure(e);
|
|
64
76
|
}
|
|
65
77
|
}
|
|
78
|
+
async listToolsOnce() {
|
|
79
|
+
return (await (await this.ensure()).request({ method: "tools/list" }, ListToolsResultSchema)).tools;
|
|
80
|
+
}
|
|
81
|
+
async callToolOnce(name, args) {
|
|
82
|
+
return (await this.ensure()).request({
|
|
83
|
+
method: "tools/call",
|
|
84
|
+
params: {
|
|
85
|
+
name,
|
|
86
|
+
arguments: args ?? {}
|
|
87
|
+
}
|
|
88
|
+
}, CallToolResultSchema);
|
|
89
|
+
}
|
|
66
90
|
async ensure() {
|
|
67
91
|
if (this.client) return this.client;
|
|
68
92
|
if (!this.credentials) throw new PmError("unauthorized", credentialHint());
|
|
@@ -97,6 +121,9 @@ var PmRemote = class {
|
|
|
97
121
|
function messageOf(e) {
|
|
98
122
|
return e instanceof Error ? e.message : String(e);
|
|
99
123
|
}
|
|
124
|
+
function isSessionInvalid(e) {
|
|
125
|
+
return /session not found|session expired|\b404\b/i.test(messageOf(e));
|
|
126
|
+
}
|
|
100
127
|
const DEFAULT_MAX_MB = 50;
|
|
101
128
|
function resolveUploadRoots() {
|
|
102
129
|
return [...(process.env.BFI_UPLOAD_ROOTS || "").split(path.delimiter).map((s) => s.trim()).filter(Boolean), process.cwd()].map((p) => path.resolve(p));
|
|
@@ -135,20 +162,27 @@ function absoluteUploadUrl(uploadUrl, origin = PM_ORIGIN, pmUrl = process.env.BF
|
|
|
135
162
|
return url.toString();
|
|
136
163
|
}
|
|
137
164
|
async function uploadToTicket(url, file, fetchImpl = fetch) {
|
|
138
|
-
|
|
165
|
+
if (file.size === 0) throw new Error("文件为空,无法上传");
|
|
139
166
|
const form = new FormData();
|
|
140
|
-
|
|
167
|
+
const blob = typeof openAsBlob === "function" ? await openAsBlob(file.realPath) : new Blob([new Uint8Array(await readFile(file.realPath))]);
|
|
168
|
+
form.append("file", blob, file.name);
|
|
169
|
+
const timeoutMs = Number(process.env.BFI_UPLOAD_TIMEOUT_MS || 12e4);
|
|
141
170
|
const resp = await fetchImpl(url, {
|
|
142
171
|
method: "POST",
|
|
143
|
-
body: form
|
|
172
|
+
body: form,
|
|
173
|
+
signal: AbortSignal.timeout(Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : 12e4)
|
|
144
174
|
});
|
|
145
175
|
const text = await resp.text();
|
|
146
176
|
if (!resp.ok) throw new Error(`附件上传失败 HTTP ${resp.status}: ${text.slice(0, 200)}`);
|
|
177
|
+
let parsed;
|
|
147
178
|
try {
|
|
148
|
-
|
|
179
|
+
parsed = JSON.parse(text);
|
|
149
180
|
} catch {
|
|
150
181
|
throw new Error(`附件上传响应非 JSON: ${text.slice(0, 200)}`);
|
|
151
182
|
}
|
|
183
|
+
const code = parsed.code;
|
|
184
|
+
if (code !== void 0 && String(code) !== "0" && String(code) !== "200") throw new Error(`附件上传被拒绝(code=${String(code)}): ${String(parsed.msg || text.slice(0, 200))}`);
|
|
185
|
+
return parsed;
|
|
152
186
|
}
|
|
153
187
|
const LOCAL_TOOLS = [{
|
|
154
188
|
name: "attachment_upload",
|
|
@@ -181,6 +215,15 @@ const LOCAL_TOOLS = [{
|
|
|
181
215
|
type: "string",
|
|
182
216
|
description: "评论正文"
|
|
183
217
|
},
|
|
218
|
+
entity_type: {
|
|
219
|
+
type: "string",
|
|
220
|
+
enum: [
|
|
221
|
+
"DEFECT",
|
|
222
|
+
"FEATURE",
|
|
223
|
+
"SUBTASK"
|
|
224
|
+
],
|
|
225
|
+
description: "单据类型,默认 DEFECT"
|
|
226
|
+
},
|
|
184
227
|
file_paths: {
|
|
185
228
|
type: "array",
|
|
186
229
|
items: { type: "string" },
|
|
@@ -238,8 +281,9 @@ function buildServer(remote, fetchImpl) {
|
|
|
238
281
|
const ticket = extractJson(await pm.callTool("get_upload_ticket", { file_name: (fileName || file.name).trim() }));
|
|
239
282
|
const uploadUrl = String(ticket.upload_url || "");
|
|
240
283
|
if (!uploadUrl) throw new PmError("ticket_failed", `签发 ticket 失败: ${JSON.stringify(ticket).slice(0, 200)}`);
|
|
241
|
-
const
|
|
242
|
-
|
|
284
|
+
const response = await uploadToTicket(absoluteUploadUrl(uploadUrl), file, fetchImpl);
|
|
285
|
+
const uploaded = response.data && typeof response.data === "object" ? response.data : response;
|
|
286
|
+
if (!uploaded.aid) throw new PmError("upload_failed", `上传响应缺少 aid: ${JSON.stringify(response).slice(0, 200)}`);
|
|
243
287
|
return {
|
|
244
288
|
...uploaded,
|
|
245
289
|
fileName: uploaded.fileName || file.name,
|
|
@@ -256,17 +300,36 @@ function buildServer(remote, fetchImpl) {
|
|
|
256
300
|
if (!input.object_id) throw new PmError("invalid_params", "object_id 不能为空");
|
|
257
301
|
if (!paths.length) throw new PmError("invalid_params", "file_paths 不能为空");
|
|
258
302
|
const uploads = [];
|
|
259
|
-
for (const p of paths)
|
|
303
|
+
for (const p of paths) try {
|
|
304
|
+
uploads.push(await uploadOne(p));
|
|
305
|
+
} catch (e) {
|
|
306
|
+
return textResult({
|
|
307
|
+
ok: false,
|
|
308
|
+
error_kind: "upload_failed",
|
|
309
|
+
message: `第 ${uploads.length + 1} 个文件上传失败: ${e instanceof Error ? e.message : String(e)}`,
|
|
310
|
+
uploaded: uploads
|
|
311
|
+
}, true);
|
|
312
|
+
}
|
|
260
313
|
const aids = uploads.map((u) => String(u.aid));
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
314
|
+
try {
|
|
315
|
+
return textResult({
|
|
316
|
+
ok: true,
|
|
317
|
+
uploads,
|
|
318
|
+
comment: extractJson(await pm.callTool("add_comment", {
|
|
319
|
+
object_id: String(input.object_id),
|
|
320
|
+
content: input.content ? String(input.content) : "",
|
|
321
|
+
entity_type: input.entity_type ? String(input.entity_type) : "DEFECT",
|
|
322
|
+
attache_ids: aids
|
|
323
|
+
}))
|
|
324
|
+
});
|
|
325
|
+
} catch (e) {
|
|
326
|
+
return textResult({
|
|
327
|
+
ok: false,
|
|
328
|
+
error_kind: e instanceof PmError ? e.errorKind : "internal_error",
|
|
329
|
+
message: `附件已上传,但评论失败: ${e instanceof Error ? e.message : String(e)}`,
|
|
330
|
+
uploaded: uploads
|
|
331
|
+
}, true);
|
|
332
|
+
}
|
|
270
333
|
}
|
|
271
334
|
return await pm.callTool(name, input);
|
|
272
335
|
} catch (e) {
|