tdecollab 0.3.4 → 0.3.5

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 CHANGED
@@ -12,6 +12,16 @@ Confluence, JIRA, GitLab을 하나의 CLI / TUI / MCP 서버로 통합 제공하
12
12
  | **JIRA** | 이슈 CRUD, JQL 검색, 상태 변경(트랜지션), 코멘트 관리, 프로젝트/보드 조회 |
13
13
  | **GitLab** | 프로젝트 조회, MR 관리, 파이프라인 조회, 브랜치 관리, 파일 조회 |
14
14
 
15
+ ## Obsidian 플러그인
16
+
17
+ 현재 활성화된 마크다운 노트를 Confluence 페이지로 업로드하거나, 반대로 다운로드할 수 있는 Obsidian 플러그인을 제공합니다.
18
+
19
+ ### 설치 (BRAT)
20
+ 1. Obsidian에서 **BRAT** 플러그인을 먼저 설치 및 활성화합니다.
21
+ 2. `BRAT: Add a beta plugin for testing` 명령을 실행합니다.
22
+ 3. **`goodjoon/tdecollab_public`** 주소를 입력하여 설치합니다.
23
+ 4. 설정에서 **TDE Collab Confluence**를 활성화합니다.
24
+
15
25
  ## 요구사항
16
26
 
17
27
  - Node.js 20 이상
@@ -51,7 +51,7 @@ var JiraProjectApi = class {
51
51
  this.client = client;
52
52
  }
53
53
  async getProjects() {
54
- const response = await this.client.get("/rest/api/2/project");
54
+ const response = await this.client.get("rest/api/2/project");
55
55
  return response.data;
56
56
  }
