tdecollab 0.1.2 → 0.2.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/README.md +6 -0
- package/dist/chunk-2IQ4QMK3.js +749 -0
- package/dist/chunk-2IQ4QMK3.js.map +1 -0
- package/dist/chunk-SJ7KPK6Q.js +58 -0
- package/dist/chunk-SJ7KPK6Q.js.map +1 -0
- package/dist/chunk-T73I3OT6.js +964 -0
- package/dist/chunk-T73I3OT6.js.map +1 -0
- package/dist/cli.js +599 -7
- package/dist/cli.js.map +1 -1
- package/dist/image-downloader-D57KFAIQ.js +123 -0
- package/dist/image-downloader-D57KFAIQ.js.map +1 -0
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/server-HS774DWY.js +9 -0
- package/package.json +2 -2
- package/dist/chunk-3TQ2OSYU.js +0 -287
- package/dist/chunk-3TQ2OSYU.js.map +0 -1
- package/dist/chunk-N44NISLJ.js +0 -361
- package/dist/chunk-N44NISLJ.js.map +0 -1
- package/dist/server-H24WSQEE.js +0 -8
- /package/dist/{server-H24WSQEE.js.map → server-HS774DWY.js.map} +0 -0
package/dist/chunk-3TQ2OSYU.js
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ConfluenceContentApi,
|
|
3
|
-
ConfluenceSearchApi,
|
|
4
|
-
ConfluenceSpaceApi,
|
|
5
|
-
MarkdownToStorageConverter,
|
|
6
|
-
StorageToMarkdownConverter,
|
|
7
|
-
createConfluenceClient,
|
|
8
|
-
loadConfluenceConfig,
|
|
9
|
-
logger
|
|
10
|
-
} from "./chunk-N44NISLJ.js";
|
|
11
|
-
|
|
12
|
-
// src/mcp/server.ts
|
|
13
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
-
|
|
16
|
-
// src/confluence/tools/index.ts
|
|
17
|
-
import { z } from "zod";
|
|
18
|
-
|
|
19
|
-
// src/confluence/api/label.ts
|
|
20
|
-
var ConfluenceLabelApi = class {
|
|
21
|
-
constructor(client) {
|
|
22
|
-
this.client = client;
|
|
23
|
-
}
|
|
24
|
-
async getLabels(pageId) {
|
|
25
|
-
const response = await this.client.get(`/rest/api/content/${pageId}/label`);
|
|
26
|
-
return response.data.results;
|
|
27
|
-
}
|
|
28
|
-
async addLabels(pageId, labels) {
|
|
29
|
-
const data = labels.map((name) => ({ prefix: "global", name }));
|
|
30
|
-
await this.client.post(`/rest/api/content/${pageId}/label`, data);
|
|
31
|
-
}
|
|
32
|
-
async removeLabel(pageId, labelName) {
|
|
33
|
-
await this.client.delete(`/rest/api/content/${pageId}/label`, {
|
|
34
|
-
params: { name: labelName }
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// src/confluence/tools/index.ts
|
|
40
|
-
function registerConfluenceTools(server) {
|
|
41
|
-
try {
|
|
42
|
-
const config = loadConfluenceConfig();
|
|
43
|
-
const client = createConfluenceClient(config);
|
|
44
|
-
const contentApi = new ConfluenceContentApi(client);
|
|
45
|
-
const spaceApi = new ConfluenceSpaceApi(client);
|
|
46
|
-
const searchApi = new ConfluenceSearchApi(client);
|
|
47
|
-
const labelApi = new ConfluenceLabelApi(client);
|
|
48
|
-
const mdToStorage = new MarkdownToStorageConverter();
|
|
49
|
-
const storageToMd = new StorageToMarkdownConverter();
|
|
50
|
-
server.tool(
|
|
51
|
-
"confluence_get_page",
|
|
52
|
-
"TDE Confluence \uD398\uC774\uC9C0 \uC0C1\uC138 \uC870\uD68C. \uD398\uC774\uC9C0 \uB0B4\uC6A9\uC744 Markdown\uC73C\uB85C \uBCC0\uD658\uD558\uC5EC \uBC18\uD658\uD569\uB2C8\uB2E4.",
|
|
53
|
-
{
|
|
54
|
-
pageId: z.string().describe("\uD398\uC774\uC9C0 ID")
|
|
55
|
-
},
|
|
56
|
-
async ({ pageId }) => {
|
|
57
|
-
const page = await contentApi.getPage(pageId);
|
|
58
|
-
const md = page.body?.storage?.value ? storageToMd.convert(page.body.storage.value) : "";
|
|
59
|
-
return {
|
|
60
|
-
content: [
|
|
61
|
-
{
|
|
62
|
-
type: "text",
|
|
63
|
-
text: `Title: ${page.title}
|
|
64
|
-
ID: ${page.id}
|
|
65
|
-
Space: ${page.space?.name} (${page.space?.key})
|
|
66
|
-
URL: ${page._links?.base}${page._links?.webui}
|
|
67
|
-
|
|
68
|
-
${md}`
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
);
|
|
74
|
-
server.tool(
|
|
75
|
-
"confluence_create_page",
|
|
76
|
-
"TDE Confluence\uC5D0 \uC0C8 \uD398\uC774\uC9C0\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4. Markdown \uD615\uC2DD\uC758 \uB0B4\uC6A9\uC744 Confluence Storage Format\uC73C\uB85C \uC790\uB3D9 \uBCC0\uD658\uD569\uB2C8\uB2E4.",
|
|
77
|
-
{
|
|
78
|
-
spaceKey: z.string().describe("\uC2A4\uD398\uC774\uC2A4 \uD0A4"),
|
|
79
|
-
title: z.string().describe("\uD398\uC774\uC9C0 \uC81C\uBAA9"),
|
|
80
|
-
content: z.string().describe("\uD398\uC774\uC9C0 \uB0B4\uC6A9 (Markdown)"),
|
|
81
|
-
parentId: z.string().optional().describe("\uBD80\uBAA8 \uD398\uC774\uC9C0 ID"),
|
|
82
|
-
labels: z.array(z.string()).optional().describe("\uB77C\uBCA8 \uBAA9\uB85D")
|
|
83
|
-
},
|
|
84
|
-
async ({ spaceKey, title, content, parentId, labels }) => {
|
|
85
|
-
const storageBody = mdToStorage.convert(content);
|
|
86
|
-
const page = await contentApi.createPage({
|
|
87
|
-
spaceKey,
|
|
88
|
-
title,
|
|
89
|
-
body: storageBody,
|
|
90
|
-
parentId,
|
|
91
|
-
labels
|
|
92
|
-
});
|
|
93
|
-
return {
|
|
94
|
-
content: [
|
|
95
|
-
{
|
|
96
|
-
type: "text",
|
|
97
|
-
text: `\uD398\uC774\uC9C0 \uC0DD\uC131 \uC131\uACF5: ${page.title} (ID: ${page.id})
|
|
98
|
-
URL: ${page._links?.base}${page._links?.webui}`
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
);
|
|
104
|
-
server.tool(
|
|
105
|
-
"confluence_update_page",
|
|
106
|
-
"TDE Confluence \uD398\uC774\uC9C0\uB97C \uC218\uC815\uD569\uB2C8\uB2E4. Markdown \uD615\uC2DD\uC758 \uB0B4\uC6A9\uC73C\uB85C \uC5C5\uB370\uC774\uD2B8\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
107
|
-
{
|
|
108
|
-
pageId: z.string().describe("\uD398\uC774\uC9C0 ID"),
|
|
109
|
-
title: z.string().describe("\uD398\uC774\uC9C0 \uC81C\uBAA9"),
|
|
110
|
-
content: z.string().describe("\uD398\uC774\uC9C0 \uB0B4\uC6A9 (Markdown)"),
|
|
111
|
-
version: z.number().describe("\uD604\uC7AC \uD398\uC774\uC9C0 \uBC84\uC804 (\uCDA9\uB3CC \uBC29\uC9C0\uC6A9)")
|
|
112
|
-
},
|
|
113
|
-
async ({ pageId, title, content, version }) => {
|
|
114
|
-
const storageBody = mdToStorage.convert(content);
|
|
115
|
-
const page = await contentApi.updatePage({
|
|
116
|
-
id: pageId,
|
|
117
|
-
title,
|
|
118
|
-
body: storageBody,
|
|
119
|
-
version
|
|
120
|
-
});
|
|
121
|
-
return {
|
|
122
|
-
content: [
|
|
123
|
-
{
|
|
124
|
-
type: "text",
|
|
125
|
-
text: `\uD398\uC774\uC9C0 \uC218\uC815 \uC131\uACF5: ${page.title} (Version: ${page.version?.number})`
|
|
126
|
-
}
|
|
127
|
-
]
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
);
|
|
131
|
-
server.tool(
|
|
132
|
-
"confluence_search_pages",
|
|
133
|
-
"TDE Confluence\uC5D0\uC11C \uD398\uC774\uC9C0\uB97C \uAC80\uC0C9\uD569\uB2C8\uB2E4. CQL(Confluence Query Language)\uC744 \uC0AC\uC6A9\uD558\uC5EC \uACE0\uAE09 \uAC80\uC0C9\uC774 \uAC00\uB2A5\uD569\uB2C8\uB2E4.",
|
|
134
|
-
{
|
|
135
|
-
cql: z.string().describe('Confluence Query Language (\uC608: title ~ "guide")'),
|
|
136
|
-
limit: z.number().default(10).describe("\uACB0\uACFC \uAC1C\uC218 \uC81C\uD55C")
|
|
137
|
-
},
|
|
138
|
-
async ({ cql, limit }) => {
|
|
139
|
-
const result = await searchApi.searchByCql(cql, 0, limit);
|
|
140
|
-
const summary = result.results.map((p) => `- [${p.id}] ${p.title} (Space: ${p.space?.key})`).join("\n");
|
|
141
|
-
return {
|
|
142
|
-
content: [
|
|
143
|
-
{
|
|
144
|
-
type: "text",
|
|
145
|
-
text: `\uAC80\uC0C9 \uACB0\uACFC (${result.size}/${result.totalSize}):
|
|
146
|
-
${summary}`
|
|
147
|
-
}
|
|
148
|
-
]
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
server.tool(
|
|
153
|
-
"confluence_get_spaces",
|
|
154
|
-
"TDE Confluence\uC758 \uC2A4\uD398\uC774\uC2A4 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
155
|
-
{
|
|
156
|
-
limit: z.number().default(20).describe("\uACB0\uACFC \uAC1C\uC218 \uC81C\uD55C")
|
|
157
|
-
},
|
|
158
|
-
async ({ limit }) => {
|
|
159
|
-
const spaces = await spaceApi.getSpaces("global", 0, limit);
|
|
160
|
-
const summary = spaces.map((s) => `- [${s.key}] ${s.name}`).join("\n");
|
|
161
|
-
return {
|
|
162
|
-
content: [{ type: "text", text: `\uC2A4\uD398\uC774\uC2A4 \uBAA9\uB85D:
|
|
163
|
-
${summary}` }]
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
);
|
|
167
|
-
server.tool(
|
|
168
|
-
"confluence_delete_page",
|
|
169
|
-
"TDE Confluence \uD398\uC774\uC9C0\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4.",
|
|
170
|
-
{
|
|
171
|
-
pageId: z.string().describe("\uD398\uC774\uC9C0 ID")
|
|
172
|
-
},
|
|
173
|
-
async ({ pageId }) => {
|
|
174
|
-
await contentApi.deletePage(pageId);
|
|
175
|
-
return {
|
|
176
|
-
content: [
|
|
177
|
-
{
|
|
178
|
-
type: "text",
|
|
179
|
-
text: `\uD398\uC774\uC9C0 \uC0AD\uC81C \uC131\uACF5 (ID: ${pageId})`
|
|
180
|
-
}
|
|
181
|
-
]
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
);
|
|
185
|
-
server.tool(
|
|
186
|
-
"confluence_get_page_tree",
|
|
187
|
-
"TDE Confluence \uD398\uC774\uC9C0\uC758 \uD558\uC704 \uD398\uC774\uC9C0(\uC790\uC2DD \uD398\uC774\uC9C0) \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
188
|
-
{
|
|
189
|
-
pageId: z.string().describe("\uD398\uC774\uC9C0 ID"),
|
|
190
|
-
limit: z.number().default(20).describe("\uACB0\uACFC \uAC1C\uC218 \uC81C\uD55C")
|
|
191
|
-
},
|
|
192
|
-
async ({ pageId, limit }) => {
|
|
193
|
-
const children = await contentApi.getChildPages(pageId, 0, limit);
|
|
194
|
-
const summary = children.map((p) => `- [${p.id}] ${p.title}`).join("\n");
|
|
195
|
-
return {
|
|
196
|
-
content: [
|
|
197
|
-
{
|
|
198
|
-
type: "text",
|
|
199
|
-
text: `\uC790\uC2DD \uD398\uC774\uC9C0 \uBAA9\uB85D (${children.length}):
|
|
200
|
-
${summary}`
|
|
201
|
-
}
|
|
202
|
-
]
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
);
|
|
206
|
-
server.tool(
|
|
207
|
-
"confluence_manage_labels",
|
|
208
|
-
"TDE Confluence \uD398\uC774\uC9C0\uC758 \uB77C\uBCA8\uC744 \uAD00\uB9AC\uD569\uB2C8\uB2E4. \uB77C\uBCA8 \uC870\uD68C, \uCD94\uAC00, \uC0AD\uC81C \uAE30\uB2A5\uC744 \uC81C\uACF5\uD569\uB2C8\uB2E4.",
|
|
209
|
-
{
|
|
210
|
-
pageId: z.string().describe("\uD398\uC774\uC9C0 ID"),
|
|
211
|
-
action: z.enum(["list", "add", "remove"]).describe("\uC791\uC5C5 \uC720\uD615"),
|
|
212
|
-
labels: z.array(z.string()).optional().describe("\uCD94\uAC00\uD560 \uB77C\uBCA8 \uBAA9\uB85D (add \uC791\uC5C5 \uC2DC \uD544\uC218)"),
|
|
213
|
-
label: z.string().optional().describe("\uC0AD\uC81C\uD560 \uB77C\uBCA8 (remove \uC791\uC5C5 \uC2DC \uD544\uC218)")
|
|
214
|
-
},
|
|
215
|
-
async ({ pageId, action, labels, label }) => {
|
|
216
|
-
if (action === "list") {
|
|
217
|
-
const result = await labelApi.getLabels(pageId);
|
|
218
|
-
const summary = result.map((l) => l.name).join(", ");
|
|
219
|
-
return {
|
|
220
|
-
content: [{ type: "text", text: `\uB77C\uBCA8 \uBAA9\uB85D: ${summary}` }]
|
|
221
|
-
};
|
|
222
|
-
} else if (action === "add") {
|
|
223
|
-
if (!labels || labels.length === 0) {
|
|
224
|
-
throw new Error("\uB77C\uBCA8 \uBAA9\uB85D\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.");
|
|
225
|
-
}
|
|
226
|
-
await labelApi.addLabels(pageId, labels);
|
|
227
|
-
return {
|
|
228
|
-
content: [{ type: "text", text: `\uB77C\uBCA8 \uCD94\uAC00 \uC131\uACF5: ${labels.join(", ")}` }]
|
|
229
|
-
};
|
|
230
|
-
} else if (action === "remove") {
|
|
231
|
-
if (!label) {
|
|
232
|
-
throw new Error("\uC0AD\uC81C\uD560 \uB77C\uBCA8\uC744 \uC785\uB825\uD574\uC8FC\uC138\uC694.");
|
|
233
|
-
}
|
|
234
|
-
await labelApi.removeLabel(pageId, label);
|
|
235
|
-
return {
|
|
236
|
-
content: [{ type: "text", text: `\uB77C\uBCA8 \uC0AD\uC81C \uC131\uACF5: ${label}` }]
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
return { content: [] };
|
|
240
|
-
}
|
|
241
|
-
);
|
|
242
|
-
server.tool(
|
|
243
|
-
"confluence_convert_content",
|
|
244
|
-
"TDE Confluence \uCEE8\uD150\uCE20 \uD3EC\uB9F7\uC744 \uBCC0\uD658\uD569\uB2C8\uB2E4. Markdown\uACFC Confluence Storage Format \uAC04 \uC591\uBC29\uD5A5 \uBCC0\uD658\uC744 \uC9C0\uC6D0\uD569\uB2C8\uB2E4.",
|
|
245
|
-
{
|
|
246
|
-
content: z.string().describe("\uBCC0\uD658\uD560 \uCEE8\uD150\uCE20"),
|
|
247
|
-
format: z.enum(["storage_to_markdown", "markdown_to_storage"]).describe("\uBCC0\uD658 \uBC29\uD5A5")
|
|
248
|
-
},
|
|
249
|
-
async ({ content, format }) => {
|
|
250
|
-
let result = "";
|
|
251
|
-
if (format === "storage_to_markdown") {
|
|
252
|
-
result = storageToMd.convert(content);
|
|
253
|
-
} else {
|
|
254
|
-
result = mdToStorage.convert(content);
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
content: [{ type: "text", text: result }]
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
);
|
|
261
|
-
} catch (error) {
|
|
262
|
-
logger.warn(`Confluence \uC124\uC815 \uB85C\uB4DC \uC2E4\uD328 \uB610\uB294 \uB3C4\uAD6C \uB4F1\uB85D \uC911 \uC624\uB958 \uBC1C\uC0DD: ${error.message}`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// src/mcp/server.ts
|
|
267
|
-
async function runServer() {
|
|
268
|
-
try {
|
|
269
|
-
const server = new McpServer({
|
|
270
|
-
name: "TDE Collab",
|
|
271
|
-
version: "1.0.0",
|
|
272
|
-
description: "TDE \uD3EC\uD138(Confluence, JIRA, GitLab) \uD1B5\uD569 \uB3C4\uAD6C. TDE Confluence \uD398\uC774\uC9C0 \uAD00\uB9AC, \uAC80\uC0C9, \uD3B8\uC9D1 \uAE30\uB2A5\uC744 \uC81C\uACF5\uD569\uB2C8\uB2E4."
|
|
273
|
-
});
|
|
274
|
-
registerConfluenceTools(server);
|
|
275
|
-
const transport = new StdioServerTransport();
|
|
276
|
-
await server.connect(transport);
|
|
277
|
-
logger.info("TDE Collab MCP Server running on stdio");
|
|
278
|
-
} catch (error) {
|
|
279
|
-
logger.error(`Fatal error in MCP Server: ${error}`);
|
|
280
|
-
process.exit(1);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
export {
|
|
285
|
-
runServer
|
|
286
|
-
};
|
|
287
|
-
//# sourceMappingURL=chunk-3TQ2OSYU.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp/server.ts","../src/confluence/tools/index.ts","../src/confluence/api/label.ts"],"sourcesContent":["\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { registerConfluenceTools } from '../confluence/tools/index.js';\nimport { logger } from '../common/logger.js';\n\nexport async function runServer() {\n try {\n const server = new McpServer({\n name: 'TDE Collab',\n version: '1.0.0',\n description: 'TDE 포털(Confluence, JIRA, GitLab) 통합 도구. TDE Confluence 페이지 관리, 검색, 편집 기능을 제공합니다.',\n });\n\n // Confluence 도구 등록\n registerConfluenceTools(server);\n\n // Stdio 전송 계층 연결\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n logger.info('TDE Collab MCP Server running on stdio');\n } catch (error) {\n logger.error(`Fatal error in MCP Server: ${error}`);\n process.exit(1);\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport { ConfluenceContentApi } from '../api/content.js';\nimport { ConfluenceSpaceApi } from '../api/space.js';\nimport { ConfluenceSearchApi } from '../api/search.js';\nimport { ConfluenceLabelApi } from '../api/label.js';\nimport { createConfluenceClient } from '../api/client.js';\nimport { MarkdownToStorageConverter } from '../converters/md-to-storage.js';\nimport { StorageToMarkdownConverter } from '../converters/storage-to-md.js';\nimport { loadConfluenceConfig } from '../../common/config.js';\nimport { logger } from '../../common/logger.js';\n\nexport function registerConfluenceTools(server: McpServer) {\n try {\n const config = loadConfluenceConfig();\n const client = createConfluenceClient(config);\n\n const contentApi = new ConfluenceContentApi(client);\n const spaceApi = new ConfluenceSpaceApi(client);\n const searchApi = new ConfluenceSearchApi(client);\n const labelApi = new ConfluenceLabelApi(client);\n\n const mdToStorage = new MarkdownToStorageConverter();\n const storageToMd = new StorageToMarkdownConverter();\n\n // Tools\n\n server.tool(\n 'confluence_get_page',\n 'TDE Confluence 페이지 상세 조회. 페이지 내용을 Markdown으로 변환하여 반환합니다.',\n {\n pageId: z.string().describe('페이지 ID'),\n },\n async ({ pageId }) => {\n const page = await contentApi.getPage(pageId);\n const md = page.body?.storage?.value ? storageToMd.convert(page.body.storage.value) : '';\n\n return {\n content: [\n {\n type: 'text',\n text: `Title: ${page.title}\\nID: ${page.id}\\nSpace: ${page.space?.name} (${page.space?.key})\\nURL: ${page._links?.base}${page._links?.webui}\\n\\n${md}`,\n },\n ],\n };\n }\n );\n\n server.tool(\n 'confluence_create_page',\n 'TDE Confluence에 새 페이지를 생성합니다. Markdown 형식의 내용을 Confluence Storage Format으로 자동 변환합니다.',\n {\n spaceKey: z.string().describe('스페이스 키'),\n title: z.string().describe('페이지 제목'),\n content: z.string().describe('페이지 내용 (Markdown)'),\n parentId: z.string().optional().describe('부모 페이지 ID'),\n labels: z.array(z.string()).optional().describe('라벨 목록'),\n },\n async ({ spaceKey, title, content, parentId, labels }) => {\n const storageBody = mdToStorage.convert(content);\n const page = await contentApi.createPage({\n spaceKey,\n title,\n body: storageBody,\n parentId,\n labels\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `페이지 생성 성공: ${page.title} (ID: ${page.id})\\nURL: ${page._links?.base}${page._links?.webui}`,\n },\n ],\n };\n }\n );\n\n server.tool(\n 'confluence_update_page',\n 'TDE Confluence 페이지를 수정합니다. Markdown 형식의 내용으로 업데이트할 수 있습니다.',\n {\n pageId: z.string().describe('페이지 ID'),\n title: z.string().describe('페이지 제목'),\n content: z.string().describe('페이지 내용 (Markdown)'),\n version: z.number().describe('현재 페이지 버전 (충돌 방지용)'),\n },\n async ({ pageId, title, content, version }) => {\n const storageBody = mdToStorage.convert(content);\n const page = await contentApi.updatePage({\n id: pageId,\n title,\n body: storageBody,\n version\n });\n\n return {\n content: [\n {\n type: 'text',\n text: `페이지 수정 성공: ${page.title} (Version: ${page.version?.number})`,\n },\n ],\n };\n }\n );\n\n server.tool(\n 'confluence_search_pages',\n 'TDE Confluence에서 페이지를 검색합니다. CQL(Confluence Query Language)을 사용하여 고급 검색이 가능합니다.',\n {\n cql: z.string().describe('Confluence Query Language (예: title ~ \"guide\")'),\n limit: z.number().default(10).describe('결과 개수 제한'),\n },\n async ({ cql, limit }) => {\n const result = await searchApi.searchByCql(cql, 0, limit);\n const summary = result.results.map(p => `- [${p.id}] ${p.title} (Space: ${p.space?.key})`).join('\\n');\n\n return {\n content: [\n {\n type: 'text',\n text: `검색 결과 (${result.size}/${result.totalSize}):\\n${summary}`,\n },\n ],\n };\n }\n );\n\n server.tool(\n 'confluence_get_spaces',\n 'TDE Confluence의 스페이스 목록을 조회합니다.',\n {\n limit: z.number().default(20).describe('결과 개수 제한')\n },\n async ({ limit }) => {\n const spaces = await spaceApi.getSpaces('global', 0, limit);\n const summary = spaces.map(s => `- [${s.key}] ${s.name}`).join('\\n');\n return {\n content: [{ type: 'text', text: `스페이스 목록:\\n${summary}` }]\n };\n }\n );\n\n server.tool(\n 'confluence_delete_page',\n 'TDE Confluence 페이지를 삭제합니다.',\n {\n pageId: z.string().describe('페이지 ID'),\n },\n async ({ pageId }) => {\n await contentApi.deletePage(pageId);\n return {\n content: [\n {\n type: 'text',\n text: `페이지 삭제 성공 (ID: ${pageId})`,\n },\n ],\n };\n }\n );\n\n server.tool(\n 'confluence_get_page_tree',\n 'TDE Confluence 페이지의 하위 페이지(자식 페이지) 목록을 조회합니다.',\n {\n pageId: z.string().describe('페이지 ID'),\n limit: z.number().default(20).describe('결과 개수 제한'),\n },\n async ({ pageId, limit }) => {\n const children = await contentApi.getChildPages(pageId, 0, limit);\n const summary = children.map(p => `- [${p.id}] ${p.title}`).join('\\n');\n return {\n content: [\n {\n type: 'text',\n text: `자식 페이지 목록 (${children.length}):\\n${summary}`,\n },\n ],\n };\n }\n );\n\n server.tool(\n 'confluence_manage_labels',\n 'TDE Confluence 페이지의 라벨을 관리합니다. 라벨 조회, 추가, 삭제 기능을 제공합니다.',\n {\n pageId: z.string().describe('페이지 ID'),\n action: z.enum(['list', 'add', 'remove']).describe('작업 유형'),\n labels: z.array(z.string()).optional().describe('추가할 라벨 목록 (add 작업 시 필수)'),\n label: z.string().optional().describe('삭제할 라벨 (remove 작업 시 필수)'),\n },\n async ({ pageId, action, labels, label }) => {\n if (action === 'list') {\n const result = await labelApi.getLabels(pageId);\n const summary = result.map(l => l.name).join(', ');\n return {\n content: [{ type: 'text', text: `라벨 목록: ${summary}` }],\n };\n } else if (action === 'add') {\n if (!labels || labels.length === 0) {\n throw new Error('라벨 목록을 입력해주세요.');\n }\n await labelApi.addLabels(pageId, labels);\n return {\n content: [{ type: 'text', text: `라벨 추가 성공: ${labels.join(', ')}` }],\n };\n } else if (action === 'remove') {\n if (!label) {\n throw new Error('삭제할 라벨을 입력해주세요.');\n }\n await labelApi.removeLabel(pageId, label);\n return {\n content: [{ type: 'text', text: `라벨 삭제 성공: ${label}` }],\n };\n }\n return { content: [] };\n }\n );\n\n server.tool(\n 'confluence_convert_content',\n 'TDE Confluence 컨텐츠 포맷을 변환합니다. Markdown과 Confluence Storage Format 간 양방향 변환을 지원합니다.',\n {\n content: z.string().describe('변환할 컨텐츠'),\n format: z.enum(['storage_to_markdown', 'markdown_to_storage']).describe('변환 방향'),\n },\n async ({ content, format }) => {\n let result = '';\n if (format === 'storage_to_markdown') {\n result = storageToMd.convert(content);\n } else {\n result = mdToStorage.convert(content);\n }\n return {\n content: [{ type: 'text', text: result }],\n };\n }\n );\n\n } catch (error) {\n logger.warn(`Confluence 설정 로드 실패 또는 도구 등록 중 오류 발생: ${(error as Error).message}`);\n // Do not throw, just skip registration if config is missing (optional module pattern)\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { ConfluenceLabel } from '../types.js';\n\nexport class ConfluenceLabelApi {\n constructor(private client: AxiosInstance) { }\n\n async getLabels(pageId: string): Promise<ConfluenceLabel[]> {\n const response = await this.client.get(`/rest/api/content/${pageId}/label`);\n return response.data.results;\n }\n\n async addLabels(pageId: string, labels: string[]): Promise<void> {\n const data = labels.map(name => ({ prefix: 'global', name }));\n await this.client.post(`/rest/api/content/${pageId}/label`, data);\n }\n\n async removeLabel(pageId: string, labelName: string): Promise<void> {\n // Label deletion usually requires query param for name\n // /rest/api/content/{id}/label?name={name}\n await this.client.delete(`/rest/api/content/${pageId}/label`, {\n params: { name: labelName }\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;AACA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACDrC,SAAS,SAAS;;;ACEX,IAAM,qBAAN,MAAyB;AAAA,EAC5B,YAAoB,QAAuB;AAAvB;AAAA,EAAyB;AAAA,EAE7C,MAAM,UAAU,QAA4C;AACxD,UAAM,WAAW,MAAM,KAAK,OAAO,IAAI,qBAAqB,MAAM,QAAQ;AAC1E,WAAO,SAAS,KAAK;AAAA,EACzB;AAAA,EAEA,MAAM,UAAU,QAAgB,QAAiC;AAC7D,UAAM,OAAO,OAAO,IAAI,WAAS,EAAE,QAAQ,UAAU,KAAK,EAAE;AAC5D,UAAM,KAAK,OAAO,KAAK,qBAAqB,MAAM,UAAU,IAAI;AAAA,EACpE;AAAA,EAEA,MAAM,YAAY,QAAgB,WAAkC;AAGhE,UAAM,KAAK,OAAO,OAAO,qBAAqB,MAAM,UAAU;AAAA,MAC1D,QAAQ,EAAE,MAAM,UAAU;AAAA,IAC9B,CAAC;AAAA,EACL;AACJ;;;ADXO,SAAS,wBAAwB,QAAmB;AACvD,MAAI;AACA,UAAM,SAAS,qBAAqB;AACpC,UAAM,SAAS,uBAAuB,MAAM;AAE5C,UAAM,aAAa,IAAI,qBAAqB,MAAM;AAClD,UAAM,WAAW,IAAI,mBAAmB,MAAM;AAC9C,UAAM,YAAY,IAAI,oBAAoB,MAAM;AAChD,UAAM,WAAW,IAAI,mBAAmB,MAAM;AAE9C,UAAM,cAAc,IAAI,2BAA2B;AACnD,UAAM,cAAc,IAAI,2BAA2B;AAInD,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,QAAQ,EAAE,OAAO,EAAE,SAAS,uBAAQ;AAAA,MACxC;AAAA,MACA,OAAO,EAAE,OAAO,MAAM;AAClB,cAAM,OAAO,MAAM,WAAW,QAAQ,MAAM;AAC5C,cAAM,KAAK,KAAK,MAAM,SAAS,QAAQ,YAAY,QAAQ,KAAK,KAAK,QAAQ,KAAK,IAAI;AAEtF,eAAO;AAAA,UACH,SAAS;AAAA,YACL;AAAA,cACI,MAAM;AAAA,cACN,MAAM,UAAU,KAAK,KAAK;AAAA,MAAS,KAAK,EAAE;AAAA,SAAY,KAAK,OAAO,IAAI,KAAK,KAAK,OAAO,GAAG;AAAA,OAAW,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,KAAK;AAAA;AAAA,EAAO,EAAE;AAAA,YACxJ;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,UAAU,EAAE,OAAO,EAAE,SAAS,iCAAQ;AAAA,QACtC,OAAO,EAAE,OAAO,EAAE,SAAS,iCAAQ;AAAA,QACnC,SAAS,EAAE,OAAO,EAAE,SAAS,4CAAmB;AAAA,QAChD,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oCAAW;AAAA,QACpD,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,2BAAO;AAAA,MAC3D;AAAA,MACA,OAAO,EAAE,UAAU,OAAO,SAAS,UAAU,OAAO,MAAM;AACtD,cAAM,cAAc,YAAY,QAAQ,OAAO;AAC/C,cAAM,OAAO,MAAM,WAAW,WAAW;AAAA,UACrC;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QACJ,CAAC;AAED,eAAO;AAAA,UACH,SAAS;AAAA,YACL;AAAA,cACI,MAAM;AAAA,cACN,MAAM,iDAAc,KAAK,KAAK,SAAS,KAAK,EAAE;AAAA,OAAW,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,KAAK;AAAA,YACnG;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,QAAQ,EAAE,OAAO,EAAE,SAAS,uBAAQ;AAAA,QACpC,OAAO,EAAE,OAAO,EAAE,SAAS,iCAAQ;AAAA,QACnC,SAAS,EAAE,OAAO,EAAE,SAAS,4CAAmB;AAAA,QAChD,SAAS,EAAE,OAAO,EAAE,SAAS,gFAAoB;AAAA,MACrD;AAAA,MACA,OAAO,EAAE,QAAQ,OAAO,SAAS,QAAQ,MAAM;AAC3C,cAAM,cAAc,YAAY,QAAQ,OAAO;AAC/C,cAAM,OAAO,MAAM,WAAW,WAAW;AAAA,UACrC,IAAI;AAAA,UACJ;AAAA,UACA,MAAM;AAAA,UACN;AAAA,QACJ,CAAC;AAED,eAAO;AAAA,UACH,SAAS;AAAA,YACL;AAAA,cACI,MAAM;AAAA,cACN,MAAM,iDAAc,KAAK,KAAK,cAAc,KAAK,SAAS,MAAM;AAAA,YACpE;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,KAAK,EAAE,OAAO,EAAE,SAAS,qDAAgD;AAAA,QACzE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,wCAAU;AAAA,MACrD;AAAA,MACA,OAAO,EAAE,KAAK,MAAM,MAAM;AACtB,cAAM,SAAS,MAAM,UAAU,YAAY,KAAK,GAAG,KAAK;AACxD,cAAM,UAAU,OAAO,QAAQ,IAAI,OAAK,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,OAAO,GAAG,GAAG,EAAE,KAAK,IAAI;AAEpG,eAAO;AAAA,UACH,SAAS;AAAA,YACL;AAAA,cACI,MAAM;AAAA,cACN,MAAM,8BAAU,OAAO,IAAI,IAAI,OAAO,SAAS;AAAA,EAAO,OAAO;AAAA,YACjE;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,wCAAU;AAAA,MACrD;AAAA,MACA,OAAO,EAAE,MAAM,MAAM;AACjB,cAAM,SAAS,MAAM,SAAS,UAAU,UAAU,GAAG,KAAK;AAC1D,cAAM,UAAU,OAAO,IAAI,OAAK,MAAM,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI;AACnE,eAAO;AAAA,UACH,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM;AAAA,EAAa,OAAO,GAAG,CAAC;AAAA,QAC5D;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,QAAQ,EAAE,OAAO,EAAE,SAAS,uBAAQ;AAAA,MACxC;AAAA,MACA,OAAO,EAAE,OAAO,MAAM;AAClB,cAAM,WAAW,WAAW,MAAM;AAClC,eAAO;AAAA,UACH,SAAS;AAAA,YACL;AAAA,cACI,MAAM;AAAA,cACN,MAAM,qDAAkB,MAAM;AAAA,YAClC;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,QAAQ,EAAE,OAAO,EAAE,SAAS,uBAAQ;AAAA,QACpC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,SAAS,wCAAU;AAAA,MACrD;AAAA,MACA,OAAO,EAAE,QAAQ,MAAM,MAAM;AACzB,cAAM,WAAW,MAAM,WAAW,cAAc,QAAQ,GAAG,KAAK;AAChE,cAAM,UAAU,SAAS,IAAI,OAAK,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK,IAAI;AACrE,eAAO;AAAA,UACH,SAAS;AAAA,YACL;AAAA,cACI,MAAM;AAAA,cACN,MAAM,iDAAc,SAAS,MAAM;AAAA,EAAO,OAAO;AAAA,YACrD;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,QAAQ,EAAE,OAAO,EAAE,SAAS,uBAAQ;AAAA,QACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ,OAAO,QAAQ,CAAC,EAAE,SAAS,2BAAO;AAAA,QAC1D,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qFAAyB;AAAA,QACzE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2EAAyB;AAAA,MACnE;AAAA,MACA,OAAO,EAAE,QAAQ,QAAQ,QAAQ,MAAM,MAAM;AACzC,YAAI,WAAW,QAAQ;AACnB,gBAAM,SAAS,MAAM,SAAS,UAAU,MAAM;AAC9C,gBAAM,UAAU,OAAO,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,IAAI;AACjD,iBAAO;AAAA,YACH,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,8BAAU,OAAO,GAAG,CAAC;AAAA,UACzD;AAAA,QACJ,WAAW,WAAW,OAAO;AACzB,cAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAChC,kBAAM,IAAI,MAAM,uEAAgB;AAAA,UACpC;AACA,gBAAM,SAAS,UAAU,QAAQ,MAAM;AACvC,iBAAO;AAAA,YACH,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,2CAAa,OAAO,KAAK,IAAI,CAAC,GAAG,CAAC;AAAA,UACtE;AAAA,QACJ,WAAW,WAAW,UAAU;AAC5B,cAAI,CAAC,OAAO;AACR,kBAAM,IAAI,MAAM,6EAAiB;AAAA,UACrC;AACA,gBAAM,SAAS,YAAY,QAAQ,KAAK;AACxC,iBAAO;AAAA,YACH,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,2CAAa,KAAK,GAAG,CAAC;AAAA,UAC1D;AAAA,QACJ;AACA,eAAO,EAAE,SAAS,CAAC,EAAE;AAAA,MACzB;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,QACI,SAAS,EAAE,OAAO,EAAE,SAAS,uCAAS;AAAA,QACtC,QAAQ,EAAE,KAAK,CAAC,uBAAuB,qBAAqB,CAAC,EAAE,SAAS,2BAAO;AAAA,MACnF;AAAA,MACA,OAAO,EAAE,SAAS,OAAO,MAAM;AAC3B,YAAI,SAAS;AACb,YAAI,WAAW,uBAAuB;AAClC,mBAAS,YAAY,QAAQ,OAAO;AAAA,QACxC,OAAO;AACH,mBAAS,YAAY,QAAQ,OAAO;AAAA,QACxC;AACA,eAAO;AAAA,UACH,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC;AAAA,QAC5C;AAAA,MACJ;AAAA,IACJ;AAAA,EAEJ,SAAS,OAAO;AACZ,WAAO,KAAK,8HAA0C,MAAgB,OAAO,EAAE;AAAA,EAEnF;AACJ;;;ADhPA,eAAsB,YAAY;AAC9B,MAAI;AACA,UAAM,SAAS,IAAI,UAAU;AAAA,MACzB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,IACjB,CAAC;AAGD,4BAAwB,MAAM;AAG9B,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,QAAQ,SAAS;AAE9B,WAAO,KAAK,wCAAwC;AAAA,EACxD,SAAS,OAAO;AACZ,WAAO,MAAM,8BAA8B,KAAK,EAAE;AAClD,YAAQ,KAAK,CAAC;AAAA,EAClB;AACJ;","names":[]}
|
package/dist/chunk-N44NISLJ.js
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
// src/common/logger.ts
|
|
2
|
-
var Logger = class {
|
|
3
|
-
level = "info";
|
|
4
|
-
constructor() {
|
|
5
|
-
this.level = process.env.LOG_LEVEL || "info";
|
|
6
|
-
}
|
|
7
|
-
// 로그 메시지 포맷팅 (타임스탬프, 레벨 포함)
|
|
8
|
-
formatMessage(level, message, ...args) {
|
|
9
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10
|
-
let formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
|
|
11
|
-
if (args.length > 0) {
|
|
12
|
-
formattedMessage += " " + args.map(
|
|
13
|
-
(arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg)
|
|
14
|
-
).join(" ");
|
|
15
|
-
}
|
|
16
|
-
formattedMessage = this.maskSensitiveData(formattedMessage);
|
|
17
|
-
return formattedMessage;
|
|
18
|
-
}
|
|
19
|
-
// 민감한 데이터(키, 패스워드 등) 마스킹 처리
|
|
20
|
-
maskSensitiveData(text) {
|
|
21
|
-
return text.replace(/((?:token|password|secret|key)["']?\s*[:=]\s*["']?)([^"'\s]+)(["']?)/gi, "$1******$3");
|
|
22
|
-
}
|
|
23
|
-
// Debug 레벨 로그 출력
|
|
24
|
-
debug(message, ...args) {
|
|
25
|
-
if (this.shouldLog("debug")) {
|
|
26
|
-
console.error(this.formatMessage("debug", message, ...args));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
// Info 레벨 로그 출력
|
|
30
|
-
info(message, ...args) {
|
|
31
|
-
if (this.shouldLog("info")) {
|
|
32
|
-
console.error(this.formatMessage("info", message, ...args));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// Warn 레벨 로그 출력
|
|
36
|
-
warn(message, ...args) {
|
|
37
|
-
if (this.shouldLog("warn")) {
|
|
38
|
-
console.error(this.formatMessage("warn", message, ...args));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
// Error 레벨 로그 출력
|
|
42
|
-
error(message, ...args) {
|
|
43
|
-
if (this.shouldLog("error")) {
|
|
44
|
-
console.error(this.formatMessage("error", message, ...args));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// 현재 설정된 로그 레벨에 따라 출력 여부 결정
|
|
48
|
-
shouldLog(level) {
|
|
49
|
-
const levels = ["debug", "info", "warn", "error"];
|
|
50
|
-
return levels.indexOf(level) >= levels.indexOf(this.level);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
var logger = new Logger();
|
|
54
|
-
|
|
55
|
-
// src/confluence/api/content.ts
|
|
56
|
-
var ConfluenceContentApi = class {
|
|
57
|
-
constructor(client) {
|
|
58
|
-
this.client = client;
|
|
59
|
-
}
|
|
60
|
-
async getPage(id, expand) {
|
|
61
|
-
const expandParam = expand ? expand.join(",") : "body.storage,version,space,metadata.labels";
|
|
62
|
-
const response = await this.client.get(`/rest/api/content/${id}`, {
|
|
63
|
-
params: { expand: expandParam }
|
|
64
|
-
});
|
|
65
|
-
return response.data;
|
|
66
|
-
}
|
|
67
|
-
async getPageByTitle(spaceKey, title, expand) {
|
|
68
|
-
const expandParam = expand ? expand.join(",") : "body.storage,version,space";
|
|
69
|
-
const response = await this.client.get("/rest/api/content", {
|
|
70
|
-
params: {
|
|
71
|
-
spaceKey,
|
|
72
|
-
title,
|
|
73
|
-
expand: expandParam,
|
|
74
|
-
limit: 1
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
if (response.data.results && response.data.results.length > 0) {
|
|
78
|
-
return response.data.results[0];
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
async createPage(params) {
|
|
83
|
-
const data = {
|
|
84
|
-
type: "page",
|
|
85
|
-
title: params.title,
|
|
86
|
-
space: { key: params.spaceKey },
|
|
87
|
-
body: {
|
|
88
|
-
storage: {
|
|
89
|
-
value: params.body,
|
|
90
|
-
representation: "storage"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
if (params.parentId) {
|
|
95
|
-
data.ancestors = [{ id: params.parentId }];
|
|
96
|
-
}
|
|
97
|
-
const response = await this.client.post("/rest/api/content", data);
|
|
98
|
-
if (params.labels && params.labels.length > 0) {
|
|
99
|
-
await this.addLabels(response.data.id, params.labels);
|
|
100
|
-
}
|
|
101
|
-
return response.data;
|
|
102
|
-
}
|
|
103
|
-
async updatePage(params) {
|
|
104
|
-
const data = {
|
|
105
|
-
version: { number: params.version + 1 },
|
|
106
|
-
title: params.title,
|
|
107
|
-
type: "page",
|
|
108
|
-
body: {
|
|
109
|
-
storage: {
|
|
110
|
-
value: params.body,
|
|
111
|
-
representation: "storage"
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const response = await this.client.put(`/rest/api/content/${params.id}`, data);
|
|
116
|
-
return response.data;
|
|
117
|
-
}
|
|
118
|
-
async deletePage(id) {
|
|
119
|
-
await this.client.delete(`/rest/api/content/${id}`);
|
|
120
|
-
}
|
|
121
|
-
async getChildPages(id, start = 0, limit = 25) {
|
|
122
|
-
const response = await this.client.get(`/rest/api/content/${id}/child/page`, {
|
|
123
|
-
params: { start, limit }
|
|
124
|
-
});
|
|
125
|
-
return response.data.results;
|
|
126
|
-
}
|
|
127
|
-
// Label helper inside content api or separate?
|
|
128
|
-
// Let's implement basic label addition here since it's used in create.
|
|
129
|
-
// Actually, label logic is in label.ts, but due to circular dependency or convenience...
|
|
130
|
-
// Let's implement it here privately or import it.
|
|
131
|
-
// Better to keep it separate as per plan, but `this.client` is available here.
|
|
132
|
-
// I'll implement a simple one here or use the separate class later.
|
|
133
|
-
// For now, simple implementation to support createPage.
|
|
134
|
-
async addLabels(id, labels) {
|
|
135
|
-
const data = labels.map((name) => ({ prefix: "global", name }));
|
|
136
|
-
await this.client.post(`/rest/api/content/${id}/label`, data);
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// src/confluence/api/space.ts
|
|
141
|
-
var ConfluenceSpaceApi = class {
|
|
142
|
-
constructor(client) {
|
|
143
|
-
this.client = client;
|
|
144
|
-
}
|
|
145
|
-
async getSpaces(type = "global", start = 0, limit = 25) {
|
|
146
|
-
const response = await this.client.get("/rest/api/space", {
|
|
147
|
-
params: { type, start, limit }
|
|
148
|
-
});
|
|
149
|
-
return response.data.results;
|
|
150
|
-
}
|
|
151
|
-
async getSpace(spaceKey) {
|
|
152
|
-
const response = await this.client.get(`/rest/api/space/${spaceKey}`);
|
|
153
|
-
return response.data;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// src/confluence/api/search.ts
|
|
158
|
-
var ConfluenceSearchApi = class {
|
|
159
|
-
constructor(client) {
|
|
160
|
-
this.client = client;
|
|
161
|
-
}
|
|
162
|
-
async searchByCql(cql, start = 0, limit = 25, expand) {
|
|
163
|
-
const expandParam = expand ? expand.join(",") : "body.storage,version,space,metadata.labels";
|
|
164
|
-
const response = await this.client.get("/rest/api/content/search", {
|
|
165
|
-
params: {
|
|
166
|
-
cql,
|
|
167
|
-
start,
|
|
168
|
-
limit,
|
|
169
|
-
expand: expandParam
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
return response.data;
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
// src/common/http-client.ts
|
|
177
|
-
import axios from "axios";
|
|
178
|
-
|
|
179
|
-
// src/common/errors.ts
|
|
180
|
-
var TdeCollabError = class extends Error {
|
|
181
|
-
constructor(message) {
|
|
182
|
-
super(message);
|
|
183
|
-
this.name = this.constructor.name;
|
|
184
|
-
Error.captureStackTrace(this, this.constructor);
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
var ApiError = class extends TdeCollabError {
|
|
188
|
-
constructor(statusCode, message, data) {
|
|
189
|
-
super(`API \uC694\uCCAD \uC2E4\uD328 (${statusCode}): ${message}`);
|
|
190
|
-
this.statusCode = statusCode;
|
|
191
|
-
this.data = data;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
var AuthError = class extends TdeCollabError {
|
|
195
|
-
constructor(message = "\uC778\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4") {
|
|
196
|
-
super(message);
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
var NotFoundError = class extends TdeCollabError {
|
|
200
|
-
constructor(resource, id) {
|
|
201
|
-
super(`${resource} '${id}'\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4`);
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
var ConflictError = class extends TdeCollabError {
|
|
205
|
-
constructor(message) {
|
|
206
|
-
super(message);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
// src/common/http-client.ts
|
|
211
|
-
function createHttpClient(config) {
|
|
212
|
-
const client = axios.create({
|
|
213
|
-
baseURL: config.baseUrl,
|
|
214
|
-
timeout: 3e4,
|
|
215
|
-
// 30초 타임아웃
|
|
216
|
-
headers: {
|
|
217
|
-
"Content-Type": "application/json"
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
client.interceptors.request.use((reqConfig) => {
|
|
221
|
-
if (config.auth.username && config.auth.token) {
|
|
222
|
-
const token = Buffer.from(`${config.auth.username}:${config.auth.token}`).toString("base64");
|
|
223
|
-
reqConfig.headers.Authorization = `Basic ${token}`;
|
|
224
|
-
} else if (config.auth.token) {
|
|
225
|
-
if (!reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
|
|
226
|
-
reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return reqConfig;
|
|
230
|
-
});
|
|
231
|
-
client.interceptors.response.use(
|
|
232
|
-
(response) => {
|
|
233
|
-
logger.debug(`[HTTP] ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`);
|
|
234
|
-
return response;
|
|
235
|
-
},
|
|
236
|
-
(error) => {
|
|
237
|
-
if (error.response) {
|
|
238
|
-
const status = error.response.status;
|
|
239
|
-
const method = error.config?.method?.toUpperCase();
|
|
240
|
-
const url = error.config?.url;
|
|
241
|
-
const message = error.response.data?.message || error.message;
|
|
242
|
-
logger.error(`[HTTP] ${status} ${method} ${url} - ${message}`);
|
|
243
|
-
if (status === 401 || status === 403) {
|
|
244
|
-
throw new AuthError(`\uC778\uC99D \uC2E4\uD328: ${message}`);
|
|
245
|
-
}
|
|
246
|
-
if (status === 404) {
|
|
247
|
-
throw new NotFoundError("\uB9AC\uC18C\uC2A4", url || "unknown");
|
|
248
|
-
}
|
|
249
|
-
if (status === 409) {
|
|
250
|
-
throw new ConflictError(`\uCDA9\uB3CC \uBC1C\uC0DD: ${message}`);
|
|
251
|
-
}
|
|
252
|
-
throw new ApiError(status, message, error.response.data);
|
|
253
|
-
} else if (error.request) {
|
|
254
|
-
logger.error(`[HTTP] No Response: ${error.message}`);
|
|
255
|
-
throw new ApiError(0, `\uC11C\uBC84\uB85C\uBD80\uD130 \uC751\uB2F5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${error.message}`);
|
|
256
|
-
} else {
|
|
257
|
-
logger.error(`[HTTP] Request Error: ${error.message}`);
|
|
258
|
-
throw new ApiError(0, `\uC694\uCCAD \uC124\uC815 \uC911 \uC624\uB958 \uBC1C\uC0DD: ${error.message}`);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
);
|
|
262
|
-
return client;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// src/confluence/api/client.ts
|
|
266
|
-
function createConfluenceClient(config) {
|
|
267
|
-
return createHttpClient(config);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// src/confluence/converters/md-to-storage.ts
|
|
271
|
-
import MarkdownIt from "markdown-it";
|
|
272
|
-
var MarkdownToStorageConverter = class {
|
|
273
|
-
md;
|
|
274
|
-
constructor() {
|
|
275
|
-
this.md = new MarkdownIt({
|
|
276
|
-
html: true,
|
|
277
|
-
linkify: true,
|
|
278
|
-
breaks: true
|
|
279
|
-
});
|
|
280
|
-
this.md.renderer.rules.fence = (tokens, idx) => {
|
|
281
|
-
const token = tokens[idx];
|
|
282
|
-
const code = token.content.trim();
|
|
283
|
-
const lang = token.info.trim();
|
|
284
|
-
return `<ac:structured-macro ac:name="code" ac:schema-version="1">
|
|
285
|
-
<ac:parameter ac:name="language">${lang || "text"}</ac:parameter>
|
|
286
|
-
<ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>
|
|
287
|
-
</ac:structured-macro>`;
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
convert(markdown) {
|
|
291
|
-
return this.md.render(markdown);
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
// src/confluence/converters/storage-to-md.ts
|
|
296
|
-
var StorageToMarkdownConverter = class {
|
|
297
|
-
convert(storageHtml) {
|
|
298
|
-
let md = storageHtml;
|
|
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
|
|
326
|
-
import dotenv from "dotenv";
|
|
327
|
-
dotenv.config();
|
|
328
|
-
function getEnvOrThrow(key, description) {
|
|
329
|
-
const value = process.env[key];
|
|
330
|
-
if (!value) {
|
|
331
|
-
const errorMsg = `\uD658\uACBD\uBCC0\uC218 '${key}'\uAC00 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. (${description})`;
|
|
332
|
-
logger.error(errorMsg);
|
|
333
|
-
throw new Error(errorMsg);
|
|
334
|
-
}
|
|
335
|
-
return value;
|
|
336
|
-
}
|
|
337
|
-
function loadConfluenceConfig() {
|
|
338
|
-
const baseUrl = getEnvOrThrow("CONFLUENCE_BASE_URL", "Confluence \uAE30\uBCF8 URL");
|
|
339
|
-
const username = process.env.CONFLUENCE_USERNAME;
|
|
340
|
-
const token = getEnvOrThrow("CONFLUENCE_API_TOKEN", "Confluence API \uD1A0\uD070");
|
|
341
|
-
return {
|
|
342
|
-
baseUrl,
|
|
343
|
-
auth: {
|
|
344
|
-
username,
|
|
345
|
-
token
|
|
346
|
-
// API 토큰은 일반적으로 패스워드 필드에 사용되지만, 여기서는 token으로 저장하여 범용성 확보
|
|
347
|
-
}
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
export {
|
|
352
|
-
ConfluenceContentApi,
|
|
353
|
-
ConfluenceSpaceApi,
|
|
354
|
-
ConfluenceSearchApi,
|
|
355
|
-
logger,
|
|
356
|
-
createConfluenceClient,
|
|
357
|
-
MarkdownToStorageConverter,
|
|
358
|
-
StorageToMarkdownConverter,
|
|
359
|
-
loadConfluenceConfig
|
|
360
|
-
};
|
|
361
|
-
//# sourceMappingURL=chunk-N44NISLJ.js.map
|