tdecollab 0.2.2 → 0.3.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/LICENSE +21 -0
- package/README.md +53 -217
- package/dist/{chunk-2IQ4QMK3.js → chunk-6AFNYE7N.js} +283 -288
- package/dist/chunk-6AFNYE7N.js.map +1 -0
- package/dist/{chunk-T73I3OT6.js → chunk-GMC75YB2.js} +181 -37
- package/dist/chunk-GMC75YB2.js.map +1 -0
- package/dist/{chunk-SJ7KPK6Q.js → chunk-IFYMZLQI.js} +21 -6
- package/dist/chunk-IFYMZLQI.js.map +1 -0
- package/dist/{image-downloader-D57KFAIQ.js → chunk-JBDK5WP3.js} +8 -6
- package/dist/chunk-JBDK5WP3.js.map +1 -0
- package/dist/chunk-JI2YUE7N.js +172 -0
- package/dist/chunk-JI2YUE7N.js.map +1 -0
- package/dist/cli.js +138 -18
- package/dist/cli.js.map +1 -1
- package/dist/image-downloader-VKPGS3TY.js +8 -0
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/server-M353VF2O.js +10 -0
- package/dist/server-M353VF2O.js.map +1 -0
- package/dist/tui/index.js +2754 -0
- package/dist/tui/index.js.map +1 -0
- package/package.json +33 -9
- package/dist/chunk-2IQ4QMK3.js.map +0 -1
- package/dist/chunk-SJ7KPK6Q.js.map +0 -1
- package/dist/chunk-T73I3OT6.js.map +0 -1
- package/dist/cli.d.ts +0 -1
- package/dist/image-downloader-D57KFAIQ.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/server-HS774DWY.js +0 -9
- /package/dist/{server-HS774DWY.js.map → image-downloader-VKPGS3TY.js.map} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
logger
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IFYMZLQI.js";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// tools/confluence/api/content.ts
|
|
6
6
|
var ConfluenceContentApi = class {
|
|
7
7
|
constructor(client) {
|
|
8
8
|
this.client = client;
|
|
@@ -85,7 +85,40 @@ var ConfluenceContentApi = class {
|
|
|
85
85
|
const data = labels.map((name) => ({ prefix: "global", name }));
|
|
86
86
|
await this.client.post(`/rest/api/content/${id}/label`, data);
|
|
87
87
|
}
|
|
88
|
-
// Attachment 관련 메서드
|
|
88
|
+
// Attachment 관련 메서드 (upsert: 기존 파일이 있으면 업데이트, 없으면 신규 업로드)
|
|
89
|
+
async uploadAttachment(pageId, filename, fileContent, contentType) {
|
|
90
|
+
const FormData = (await import("form-data")).default;
|
|
91
|
+
const existingAttachments = await this.getAttachments(pageId, filename);
|
|
92
|
+
const existing = existingAttachments.find((a) => a.title === filename);
|
|
93
|
+
const form = new FormData();
|
|
94
|
+
form.append("file", fileContent, {
|
|
95
|
+
filename,
|
|
96
|
+
contentType: contentType || "application/octet-stream"
|
|
97
|
+
});
|
|
98
|
+
const headers = {
|
|
99
|
+
...form.getHeaders(),
|
|
100
|
+
"X-Atlassian-Token": "nocheck",
|
|
101
|
+
"Accept": "application/json"
|
|
102
|
+
};
|
|
103
|
+
let response;
|
|
104
|
+
if (existing) {
|
|
105
|
+
response = await this.client.post(
|
|
106
|
+
`/rest/api/content/${pageId}/child/attachment/${existing.id}/data`,
|
|
107
|
+
form,
|
|
108
|
+
{ headers }
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
response = await this.client.post(
|
|
112
|
+
`/rest/api/content/${pageId}/child/attachment`,
|
|
113
|
+
form,
|
|
114
|
+
{ headers }
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (response.data && response.data.results && response.data.results.length > 0) {
|
|
118
|
+
return response.data.results[0];
|
|
119
|
+
}
|
|
120
|
+
return response.data;
|
|
121
|
+
}
|
|
89
122
|
async getAttachments(pageId, filename) {
|
|
90
123
|
const response = await this.client.get(`/rest/api/content/${pageId}/child/attachment`, {
|
|
91
124
|
params: {
|
|
@@ -103,7 +136,7 @@ var ConfluenceContentApi = class {
|
|
|
103
136
|
}
|
|
104
137
|
};
|
|
105
138
|
|
|
106
|
-
//
|
|
139
|
+
// tools/confluence/api/space.ts
|
|
107
140
|
var ConfluenceSpaceApi = class {
|
|
108
141
|
constructor(client) {
|
|
109
142
|
this.client = client;
|
|
@@ -120,7 +153,7 @@ var ConfluenceSpaceApi = class {
|
|
|
120
153
|
}
|
|
121
154
|
};
|
|
122
155
|
|
|
123
|
-
//
|
|
156
|
+
// tools/confluence/api/search.ts
|
|
124
157
|
var ConfluenceSearchApi = class {
|
|
125
158
|
constructor(client) {
|
|
126
159
|
this.client = client;
|
|
@@ -139,10 +172,10 @@ var ConfluenceSearchApi = class {
|
|
|
139
172
|
}
|
|
140
173
|
};
|
|
141
174
|
|
|
142
|
-
//
|
|
175
|
+
// tools/common/http-client.ts
|
|
143
176
|
import axios from "axios";
|
|
144
177
|
|
|
145
|
-
//
|
|
178
|
+
// tools/common/errors.ts
|
|
146
179
|
var TdeCollabError = class extends Error {
|
|
147
180
|
constructor(message) {
|
|
148
181
|
super(message);
|
|
@@ -173,7 +206,7 @@ var ConflictError = class extends TdeCollabError {
|
|
|
173
206
|
}
|
|
174
207
|
};
|
|
175
208
|
|
|
176
|
-
//
|
|
209
|
+
// tools/common/http-client.ts
|
|
177
210
|
function createHttpClient(config) {
|
|
178
211
|
const client = axios.create({
|
|
179
212
|
baseURL: config.baseUrl,
|
|
@@ -184,19 +217,25 @@ function createHttpClient(config) {
|
|
|
184
217
|
}
|
|
185
218
|
});
|
|
186
219
|
client.interceptors.request.use((reqConfig) => {
|
|
187
|
-
if (config.auth.
|
|
220
|
+
if (config.auth.token && !reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
|
|
221
|
+
reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
|
|
222
|
+
} else if (config.auth.username && config.auth.token && !reqConfig.headers.Authorization) {
|
|
188
223
|
const token = Buffer.from(`${config.auth.username}:${config.auth.token}`).toString("base64");
|
|
189
224
|
reqConfig.headers.Authorization = `Basic ${token}`;
|
|
190
|
-
} else if (config.auth.token) {
|
|
191
|
-
if (!reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
|
|
192
|
-
reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
|
|
193
|
-
}
|
|
194
225
|
}
|
|
226
|
+
logger.debug(`[HTTP Request] ${reqConfig.method?.toUpperCase()} ${reqConfig.url}`, {
|
|
227
|
+
headers: reqConfig.headers,
|
|
228
|
+
params: reqConfig.params,
|
|
229
|
+
data: reqConfig.data
|
|
230
|
+
});
|
|
195
231
|
return reqConfig;
|
|
196
232
|
});
|
|
197
233
|
client.interceptors.response.use(
|
|
198
234
|
(response) => {
|
|
199
|
-
logger.debug(`[HTTP] ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}
|
|
235
|
+
logger.debug(`[HTTP Response] ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`, {
|
|
236
|
+
headers: response.headers,
|
|
237
|
+
data: response.data
|
|
238
|
+
});
|
|
200
239
|
return response;
|
|
201
240
|
},
|
|
202
241
|
(error) => {
|
|
@@ -205,7 +244,11 @@ function createHttpClient(config) {
|
|
|
205
244
|
const method = error.config?.method?.toUpperCase();
|
|
206
245
|
const url = error.config?.url;
|
|
207
246
|
const message = error.response.data?.message || error.message;
|
|
208
|
-
logger.error(`[HTTP] ${status} ${method} ${url}
|
|
247
|
+
logger.error(`[HTTP Error] ${status} ${method} ${url}`, {
|
|
248
|
+
message,
|
|
249
|
+
responseData: error.response.data,
|
|
250
|
+
requestHeaders: error.config?.headers
|
|
251
|
+
});
|
|
209
252
|
if (status === 401 || status === 403) {
|
|
210
253
|
throw new AuthError(`\uC778\uC99D \uC2E4\uD328: ${message}`);
|
|
211
254
|
}
|
|
@@ -228,101 +271,12 @@ function createHttpClient(config) {
|
|
|
228
271
|
return client;
|
|
229
272
|
}
|
|
230
273
|
|
|
231
|
-
//
|
|
274
|
+
// tools/confluence/api/client.ts
|
|
232
275
|
function createConfluenceClient(config) {
|
|
233
276
|
return createHttpClient(config);
|
|
234
277
|
}
|
|
235
278
|
|
|
236
|
-
//
|
|
237
|
-
import MarkdownIt from "markdown-it";
|
|
238
|
-
var MarkdownToStorageConverter = class {
|
|
239
|
-
md;
|
|
240
|
-
constructor() {
|
|
241
|
-
this.md = new MarkdownIt({
|
|
242
|
-
html: true,
|
|
243
|
-
linkify: true,
|
|
244
|
-
breaks: true
|
|
245
|
-
});
|
|
246
|
-
this.md.renderer.rules.fence = (tokens, idx) => {
|
|
247
|
-
const token = tokens[idx];
|
|
248
|
-
const code = token.content.trim();
|
|
249
|
-
const lang = token.info.trim();
|
|
250
|
-
return `<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
251
|
-
<ac:parameter ac:name="language">${lang || "text"}</ac:parameter>
|
|
252
|
-
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
253
|
-
</ac:structured-macro>`;
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
convert(markdown) {
|
|
257
|
-
return this.md.render(markdown);
|
|
258
|
-
}
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
// src/confluence/converters/storage-to-md.ts
|
|
262
|
-
var StorageToMarkdownConverter = class {
|
|
263
|
-
convert(storageHtml, imageUrlMap) {
|
|
264
|
-
let md = storageHtml;
|
|
265
|
-
if (imageUrlMap && imageUrlMap.size > 0) {
|
|
266
|
-
imageUrlMap.forEach((localPath, originalTag) => {
|
|
267
|
-
const filename = localPath.split("/").pop() || "image";
|
|
268
|
-
const altText = filename.replace(/\.[^.]+$/, "");
|
|
269
|
-
const markdownImage = ``;
|
|
270
|
-
md = md.replace(originalTag, markdownImage);
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
md = md.replace(
|
|
274
|
-
/<ac:image[^>]*>[\s\S]*?<ri:attachment\s+ri:filename="([^"]+)"[\s\S]*?<\/ac:image>/g,
|
|
275
|
-
(match, filename) => {
|
|
276
|
-
return ``;
|
|
277
|
-
}
|
|
278
|
-
);
|
|
279
|
-
md = md.replace(
|
|
280
|
-
/<ac:image[^>]*>[\s\S]*?<ri:url\s+ri:value="([^"]+)"[\s\S]*?<\/ac:image>/g,
|
|
281
|
-
(match, url) => {
|
|
282
|
-
const filename = url.split("/").pop() || "image";
|
|
283
|
-
return ``;
|
|
284
|
-
}
|
|
285
|
-
);
|
|
286
|
-
md = md.replace(
|
|
287
|
-
/<img\s+[^>]*\/?>/g,
|
|
288
|
-
(match) => {
|
|
289
|
-
const srcMatch = /src="([^"]+)"/.exec(match);
|
|
290
|
-
const altMatch = /alt="([^"]*)"/.exec(match);
|
|
291
|
-
if (srcMatch) {
|
|
292
|
-
const src = srcMatch[1];
|
|
293
|
-
const altText = altMatch ? altMatch[1] : src.split("/").pop() || "image";
|
|
294
|
-
return ``;
|
|
295
|
-
}
|
|
296
|
-
return match;
|
|
297
|
-
}
|
|
298
|
-
);
|
|
299
|
-
md = md.replace(/<ac:structured-macro[^>]*ac:name="code"[^>]*>[\s\S]*?<ac:parameter[^>]*ac:name="language">([^<]*)<\/ac:parameter>[\s\S]*?<ac:plain-text-body><!\[CDATA\[([\s\S]*?)\]\]><\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g, (match, lang, code) => {
|
|
300
|
-
return `\`\`\`${lang}
|
|
301
|
-
${code}
|
|
302
|
-
\`\`\``;
|
|
303
|
-
});
|
|
304
|
-
md = md.replace(/<h1>(.*?)<\/h1>/g, "# $1\n");
|
|
305
|
-
md = md.replace(/<h2>(.*?)<\/h2>/g, "## $1\n");
|
|
306
|
-
md = md.replace(/<h3>(.*?)<\/h3>/g, "### $1\n");
|
|
307
|
-
md = md.replace(/<p>(.*?)<\/p>/g, "$1\n\n");
|
|
308
|
-
md = md.replace(/<strong>(.*?)<\/strong>/g, "**$1**");
|
|
309
|
-
md = md.replace(/<b>(.*?)<\/b>/g, "**$1**");
|
|
310
|
-
md = md.replace(/<em>(.*?)<\/em>/g, "*$1*");
|
|
311
|
-
md = md.replace(/<i>(.*?)<\/i>/g, "*$1*");
|
|
312
|
-
md = md.replace(/<ul>([\s\S]*?)<\/ul>/g, (match, content) => {
|
|
313
|
-
return content.replace(/<li>(.*?)<\/li>/g, "- $1\n");
|
|
314
|
-
});
|
|
315
|
-
md = md.replace(/<ol>([\s\S]*?)<\/ol>/g, (match, content) => {
|
|
316
|
-
let i = 1;
|
|
317
|
-
return content.replace(/<li>(.*?)<\/li>/g, () => `${i++}. $1
|
|
318
|
-
`);
|
|
319
|
-
});
|
|
320
|
-
md = md.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"');
|
|
321
|
-
return md.trim();
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
// src/common/config.ts
|
|
279
|
+
// tools/common/config.ts
|
|
326
280
|
import dotenv from "dotenv";
|
|
327
281
|
dotenv.config();
|
|
328
282
|
function getEnvOrThrow(key, description) {
|
|
@@ -337,20 +291,31 @@ function getEnvOrThrow(key, description) {
|
|
|
337
291
|
function loadConfluenceConfig() {
|
|
338
292
|
const baseUrl = getEnvOrThrow("CONFLUENCE_BASE_URL", "Confluence \uAE30\uBCF8 URL");
|
|
339
293
|
const username = process.env.CONFLUENCE_USERNAME;
|
|
340
|
-
const token = getEnvOrThrow("CONFLUENCE_API_TOKEN", "Confluence
|
|
294
|
+
const token = getEnvOrThrow("CONFLUENCE_API_TOKEN", "Confluence PAT \uD1A0\uD070");
|
|
295
|
+
const mermaidMacroName = process.env.CONFLUENCE_MERMAID_MACRO_NAME || "mermaiddiagram";
|
|
296
|
+
const inlineCodeStyle = process.env.CONFLUENCE_INLINE_CODE_STYLE || "color: #d04437; font-weight: bold;";
|
|
341
297
|
return {
|
|
342
298
|
baseUrl,
|
|
343
299
|
auth: {
|
|
344
300
|
username,
|
|
345
301
|
token
|
|
346
|
-
|
|
347
|
-
|
|
302
|
+
},
|
|
303
|
+
mermaidMacroName,
|
|
304
|
+
inlineCodeStyle
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
function loadAIConfig() {
|
|
308
|
+
return {
|
|
309
|
+
openaiApiKey: process.env.OPENAI_API_KEY,
|
|
310
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
311
|
+
defaultProvider: process.env.AI_PROVIDER || "openai",
|
|
312
|
+
defaultModel: process.env.AI_MODEL || "gpt-4o"
|
|
348
313
|
};
|
|
349
314
|
}
|
|
350
315
|
function loadJiraConfig() {
|
|
351
316
|
const baseUrl = getEnvOrThrow("JIRA_BASE_URL", "JIRA \uAE30\uBCF8 URL");
|
|
352
317
|
const username = process.env.JIRA_USERNAME;
|
|
353
|
-
const token = getEnvOrThrow("JIRA_API_TOKEN", "JIRA
|
|
318
|
+
const token = getEnvOrThrow("JIRA_API_TOKEN", "JIRA PAT \uD1A0\uD070");
|
|
354
319
|
return {
|
|
355
320
|
baseUrl,
|
|
356
321
|
auth: {
|
|
@@ -370,7 +335,205 @@ function loadGitlabConfig() {
|
|
|
370
335
|
};
|
|
371
336
|
}
|
|
372
337
|
|
|
373
|
-
//
|
|
338
|
+
// tools/confluence/converters/md-to-storage.ts
|
|
339
|
+
import MarkdownIt from "markdown-it";
|
|
340
|
+
var MarkdownToStorageConverter = class {
|
|
341
|
+
md;
|
|
342
|
+
mermaidMacroName;
|
|
343
|
+
inlineCodeStyle;
|
|
344
|
+
constructor() {
|
|
345
|
+
const config = loadConfluenceConfig();
|
|
346
|
+
this.mermaidMacroName = config.mermaidMacroName;
|
|
347
|
+
this.inlineCodeStyle = config.inlineCodeStyle;
|
|
348
|
+
this.md = new MarkdownIt({
|
|
349
|
+
html: true,
|
|
350
|
+
linkify: true,
|
|
351
|
+
breaks: true,
|
|
352
|
+
xhtmlOut: true
|
|
353
|
+
// Confluence XML 파서와의 호환성을 위해 XHTML 출력 활성화
|
|
354
|
+
});
|
|
355
|
+
this.md.renderer.rules.fence = (tokens, idx) => {
|
|
356
|
+
const token = tokens[idx];
|
|
357
|
+
const code = token.content.trim();
|
|
358
|
+
const lang = token.info.trim().toLowerCase();
|
|
359
|
+
if (lang === "mermaid") {
|
|
360
|
+
return `<ac:structured-macro ac:name="${this.mermaidMacroName}" ac:schema-version="1">
|
|
361
|
+
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
362
|
+
</ac:structured-macro>`;
|
|
363
|
+
}
|
|
364
|
+
if (lang === "plantuml") {
|
|
365
|
+
return `<ac:structured-macro ac:name="plantuml" ac:schema-version="1">
|
|
366
|
+
<ac:parameter ac:name="atlassian-macro-output-type">INLINE</ac:parameter>
|
|
367
|
+
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
368
|
+
</ac:structured-macro>`;
|
|
369
|
+
}
|
|
370
|
+
return `<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
371
|
+
<ac:parameter ac:name="language">${lang || "text"}</ac:parameter>
|
|
372
|
+
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
373
|
+
</ac:structured-macro>`;
|
|
374
|
+
};
|
|
375
|
+
this.md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
|
376
|
+
const token = tokens[idx];
|
|
377
|
+
const src = token.attrGet("src") || "";
|
|
378
|
+
const alt = token.content || "";
|
|
379
|
+
const isExternal = src.startsWith("http://") || src.startsWith("https://");
|
|
380
|
+
const altAttr = alt ? ` ac:alt="${this.md.utils.escapeHtml(alt)}"` : "";
|
|
381
|
+
if (isExternal) {
|
|
382
|
+
return `<ac:image${altAttr}><ri:url ri:value="${src}" /></ac:image>`;
|
|
383
|
+
} else {
|
|
384
|
+
const filename = src.split("/").pop() || src;
|
|
385
|
+
return `<ac:image${altAttr}><ri:attachment ri:filename="${filename}" /></ac:image>`;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
this.md.renderer.rules.code_inline = (tokens, idx) => {
|
|
389
|
+
const token = tokens[idx];
|
|
390
|
+
const content = this.md.utils.escapeHtml(token.content);
|
|
391
|
+
return `<code style="${this.inlineCodeStyle}">${content}</code>`;
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
convert(markdown) {
|
|
395
|
+
return this.md.render(markdown);
|
|
396
|
+
}
|
|
397
|
+
extractLocalImages(markdown) {
|
|
398
|
+
const tokens = this.md.parse(markdown, {});
|
|
399
|
+
const localImages = /* @__PURE__ */ new Set();
|
|
400
|
+
const walk = (tokens2) => {
|
|
401
|
+
for (const token of tokens2) {
|
|
402
|
+
if (token.type === "image") {
|
|
403
|
+
const src = token.attrGet("src") || "";
|
|
404
|
+
if (!src.startsWith("http://") && !src.startsWith("https://")) {
|
|
405
|
+
localImages.add(src);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (token.children) {
|
|
409
|
+
walk(token.children);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
walk(tokens);
|
|
414
|
+
return Array.from(localImages);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// tools/confluence/converters/storage-to-md.ts
|
|
419
|
+
import TurndownService from "turndown";
|
|
420
|
+
import { gfm } from "turndown-plugin-gfm";
|
|
421
|
+
import { JSDOM } from "jsdom";
|
|
422
|
+
var StorageToMarkdownConverter = class {
|
|
423
|
+
turndown;
|
|
424
|
+
constructor() {
|
|
425
|
+
this.turndown = new TurndownService({
|
|
426
|
+
headingStyle: "atx",
|
|
427
|
+
hr: "---",
|
|
428
|
+
bulletListMarker: "-",
|
|
429
|
+
codeBlockStyle: "fenced"
|
|
430
|
+
});
|
|
431
|
+
this.turndown.use(gfm);
|
|
432
|
+
this.setupRules();
|
|
433
|
+
}
|
|
434
|
+
setupRules() {
|
|
435
|
+
this.turndown.addRule("tables", {
|
|
436
|
+
filter: ["table"],
|
|
437
|
+
replacement: (content, node) => {
|
|
438
|
+
const element = node;
|
|
439
|
+
const rows = Array.from(element.rows);
|
|
440
|
+
if (rows.length === 0) return "";
|
|
441
|
+
let mdTable = "\n\n";
|
|
442
|
+
rows.forEach((row, index) => {
|
|
443
|
+
const cells = Array.from(row.cells);
|
|
444
|
+
const cellContents = cells.map((cell) => {
|
|
445
|
+
return this.turndown.turndown(cell.innerHTML).replace(/\n/g, " ").trim();
|
|
446
|
+
});
|
|
447
|
+
mdTable += `| ${cellContents.join(" | ")} |
|
|
448
|
+
`;
|
|
449
|
+
if (index === 0) {
|
|
450
|
+
const separators = cells.map((cell) => {
|
|
451
|
+
const style = cell.getAttribute("style") || "";
|
|
452
|
+
const align = style.match(/text-align:\s*(\w+)/i)?.[1]?.toLowerCase();
|
|
453
|
+
if (align === "center") return ":---:";
|
|
454
|
+
if (align === "right") return "---:";
|
|
455
|
+
if (align === "left") return ":---";
|
|
456
|
+
return "---";
|
|
457
|
+
});
|
|
458
|
+
mdTable += `| ${separators.join(" | ")} |
|
|
459
|
+
`;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
return mdTable + "\n";
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
this.turndown.addRule("confluenceMacro", {
|
|
466
|
+
filter: (node) => {
|
|
467
|
+
return node.nodeName === "DIV" && node.getAttribute("data-macro-name-tag") !== null;
|
|
468
|
+
},
|
|
469
|
+
replacement: (content, node) => {
|
|
470
|
+
const element = node;
|
|
471
|
+
const macroName = element.getAttribute("data-macro-name");
|
|
472
|
+
if (macroName === "code") {
|
|
473
|
+
const lang = element.querySelector('[data-macro-param-name="language"]')?.textContent || "text";
|
|
474
|
+
let body = element.querySelector("[data-macro-body]")?.textContent || "";
|
|
475
|
+
body = body.replace(/__CDATA_START__/g, "").replace(/__CDATA_END__/g, "");
|
|
476
|
+
return `
|
|
477
|
+
\`\`\`${lang}
|
|
478
|
+
${body.trim()}
|
|
479
|
+
\`\`\`
|
|
480
|
+
`;
|
|
481
|
+
}
|
|
482
|
+
if (macroName === "mermaid" || macroName === "mermaiddiagram" || macroName === "capable-mermaid" || macroName === "mermaid-macro") {
|
|
483
|
+
let body = element.querySelector("[data-macro-body]")?.textContent || "";
|
|
484
|
+
body = body.replace(/__CDATA_START__/g, "").replace(/__CDATA_END__/g, "");
|
|
485
|
+
return `
|
|
486
|
+
\`\`\`mermaid
|
|
487
|
+
${body.trim()}
|
|
488
|
+
\`\`\`
|
|
489
|
+
`;
|
|
490
|
+
}
|
|
491
|
+
if (macroName === "plantuml") {
|
|
492
|
+
let body = element.querySelector("[data-macro-body]")?.textContent || "";
|
|
493
|
+
body = body.replace(/__CDATA_START__/g, "").replace(/__CDATA_END__/g, "");
|
|
494
|
+
return `
|
|
495
|
+
\`\`\`plantuml
|
|
496
|
+
${body.trim()}
|
|
497
|
+
\`\`\`
|
|
498
|
+
`;
|
|
499
|
+
}
|
|
500
|
+
return `
|
|
501
|
+
<!-- Macro: ${macroName} -->
|
|
502
|
+
`;
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
convert(storageHtml, imageUrlMap) {
|
|
507
|
+
if (!storageHtml) return "";
|
|
508
|
+
let processedHtml = storageHtml.replace(/<!\[CDATA\[([\s\S]*?)\]\]>/gi, (match, p1) => {
|
|
509
|
+
return `__CDATA_START__${p1}__CDATA_END__`;
|
|
510
|
+
}).replace(/<ac:structured-macro\s+ac:name="([^"]*)"/gi, '<div data-macro-name-tag data-macro-name="$1"').replace(/<\/ac:structured-macro>/gi, "</div>").replace(/<ac:parameter\s+ac:name="([^"]*)"/gi, '<div data-macro-param-tag data-macro-param-name="$1"').replace(/<\/ac:parameter>/gi, "</div>").replace(/<ac:plain-text-body>/gi, "<pre data-macro-body>").replace(/<\/ac:plain-text-body>/gi, "</pre>").replace(/<ac:image([^>]*)>[\s\S]*?<ri:attachment\s+ri:filename="([^"]*)"\s*\/?>[\s\S]*?<\/ac:image>/gi, (match, attrs, filename) => {
|
|
511
|
+
const altMatch = attrs.match(/ac:alt="([^"]*)"/i);
|
|
512
|
+
const alt = altMatch ? altMatch[1] : filename;
|
|
513
|
+
return `<img src="${filename}" alt="${alt}" />`;
|
|
514
|
+
}).replace(/<ac:image([^>]*)>[\s\S]*?<ri:url\s+ri:value="([^"]*)"\s*\/?>[\s\S]*?<\/ac:image>/gi, (match, attrs, url) => {
|
|
515
|
+
const altMatch = attrs.match(/ac:alt="([^"]*)"/i);
|
|
516
|
+
const alt = altMatch ? altMatch[1] : "";
|
|
517
|
+
return `<img src="${url}" alt="${alt}" />`;
|
|
518
|
+
});
|
|
519
|
+
const dom = new JSDOM(processedHtml);
|
|
520
|
+
const document = dom.window.document;
|
|
521
|
+
if (imageUrlMap && imageUrlMap.size > 0) {
|
|
522
|
+
const images = document.querySelectorAll("img");
|
|
523
|
+
images.forEach((img) => {
|
|
524
|
+
const src = img.getAttribute("src");
|
|
525
|
+
if (src && imageUrlMap.has(src)) {
|
|
526
|
+
img.setAttribute("src", imageUrlMap.get(src));
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
let markdown = this.turndown.turndown(document.body.innerHTML).trim();
|
|
531
|
+
markdown = markdown.replace(/^(#{1,6}\s.*?)\\\./gm, "$1.");
|
|
532
|
+
return markdown + "\n";
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// tools/jira/api/issue.ts
|
|
374
537
|
var JiraIssueApi = class {
|
|
375
538
|
constructor(client) {
|
|
376
539
|
this.client = client;
|
|
@@ -430,7 +593,7 @@ var JiraIssueApi = class {
|
|
|
430
593
|
}
|
|
431
594
|
};
|
|
432
595
|
|
|
433
|
-
//
|
|
596
|
+
// tools/jira/api/search.ts
|
|
434
597
|
var JiraSearchApi = class {
|
|
435
598
|
constructor(client) {
|
|
436
599
|
this.client = client;
|
|
@@ -449,111 +612,12 @@ var JiraSearchApi = class {
|
|
|
449
612
|
}
|
|
450
613
|
};
|
|
451
614
|
|
|
452
|
-
//
|
|
453
|
-
var JiraTransitionApi = class {
|
|
454
|
-
constructor(client) {
|
|
455
|
-
this.client = client;
|
|
456
|
-
}
|
|
457
|
-
async getTransitions(issueKey) {
|
|
458
|
-
const response = await this.client.get(`/rest/api/2/issue/${issueKey}/transitions`);
|
|
459
|
-
return response.data.transitions;
|
|
460
|
-
}
|
|
461
|
-
async doTransition(issueKey, transitionId, fields) {
|
|
462
|
-
const data = {
|
|
463
|
-
transition: { id: transitionId }
|
|
464
|
-
};
|
|
465
|
-
if (fields) {
|
|
466
|
-
data.fields = fields;
|
|
467
|
-
}
|
|
468
|
-
await this.client.post(`/rest/api/2/issue/${issueKey}/transitions`, data);
|
|
469
|
-
}
|
|
470
|
-
};
|
|
471
|
-
|
|
472
|
-
// src/jira/api/comment.ts
|
|
473
|
-
var JiraCommentApi = class {
|
|
474
|
-
constructor(client) {
|
|
475
|
-
this.client = client;
|
|
476
|
-
}
|
|
477
|
-
async getComments(issueKey, startAt = 0, maxResults = 50) {
|
|
478
|
-
const response = await this.client.get(`/rest/api/2/issue/${issueKey}/comment`, {
|
|
479
|
-
params: { startAt, maxResults }
|
|
480
|
-
});
|
|
481
|
-
return response.data;
|
|
482
|
-
}
|
|
483
|
-
async addComment(issueKey, body) {
|
|
484
|
-
const response = await this.client.post(`/rest/api/2/issue/${issueKey}/comment`, { body });
|
|
485
|
-
return response.data;
|
|
486
|
-
}
|
|
487
|
-
async updateComment(issueKey, commentId, body) {
|
|
488
|
-
const response = await this.client.put(
|
|
489
|
-
`/rest/api/2/issue/${issueKey}/comment/${commentId}`,
|
|
490
|
-
{ body }
|
|
491
|
-
);
|
|
492
|
-
return response.data;
|
|
493
|
-
}
|
|
494
|
-
async deleteComment(issueKey, commentId) {
|
|
495
|
-
await this.client.delete(`/rest/api/2/issue/${issueKey}/comment/${commentId}`);
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
|
|
499
|
-
// src/jira/api/project.ts
|
|
500
|
-
var JiraProjectApi = class {
|
|
501
|
-
constructor(client) {
|
|
502
|
-
this.client = client;
|
|
503
|
-
}
|
|
504
|
-
async getProjects() {
|
|
505
|
-
const response = await this.client.get("/rest/api/2/project");
|
|
506
|
-
return response.data;
|
|
507
|
-
}
|
|
508
|
-
async getProject(projectKey) {
|
|
509
|
-
const response = await this.client.get(`/rest/api/2/project/${projectKey}`);
|
|
510
|
-
return response.data;
|
|
511
|
-
}
|
|
512
|
-
async getBoards(projectKeyOrId, type) {
|
|
513
|
-
const params = {};
|
|
514
|
-
if (projectKeyOrId) params.projectKeyOrId = projectKeyOrId;
|
|
515
|
-
if (type) params.type = type;
|
|
516
|
-
const response = await this.client.get("/rest/agile/1.0/board", { params });
|
|
517
|
-
return response.data;
|
|
518
|
-
}
|
|
519
|
-
async getSprints(boardId, state) {
|
|
520
|
-
const params = {};
|
|
521
|
-
if (state) params.state = state;
|
|
522
|
-
const response = await this.client.get(`/rest/agile/1.0/board/${boardId}/sprint`, {
|
|
523
|
-
params
|
|
524
|
-
});
|
|
525
|
-
return response.data;
|
|
526
|
-
}
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
// src/jira/api/client.ts
|
|
615
|
+
// tools/jira/api/client.ts
|
|
530
616
|
function createJiraClient(config) {
|
|
531
617
|
return createHttpClient(config);
|
|
532
618
|
}
|
|
533
619
|
|
|
534
|
-
//
|
|
535
|
-
var GitlabProjectApi = class {
|
|
536
|
-
constructor(client) {
|
|
537
|
-
this.client = client;
|
|
538
|
-
}
|
|
539
|
-
async getProjects(params) {
|
|
540
|
-
const response = await this.client.get("/projects", {
|
|
541
|
-
params: {
|
|
542
|
-
search: params?.search,
|
|
543
|
-
owned: params?.owned,
|
|
544
|
-
membership: params?.membership,
|
|
545
|
-
per_page: params?.perPage || 20
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
return response.data;
|
|
549
|
-
}
|
|
550
|
-
async getProject(projectId) {
|
|
551
|
-
const response = await this.client.get(`/projects/${encodeURIComponent(projectId)}`);
|
|
552
|
-
return response.data;
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
// src/gitlab/api/merge-request.ts
|
|
620
|
+
// tools/gitlab/api/merge-request.ts
|
|
557
621
|
var GitlabMergeRequestApi = class {
|
|
558
622
|
constructor(client) {
|
|
559
623
|
this.client = client;
|
|
@@ -614,7 +678,7 @@ var GitlabMergeRequestApi = class {
|
|
|
614
678
|
}
|
|
615
679
|
};
|
|
616
680
|
|
|
617
|
-
//
|
|
681
|
+
// tools/gitlab/api/pipeline.ts
|
|
618
682
|
var GitlabPipelineApi = class {
|
|
619
683
|
constructor(client) {
|
|
620
684
|
this.client = client;
|
|
@@ -649,71 +713,7 @@ var GitlabPipelineApi = class {
|
|
|
649
713
|
}
|
|
650
714
|
};
|
|
651
715
|
|
|
652
|
-
//
|
|
653
|
-
var GitlabBranchApi = class {
|
|
654
|
-
constructor(client) {
|
|
655
|
-
this.client = client;
|
|
656
|
-
}
|
|
657
|
-
async getBranches(projectId, params) {
|
|
658
|
-
const response = await this.client.get(`/projects/${projectId}/repository/branches`, {
|
|
659
|
-
params: {
|
|
660
|
-
search: params?.search,
|
|
661
|
-
per_page: params?.perPage || 20
|
|
662
|
-
}
|
|
663
|
-
});
|
|
664
|
-
return response.data;
|
|
665
|
-
}
|
|
666
|
-
async getBranch(projectId, branchName) {
|
|
667
|
-
const response = await this.client.get(
|
|
668
|
-
`/projects/${projectId}/repository/branches/${encodeURIComponent(branchName)}`
|
|
669
|
-
);
|
|
670
|
-
return response.data;
|
|
671
|
-
}
|
|
672
|
-
async createBranch(projectId, branchName, ref) {
|
|
673
|
-
const response = await this.client.post(`/projects/${projectId}/repository/branches`, {
|
|
674
|
-
branch: branchName,
|
|
675
|
-
ref
|
|
676
|
-
});
|
|
677
|
-
return response.data;
|
|
678
|
-
}
|
|
679
|
-
async deleteBranch(projectId, branchName) {
|
|
680
|
-
await this.client.delete(
|
|
681
|
-
`/projects/${projectId}/repository/branches/${encodeURIComponent(branchName)}`
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
|
|
686
|
-
// src/gitlab/api/repository.ts
|
|
687
|
-
var GitlabRepositoryApi = class {
|
|
688
|
-
constructor(client) {
|
|
689
|
-
this.client = client;
|
|
690
|
-
}
|
|
691
|
-
async getFile(projectId, filePath, ref) {
|
|
692
|
-
const encodedPath = encodeURIComponent(filePath);
|
|
693
|
-
const response = await this.client.get(
|
|
694
|
-
`/projects/${projectId}/repository/files/${encodedPath}`,
|
|
695
|
-
{ params: { ref: ref || "HEAD" } }
|
|
696
|
-
);
|
|
697
|
-
const file = response.data;
|
|
698
|
-
if (file.encoding === "base64") {
|
|
699
|
-
file.content = Buffer.from(file.content, "base64").toString("utf-8");
|
|
700
|
-
}
|
|
701
|
-
return file;
|
|
702
|
-
}
|
|
703
|
-
async getTree(projectId, params) {
|
|
704
|
-
const response = await this.client.get(`/projects/${projectId}/repository/tree`, {
|
|
705
|
-
params: {
|
|
706
|
-
path: params?.path,
|
|
707
|
-
ref: params?.ref,
|
|
708
|
-
recursive: params?.recursive,
|
|
709
|
-
per_page: params?.perPage || 100
|
|
710
|
-
}
|
|
711
|
-
});
|
|
712
|
-
return response.data;
|
|
713
|
-
}
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
// src/gitlab/api/client.ts
|
|
716
|
+
// tools/gitlab/api/client.ts
|
|
717
717
|
function createGitlabClient(config) {
|
|
718
718
|
const client = createHttpClient({
|
|
719
719
|
...config,
|
|
@@ -728,22 +728,17 @@ export {
|
|
|
728
728
|
ConfluenceSpaceApi,
|
|
729
729
|
ConfluenceSearchApi,
|
|
730
730
|
createConfluenceClient,
|
|
731
|
-
MarkdownToStorageConverter,
|
|
732
|
-
StorageToMarkdownConverter,
|
|
733
731
|
loadConfluenceConfig,
|
|
732
|
+
loadAIConfig,
|
|
734
733
|
loadJiraConfig,
|
|
735
734
|
loadGitlabConfig,
|
|
735
|
+
MarkdownToStorageConverter,
|
|
736
|
+
StorageToMarkdownConverter,
|
|
736
737
|
JiraIssueApi,
|
|
737
738
|
JiraSearchApi,
|
|
738
|
-
JiraTransitionApi,
|
|
739
|
-
JiraCommentApi,
|
|
740
|
-
JiraProjectApi,
|
|
741
739
|
createJiraClient,
|
|
742
|
-
GitlabProjectApi,
|
|
743
740
|
GitlabMergeRequestApi,
|
|
744
741
|
GitlabPipelineApi,
|
|
745
|
-
GitlabBranchApi,
|
|
746
|
-
GitlabRepositoryApi,
|
|
747
742
|
createGitlabClient
|
|
748
743
|
};
|
|
749
|
-
//# sourceMappingURL=chunk-
|
|
744
|
+
//# sourceMappingURL=chunk-6AFNYE7N.js.map
|