57
57
  async getProject(projectKey) {
@@ -62,7 +62,7 @@ var JiraProjectApi = class {
62
62
  const params = {};
63
63
  if (projectKeyOrId) params.projectKeyOrId = projectKeyOrId;
64
64
  if (type) params.type = type;
65
- const response = await this.client.get("/rest/agile/1.0/board", { params });
65
+ const response = await this.client.get("rest/agile/1.0/board", { params });
66
66
  return response.data;
67
67
  }
68
68
  async getSprints(boardId, state) {
@@ -81,7 +81,7 @@ var GitlabProjectApi = class {
81
81
  this.client = client;
82
82
  }
83
83
  async getProjects(params) {
84
- const response = await this.client.get("/projects", {
84
+ const response = await this.client.get("projects", {
85
85
  params: {
86
86
  search: params?.search,
87
87
  owned: params?.owned,
@@ -169,4 +169,4 @@ export {
169
169
  GitlabBranchApi,
170
170
  GitlabRepositoryApi
171
171
  };
172
- //# sourceMappingURL=chunk-JI2YUE7N.js.map
172
+ //# sourceMappingURL=chunk-4QW3PXPJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../tools/jira/api/transition.ts","../tools/jira/api/comment.ts","../tools/jira/api/project.ts","../tools/gitlab/api/project.ts","../tools/gitlab/api/branch.ts","../tools/gitlab/api/repository.ts"],"sourcesContent":["import { AxiosInstance } from 'axios';\nimport { JiraTransition } from '../types.js';\n\nexport class JiraTransitionApi {\n constructor(private client: AxiosInstance) {}\n\n async getTransitions(issueKey: string): Promise<JiraTransition[]> {\n const response = await this.client.get(`/rest/api/2/issue/${issueKey}/transitions`);\n return response.data.transitions;\n }\n\n async doTransition(\n issueKey: string,\n transitionId: string,\n fields?: Record<string, unknown>,\n ): Promise<void> {\n const data: Record<string, unknown> = {\n transition: { id: transitionId },\n };\n if (fields) {\n data.fields = fields;\n }\n await this.client.post(`/rest/api/2/issue/${issueKey}/transitions`, data);\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { JiraComment } from '../types.js';\n\nexport class JiraCommentApi {\n constructor(private client: AxiosInstance) {}\n\n async getComments(\n issueKey: string,\n startAt = 0,\n maxResults = 50,\n ): Promise<{ comments: JiraComment[]; total: number; startAt: number; maxResults: number }> {\n const response = await this.client.get(`/rest/api/2/issue/${issueKey}/comment`, {\n params: { startAt, maxResults },\n });\n return response.data;\n }\n\n async addComment(issueKey: string, body: string): Promise<JiraComment> {\n const response = await this.client.post(`/rest/api/2/issue/${issueKey}/comment`, { body });\n return response.data;\n }\n\n async updateComment(issueKey: string, commentId: string, body: string): Promise<JiraComment> {\n const response = await this.client.put(\n `/rest/api/2/issue/${issueKey}/comment/${commentId}`,\n { body },\n );\n return response.data;\n }\n\n async deleteComment(issueKey: string, commentId: string): Promise<void> {\n await this.client.delete(`/rest/api/2/issue/${issueKey}/comment/${commentId}`);\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { JiraProject, JiraBoard, JiraSprint } from '../types.js';\n\nexport class JiraProjectApi {\n constructor(private client: AxiosInstance) {}\n\n async getProjects(): Promise<JiraProject[]> {\n const response = await this.client.get('rest/api/2/project');\n return response.data;\n }\n\n async getProject(projectKey: string): Promise<JiraProject> {\n const response = await this.client.get(`/rest/api/2/project/${projectKey}`);\n return response.data;\n }\n\n async getBoards(\n projectKeyOrId?: string,\n type?: string,\n ): Promise<{ values: JiraBoard[]; total: number }> {\n const params: Record<string, string> = {};\n if (projectKeyOrId) params.projectKeyOrId = projectKeyOrId;\n if (type) params.type = type;\n const response = await this.client.get('rest/agile/1.0/board', { params });\n return response.data;\n }\n\n async getSprints(\n boardId: number,\n state?: string,\n ): Promise<{ values: JiraSprint[]; total: number }> {\n const params: Record<string, string> = {};\n if (state) params.state = state;\n const response = await this.client.get(`/rest/agile/1.0/board/${boardId}/sprint`, {\n params,\n });\n return response.data;\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { GitlabProject } from '../types.js';\n\nexport class GitlabProjectApi {\n constructor(private client: AxiosInstance) {}\n\n async getProjects(params?: {\n search?: string;\n owned?: boolean;\n membership?: boolean;\n perPage?: number;\n }): Promise<GitlabProject[]> {\n const response = await this.client.get('projects', {\n params: {\n search: params?.search,\n owned: params?.owned,\n membership: params?.membership,\n per_page: params?.perPage || 20,\n },\n });\n return response.data;\n }\n\n async getProject(projectId: number | string): Promise<GitlabProject> {\n const response = await this.client.get(`/projects/${encodeURIComponent(projectId)}`);\n return response.data;\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { GitlabBranch } from '../types.js';\n\nexport class GitlabBranchApi {\n constructor(private client: AxiosInstance) {}\n\n async getBranches(\n projectId: number,\n params?: { search?: string; perPage?: number },\n ): Promise<GitlabBranch[]> {\n const response = await this.client.get(`/projects/${projectId}/repository/branches`, {\n params: {\n search: params?.search,\n per_page: params?.perPage || 20,\n },\n });\n return response.data;\n }\n\n async getBranch(projectId: number, branchName: string): Promise<GitlabBranch> {\n const response = await this.client.get(\n `/projects/${projectId}/repository/branches/${encodeURIComponent(branchName)}`,\n );\n return response.data;\n }\n\n async createBranch(\n projectId: number,\n branchName: string,\n ref: string,\n ): Promise<GitlabBranch> {\n const response = await this.client.post(`/projects/${projectId}/repository/branches`, {\n branch: branchName,\n ref,\n });\n return response.data;\n }\n\n async deleteBranch(projectId: number, branchName: string): Promise<void> {\n await this.client.delete(\n `/projects/${projectId}/repository/branches/${encodeURIComponent(branchName)}`,\n );\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { GitlabRepositoryFile, GitlabTreeEntry } from '../types.js';\n\nexport class GitlabRepositoryApi {\n constructor(private client: AxiosInstance) {}\n\n async getFile(\n projectId: number,\n filePath: string,\n ref?: string,\n ): Promise<GitlabRepositoryFile> {\n const encodedPath = encodeURIComponent(filePath);\n const response = await this.client.get(\n `/projects/${projectId}/repository/files/${encodedPath}`,\n { params: { ref: ref || 'HEAD' } },\n );\n const file: GitlabRepositoryFile = response.data;\n // Base64 디코딩\n if (file.encoding === 'base64') {\n file.content = Buffer.from(file.content, 'base64').toString('utf-8');\n }\n return file;\n }\n\n async getTree(\n projectId: number,\n params?: { path?: string; ref?: string; recursive?: boolean; perPage?: number },\n ): Promise<GitlabTreeEntry[]> {\n const response = await this.client.get(`/projects/${projectId}/repository/tree`, {\n params: {\n path: params?.path,\n ref: params?.ref,\n recursive: params?.recursive,\n per_page: params?.perPage || 100,\n },\n });\n return response.data;\n }\n}\n"],"mappings":"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","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  JiraCommentApi,
6
6
  JiraProjectApi,
7
7
  JiraTransitionApi
8
- } from "./chunk-JI2YUE7N.js";
8
+ } from "./chunk-4QW3PXPJ.js";
9
9
  import {
10
10
  ConfluenceContentApi,
11
11
  ConfluenceSearchApi,
@@ -24,10 +24,10 @@ import {
24
24
  loadGitlabConfig,
25
25
  loadJiraConfig,
26
26
  tryBuildJiraIssueMap
27
- } from "./chunk-5OB3KU5D.js";
27
+ } from "./chunk-JUM5ZG64.js";
28
28
  import {
29
29
  logger
30
- } from "./chunk-IFYMZLQI.js";
30
+ } from "./chunk-6RIA6AJ3.js";
31
31
 
32
32
  // tools/mcp/server.ts
33
33
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -138,7 +138,7 @@ function registerConfluenceTools(server) {
138
138
  if (page.body?.storage?.value) {
139
139
  let imageUrlMap;
140
140
  if (downloadImages) {
141
- const { ImageDownloader } = await import("./image-downloader-VKPGS3TY.js");
141
+ const { ImageDownloader } = await import("./image-downloader-AKNR5YKJ.js");
142
142
  const downloader = new ImageDownloader(contentApi, {
143
143
  outputDir: imageDir || "./images",
144
144
  pageId: page.id,
@@ -1107,4 +1107,4 @@ async function runServer() {
1107
1107
  export {
1108
1108
  runServer
1109
1109
  };
1110
- //# sourceMappingURL=chunk-4DPCLTF4.js.map
1110
+ //# sourceMappingURL=chunk-5IY42AV6.js.map
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // tools/common/logger.ts
2
9
  var Logger = class {
3
10
  level = "info";
@@ -68,6 +75,7 @@ var Logger = class {
68
75
  var logger = new Logger();
69
76
 
70
77
  export {
78
+ __require,
71
79
  logger
72
80
  };
73
- //# sourceMappingURL=chunk-IFYMZLQI.js.map
81
+ //# sourceMappingURL=chunk-6RIA6AJ3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../tools/common/logger.ts"],"sourcesContent":["export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nclass Logger {\n private level: LogLevel = 'info';\n\n constructor() {\n this.level = (process.env.LOG_LEVEL as LogLevel) || 'info';\n }\n\n // 로그 레벨 설정\n setLevel(level: LogLevel) {\n this.level = level;\n }\n\n // 로그 메시지 포맷팅 (타임스탬프, 레벨 포함)\n private formatMessage(level: LogLevel, message: string, ...args: any[]): string {\n const timestamp = new Date().toISOString();\n let formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;\n\n if (args.length > 0) {\n formattedMessage += ' ' + args.map(arg => {\n if (arg instanceof Error) {\n return arg.stack || arg.message;\n }\n return typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg);\n }).join(' ');\n }\n\n // 민감 정보 마스킹 (토큰, 비밀번호 등)\n formattedMessage = this.maskSensitiveData(formattedMessage);\n\n return formattedMessage;\n }\n\n // 민감한 데이터(키, 패스워드 등) 마스킹 처리\n private maskSensitiveData(text: string): string {\n // 1. Authorization 헤더 마스킹 (Basic ***, Bearer ***)\n let masked = text.replace(/(Authorization[\"']?\\s*[:=]\\s*[\"']?)(Basic|Bearer)\\s+([^\"'\\s]+)([\"']?)/gi, (match, p1, p2, p3, p4) => {\n const maskedToken = p3.length > 8 ? p3.substring(0, 4) + '...' + p3.substring(p3.length - 4) : '******';\n return `${p1}${p2} ${maskedToken}${p4}`;\n });\n\n // 2. 키워드 기반 마스킹 (token, password, secret, key 등)\n // 헤더 이름(PRIVATE-TOKEN 등)과 변수명 모두 대응 가능하도록 개선\n masked = masked.replace(/((?:token|password|secret|key|api_token|private_token)[\"']?\\s*[:=]\\s*[\"']?)([^\"'\\s]+)([\"']?)/gi, (match, p1, p2, p3) => {\n const maskedValue = p2.length > 8 ? p2.substring(0, 4) + '...' + p2.substring(p2.length - 4) : '******';\n return `${p1}${maskedValue}${p3}`;\n });\n\n return masked;\n }\n\n // Debug 레벨 로그 출력\n debug(message: string, ...args: any[]) {\n if (this.shouldLog('debug')) {\n console.error(this.formatMessage('debug', message, ...args));\n }\n }\n\n // Info 레벨 로그 출력\n info(message: string, ...args: any[]) {\n if (this.shouldLog('info')) {\n console.error(this.formatMessage('info', message, ...args));\n }\n }\n\n // Warn 레벨 로그 출력\n warn(message: string, ...args: any[]) {\n if (this.shouldLog('warn')) {\n console.error(this.formatMessage('warn', message, ...args));\n }\n }\n\n // Error 레벨 로그 출력\n error(message: string, ...args: any[]) {\n if (this.shouldLog('error')) {\n console.error(this.formatMessage('error', message, ...args));\n }\n }\n\n // 현재 설정된 로그 레벨에 따라 출력 여부 결정\n private shouldLog(level: LogLevel): boolean {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n return levels.indexOf(level) >= levels.indexOf(this.level);\n }\n}\n\nexport const logger = new Logger();\n"],"mappings":";AAEA,IAAM,SAAN,MAAa;AAAA,EACD,QAAkB;AAAA,EAE1B,cAAc;AACV,SAAK,QAAS,QAAQ,IAAI,aAA0B;AAAA,EACxD;AAAA;AAAA,EAGA,SAAS,OAAiB;AACtB,SAAK,QAAQ;AAAA,EACjB;AAAA;AAAA,EAGQ,cAAc,OAAiB,YAAoB,MAAqB;AAC5E,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAI,mBAAmB,IAAI,SAAS,MAAM,MAAM,YAAY,CAAC,KAAK,OAAO;AAEzE,QAAI,KAAK,SAAS,GAAG;AACjB,0BAAoB,MAAM,KAAK,IAAI,SAAO;AACtC,YAAI,eAAe,OAAO;AACtB,iBAAO,IAAI,SAAS,IAAI;AAAA,QAC5B;AACA,eAAO,OAAO,QAAQ,WAAW,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,OAAO,GAAG;AAAA,MAC9E,CAAC,EAAE,KAAK,GAAG;AAAA,IACf;AAGA,uBAAmB,KAAK,kBAAkB,gBAAgB;AAE1D,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,kBAAkB,MAAsB;AAE5C,QAAI,SAAS,KAAK,QAAQ,2EAA2E,CAAC,OAAO,IAAI,IAAI,IAAI,OAAO;AAC5H,YAAM,cAAc,GAAG,SAAS,IAAI,GAAG,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC,IAAI;AAC/F,aAAO,GAAG,EAAE,GAAG,EAAE,IAAI,WAAW,GAAG,EAAE;AAAA,IACzC,CAAC;AAID,aAAS,OAAO,QAAQ,kGAAkG,CAAC,OAAO,IAAI,IAAI,OAAO;AAC7I,YAAM,cAAc,GAAG,SAAS,IAAI,GAAG,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC,IAAI;AAC/F,aAAO,GAAG,EAAE,GAAG,WAAW,GAAG,EAAE;AAAA,IACnC,CAAC;AAED,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,MAAM,YAAoB,MAAa;AACnC,QAAI,KAAK,UAAU,OAAO,GAAG;AACzB,cAAQ,MAAM,KAAK,cAAc,SAAS,SAAS,GAAG,IAAI,CAAC;AAAA,IAC/D;AAAA,EACJ;AAAA;AAAA,EAGA,KAAK,YAAoB,MAAa;AAClC,QAAI,KAAK,UAAU,MAAM,GAAG;AACxB,cAAQ,MAAM,KAAK,cAAc,QAAQ,SAAS,GAAG,IAAI,CAAC;AAAA,IAC9D;AAAA,EACJ;AAAA;AAAA,EAGA,KAAK,YAAoB,MAAa;AAClC,QAAI,KAAK,UAAU,MAAM,GAAG;AACxB,cAAQ,MAAM,KAAK,cAAc,QAAQ,SAAS,GAAG,IAAI,CAAC;AAAA,IAC9D;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,YAAoB,MAAa;AACnC,QAAI,KAAK,UAAU,OAAO,GAAG;AACzB,cAAQ,MAAM,KAAK,cAAc,SAAS,SAAS,GAAG,IAAI,CAAC;AAAA,IAC/D;AAAA,EACJ;AAAA;AAAA,EAGQ,UAAU,OAA0B;AACxC,UAAM,SAAqB,CAAC,SAAS,QAAQ,QAAQ,OAAO;AAC5D,WAAO,OAAO,QAAQ,KAAK,KAAK,OAAO,QAAQ,KAAK,KAAK;AAAA,EAC7D;AACJ;AAEO,IAAM,SAAS,IAAI,OAAO;","names":[]}
1
+ {"version":3,"sources":["../tools/common/logger.ts"],"sourcesContent":["export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nclass Logger {\n private level: LogLevel = 'info';\n\n constructor() {\n this.level = (process.env.LOG_LEVEL as LogLevel) || 'info';\n }\n\n // 로그 레벨 설정\n setLevel(level: LogLevel) {\n this.level = level;\n }\n\n // 로그 메시지 포맷팅 (타임스탬프, 레벨 포함)\n private formatMessage(level: LogLevel, message: string, ...args: any[]): string {\n const timestamp = new Date().toISOString();\n let formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;\n\n if (args.length > 0) {\n formattedMessage += ' ' + args.map(arg => {\n if (arg instanceof Error) {\n return arg.stack || arg.message;\n }\n return typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg);\n }).join(' ');\n }\n\n // 민감 정보 마스킹 (토큰, 비밀번호 등)\n formattedMessage = this.maskSensitiveData(formattedMessage);\n\n return formattedMessage;\n }\n\n // 민감한 데이터(키, 패스워드 등) 마스킹 처리\n private maskSensitiveData(text: string): string {\n // 1. Authorization 헤더 마스킹 (Basic ***, Bearer ***)\n let masked = text.replace(/(Authorization[\"']?\\s*[:=]\\s*[\"']?)(Basic|Bearer)\\s+([^\"'\\s]+)([\"']?)/gi, (match, p1, p2, p3, p4) => {\n const maskedToken = p3.length > 8 ? p3.substring(0, 4) + '...' + p3.substring(p3.length - 4) : '******';\n return `${p1}${p2} ${maskedToken}${p4}`;\n });\n\n // 2. 키워드 기반 마스킹 (token, password, secret, key 등)\n // 헤더 이름(PRIVATE-TOKEN 등)과 변수명 모두 대응 가능하도록 개선\n masked = masked.replace(/((?:token|password|secret|key|api_token|private_token)[\"']?\\s*[:=]\\s*[\"']?)([^\"'\\s]+)([\"']?)/gi, (match, p1, p2, p3) => {\n const maskedValue = p2.length > 8 ? p2.substring(0, 4) + '...' + p2.substring(p2.length - 4) : '******';\n return `${p1}${maskedValue}${p3}`;\n });\n\n return masked;\n }\n\n // Debug 레벨 로그 출력\n debug(message: string, ...args: any[]) {\n if (this.shouldLog('debug')) {\n console.error(this.formatMessage('debug', message, ...args));\n }\n }\n\n // Info 레벨 로그 출력\n info(message: string, ...args: any[]) {\n if (this.shouldLog('info')) {\n console.error(this.formatMessage('info', message, ...args));\n }\n }\n\n // Warn 레벨 로그 출력\n warn(message: string, ...args: any[]) {\n if (this.shouldLog('warn')) {\n console.error(this.formatMessage('warn', message, ...args));\n }\n }\n\n // Error 레벨 로그 출력\n error(message: string, ...args: any[]) {\n if (this.shouldLog('error')) {\n console.error(this.formatMessage('error', message, ...args));\n }\n }\n\n // 현재 설정된 로그 레벨에 따라 출력 여부 결정\n private shouldLog(level: LogLevel): boolean {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n return levels.indexOf(level) >= levels.indexOf(this.level);\n }\n}\n\nexport const logger = new Logger();\n"],"mappings":";;;;;;;;AAEA,IAAM,SAAN,MAAa;AAAA,EACD,QAAkB;AAAA,EAE1B,cAAc;AACV,SAAK,QAAS,QAAQ,IAAI,aAA0B;AAAA,EACxD;AAAA;AAAA,EAGA,SAAS,OAAiB;AACtB,SAAK,QAAQ;AAAA,EACjB;AAAA;AAAA,EAGQ,cAAc,OAAiB,YAAoB,MAAqB;AAC5E,UAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAI,mBAAmB,IAAI,SAAS,MAAM,MAAM,YAAY,CAAC,KAAK,OAAO;AAEzE,QAAI,KAAK,SAAS,GAAG;AACjB,0BAAoB,MAAM,KAAK,IAAI,SAAO;AACtC,YAAI,eAAe,OAAO;AACtB,iBAAO,IAAI,SAAS,IAAI;AAAA,QAC5B;AACA,eAAO,OAAO,QAAQ,WAAW,KAAK,UAAU,KAAK,MAAM,CAAC,IAAI,OAAO,GAAG;AAAA,MAC9E,CAAC,EAAE,KAAK,GAAG;AAAA,IACf;AAGA,uBAAmB,KAAK,kBAAkB,gBAAgB;AAE1D,WAAO;AAAA,EACX;AAAA;AAAA,EAGQ,kBAAkB,MAAsB;AAE5C,QAAI,SAAS,KAAK,QAAQ,2EAA2E,CAAC,OAAO,IAAI,IAAI,IAAI,OAAO;AAC5H,YAAM,cAAc,GAAG,SAAS,IAAI,GAAG,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC,IAAI;AAC/F,aAAO,GAAG,EAAE,GAAG,EAAE,IAAI,WAAW,GAAG,EAAE;AAAA,IACzC,CAAC;AAID,aAAS,OAAO,QAAQ,kGAAkG,CAAC,OAAO,IAAI,IAAI,OAAO;AAC7I,YAAM,cAAc,GAAG,SAAS,IAAI,GAAG,UAAU,GAAG,CAAC,IAAI,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC,IAAI;AAC/F,aAAO,GAAG,EAAE,GAAG,WAAW,GAAG,EAAE;AAAA,IACnC,CAAC;AAED,WAAO;AAAA,EACX;AAAA;AAAA,EAGA,MAAM,YAAoB,MAAa;AACnC,QAAI,KAAK,UAAU,OAAO,GAAG;AACzB,cAAQ,MAAM,KAAK,cAAc,SAAS,SAAS,GAAG,IAAI,CAAC;AAAA,IAC/D;AAAA,EACJ;AAAA;AAAA,EAGA,KAAK,YAAoB,MAAa;AAClC,QAAI,KAAK,UAAU,MAAM,GAAG;AACxB,cAAQ,MAAM,KAAK,cAAc,QAAQ,SAAS,GAAG,IAAI,CAAC;AAAA,IAC9D;AAAA,EACJ;AAAA;AAAA,EAGA,KAAK,YAAoB,MAAa;AAClC,QAAI,KAAK,UAAU,MAAM,GAAG;AACxB,cAAQ,MAAM,KAAK,cAAc,QAAQ,SAAS,GAAG,IAAI,CAAC;AAAA,IAC9D;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,YAAoB,MAAa;AACnC,QAAI,KAAK,UAAU,OAAO,GAAG;AACzB,cAAQ,MAAM,KAAK,cAAc,SAAS,SAAS,GAAG,IAAI,CAAC;AAAA,IAC/D;AAAA,EACJ;AAAA;AAAA,EAGQ,UAAU,OAA0B;AACxC,UAAM,SAAqB,CAAC,SAAS,QAAQ,QAAQ,OAAO;AAC5D,WAAO,OAAO,QAAQ,KAAK,KAAK,OAAO,QAAQ,KAAK,KAAK;AAAA,EAC7D;AACJ;AAEO,IAAM,SAAS,IAAI,OAAO;","names":[]}
@@ -1,6 +1,7 @@
1
1
  import {
2
+ __require,
2
3
  logger
3
- } from "./chunk-IFYMZLQI.js";
4
+ } from "./chunk-6RIA6AJ3.js";
4
5
 
5
6
  // tools/common/env-loader.ts
6
7
  import dotenv from "dotenv";
@@ -38,14 +39,14 @@ var ConfluenceContentApi = class {
38
39
  }
39
40
  async getPage(id, expand) {
40
41
  const expandParam = expand ? expand.join(",") : "body.storage,version,space,metadata.labels";
41
- const response = await this.client.get(`/rest/api/content/${id}`, {
42
+ const response = await this.client.get(`rest/api/content/${id}`, {
42
43
  params: { expand: expandParam }
43
44
  });
44
45
  return response.data;
45
46
  }
46
47
  async getPageByTitle(spaceKey, title, expand) {
47
48
  const expandParam = expand ? expand.join(",") : "body.storage,version,space";
48
- const response = await this.client.get("/rest/api/content", {
49
+ const response = await this.client.get("rest/api/content", {
49
50
  params: {
50
51
  spaceKey,
51
52
  title,
@@ -73,7 +74,7 @@ var ConfluenceContentApi = class {
73
74
  if (params.parentId) {
74
75
  data.ancestors = [{ id: params.parentId }];
75
76
  }
76
- const response = await this.client.post("/rest/api/content", data);
77
+ const response = await this.client.post("rest/api/content", data);
77
78
  if (params.labels && params.labels.length > 0) {
78
79
  await this.addLabels(response.data.id, params.labels);
79
80
  }
@@ -91,14 +92,14 @@ var ConfluenceContentApi = class {
91
92
  }
92
93
  }
93
94
  };
94
- const response = await this.client.put(`/rest/api/content/${params.id}`, data);
95
+ const response = await this.client.put(`rest/api/content/${params.id}`, data);
95
96
  return response.data;
96
97
  }
97
98
  async deletePage(id) {
98
- await this.client.delete(`/rest/api/content/${id}`);
99
+ await this.client.delete(`rest/api/content/${id}`);
99
100
  }
100
101
  async getChildPages(id, start = 0, limit = 25) {
101
- const response = await this.client.get(`/rest/api/content/${id}/child/page`, {
102
+ const response = await this.client.get(`rest/api/content/${id}/child/page`, {
102
103
  params: { start, limit }
103
104
  });
104
105
  return response.data.results;
@@ -112,7 +113,7 @@ var ConfluenceContentApi = class {
112
113
  // For now, simple implementation to support createPage.
113
114
  async addLabels(id, labels) {
114
115
  const data = labels.map((name) => ({ prefix: "global", name }));
115
- await this.client.post(`/rest/api/content/${id}/label`, data);
116
+ await this.client.post(`rest/api/content/${id}/label`, data);
116
117
  }
117
118
  // Attachment 관련 메서드 (upsert: 기존 파일이 있으면 업데이트, 없으면 신규 업로드)
118
119
  async uploadAttachment(pageId, filename, fileContent, contentType) {
@@ -132,13 +133,13 @@ var ConfluenceContentApi = class {
132
133
  let response;
133
134
  if (existing) {
134
135
  response = await this.client.post(
135
- `/rest/api/content/${pageId}/child/attachment/${existing.id}/data`,
136
+ `rest/api/content/${pageId}/child/attachment/${existing.id}/data`,
136
137
  form,
137
138
  { headers }
138
139
  );
139
140
  } else {
140
141
  response = await this.client.post(
141
- `/rest/api/content/${pageId}/child/attachment`,
142
+ `rest/api/content/${pageId}/child/attachment`,
142
143
  form,
143
144
  { headers }
144
145
  );
@@ -149,7 +150,7 @@ var ConfluenceContentApi = class {
149
150
  return response.data;
150
151
  }
151
152
  async getAttachments(pageId, filename) {
152
- const response = await this.client.get(`/rest/api/content/${pageId}/child/attachment`, {
153
+ const response = await this.client.get(`rest/api/content/${pageId}/child/attachment`, {
153
154
  params: {
154
155
  filename,
155
156
  expand: "version"
@@ -171,7 +172,7 @@ var ConfluenceSpaceApi = class {
171
172
  this.client = client;
172
173
  }
173
174
  async getSpaces(type = "global", start = 0, limit = 25) {
174
- const response = await this.client.get("/rest/api/space", {
175
+ const response = await this.client.get("rest/api/space", {
175
176
  params: { type, start, limit }
176
177
  });
177
178
  return response.data.results;
@@ -189,7 +190,7 @@ var ConfluenceSearchApi = class {
189
190
  }
190
191
  async searchByCql(cql, start = 0, limit = 25, expand) {
191
192
  const expandParam = expand ? expand.join(",") : "body.storage,version,space,metadata.labels";
192
- const response = await this.client.get("/rest/api/content/search", {
193
+ const response = await this.client.get("rest/api/content/search", {
193
194
  params: {
194
195
  cql,
195
196
  start,
@@ -237,20 +238,23 @@ var ConflictError = class extends TdeCollabError {
237
238
 
238
239
  // tools/common/http-client.ts
239
240
  function createHttpClient(config) {
241
+ const normalizedBaseUrl = config.baseUrl.endsWith("/") ? config.baseUrl : `${config.baseUrl}/`;
240
242
  const client = axios.create({
241
- baseURL: config.baseUrl,
243
+ baseURL: normalizedBaseUrl,
242
244
  timeout: 3e4,
243
245
  // 30초 타임아웃
246
+ adapter: "http",
247
+ // Electron 환경(Obsidian)에서 XHR 대신 Node.js http 모듈을 사용하여 CORS 우회
244
248
  headers: {
245
249
  "Content-Type": "application/json"
246
250
  }
247
251
  });
248
252
  client.interceptors.request.use((reqConfig) => {
249
- if (config.auth.token && !reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
250
- reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
251
- } else if (config.auth.username && config.auth.token && !reqConfig.headers.Authorization) {
253
+ if (config.auth.username && config.auth.token && !reqConfig.headers.Authorization) {
252
254
  const token = Buffer.from(`${config.auth.username}:${config.auth.token}`).toString("base64");
253
255
  reqConfig.headers.Authorization = `Basic ${token}`;
256
+ } else if (config.auth.token && !reqConfig.headers.Authorization && !reqConfig.headers["PRIVATE-TOKEN"]) {
257
+ reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;
254
258
  }
255
259
  logger.debug(`[HTTP Request] ${reqConfig.method?.toUpperCase()} ${reqConfig.url}`, {
256
260
  headers: reqConfig.headers,
@@ -446,7 +450,6 @@ var MarkdownToStorageConverter = class {
446
450
  // tools/confluence/converters/storage-to-md.ts
447
451
  import TurndownService from "turndown";
448
452
  import { gfm } from "turndown-plugin-gfm";
449
- import { JSDOM } from "jsdom";
450
453
  var StorageToMarkdownConverter = class {
451
454
  turndown;
452
455
  jiraBaseUrl;
@@ -577,8 +580,15 @@ ${bodyMd}
577
580
  const alt = altMatch ? altMatch[1] : "";
578
581
  return `<img src="${url}" alt="${alt}" />`;
579
582
  });
580
- const dom = new JSDOM(processedHtml);
581
- const document = dom.window.document;
583
+ let document;
584
+ if (typeof window !== "undefined" && typeof window.DOMParser !== "undefined") {
585
+ const parser = new window.DOMParser();
586
+ document = parser.parseFromString(processedHtml, "text/html");
587
+ } else {
588
+ const { JSDOM } = __require("jsdom");
589
+ const dom = new JSDOM(processedHtml);
590
+ document = dom.window.document;
591
+ }
582
592
  if (imageUrlMap && imageUrlMap.size > 0) {
583
593
  const images = document.querySelectorAll("img");
584
594
  images.forEach((img) => {
@@ -630,7 +640,7 @@ var JiraIssueApi = class {
630
640
  if (params.customFields) {
631
641
  Object.assign(fields, params.customFields);
632
642
  }
633
- const response = await this.client.post("/rest/api/2/issue", { fields });
643
+ const response = await this.client.post("rest/api/2/issue", { fields });
634
644
  return response.data;
635
645
  }
636
646
  async updateIssue(issueKey, params) {
@@ -716,7 +726,7 @@ var JiraSearchApi = class {
716
726
  if (fields && fields.length > 0) {
717
727
  params.fields = fields.join(",");
718
728
  }
719
- const response = await this.client.get("/rest/api/2/search", { params });
729
+ const response = await this.client.get("rest/api/2/search", { params });
720
730
  return response.data;
721
731
  }
722
732
  };
@@ -847,4 +857,4 @@ export {
847
857
  GitlabPipelineApi,
848
858
  createGitlabClient
849
859
  };
850
- //# sourceMappingURL=chunk-5OB3KU5D.js.map
860
+ //# sourceMappingURL=chunk-JUM5ZG64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../tools/common/env-loader.ts","../tools/confluence/api/content.ts","../tools/confluence/api/space.ts","../tools/confluence/api/search.ts","../tools/common/http-client.ts","../tools/common/errors.ts","../tools/confluence/api/client.ts","../tools/common/config.ts","../tools/confluence/converters/md-to-storage.ts","../tools/confluence/converters/storage-to-md.ts","../tools/jira/api/issue.ts","../tools/jira/api/client.ts","../tools/confluence/converters/jira-enricher.ts","../tools/jira/api/search.ts","../tools/gitlab/api/merge-request.ts","../tools/gitlab/api/pipeline.ts","../tools/gitlab/api/client.ts"],"sourcesContent":["import dotenv from 'dotenv';\nimport path from 'path';\nimport os from 'os';\n\n// 환경변수 로딩 우선순위:\n// 1. 셸 환경변수 (이미 process.env 에 존재하므로 자동 우선)\n// 2. 현재 작업 디렉토리의 tdecollab.env\n// 3. ~/.config/tdecollab/.env (사용자 글로벌 설정)\n//\n// dotenv 기본 동작이 override: false 이므로,\n// 이미 process.env 에 있는 키는 덮어쓰지 않는다.\n// 따라서 우선순위 높은 출처(env vars)부터 로드하면 자연스럽게 우선순위가 적용된다.\n\nlet loaded = false;\n\nexport interface LoadEnvResult {\n loadedFiles: string[];\n skippedFiles: string[];\n}\n\nfunction getHomeDir(): string {\n return process.env.HOME || process.env.USERPROFILE || os.homedir();\n}\n\nexport function loadEnv(): LoadEnvResult {\n const result: LoadEnvResult = { loadedFiles: [], skippedFiles: [] };\n if (loaded) return result;\n loaded = true;\n\n const candidates = [\n // 우선순위 2: 현재 디렉토리\n path.resolve(process.cwd(), 'tdecollab.env'),\n // 우선순위 3: 사용자 글로벌\n path.join(getHomeDir(), '.config', 'tdecollab', '.env'),\n ];\n\n for (const filepath of candidates) {\n const out = dotenv.config({ path: filepath, override: false });\n if (out.error) {\n // 파일이 없으면 자연스럽게 skip (오류로 취급하지 않음)\n result.skippedFiles.push(filepath);\n } else {\n result.loadedFiles.push(filepath);\n }\n }\n\n return result;\n}\n\n// 디버그용 — 어떤 파일이 로드되었는지 확인할 때 사용\nexport function getEnvLoadDebug(): string[] {\n return loaded ? ['env-loader: 1회 호출됨'] : ['env-loader: 호출되지 않음'];\n}\n","import { AxiosInstance } from 'axios';\nimport { ConfluencePageResponse, CreatePageParams, UpdatePageParams, ConfluenceLabel, ConfluenceAttachmentResponse } from '../types.js';\n\n\nexport class ConfluenceContentApi {\n constructor(private client: AxiosInstance) { }\n\n async getPage(id: string, expand?: string[]): Promise<ConfluencePageResponse> {\n const expandParam = expand ? expand.join(',') : 'body.storage,version,space,metadata.labels';\n const response = await this.client.get(`rest/api/content/${id}`, {\n params: { expand: expandParam }\n });\n return response.data;\n }\n\n async getPageByTitle(spaceKey: string, title: string, expand?: string[]): Promise<ConfluencePageResponse | null> {\n const expandParam = expand ? expand.join(',') : 'body.storage,version,space';\n const response = await this.client.get('rest/api/content', {\n params: {\n spaceKey,\n title,\n expand: expandParam,\n limit: 1\n }\n });\n\n if (response.data.results && response.data.results.length > 0) {\n return response.data.results[0];\n }\n return null;\n }\n\n async createPage(params: CreatePageParams): Promise<ConfluencePageResponse> {\n const data: any = {\n type: 'page',\n title: params.title,\n space: { key: params.spaceKey },\n body: {\n storage: {\n value: params.body,\n representation: 'storage'\n }\n }\n };\n\n if (params.parentId) {\n data.ancestors = [{ id: params.parentId }];\n }\n\n const response = await this.client.post('rest/api/content', data);\n\n // add labels if provided\n if (params.labels && params.labels.length > 0) {\n await this.addLabels(response.data.id, params.labels);\n // refetch to include labels in response if needed, \n // but simpler to just return the create response.\n // Or we can construct the object.\n }\n\n return response.data;\n }\n\n async updatePage(params: UpdatePageParams): Promise<ConfluencePageResponse> {\n const data = {\n version: { number: params.version + 1 },\n title: params.title,\n type: 'page',\n body: {\n storage: {\n value: params.body,\n representation: 'storage'\n }\n }\n };\n\n const response = await this.client.put(`rest/api/content/${params.id}`, data);\n return response.data;\n }\n\n async deletePage(id: string): Promise<void> {\n await this.client.delete(`rest/api/content/${id}`);\n }\n\n async getChildPages(id: string, start = 0, limit = 25): Promise<ConfluencePageResponse[]> {\n const response = await this.client.get(`rest/api/content/${id}/child/page`, {\n params: { start, limit }\n });\n return response.data.results;\n }\n\n // Label helper inside content api or separate? \n // Let's implement basic label addition here since it's used in create.\n // Actually, label logic is in label.ts, but due to circular dependency or convenience...\n // Let's implement it here privately or import it.\n // Better to keep it separate as per plan, but `this.client` is available here.\n // I'll implement a simple one here or use the separate class later. \n // For now, simple implementation to support createPage.\n private async addLabels(id: string, labels: string[]): Promise<void> {\n const data = labels.map(name => ({ prefix: 'global', name }));\n await this.client.post(`rest/api/content/${id}/label`, data);\n }\n\n // Attachment 관련 메서드 (upsert: 기존 파일이 있으면 업데이트, 없으면 신규 업로드)\n async uploadAttachment(pageId: string, filename: string, fileContent: Buffer, contentType?: string): Promise<ConfluenceAttachmentResponse> {\n const FormData = (await import('form-data')).default;\n\n // 기존 첨부파일 존재 여부 확인\n const existingAttachments = await this.getAttachments(pageId, filename);\n const existing = existingAttachments.find(a => a.title === filename);\n\n const form = new FormData();\n form.append('file', fileContent, {\n filename: filename,\n contentType: contentType || 'application/octet-stream'\n });\n\n const headers = {\n ...form.getHeaders(),\n 'X-Atlassian-Token': 'nocheck',\n 'Accept': 'application/json',\n };\n\n let response;\n if (existing) {\n // 기존 파일 업데이트 (POST to /data endpoint)\n response = await this.client.post(\n `rest/api/content/${pageId}/child/attachment/${existing.id}/data`,\n form,\n { headers }\n );\n } else {\n // 신규 업로드\n response = await this.client.post(\n `rest/api/content/${pageId}/child/attachment`,\n form,\n { headers }\n );\n }\n\n // Confluence API returns { results: [Attachment] } \n if (response.data && response.data.results && response.data.results.length > 0) {\n return response.data.results[0];\n }\n return response.data;\n }\n\n async getAttachments(pageId: string, filename?: string): Promise<ConfluenceAttachmentResponse[]> {\n const response = await this.client.get(`rest/api/content/${pageId}/child/attachment`, {\n params: {\n filename,\n expand: 'version'\n }\n });\n return response.data.results;\n }\n\n async downloadAttachment(downloadUrl: string): Promise<Buffer> {\n const response = await this.client.get(downloadUrl, {\n responseType: 'arraybuffer'\n });\n return Buffer.from(response.data);\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { ConfluenceSpaceResponse } from '../types.js';\n\nexport class ConfluenceSpaceApi {\n constructor(private client: AxiosInstance) { }\n\n async getSpaces(type: string = 'global', start = 0, limit = 25): Promise<ConfluenceSpaceResponse[]> {\n const response = await this.client.get('rest/api/space', {\n params: { type, start, limit }\n });\n return response.data.results;\n }\n\n async getSpace(spaceKey: string): Promise<ConfluenceSpaceResponse> {\n const response = await this.client.get(`/rest/api/space/${spaceKey}`);\n return response.data;\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { ConfluenceSearchResponse } from '../types.js';\n\nexport class ConfluenceSearchApi {\n constructor(private client: AxiosInstance) { }\n\n async searchByCql(cql: string, start = 0, limit = 25, expand?: string[]): Promise<ConfluenceSearchResponse> {\n const expandParam = expand ? expand.join(',') : 'body.storage,version,space,metadata.labels';\n const response = await this.client.get('rest/api/content/search', {\n params: {\n cql,\n start,\n limit,\n expand: expandParam\n }\n });\n\n return response.data;\n }\n}\n","import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';\nimport { ApiError, AuthError, NotFoundError, ConflictError } from './errors.js';\nimport { logger } from './logger.js';\nimport { ServiceConfig } from './types.js';\n\nexport function createHttpClient(config: ServiceConfig): AxiosInstance {\n // URL 정규화 (항상 슬래시로 끝나도록 하여 하위 경로가 유지되게 함)\n const normalizedBaseUrl = config.baseUrl.endsWith('/') ? config.baseUrl : `${config.baseUrl}/`;\n\n const client = axios.create({\n baseURL: normalizedBaseUrl,\n timeout: 30000, // 30초 타임아웃\n adapter: 'http', // Electron 환경(Obsidian)에서 XHR 대신 Node.js http 모듈을 사용하여 CORS 우회\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n // 요청 인터셉터: 인증 헤더 주입 및 로깅\n client.interceptors.request.use((reqConfig) => {\n // 1. Basic 인증 (Username + Token)\n if (config.auth.username && config.auth.token && !reqConfig.headers.Authorization) {\n const token = Buffer.from(`${config.auth.username}:${config.auth.token}`).toString('base64');\n reqConfig.headers.Authorization = `Basic ${token}`;\n }\n // 2. 토큰 기반 Bearer 인증 (Confluence/JIRA PAT 권장 방식)\n else if (config.auth.token && !reqConfig.headers.Authorization && !reqConfig.headers['PRIVATE-TOKEN']) {\n // GitLab은 PRIVATE-TOKEN 헤더를 선호하므로 제외\n reqConfig.headers.Authorization = `Bearer ${config.auth.token}`;\n }\n\n logger.debug(`[HTTP Request] ${reqConfig.method?.toUpperCase()} ${reqConfig.url}`, {\n headers: reqConfig.headers,\n params: reqConfig.params,\n data: reqConfig.data,\n });\n\n return reqConfig;\n });\n\n // 응답 인터셉터: 에러 핸들링 및 로깅\n client.interceptors.response.use(\n (response: AxiosResponse) => {\n logger.debug(`[HTTP Response] ${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`, {\n headers: response.headers,\n data: response.data,\n });\n return response;\n },\n (error: AxiosError) => {\n if (error.response) {\n const status = error.response.status;\n const method = error.config?.method?.toUpperCase();\n const url = error.config?.url;\n const message = (error.response.data as any)?.message || error.message;\n\n logger.error(`[HTTP Error] ${status} ${method} ${url}`, {\n message,\n responseData: error.response.data,\n requestHeaders: error.config?.headers,\n });\n\n // 상태 코드별 커스텀 에러 매핑\n if (status === 401 || status === 403) {\n throw new AuthError(`인증 실패: ${message}`);\n }\n if (status === 404) {\n throw new NotFoundError('리소스', url || 'unknown');\n }\n if (status === 409) {\n throw new ConflictError(`충돌 발생: ${message}`);\n }\n\n throw new ApiError(status, message, error.response.data);\n } else if (error.request) {\n logger.error(`[HTTP] No Response: ${error.message}`);\n throw new ApiError(0, `서버로부터 응답이 없습니다: ${error.message}`);\n } else {\n logger.error(`[HTTP] Request Error: ${error.message}`);\n throw new ApiError(0, `요청 설정 중 오류 발생: ${error.message}`);\n }\n }\n );\n\n return client;\n}\n","// TDE Collab 기본 에러 클래스\nexport class TdeCollabError extends Error {\n constructor(message: string) {\n super(message);\n this.name = this.constructor.name;\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n// API 요청 실패 에러\nexport class ApiError extends TdeCollabError {\n constructor(\n public statusCode: number,\n message: string,\n public data?: any\n ) {\n super(`API 요청 실패 (${statusCode}): ${message}`);\n }\n}\n\n// 인증 실패 에러\nexport class AuthError extends TdeCollabError {\n constructor(message: string = '인증에 실패했습니다') {\n super(message);\n }\n}\n\n// 리소스 미발견 에러\nexport class NotFoundError extends TdeCollabError {\n constructor(resource: string, id: string) {\n super(`${resource} '${id}'를 찾을 수 없습니다`);\n }\n}\n\n// 리소스 충돌 에러\nexport class ConflictError extends TdeCollabError {\n constructor(message: string) {\n super(message);\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { ConfluenceConfig } from '../../common/types.js';\nimport { createHttpClient } from '../../common/http-client.js';\n\nexport function createConfluenceClient(config: ConfluenceConfig): AxiosInstance {\n // Confluence API base context path handling\n // If user provides 'https://example.atlassian.net/wiki', we should use that.\n // API path usually appends '/rest/api/content' etc.\n\n // Ensure config.baseUrl includes '/wiki' if it's a cloud instance, usually users put it in env.\n // But standard is commonly base domain.\n // We'll trust the config for now, but client consumers should append specific endpoints.\n // Typically, Confluence API is at /wiki/rest/api if cloud, or /rest/api if server.\n // Let's assume baseUrl in config points to the root of the instance, e.g. https://site.atlassian.net/wiki\n\n return createHttpClient(config);\n}\n","import { ConfluenceConfig, GitlabConfig, JiraConfig } from './types.js';\nimport { logger } from './logger.js';\nimport { loadEnv } from './env-loader.js';\n\n// 환경변수 로드 (우선순위: shell env > ./tdecollab.env > ~/.config/tdecollab/.env)\nloadEnv();\n\n// 환경변수 조회 및 미설정 시 에러 발생\nfunction getEnvOrThrow(key: string, description: string): string {\n const value = process.env[key];\n if (!value) {\n const errorMsg = `환경변수 '${key}'가 설정되지 않았습니다. (${description})`;\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n return value;\n}\n\n// Confluence 설정 로드 (PAT 인증 권장)\nexport function loadConfluenceConfig(): ConfluenceConfig & { mermaidMacroName: string, inlineCodeStyle: string } {\n const baseUrl = getEnvOrThrow('CONFLUENCE_BASE_URL', 'Confluence 기본 URL');\n // PAT 사용 시 username은 불필요 (Basic Auth 사용 시에만 필요)\n const username = process.env.CONFLUENCE_USERNAME;\n const token = getEnvOrThrow('CONFLUENCE_API_TOKEN', 'Confluence PAT 토큰');\n \n // Mermaid 매크로 이름 (기본값: mermaiddiagram)\n const mermaidMacroName = process.env.CONFLUENCE_MERMAID_MACRO_NAME || 'mermaiddiagram';\n\n // 인라인 코드 강조 스타일 (기본값: 붉은색 굵게)\n const inlineCodeStyle = process.env.CONFLUENCE_INLINE_CODE_STYLE || 'color: #d04437; font-weight: bold;';\n\n return {\n baseUrl,\n auth: {\n username,\n token,\n },\n mermaidMacroName,\n inlineCodeStyle,\n };\n}\n\n// AI 서비스 설정 로드\nexport function loadAIConfig() {\n return {\n openaiApiKey: process.env.OPENAI_API_KEY,\n anthropicApiKey: process.env.ANTHROPIC_API_KEY,\n defaultProvider: process.env.AI_PROVIDER || 'openai',\n defaultModel: process.env.AI_MODEL || 'gpt-4o',\n };\n}\n\n// JIRA 설정 로드 (PAT 인증 권장)\nexport function loadJiraConfig(): JiraConfig {\n const baseUrl = getEnvOrThrow('JIRA_BASE_URL', 'JIRA 기본 URL');\n // PAT 사용 시 username은 불필요\n const username = process.env.JIRA_USERNAME;\n const token = getEnvOrThrow('JIRA_API_TOKEN', 'JIRA PAT 토큰');\n\n return {\n baseUrl,\n auth: {\n username,\n token,\n },\n };\n}\n\n// GitLab 설정 로드\nexport function loadGitlabConfig(): GitlabConfig {\n const baseUrl = process.env.GITLAB_BASE_URL || 'https://gitlab.com';\n const token = getEnvOrThrow('GITLAB_PRIVATE_TOKEN', 'GitLab Private Token');\n\n return {\n baseUrl,\n auth: {\n token,\n },\n };\n}\n","import MarkdownIt from 'markdown-it';\nimport { loadConfluenceConfig } from '../../common/config.js';\n\nexport class MarkdownToStorageConverter {\n private md: MarkdownIt;\n private mermaidMacroName: string;\n private inlineCodeStyle: string;\n\n constructor() {\n const config = loadConfluenceConfig();\n this.mermaidMacroName = config.mermaidMacroName;\n this.inlineCodeStyle = config.inlineCodeStyle;\n\n this.md = new MarkdownIt({\n html: true,\n linkify: true,\n breaks: true,\n xhtmlOut: true // Confluence XML 파서와의 호환성을 위해 XHTML 출력 활성화\n });\n\n // Custom renderer for code blocks\n this.md.renderer.rules.fence = (tokens, idx) => {\n const token = tokens[idx];\n const code = token.content.trim();\n const lang = token.info.trim().toLowerCase();\n\n // Mermaid 처리 (기존 로직 유지)\n if (lang === 'mermaid') {\n return `<ac:structured-macro ac:name=\"${this.mermaidMacroName}\" ac:schema-version=\"1\">\n <ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>\n</ac:structured-macro>`;\n }\n\n // PlantUML 처리 → plantuml 매크로로 변환\n if (lang === 'plantuml') {\n return `<ac:structured-macro ac:name=\"plantuml\" ac:schema-version=\"1\">\n <ac:parameter ac:name=\"atlassian-macro-output-type\">INLINE</ac:parameter>\n <ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>\n</ac:structured-macro>`;\n }\n\n // 일반 코드 블록\n return `<ac:structured-macro ac:name=\"code\" ac:schema-version=\"1\">\n <ac:parameter ac:name=\"language\">${lang || 'text'}</ac:parameter>\n <ac:plain-text-body><![CDATA[${code}]]></ac:plain-text-body>\n</ac:structured-macro>`;\n };\n\n // Custom renderer for images\n this.md.renderer.rules.image = (tokens, idx, options, env, self) => {\n const token = tokens[idx];\n const src = token.attrGet('src') || '';\n const alt = token.content || '';\n\n // Handle URL vs Local file\n const isExternal = src.startsWith('http://') || src.startsWith('https://');\n\n // 외부 URL이면 URL 매크로, 아니면 첨부파일 매크로\n const altAttr = alt ? ` ac:alt=\"${this.md.utils.escapeHtml(alt)}\"` : '';\n if (isExternal) {\n return `<ac:image${altAttr}><ri:url ri:value=\"${src}\" /></ac:image>`;\n } else {\n // filename can just be the basename of the src path\n const filename = src.split('/').pop() || src;\n return `<ac:image${altAttr}><ri:attachment ri:filename=\"${filename}\" /></ac:image>`;\n }\n };\n\n // Custom renderer for inline code\n this.md.renderer.rules.code_inline = (tokens, idx) => {\n const token = tokens[idx];\n const content = this.md.utils.escapeHtml(token.content);\n return `<code style=\"${this.inlineCodeStyle}\">${content}</code>`;\n };\n\n // Task List 처리 (markdown-it 플러그인 없이 수동 처리 예시 - 실제로는 플러그인 도입 권장)\n }\n\n convert(markdown: string): string {\n return this.md.render(markdown);\n }\n\n extractLocalImages(markdown: string): string[] {\n const tokens = this.md.parse(markdown, {});\n const localImages = new Set<string>();\n\n const walk = (tokens: any[]) => {\n for (const token of tokens) {\n if (token.type === 'image') {\n const src = token.attrGet('src') || '';\n if (!src.startsWith('http://') && !src.startsWith('https://')) {\n localImages.add(src);\n }\n }\n if (token.children) {\n walk(token.children);\n }\n }\n };\n\n walk(tokens);\n return Array.from(localImages);\n }\n}\n","import TurndownService from 'turndown';\nimport { gfm } from 'turndown-plugin-gfm';\nimport { JSDOM } from 'jsdom';\n\nexport interface JiraIssueInfo {\n summary: string;\n status: string;\n}\n\nexport interface StorageToMarkdownOptions {\n /** JIRA 티켓 링크 생성에 사용할 베이스 URL (예: https://jira.example.com). 미설정 시 JIRA_BASE_URL 환경변수 사용. */\n jiraBaseUrl?: string;\n}\n\nexport class StorageToMarkdownConverter {\n private turndown: TurndownService;\n private jiraBaseUrl: string;\n private jiraIssueMap: Map<string, JiraIssueInfo> | undefined;\n\n constructor(options: StorageToMarkdownOptions = {}) {\n this.jiraBaseUrl = options.jiraBaseUrl || process.env.JIRA_BASE_URL || '';\n this.jiraIssueMap = undefined;\n this.turndown = new TurndownService({\n headingStyle: 'atx',\n hr: '---',\n bulletListMarker: '-',\n codeBlockStyle: 'fenced'\n });\n\n this.turndown.use(gfm);\n this.setupRules();\n }\n\n private setupRules() {\n // Table 변환 규칙 강화 (정렬 정보 보존)\n this.turndown.addRule('tables', {\n filter: ['table'],\n replacement: (content, node) => {\n const element = node as HTMLTableElement;\n const rows = Array.from(element.rows);\n if (rows.length === 0) return '';\n\n let mdTable = '\\n\\n';\n\n rows.forEach((row, index) => {\n const cells = Array.from(row.cells);\n const cellContents = cells.map(cell => {\n // 셀 내부의 개행은 공백으로 치환하여 테이블 깨짐 방지\n return this.turndown.turndown(cell.innerHTML).replace(/\\n/g, ' ').trim();\n });\n\n mdTable += `| ${cellContents.join(' | ')} |\\n`;\n\n // 헤더 행 구분선 (정렬 정보 포함)\n if (index === 0) {\n const separators = cells.map(cell => {\n const style = (cell as HTMLElement).getAttribute('style') || '';\n const align = style.match(/text-align:\\s*(\\w+)/i)?.[1]?.toLowerCase();\n if (align === 'center') return ':---:';\n if (align === 'right') return '---:';\n if (align === 'left') return ':---';\n return '---';\n });\n mdTable += `| ${separators.join(' | ')} |\\n`;\n }\n });\n\n return mdTable + '\\n';\n }\n });\n\n // Confluence 매크로 (변환된 div 태그) 처리\n this.turndown.addRule('confluenceMacro', {\n filter: (node) => {\n return node.nodeName === 'DIV' && node.getAttribute('data-macro-name-tag') !== null;\n },\n replacement: (content, node) => {\n const element = node as HTMLElement;\n const macroName = element.getAttribute('data-macro-name');\n\n if (macroName === 'code') {\n const lang = element.querySelector('[data-macro-param-name=\"language\"]')?.textContent || 'text';\n let body = element.querySelector('[data-macro-body]')?.textContent || '';\n // 임시 CDATA 태그 제거\n body = body.replace(/__CDATA_START__/g, '').replace(/__CDATA_END__/g, '');\n return `\\n\\`\\`\\`${lang}\\n${body.trim()}\\n\\`\\`\\`\\n`;\n }\n\n if (macroName === 'mermaid' || macroName === 'mermaiddiagram' || macroName === 'capable-mermaid' || macroName === 'mermaid-macro') {\n let body = element.querySelector('[data-macro-body]')?.textContent || '';\n // 임시 CDATA 태그 제거\n body = body.replace(/__CDATA_START__/g, '').replace(/__CDATA_END__/g, '');\n return `\\n\\`\\`\\`mermaid\\n${body.trim()}\\n\\`\\`\\`\\n`;\n }\n\n if (macroName === 'plantuml') {\n let body = element.querySelector('[data-macro-body]')?.textContent || '';\n body = body.replace(/__CDATA_START__/g, '').replace(/__CDATA_END__/g, '');\n return `\\n\\`\\`\\`plantuml\\n${body.trim()}\\n\\`\\`\\`\\n`;\n }\n\n if (macroName === 'expand') {\n const title = element.querySelector('[data-macro-param-name=\"title\"]')?.textContent?.trim() || '더보기';\n const richBody = element.querySelector('[data-macro-rich-body]');\n const bodyMd = richBody\n ? this.turndown.turndown(richBody.innerHTML).trim()\n : '';\n return `\\n\\n<details>\\n<summary>${title}</summary>\\n\\n${bodyMd}\\n\\n</details>\\n\\n`;\n }\n\n if (macroName === 'jira') {\n const key = element.querySelector('[data-macro-param-name=\"key\"]')?.textContent?.trim();\n if (key) {\n const base = this.jiraBaseUrl.replace(/\\/$/, '');\n const url = base ? `${base}/browse/${key}` : `https://jira.atlassian.com/browse/${key}`;\n const info = this.jiraIssueMap?.get(key);\n if (info) {\n return ` [${key}](${url}) ${info.summary} \\`${info.status}\\` `;\n }\n return ` [${key}](${url}) `;\n }\n return '';\n }\n\n return `\\n<!-- Macro: ${macroName} -->\\n`;\n }\n });\n }\n\n convert(storageHtml: string, imageUrlMap?: Map<string, string>, jiraIssueMap?: Map<string, JiraIssueInfo>): string {\n this.jiraIssueMap = jiraIssueMap;\n if (!storageHtml) return '';\n\n // 1. Confluence 전용 태그를 표준 HTML 태그로 치환 (JSDOM 호환성)\n // CDATA 섹션을 텍스트로 보존하기 위해 임시 치환\n let processedHtml = storageHtml\n .replace(/<!\\[CDATA\\[([\\s\\S]*?)\\]\\]>/gi, (match, p1) => {\n return `__CDATA_START__${p1}__CDATA_END__`;\n })\n .replace(/<ac:structured-macro\\s+ac:name=\"([^\"]*)\"/gi, '<div data-macro-name-tag data-macro-name=\"$1\"')\n .replace(/<\\/ac:structured-macro>/gi, '</div>')\n .replace(/<ac:parameter\\s+ac:name=\"([^\"]*)\"/gi, '<div data-macro-param-tag data-macro-param-name=\"$1\"')\n .replace(/<\\/ac:parameter>/gi, '</div>')\n .replace(/<ac:plain-text-body>/gi, '<pre data-macro-body>')\n .replace(/<\\/ac:plain-text-body>/gi, '</pre>')\n .replace(/<ac:rich-text-body>/gi, '<div data-macro-rich-body>')\n .replace(/<\\/ac:rich-text-body>/gi, '</div>')\n .replace(/<ac:image([^>]*)>[\\s\\S]*?<ri:attachment\\s+ri:filename=\"([^\"]*)\"\\s*\\/?>[\\s\\S]*?<\\/ac:image>/gi, (match, attrs, filename) => {\n const altMatch = attrs.match(/ac:alt=\"([^\"]*)\"/i);\n const alt = altMatch ? altMatch[1] : filename;\n return `<img src=\"${filename}\" alt=\"${alt}\" />`;\n })\n .replace(/<ac:image([^>]*)>[\\s\\S]*?<ri:url\\s+ri:value=\"([^\"]*)\"\\s*\\/?>[\\s\\S]*?<\\/ac:image>/gi, (match, attrs, url) => {\n const altMatch = attrs.match(/ac:alt=\"([^\"]*)\"/i);\n const alt = altMatch ? altMatch[1] : '';\n return `<img src=\"${url}\" alt=\"${alt}\" />`;\n });\n\n // 2. DOM 파싱 (Browser/Node 분기)\n let document: Document;\n if (typeof window !== 'undefined' && typeof window.DOMParser !== 'undefined') {\n const parser = new window.DOMParser();\n document = parser.parseFromString(processedHtml, 'text/html');\n } else {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { JSDOM } = require('jsdom');\n const dom = new JSDOM(processedHtml);\n document = dom.window.document;\n }\n\n // 이미지 처리\n if (imageUrlMap && imageUrlMap.size > 0) {\n const images = document.querySelectorAll('img');\n images.forEach(img => {\n const src = img.getAttribute('src');\n if (src && imageUrlMap.has(src)) {\n img.setAttribute('src', imageUrlMap.get(src)!);\n }\n });\n }\n\n // 3. Turndown 변환\n let markdown = this.turndown.turndown(document.body.innerHTML).trim();\n\n // 4. 헤딩 내 숫자 뒤 점 이스케이프 제거 (예: `## 1\\.` → `## 1.`)\n markdown = markdown.replace(/^(#{1,6}\\s.*?)\\\\\\./gm, '$1.');\n\n this.jiraIssueMap = undefined;\n return markdown + '\\n';\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { JiraIssueResponse, CreateIssueParams, UpdateIssueParams } from '../types.js';\n\nexport class JiraIssueApi {\n constructor(private client: AxiosInstance) {}\n\n async getIssue(issueKey: string, fields?: string[], expand?: string[]): Promise<JiraIssueResponse> {\n const params: Record<string, string> = {};\n if (fields && fields.length > 0) {\n params.fields = fields.join(',');\n }\n if (expand && expand.length > 0) {\n params.expand = expand.join(',');\n }\n const response = await this.client.get(`/rest/api/2/issue/${issueKey}`, { params });\n return response.data;\n }\n\n async createIssue(params: CreateIssueParams): Promise<JiraIssueResponse> {\n const fields: Record<string, unknown> = {\n project: { key: params.projectKey },\n summary: params.summary,\n issuetype: { name: params.issueType },\n };\n\n if (params.description) fields.description = params.description;\n if (params.assignee) fields.assignee = { name: params.assignee };\n if (params.priority) fields.priority = { name: params.priority };\n if (params.labels) fields.labels = params.labels;\n if (params.components) {\n fields.components = params.components.map((name) => ({ name }));\n }\n if (params.parentKey) {\n fields.parent = { key: params.parentKey };\n }\n if (params.customFields) {\n Object.assign(fields, params.customFields);\n }\n\n const response = await this.client.post('rest/api/2/issue', { fields });\n return response.data;\n }\n\n async updateIssue(issueKey: string, params: UpdateIssueParams): Promise<void> {\n const fields: Record<string, unknown> = {};\n\n if (params.summary) fields.summary = params.summary;\n if (params.description !== undefined) fields.description = params.description;\n if (params.assignee !== undefined) fields.assignee = params.assignee ? { name: params.assignee } : null;\n if (params.priority) fields.priority = { name: params.priority };\n if (params.labels) fields.labels = params.labels;\n if (params.components) {\n fields.components = params.components.map((name) => ({ name }));\n }\n if (params.customFields) {\n Object.assign(fields, params.customFields);\n }\n\n await this.client.put(`/rest/api/2/issue/${issueKey}`, { fields });\n }\n\n async deleteIssue(issueKey: string, deleteSubtasks = false): Promise<void> {\n await this.client.delete(`/rest/api/2/issue/${issueKey}`, {\n params: { deleteSubtasks },\n });\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { JiraConfig } from '../../common/types.js';\nimport { createHttpClient } from '../../common/http-client.js';\n\nexport function createJiraClient(config: JiraConfig): AxiosInstance {\n return createHttpClient(config);\n}\n","import { JiraIssueApi } from '../../jira/api/issue.js';\nimport { createJiraClient } from '../../jira/api/client.js';\nimport { loadJiraConfig } from '../../common/config.js';\nimport type { JiraIssueInfo } from './storage-to-md.js';\n\n/**\n * Confluence Storage XML에서 JIRA 매크로의 key 파라미터를 추출한다.\n * 중복 키는 제거하여 반환한다.\n */\nexport function extractJiraKeys(storageXml: string): string[] {\n const keys = new Set<string>();\n const macroRegex = /<ac:structured-macro\\s+ac:name=\"jira\"[\\s\\S]*?<\\/ac:structured-macro>/gi;\n const keyRegex = /<ac:parameter\\s+ac:name=\"key\">([^<]+)<\\/ac:parameter>/i;\n let match;\n while ((match = macroRegex.exec(storageXml)) !== null) {\n const keyMatch = keyRegex.exec(match[0]);\n if (keyMatch) keys.add(keyMatch[1].trim());\n }\n return [...keys];\n}\n\n/**\n * JIRA API에서 이슈 목록을 배치 조회하여 key → JiraIssueInfo 맵을 반환한다.\n * 개별 이슈 조회 실패 시 해당 키를 맵에서 제외한다 (링크만 표시됨).\n */\nexport async function buildJiraIssueMap(\n keys: string[],\n jiraApi: JiraIssueApi,\n): Promise<Map<string, JiraIssueInfo>> {\n const map = new Map<string, JiraIssueInfo>();\n if (keys.length === 0) return map;\n\n await Promise.allSettled(\n keys.map(async (key) => {\n try {\n const issue = await jiraApi.getIssue(key, ['summary', 'status']);\n map.set(key, {\n summary: issue.fields.summary ?? '',\n status: issue.fields.status?.name ?? '',\n });\n } catch {\n // 조회 실패 시 해당 키 생략 — 링크만 표시\n }\n }),\n );\n return map;\n}\n\n/**\n * Storage XML에서 JIRA 키를 추출하고, JIRA 설정이 가능한 경우 이슈 정보를 가져온다.\n * JIRA 설정이 없거나 오류 발생 시 빈 맵을 반환한다 (graceful fallback).\n */\nexport async function tryBuildJiraIssueMap(\n storageXml: string,\n): Promise<Map<string, JiraIssueInfo>> {\n try {\n const keys = extractJiraKeys(storageXml);\n if (keys.length === 0) return new Map();\n\n const config = loadJiraConfig();\n const client = createJiraClient(config);\n const api = new JiraIssueApi(client);\n return await buildJiraIssueMap(keys, api);\n } catch {\n return new Map();\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { JiraSearchResponse } from '../types.js';\n\nexport class JiraSearchApi {\n constructor(private client: AxiosInstance) {}\n\n async searchByJql(\n jql: string,\n startAt = 0,\n maxResults = 50,\n fields?: string[],\n ): Promise<JiraSearchResponse> {\n const params: Record<string, unknown> = {\n jql,\n startAt,\n maxResults,\n };\n if (fields && fields.length > 0) {\n params.fields = fields.join(',');\n }\n const response = await this.client.get('rest/api/2/search', { params });\n return response.data;\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { GitlabMergeRequest, GitlabNote } from '../types.js';\n\nexport class GitlabMergeRequestApi {\n constructor(private client: AxiosInstance) {}\n\n async getMergeRequests(\n projectId: number,\n params?: { state?: string; scope?: string; labels?: string; perPage?: number },\n ): Promise<GitlabMergeRequest[]> {\n const response = await this.client.get(`/projects/${projectId}/merge_requests`, {\n params: {\n state: params?.state || 'opened',\n scope: params?.scope,\n labels: params?.labels,\n per_page: params?.perPage || 20,\n },\n });\n return response.data;\n }\n\n async getMergeRequest(projectId: number, mrIid: number): Promise<GitlabMergeRequest> {\n const response = await this.client.get(\n `/projects/${projectId}/merge_requests/${mrIid}`,\n );\n return response.data;\n }\n\n async getMergeRequestChanges(projectId: number, mrIid: number): Promise<GitlabMergeRequest> {\n const response = await this.client.get(\n `/projects/${projectId}/merge_requests/${mrIid}/changes`,\n );\n return response.data;\n }\n\n async createMergeRequest(\n projectId: number,\n data: {\n source_branch: string;\n target_branch: string;\n title: string;\n description?: string;\n assignee_id?: number;\n reviewer_ids?: number[];\n labels?: string;\n },\n ): Promise<GitlabMergeRequest> {\n const response = await this.client.post(`/projects/${projectId}/merge_requests`, data);\n return response.data;\n }\n\n async updateMergeRequest(\n projectId: number,\n mrIid: number,\n data: {\n title?: string;\n description?: string;\n assignee_id?: number;\n reviewer_ids?: number[];\n labels?: string;\n state_event?: 'close' | 'reopen';\n },\n ): Promise<GitlabMergeRequest> {\n const response = await this.client.put(\n `/projects/${projectId}/merge_requests/${mrIid}`,\n data,\n );\n return response.data;\n }\n\n async mergeMergeRequest(\n projectId: number,\n mrIid: number,\n params?: {\n merge_commit_message?: string;\n squash?: boolean;\n should_remove_source_branch?: boolean;\n },\n ): Promise<GitlabMergeRequest> {\n const response = await this.client.put(\n `/projects/${projectId}/merge_requests/${mrIid}/merge`,\n params,\n );\n return response.data;\n }\n\n async getMergeRequestNotes(projectId: number, mrIid: number): Promise<GitlabNote[]> {\n const response = await this.client.get(\n `/projects/${projectId}/merge_requests/${mrIid}/notes`,\n );\n return response.data;\n }\n\n async addMergeRequestNote(\n projectId: number,\n mrIid: number,\n body: string,\n ): Promise<GitlabNote> {\n const response = await this.client.post(\n `/projects/${projectId}/merge_requests/${mrIid}/notes`,\n { body },\n );\n return response.data;\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { GitlabPipeline, GitlabJob } from '../types.js';\n\nexport class GitlabPipelineApi {\n constructor(private client: AxiosInstance) {}\n\n async getPipelines(\n projectId: number,\n params?: { status?: string; ref?: string; perPage?: number },\n ): Promise<GitlabPipeline[]> {\n const response = await this.client.get(`/projects/${projectId}/pipelines`, {\n params: {\n status: params?.status,\n ref: params?.ref,\n per_page: params?.perPage || 20,\n },\n });\n return response.data;\n }\n\n async getPipeline(projectId: number, pipelineId: number): Promise<GitlabPipeline> {\n const response = await this.client.get(\n `/projects/${projectId}/pipelines/${pipelineId}`,\n );\n return response.data;\n }\n\n async getPipelineJobs(projectId: number, pipelineId: number): Promise<GitlabJob[]> {\n const response = await this.client.get(\n `/projects/${projectId}/pipelines/${pipelineId}/jobs`,\n );\n return response.data;\n }\n\n async getMergeRequestPipelines(\n projectId: number,\n mrIid: number,\n ): Promise<GitlabPipeline[]> {\n const response = await this.client.get(\n `/projects/${projectId}/merge_requests/${mrIid}/pipelines`,\n );\n return response.data;\n }\n}\n","import { AxiosInstance } from 'axios';\nimport { GitlabConfig } from '../../common/types.js';\nimport { createHttpClient } from '../../common/http-client.js';\n\nexport function createGitlabClient(config: GitlabConfig): AxiosInstance {\n const client = createHttpClient({\n ...config,\n baseUrl: `${config.baseUrl}/api/v4`,\n });\n\n // GitLab Self-hosted는 PRIVATE-TOKEN 헤더 사용\n client.defaults.headers.common['PRIVATE-TOKEN'] = config.auth.token!;\n\n return client;\n}\n"],"mappings":"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","names":["tokens"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logger
3
- } from "./chunk-IFYMZLQI.js";
3
+ } from "./chunk-6RIA6AJ3.js";
4
4
 
5
5
  // tools/confluence/utils/image-downloader.ts
6
6
  import fs from "fs";
@@ -122,4 +122,4 @@ var ImageDownloader = class {
122
122
  export {
123
123
  ImageDownloader
124
124
  };
125
- //# sourceMappingURL=chunk-JBDK5WP3.js.map
125
+ //# sourceMappingURL=chunk-R4YTIP7E.js.map
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  JiraCommentApi,
7
7
  JiraProjectApi,
8
8
  JiraTransitionApi
9
- } from "./chunk-JI2YUE7N.js";
9
+ } from "./chunk-4QW3PXPJ.js";
10
10
  import {
11
11
  ConfluenceContentApi,
12
12
  ConfluenceSearchApi,
@@ -25,10 +25,10 @@ import {
25
25
  loadGitlabConfig,
26
26
  loadJiraConfig,
27
27
  tryBuildJiraIssueMap
28
- } from "./chunk-5OB3KU5D.js";
28
+ } from "./chunk-JUM5ZG64.js";
29
29
  import {
30
30
  logger
31
- } from "./chunk-IFYMZLQI.js";
31
+ } from "./chunk-6RIA6AJ3.js";
32
32
 
33
33
  // tools/cli.ts
34
34
  import { Command } from "commander";
@@ -78,7 +78,7 @@ function registerConfluenceCommands(program2) {
78
78
  if (page.body?.storage?.value) {
79
79
  let imageUrlMap;
80
80
  if (options.downloadImages) {
81
- const { ImageDownloader } = await import("./image-downloader-VKPGS3TY.js");
81
+ const { ImageDownloader } = await import("./image-downloader-AKNR5YKJ.js");
82
82
  let baseDir = process.cwd();
83
83
  if (options.output) {
84
84
  baseDir = path.dirname(path.resolve(process.cwd(), options.output));
@@ -871,7 +871,7 @@ registerConfluenceCommands(program);
871
871
  registerJiraCommands(program);
872
872
  registerGitlabCommands(program);
873
873
  program.command("mcp").description("Run MCP Server").action(async () => {
874
- const { runServer } = await import("./server-VTZMTIEK.js");
874
+ const { runServer } = await import("./server-FKROUVDL.js");
875
875
  await runServer();
876
876
  });
877
877
  program.parse(args);
@@ -0,0 +1,8 @@
1
+ import {
2
+ ImageDownloader
3
+ } from "./chunk-R4YTIP7E.js";
4
+ import "./chunk-6RIA6AJ3.js";
5
+ export {
6
+ ImageDownloader
7
+ };
8
+ //# sourceMappingURL=image-downloader-AKNR5YKJ.js.map
package/dist/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runServer
4
- } from "./chunk-4DPCLTF4.js";
5
- import "./chunk-JI2YUE7N.js";
4
+ } from "./chunk-5IY42AV6.js";
5
+ import "./chunk-4QW3PXPJ.js";
6
6
  import {
7
7
  loadEnv
8
- } from "./chunk-5OB3KU5D.js";
9
- import "./chunk-IFYMZLQI.js";
8
+ } from "./chunk-JUM5ZG64.js";
9
+ import "./chunk-6RIA6AJ3.js";
10
10
 
11
11
  // tools/index.ts
12
12
  loadEnv();
@@ -0,0 +1,10 @@
1
+ import {
2
+ runServer
3
+ } from "./chunk-5IY42AV6.js";
4
+ import "./chunk-4QW3PXPJ.js";
5
+ import "./chunk-JUM5ZG64.js";
6
+ import "./chunk-6RIA6AJ3.js";
7
+ export {
8
+ runServer
9
+ };
10
+ //# sourceMappingURL=server-FKROUVDL.js.map
package/dist/tui/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ImageDownloader
4
- } from "../chunk-JBDK5WP3.js";
4
+ } from "../chunk-R4YTIP7E.js";
5
5
  import {
6
6
  ConfluenceContentApi,
7
7
  ConfluenceSearchApi,
@@ -19,8 +19,8 @@ import {
19
19
  loadGitlabConfig,
20
20
  loadJiraConfig,
21
21
  tryBuildJiraIssueMap
22
- } from "../chunk-5OB3KU5D.js";
23
- import "../chunk-IFYMZLQI.js";
22
+ } from "../chunk-JUM5ZG64.js";
23
+ import "../chunk-6RIA6AJ3.js";
24
24
 
25
25
  // tools/tui/index.tsx
26
26
  import { render } from "ink";
@@ -1206,7 +1206,7 @@ function FormScreen({ state, onRun, onBack, onSavePreset, accent = DEFAULT_ACCEN
1206
1206
  ] })
1207
1207
  ] }),
1208
1208
  /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
1209
- urlValue ? /* @__PURE__ */ jsx10(Text9, { color: T.fg, children: urlValue }) : /* @__PURE__ */ jsx10(Text9, { color: T.fgFaint, children: "https://confluence.tde.sktelecom.com/spaces/TDE/pages/123456/..." }),
1209
+ urlValue ? /* @__PURE__ */ jsx10(Text9, { color: T.fg, children: urlValue }) : /* @__PURE__ */ jsx10(Text9, { color: T.fgFaint, children: "https://confluence.tde.example.com/spaces/TDE/pages/123456/..." }),
1210
1210
  onUrlField && /* @__PURE__ */ jsx10(Text9, { backgroundColor: URL_FILL_COLOR, color: T.bg, children: " " })
1211
1211
  ] })
1212
1212
  ]