ai-dev-requirements 0.2.0 → 0.2.1
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/index.cjs +101 -42
- package/dist/index.mjs +101 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -188,7 +188,7 @@ function loadConfig(startDir) {
|
|
|
188
188
|
}
|
|
189
189
|
//#endregion
|
|
190
190
|
//#region package.json
|
|
191
|
-
var version = "0.2.
|
|
191
|
+
var version = "0.2.1";
|
|
192
192
|
//#endregion
|
|
193
193
|
//#region ../../src/utils/map-status.ts
|
|
194
194
|
const ONES_STATUS_MAP = {
|
|
@@ -693,6 +693,21 @@ function htmlToPlainText(html) {
|
|
|
693
693
|
function getTaskDetailText(task) {
|
|
694
694
|
return task.descriptionText?.trim() || htmlToPlainText(task.desc_rich ?? task.description ?? "");
|
|
695
695
|
}
|
|
696
|
+
function extractHtmlImageReferences(html) {
|
|
697
|
+
return Array.from(html.matchAll(/<img\b[^>]*>/gi), (match) => {
|
|
698
|
+
const tag = match[0];
|
|
699
|
+
const srcMatch = tag.match(/\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)')/i);
|
|
700
|
+
const resourceMatch = tag.match(/\bdata-uuid\s*=\s*(?:"([^"]*)"|'([^']*)')/i);
|
|
701
|
+
return {
|
|
702
|
+
tag,
|
|
703
|
+
src: (srcMatch?.[1] ?? srcMatch?.[2] ?? "").replace(/&/gi, "&").trim(),
|
|
704
|
+
resourceUuid: (resourceMatch?.[1] ?? resourceMatch?.[2] ?? "").trim()
|
|
705
|
+
};
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
function containsInlineTaskImages(task) {
|
|
709
|
+
return [task.description, task.desc_rich].some((value) => typeof value === "string" && /<img\b/i.test(value)) || /\[(?:image|图片)\]/i.test(task.descriptionText ?? "");
|
|
710
|
+
}
|
|
696
711
|
function isRecord(value) {
|
|
697
712
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
698
713
|
}
|
|
@@ -1131,7 +1146,11 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1131
1146
|
return response.json();
|
|
1132
1147
|
}
|
|
1133
1148
|
async fetchRelatedActivities(taskKey) {
|
|
1134
|
-
|
|
1149
|
+
try {
|
|
1150
|
+
return (await this.onesql(RELATED_ACTIVITIES_QUERY, { key: taskKey }, "Task")).data?.task?.relatedActivities ?? [];
|
|
1151
|
+
} catch {
|
|
1152
|
+
return [];
|
|
1153
|
+
}
|
|
1135
1154
|
}
|
|
1136
1155
|
async searchTaskByNumber(taskNumber) {
|
|
1137
1156
|
const session = await this.login();
|
|
@@ -1316,30 +1335,92 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1316
1335
|
return null;
|
|
1317
1336
|
}
|
|
1318
1337
|
}
|
|
1338
|
+
getAttachmentResourceUuid(image) {
|
|
1339
|
+
if (image.src) try {
|
|
1340
|
+
const source = new URL(image.src, this.config.apiBase);
|
|
1341
|
+
if (source.origin === new URL(this.config.apiBase).origin) {
|
|
1342
|
+
const match = source.pathname.match(/\/res\/attachment\/([^/]+)$/);
|
|
1343
|
+
const resourceUuid = match?.[1] ? decodeOnesPathIdentifier(match[1]) : null;
|
|
1344
|
+
if (resourceUuid) return resourceUuid;
|
|
1345
|
+
}
|
|
1346
|
+
} catch {}
|
|
1347
|
+
return image.resourceUuid;
|
|
1348
|
+
}
|
|
1319
1349
|
/**
|
|
1320
1350
|
* Replace stale image URLs in HTML with fresh signed URLs from the attachment API.
|
|
1321
|
-
*
|
|
1351
|
+
* Prefer the resource identifier from the attachment URL because ONES data-uuid
|
|
1352
|
+
* can identify the editor node instead of the underlying attachment.
|
|
1322
1353
|
*/
|
|
1323
|
-
async refreshImageUrls(html) {
|
|
1354
|
+
async refreshImageUrls(html, freshUrlCache = /* @__PURE__ */ new Map()) {
|
|
1324
1355
|
if (!html) return html;
|
|
1325
|
-
const
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1356
|
+
const images = extractHtmlImageReferences(html).flatMap((image) => {
|
|
1357
|
+
const resourceUuid = this.getAttachmentResourceUuid(image);
|
|
1358
|
+
return resourceUuid ? [{
|
|
1359
|
+
image,
|
|
1360
|
+
resourceUuid
|
|
1361
|
+
}] : [];
|
|
1362
|
+
});
|
|
1363
|
+
if (images.length === 0) return html;
|
|
1364
|
+
const replacements = await Promise.all(images.map(async ({ image, resourceUuid }) => {
|
|
1365
|
+
let freshUrl = freshUrlCache.get(resourceUuid);
|
|
1366
|
+
if (!freshUrl) {
|
|
1367
|
+
freshUrl = this.getAttachmentUrl(resourceUuid);
|
|
1368
|
+
freshUrlCache.set(resourceUuid, freshUrl);
|
|
1369
|
+
}
|
|
1330
1370
|
return {
|
|
1331
|
-
fullMatch:
|
|
1332
|
-
|
|
1333
|
-
freshUrl
|
|
1371
|
+
fullMatch: image.tag,
|
|
1372
|
+
freshUrl: await freshUrl
|
|
1334
1373
|
};
|
|
1335
1374
|
}));
|
|
1336
1375
|
let result = html;
|
|
1337
|
-
for (const { fullMatch, freshUrl } of replacements)
|
|
1338
|
-
|
|
1376
|
+
for (const { fullMatch, freshUrl } of replacements) {
|
|
1377
|
+
if (!freshUrl) continue;
|
|
1378
|
+
const updatedImg = /\bsrc\s*=/i.test(fullMatch) ? fullMatch.replace(/\bsrc\s*=\s*(?:"[^"]*"|'[^']*')/i, `src="${freshUrl}"`) : fullMatch.replace(/<img\b/i, `<img src="${freshUrl}"`);
|
|
1339
1379
|
result = result.replace(fullMatch, updatedImg);
|
|
1340
1380
|
}
|
|
1341
1381
|
return result;
|
|
1342
1382
|
}
|
|
1383
|
+
async getFreshTaskDescriptions(task) {
|
|
1384
|
+
const taskInfo = await this.fetchTaskInfo(task.uuid);
|
|
1385
|
+
const rawDescription = typeof taskInfo.desc === "string" ? taskInfo.desc : task.description ?? "";
|
|
1386
|
+
const rawDescriptionRich = typeof taskInfo.desc_rich === "string" ? taskInfo.desc_rich : task.desc_rich ?? task.description ?? "";
|
|
1387
|
+
const freshUrlCache = /* @__PURE__ */ new Map();
|
|
1388
|
+
const [description, descriptionRich] = await Promise.all([this.refreshImageUrls(rawDescription, freshUrlCache), this.refreshImageUrls(rawDescriptionRich, freshUrlCache)]);
|
|
1389
|
+
return {
|
|
1390
|
+
description,
|
|
1391
|
+
descriptionRich
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
async getTaskImageAttachments(task) {
|
|
1395
|
+
const { description, descriptionRich } = await this.getFreshTaskDescriptions(task);
|
|
1396
|
+
const images = [...extractHtmlImageReferences(descriptionRich), ...extractHtmlImageReferences(description)];
|
|
1397
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1398
|
+
const attachments = [];
|
|
1399
|
+
for (const image of images) {
|
|
1400
|
+
if (!image.src) continue;
|
|
1401
|
+
let url;
|
|
1402
|
+
try {
|
|
1403
|
+
url = new URL(image.src, this.config.apiBase).toString();
|
|
1404
|
+
} catch {
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
if (this.classifyRemoteImageUrl(url) === "untrusted") continue;
|
|
1408
|
+
const identity = image.resourceUuid || url;
|
|
1409
|
+
if (seen.has(identity)) continue;
|
|
1410
|
+
seen.add(identity);
|
|
1411
|
+
const pathname = new URL(url).pathname;
|
|
1412
|
+
const pathName = attachmentNameFromPath(pathname);
|
|
1413
|
+
const name = pathName && pathName !== "/" ? pathName : `image-${attachments.length + 1}.png`;
|
|
1414
|
+
attachments.push({
|
|
1415
|
+
id: image.resourceUuid || `${task.uuid}-image-${attachments.length + 1}`,
|
|
1416
|
+
name,
|
|
1417
|
+
url,
|
|
1418
|
+
mimeType: mimeTypeFromFileName(pathname),
|
|
1419
|
+
size: 0
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
return attachments;
|
|
1423
|
+
}
|
|
1343
1424
|
/**
|
|
1344
1425
|
* Fetch wiki page content via REST API.
|
|
1345
1426
|
* Endpoint: /wiki/api/wiki/team/{teamUuid}/online_page/{wikiUuid}/content
|
|
@@ -1459,7 +1540,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1459
1540
|
title: `Wiki ${wikiUuid}`,
|
|
1460
1541
|
uuid: wikiUuid
|
|
1461
1542
|
});
|
|
1462
|
-
const wikiContents = await Promise.all([...wikiRefs.values()].map(async (wiki) => {
|
|
1543
|
+
const [wikiContents, taskImageAttachments] = await Promise.all([Promise.all([...wikiRefs.values()].map(async (wiki) => {
|
|
1463
1544
|
const rendered = await this.fetchWikiContent(wiki.uuid);
|
|
1464
1545
|
return {
|
|
1465
1546
|
title: wiki.title,
|
|
@@ -1467,7 +1548,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1467
1548
|
content: rendered.content,
|
|
1468
1549
|
attachments: rendered.attachments
|
|
1469
1550
|
};
|
|
1470
|
-
}));
|
|
1551
|
+
})), containsInlineTaskImages(task) ? this.getTaskImageAttachments(task) : Promise.resolve([])]);
|
|
1471
1552
|
const parts = [];
|
|
1472
1553
|
parts.push(`# #${task.number} ${task.name}`);
|
|
1473
1554
|
parts.push("");
|
|
@@ -1527,7 +1608,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1527
1608
|
parts.push(detailText);
|
|
1528
1609
|
}
|
|
1529
1610
|
const wikiAttachments = wikiContents.flatMap((wiki) => wiki.attachments);
|
|
1530
|
-
const req = toRequirement(task, parts.join("\n"), wikiAttachments);
|
|
1611
|
+
const req = toRequirement(task, parts.join("\n"), [...wikiAttachments, ...taskImageAttachments]);
|
|
1531
1612
|
req.raw = {
|
|
1532
1613
|
...req.raw,
|
|
1533
1614
|
relatedActivities,
|
|
@@ -1735,35 +1816,13 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1735
1816
|
}));
|
|
1736
1817
|
}
|
|
1737
1818
|
async getIssueDetail(params) {
|
|
1738
|
-
|
|
1739
|
-
const numMatch = params.issueId.match(/^#?(\d+)$/);
|
|
1740
|
-
if (numMatch) {
|
|
1741
|
-
const taskNumber = Number.parseInt(numMatch[1], 10);
|
|
1742
|
-
const found = ((await this.graphql(SEARCH_TASKS_QUERY, {
|
|
1743
|
-
groupBy: { tasks: {} },
|
|
1744
|
-
groupOrderBy: null,
|
|
1745
|
-
orderBy: { createTime: "DESC" },
|
|
1746
|
-
filterGroup: [{ number_in: [taskNumber] }],
|
|
1747
|
-
search: null,
|
|
1748
|
-
pagination: {
|
|
1749
|
-
limit: 10,
|
|
1750
|
-
preciseCount: false
|
|
1751
|
-
},
|
|
1752
|
-
limit: 10
|
|
1753
|
-
}, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === taskNumber);
|
|
1754
|
-
if (!found) throw new Error(`ONES: Issue #${taskNumber} not found in current team`);
|
|
1755
|
-
issueKey = `task-${found.uuid}`;
|
|
1756
|
-
} else issueKey = params.issueId.startsWith("task-") ? params.issueId : `task-${params.issueId}`;
|
|
1819
|
+
const { key: issueKey } = await this.resolveTaskRef(params.issueId);
|
|
1757
1820
|
const task = (await this.graphql(ISSUE_DETAIL_QUERY, { key: issueKey }, "Task")).data?.task;
|
|
1758
1821
|
if (!task) throw new Error(`ONES: Issue "${issueKey}" not found`);
|
|
1759
1822
|
const kind = classifyOnesWorkItem(task.issueType, task.subIssueType);
|
|
1760
1823
|
if (kind === "unknown") throw new Error(`ONES: Unable to classify "${params.issueId}" before get_issue_detail`);
|
|
1761
1824
|
if (kind === "requirement" || kind === "task") throw unsupportedWorkItemToolError(params.issueId, kind, "get_issue_detail", "get_work_item");
|
|
1762
|
-
const
|
|
1763
|
-
const rawDescription = taskInfo.desc ?? task.description ?? "";
|
|
1764
|
-
const rawDescRich = taskInfo.desc_rich ?? task.desc_rich ?? "";
|
|
1765
|
-
const freshDescription = await this.refreshImageUrls(rawDescription);
|
|
1766
|
-
const freshDescRich = await this.refreshImageUrls(rawDescRich);
|
|
1825
|
+
const { description: freshDescription, descriptionRich: freshDescRich } = await this.getFreshTaskDescriptions(task);
|
|
1767
1826
|
return {
|
|
1768
1827
|
key: task.key,
|
|
1769
1828
|
uuid: task.uuid,
|
|
@@ -2356,7 +2415,7 @@ async function downloadTrustedImages(urls, options) {
|
|
|
2356
2415
|
//#endregion
|
|
2357
2416
|
//#region ../../src/tools/get-issue-detail.ts
|
|
2358
2417
|
const GetIssueDetailSchema = zod_v4.z.object({
|
|
2359
|
-
issueId: zod_v4.z.string().describe("
|
|
2418
|
+
issueId: zod_v4.z.string().describe("ONES defect UUID, task key, number, or display ID (for example \"DEMO-2001\")"),
|
|
2360
2419
|
source: zod_v4.z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
|
|
2361
2420
|
});
|
|
2362
2421
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -165,7 +165,7 @@ function loadConfig(startDir) {
|
|
|
165
165
|
}
|
|
166
166
|
//#endregion
|
|
167
167
|
//#region package.json
|
|
168
|
-
var version = "0.2.
|
|
168
|
+
var version = "0.2.1";
|
|
169
169
|
//#endregion
|
|
170
170
|
//#region ../../src/utils/map-status.ts
|
|
171
171
|
const ONES_STATUS_MAP = {
|
|
@@ -670,6 +670,21 @@ function htmlToPlainText(html) {
|
|
|
670
670
|
function getTaskDetailText(task) {
|
|
671
671
|
return task.descriptionText?.trim() || htmlToPlainText(task.desc_rich ?? task.description ?? "");
|
|
672
672
|
}
|
|
673
|
+
function extractHtmlImageReferences(html) {
|
|
674
|
+
return Array.from(html.matchAll(/<img\b[^>]*>/gi), (match) => {
|
|
675
|
+
const tag = match[0];
|
|
676
|
+
const srcMatch = tag.match(/\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)')/i);
|
|
677
|
+
const resourceMatch = tag.match(/\bdata-uuid\s*=\s*(?:"([^"]*)"|'([^']*)')/i);
|
|
678
|
+
return {
|
|
679
|
+
tag,
|
|
680
|
+
src: (srcMatch?.[1] ?? srcMatch?.[2] ?? "").replace(/&/gi, "&").trim(),
|
|
681
|
+
resourceUuid: (resourceMatch?.[1] ?? resourceMatch?.[2] ?? "").trim()
|
|
682
|
+
};
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
function containsInlineTaskImages(task) {
|
|
686
|
+
return [task.description, task.desc_rich].some((value) => typeof value === "string" && /<img\b/i.test(value)) || /\[(?:image|图片)\]/i.test(task.descriptionText ?? "");
|
|
687
|
+
}
|
|
673
688
|
function isRecord(value) {
|
|
674
689
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
675
690
|
}
|
|
@@ -1108,7 +1123,11 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1108
1123
|
return response.json();
|
|
1109
1124
|
}
|
|
1110
1125
|
async fetchRelatedActivities(taskKey) {
|
|
1111
|
-
|
|
1126
|
+
try {
|
|
1127
|
+
return (await this.onesql(RELATED_ACTIVITIES_QUERY, { key: taskKey }, "Task")).data?.task?.relatedActivities ?? [];
|
|
1128
|
+
} catch {
|
|
1129
|
+
return [];
|
|
1130
|
+
}
|
|
1112
1131
|
}
|
|
1113
1132
|
async searchTaskByNumber(taskNumber) {
|
|
1114
1133
|
const session = await this.login();
|
|
@@ -1293,30 +1312,92 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1293
1312
|
return null;
|
|
1294
1313
|
}
|
|
1295
1314
|
}
|
|
1315
|
+
getAttachmentResourceUuid(image) {
|
|
1316
|
+
if (image.src) try {
|
|
1317
|
+
const source = new URL(image.src, this.config.apiBase);
|
|
1318
|
+
if (source.origin === new URL(this.config.apiBase).origin) {
|
|
1319
|
+
const match = source.pathname.match(/\/res\/attachment\/([^/]+)$/);
|
|
1320
|
+
const resourceUuid = match?.[1] ? decodeOnesPathIdentifier(match[1]) : null;
|
|
1321
|
+
if (resourceUuid) return resourceUuid;
|
|
1322
|
+
}
|
|
1323
|
+
} catch {}
|
|
1324
|
+
return image.resourceUuid;
|
|
1325
|
+
}
|
|
1296
1326
|
/**
|
|
1297
1327
|
* Replace stale image URLs in HTML with fresh signed URLs from the attachment API.
|
|
1298
|
-
*
|
|
1328
|
+
* Prefer the resource identifier from the attachment URL because ONES data-uuid
|
|
1329
|
+
* can identify the editor node instead of the underlying attachment.
|
|
1299
1330
|
*/
|
|
1300
|
-
async refreshImageUrls(html) {
|
|
1331
|
+
async refreshImageUrls(html, freshUrlCache = /* @__PURE__ */ new Map()) {
|
|
1301
1332
|
if (!html) return html;
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1333
|
+
const images = extractHtmlImageReferences(html).flatMap((image) => {
|
|
1334
|
+
const resourceUuid = this.getAttachmentResourceUuid(image);
|
|
1335
|
+
return resourceUuid ? [{
|
|
1336
|
+
image,
|
|
1337
|
+
resourceUuid
|
|
1338
|
+
}] : [];
|
|
1339
|
+
});
|
|
1340
|
+
if (images.length === 0) return html;
|
|
1341
|
+
const replacements = await Promise.all(images.map(async ({ image, resourceUuid }) => {
|
|
1342
|
+
let freshUrl = freshUrlCache.get(resourceUuid);
|
|
1343
|
+
if (!freshUrl) {
|
|
1344
|
+
freshUrl = this.getAttachmentUrl(resourceUuid);
|
|
1345
|
+
freshUrlCache.set(resourceUuid, freshUrl);
|
|
1346
|
+
}
|
|
1307
1347
|
return {
|
|
1308
|
-
fullMatch:
|
|
1309
|
-
|
|
1310
|
-
freshUrl
|
|
1348
|
+
fullMatch: image.tag,
|
|
1349
|
+
freshUrl: await freshUrl
|
|
1311
1350
|
};
|
|
1312
1351
|
}));
|
|
1313
1352
|
let result = html;
|
|
1314
|
-
for (const { fullMatch, freshUrl } of replacements)
|
|
1315
|
-
|
|
1353
|
+
for (const { fullMatch, freshUrl } of replacements) {
|
|
1354
|
+
if (!freshUrl) continue;
|
|
1355
|
+
const updatedImg = /\bsrc\s*=/i.test(fullMatch) ? fullMatch.replace(/\bsrc\s*=\s*(?:"[^"]*"|'[^']*')/i, `src="${freshUrl}"`) : fullMatch.replace(/<img\b/i, `<img src="${freshUrl}"`);
|
|
1316
1356
|
result = result.replace(fullMatch, updatedImg);
|
|
1317
1357
|
}
|
|
1318
1358
|
return result;
|
|
1319
1359
|
}
|
|
1360
|
+
async getFreshTaskDescriptions(task) {
|
|
1361
|
+
const taskInfo = await this.fetchTaskInfo(task.uuid);
|
|
1362
|
+
const rawDescription = typeof taskInfo.desc === "string" ? taskInfo.desc : task.description ?? "";
|
|
1363
|
+
const rawDescriptionRich = typeof taskInfo.desc_rich === "string" ? taskInfo.desc_rich : task.desc_rich ?? task.description ?? "";
|
|
1364
|
+
const freshUrlCache = /* @__PURE__ */ new Map();
|
|
1365
|
+
const [description, descriptionRich] = await Promise.all([this.refreshImageUrls(rawDescription, freshUrlCache), this.refreshImageUrls(rawDescriptionRich, freshUrlCache)]);
|
|
1366
|
+
return {
|
|
1367
|
+
description,
|
|
1368
|
+
descriptionRich
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
async getTaskImageAttachments(task) {
|
|
1372
|
+
const { description, descriptionRich } = await this.getFreshTaskDescriptions(task);
|
|
1373
|
+
const images = [...extractHtmlImageReferences(descriptionRich), ...extractHtmlImageReferences(description)];
|
|
1374
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1375
|
+
const attachments = [];
|
|
1376
|
+
for (const image of images) {
|
|
1377
|
+
if (!image.src) continue;
|
|
1378
|
+
let url;
|
|
1379
|
+
try {
|
|
1380
|
+
url = new URL(image.src, this.config.apiBase).toString();
|
|
1381
|
+
} catch {
|
|
1382
|
+
continue;
|
|
1383
|
+
}
|
|
1384
|
+
if (this.classifyRemoteImageUrl(url) === "untrusted") continue;
|
|
1385
|
+
const identity = image.resourceUuid || url;
|
|
1386
|
+
if (seen.has(identity)) continue;
|
|
1387
|
+
seen.add(identity);
|
|
1388
|
+
const pathname = new URL(url).pathname;
|
|
1389
|
+
const pathName = attachmentNameFromPath(pathname);
|
|
1390
|
+
const name = pathName && pathName !== "/" ? pathName : `image-${attachments.length + 1}.png`;
|
|
1391
|
+
attachments.push({
|
|
1392
|
+
id: image.resourceUuid || `${task.uuid}-image-${attachments.length + 1}`,
|
|
1393
|
+
name,
|
|
1394
|
+
url,
|
|
1395
|
+
mimeType: mimeTypeFromFileName(pathname),
|
|
1396
|
+
size: 0
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
return attachments;
|
|
1400
|
+
}
|
|
1320
1401
|
/**
|
|
1321
1402
|
* Fetch wiki page content via REST API.
|
|
1322
1403
|
* Endpoint: /wiki/api/wiki/team/{teamUuid}/online_page/{wikiUuid}/content
|
|
@@ -1436,7 +1517,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1436
1517
|
title: `Wiki ${wikiUuid}`,
|
|
1437
1518
|
uuid: wikiUuid
|
|
1438
1519
|
});
|
|
1439
|
-
const wikiContents = await Promise.all([...wikiRefs.values()].map(async (wiki) => {
|
|
1520
|
+
const [wikiContents, taskImageAttachments] = await Promise.all([Promise.all([...wikiRefs.values()].map(async (wiki) => {
|
|
1440
1521
|
const rendered = await this.fetchWikiContent(wiki.uuid);
|
|
1441
1522
|
return {
|
|
1442
1523
|
title: wiki.title,
|
|
@@ -1444,7 +1525,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1444
1525
|
content: rendered.content,
|
|
1445
1526
|
attachments: rendered.attachments
|
|
1446
1527
|
};
|
|
1447
|
-
}));
|
|
1528
|
+
})), containsInlineTaskImages(task) ? this.getTaskImageAttachments(task) : Promise.resolve([])]);
|
|
1448
1529
|
const parts = [];
|
|
1449
1530
|
parts.push(`# #${task.number} ${task.name}`);
|
|
1450
1531
|
parts.push("");
|
|
@@ -1504,7 +1585,7 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1504
1585
|
parts.push(detailText);
|
|
1505
1586
|
}
|
|
1506
1587
|
const wikiAttachments = wikiContents.flatMap((wiki) => wiki.attachments);
|
|
1507
|
-
const req = toRequirement(task, parts.join("\n"), wikiAttachments);
|
|
1588
|
+
const req = toRequirement(task, parts.join("\n"), [...wikiAttachments, ...taskImageAttachments]);
|
|
1508
1589
|
req.raw = {
|
|
1509
1590
|
...req.raw,
|
|
1510
1591
|
relatedActivities,
|
|
@@ -1712,35 +1793,13 @@ var OnesAdapter = class extends BaseAdapter {
|
|
|
1712
1793
|
}));
|
|
1713
1794
|
}
|
|
1714
1795
|
async getIssueDetail(params) {
|
|
1715
|
-
|
|
1716
|
-
const numMatch = params.issueId.match(/^#?(\d+)$/);
|
|
1717
|
-
if (numMatch) {
|
|
1718
|
-
const taskNumber = Number.parseInt(numMatch[1], 10);
|
|
1719
|
-
const found = ((await this.graphql(SEARCH_TASKS_QUERY, {
|
|
1720
|
-
groupBy: { tasks: {} },
|
|
1721
|
-
groupOrderBy: null,
|
|
1722
|
-
orderBy: { createTime: "DESC" },
|
|
1723
|
-
filterGroup: [{ number_in: [taskNumber] }],
|
|
1724
|
-
search: null,
|
|
1725
|
-
pagination: {
|
|
1726
|
-
limit: 10,
|
|
1727
|
-
preciseCount: false
|
|
1728
|
-
},
|
|
1729
|
-
limit: 10
|
|
1730
|
-
}, "group-task-data")).data?.buckets?.flatMap((b) => b.tasks ?? []) ?? []).find((t) => t.number === taskNumber);
|
|
1731
|
-
if (!found) throw new Error(`ONES: Issue #${taskNumber} not found in current team`);
|
|
1732
|
-
issueKey = `task-${found.uuid}`;
|
|
1733
|
-
} else issueKey = params.issueId.startsWith("task-") ? params.issueId : `task-${params.issueId}`;
|
|
1796
|
+
const { key: issueKey } = await this.resolveTaskRef(params.issueId);
|
|
1734
1797
|
const task = (await this.graphql(ISSUE_DETAIL_QUERY, { key: issueKey }, "Task")).data?.task;
|
|
1735
1798
|
if (!task) throw new Error(`ONES: Issue "${issueKey}" not found`);
|
|
1736
1799
|
const kind = classifyOnesWorkItem(task.issueType, task.subIssueType);
|
|
1737
1800
|
if (kind === "unknown") throw new Error(`ONES: Unable to classify "${params.issueId}" before get_issue_detail`);
|
|
1738
1801
|
if (kind === "requirement" || kind === "task") throw unsupportedWorkItemToolError(params.issueId, kind, "get_issue_detail", "get_work_item");
|
|
1739
|
-
const
|
|
1740
|
-
const rawDescription = taskInfo.desc ?? task.description ?? "";
|
|
1741
|
-
const rawDescRich = taskInfo.desc_rich ?? task.desc_rich ?? "";
|
|
1742
|
-
const freshDescription = await this.refreshImageUrls(rawDescription);
|
|
1743
|
-
const freshDescRich = await this.refreshImageUrls(rawDescRich);
|
|
1802
|
+
const { description: freshDescription, descriptionRich: freshDescRich } = await this.getFreshTaskDescriptions(task);
|
|
1744
1803
|
return {
|
|
1745
1804
|
key: task.key,
|
|
1746
1805
|
uuid: task.uuid,
|
|
@@ -2333,7 +2392,7 @@ async function downloadTrustedImages(urls, options) {
|
|
|
2333
2392
|
//#endregion
|
|
2334
2393
|
//#region ../../src/tools/get-issue-detail.ts
|
|
2335
2394
|
const GetIssueDetailSchema = z.object({
|
|
2336
|
-
issueId: z.string().describe("
|
|
2395
|
+
issueId: z.string().describe("ONES defect UUID, task key, number, or display ID (for example \"DEMO-2001\")"),
|
|
2337
2396
|
source: z.string().optional().describe("Source to fetch from. If omitted, uses the default source.")
|
|
2338
2397
|
});
|
|
2339
2398
|
/**
|