@hangox/pm-cli 1.1.6 → 1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/logger.ts","../src/client/api-client.ts","../src/utils/markdown-parser.ts","../src/services/issue-service.ts","../src/services/time-entry-service.ts","../src/services/user-service.ts","../src/utils/config.ts","../src/utils/url-parser.ts"],"sourcesContent":["export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nlet currentLevel: LogLevel = 'silent'\n\nconst levels: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n silent: 4,\n}\n\nfunction shouldLog(level: LogLevel): boolean {\n return levels[level] >= levels[currentLevel]\n}\n\nfunction formatMessage(level: string, message: string, data?: unknown): string {\n const timestamp = new Date().toISOString()\n const dataStr = data ? ` ${JSON.stringify(data)}` : ''\n return `[${timestamp}] [${level.toUpperCase()}] ${message}${dataStr}`\n}\n\nexport const logger = {\n setLevel(level: LogLevel): void {\n currentLevel = level\n },\n\n getLevel(): LogLevel {\n return currentLevel\n },\n\n debug(message: string, data?: unknown): void {\n if (shouldLog('debug')) {\n console.error(formatMessage('debug', message, data))\n }\n },\n\n info(message: string, data?: unknown): void {\n if (shouldLog('info')) {\n console.error(formatMessage('info', message, data))\n }\n },\n\n warn(message: string, data?: unknown): void {\n if (shouldLog('warn')) {\n console.error(formatMessage('warn', message, data))\n }\n },\n\n error(message: string, data?: unknown): void {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, data))\n }\n },\n}\n\n/**\n * 设置日志级别(供第三方调用时按需开启)\n */\nexport function setLogLevel(level: LogLevel): void {\n currentLevel = level\n}\n\nexport default logger\n","import type { ApiResponse } from '../models/types.js'\nimport logger from '../utils/logger.js'\n\n/**\n * 网易易协作 API 客户端\n */\nexport class ApiClient {\n private static readonly BASE_URL = 'http://redmineapi.nie.netease.com/api'\n private static readonly DEFAULT_TIMEOUT = 30000 // 30秒\n\n /**\n * 发送 GET 请求\n */\n async get<T>(endpoint: string, params: Record<string, unknown> = {}): Promise<ApiResponse<T>> {\n try {\n logger.debug(`API GET 请求: ${endpoint}`, { params })\n\n const queryString = this.buildQueryString(params)\n const url = `${ApiClient.BASE_URL}/${endpoint}${queryString ? `?${queryString}` : ''}`\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), ApiClient.DEFAULT_TIMEOUT)\n\n try {\n const response = await fetch(url, {\n method: 'GET',\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n logger.error(`HTTP 请求失败: ${response.status} ${response.statusText}`)\n return {\n success: false,\n message: `HTTP请求失败,状态码:${response.status}`,\n }\n }\n\n const data = (await response.json()) as ApiResponse<T>\n logger.debug('API 响应:', data)\n return data\n } finally {\n clearTimeout(timeoutId)\n }\n } catch (error) {\n return this.handleError(error)\n }\n }\n\n /**\n * 发送 POST 请求\n */\n async post<T>(endpoint: string, params: Record<string, unknown> = {}): Promise<ApiResponse<T>> {\n try {\n logger.debug(`API POST 请求: ${endpoint}`, { params })\n\n const url = `${ApiClient.BASE_URL}/${endpoint}`\n const formData = this.buildFormData(params)\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), ApiClient.DEFAULT_TIMEOUT)\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: formData,\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n logger.error(`HTTP 请求失败: ${response.status} ${response.statusText}`)\n return {\n success: false,\n message: `HTTP请求失败,状态码:${response.status}`,\n }\n }\n\n const data = (await response.json()) as ApiResponse<T>\n logger.debug('API 响应:', data)\n return data\n } finally {\n clearTimeout(timeoutId)\n }\n } catch (error) {\n return this.handleError(error)\n }\n }\n\n /**\n * 处理错误\n */\n private handleError<T>(error: unknown): ApiResponse<T> {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n logger.error('请求超时')\n return {\n success: false,\n message: '请求超时',\n }\n }\n logger.error('请求异常:', error)\n return {\n success: false,\n message: `请求异常:${error.message}`,\n }\n }\n return {\n success: false,\n message: '未知错误',\n }\n }\n\n /**\n * 构建查询字符串\n */\n private buildQueryString(params: Record<string, unknown>): string {\n return Object.entries(params)\n .filter(([, value]) => value !== null && value !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)\n .join('&')\n }\n\n /**\n * 构建表单数据\n */\n private buildFormData(params: Record<string, unknown>): string {\n return Object.entries(params)\n .filter(([, value]) => value !== null && value !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)\n .join('&')\n }\n}\n\n// 导出单例\nexport const apiClient = new ApiClient()\n","/**\n * Markdown 任务列表解析器\n * 参考实现: /Volumes/SourceCode/Users/hangox/CC/code-test-new/pm_netease/.../MarkdownParser.kt\n *\n * 支持的格式:\n * - `* 任务名 (工时d)` - 基础格式,d 表示天\n * - `* 任务名 (0.5d)` - 支持中文括号\n * - `* 任务名 (0.5)` - 纯数字默认为天\n * - `- 任务名 (工时)` - 支持 - 开头\n * - 嵌套结构(2空格缩进表示一级)\n */\n\n/**\n * 任务节点接口\n */\nexport interface TaskNode {\n subject: string\n estimatedHours?: number\n children: TaskNode[]\n}\n\n/**\n * 解析单行的结果\n */\ninterface ParsedLine {\n level: number\n subject: string\n estimatedHours?: number\n}\n\n/**\n * 从文本中提取工时并转换为天数\n * 支持格式:(0.5d)、(1d)、(0.5)、(0.5d,其他内容)等\n *\n * @param text 包含工时信息的文本\n * @returns 工时(天数),如果没有找到则返回 undefined\n */\nexport function extractHours(text: string): number | undefined {\n // 优先匹配带 'd' 的天数格式,如 (0.5d) 或 (1d,其他内容)\n // 支持中英文括号\n const daysRegex = /[((][^))]*?([\\d.]+)d[^))]*[))]/\n const daysMatch = text.match(daysRegex)\n if (daysMatch) {\n const value = parseFloat(daysMatch[1])\n return isNaN(value) ? undefined : value\n }\n\n // 匹配中文天数格式,如 (3天) 或 (1.5天)\n const chineseDaysRegex = /[((]([\\d.]+)天[))]/\n const chineseDaysMatch = text.match(chineseDaysRegex)\n if (chineseDaysMatch) {\n const value = parseFloat(chineseDaysMatch[1])\n return isNaN(value) ? undefined : value\n }\n\n // 如果没有找到天数格式,尝试匹配纯数字格式,如 (0.5)\n const hoursRegex = /[((]([\\d.]+)[))]/\n const hoursMatch = text.match(hoursRegex)\n if (hoursMatch) {\n const value = parseFloat(hoursMatch[1])\n return isNaN(value) ? undefined : value\n }\n\n return undefined\n}\n\n/**\n * 解析单行 Markdown,提取缩进级别、标题和工时\n *\n * @param line Markdown 行\n * @returns 解析结果\n */\nexport function parseMarkdownLine(line: string): ParsedLine {\n // 计算缩进级别(前导空格数除以2)\n const leadingSpaces = line.match(/^(\\s*)/)?.[1].length || 0\n const level = Math.floor(leadingSpaces / 2)\n\n // 移除前导空格和列表标记(* 或 -)\n const cleanLine = line\n .trimStart()\n .replace(/^[*-]\\s*/, '')\n .trim()\n\n // 提取工时信息\n const estimatedHours = extractHours(cleanLine)\n\n // 提取标题(移除括号内的所有内容)\n // 支持中英文括号\n const subject = cleanLine\n .replace(/\\s*[((][^))]*[))]/g, '')\n .trim()\n\n return { level, subject, estimatedHours }\n}\n\n/**\n * 解析 Markdown 格式文本为任务节点树\n *\n * @param markdown Markdown 文本\n * @returns 任务节点数组(根节点列表)\n */\nexport function parseMarkdownToNodes(markdown: string): TaskNode[] {\n const lines = markdown\n .trim()\n .split('\\n')\n .filter((line) => line.trim().length > 0)\n // 只处理以 * 或 - 开头的行(去除前导空格后)\n .filter((line) => /^\\s*[*-]/.test(line))\n\n const rootNodes: TaskNode[] = []\n // 节点栈:[缩进级别, 节点]\n const nodeStack: Array<{ level: number; node: TaskNode }> = []\n\n for (const line of lines) {\n const { level, subject, estimatedHours } = parseMarkdownLine(line)\n\n // 跳过空标题\n if (!subject) {\n continue\n }\n\n const node: TaskNode = {\n subject,\n estimatedHours,\n children: [],\n }\n\n // 根据缩进级别调整节点栈\n while (nodeStack.length > 0 && nodeStack[nodeStack.length - 1].level >= level) {\n nodeStack.pop()\n }\n\n if (nodeStack.length === 0) {\n // 根节点\n rootNodes.push(node)\n } else {\n // 子节点\n nodeStack[nodeStack.length - 1].node.children.push(node)\n }\n\n nodeStack.push({ level, node })\n }\n\n return rootNodes\n}\n\n/**\n * 统计任务树中的任务总数\n *\n * @param nodes 任务节点数组\n * @returns 任务总数\n */\nexport function countTasks(nodes: TaskNode[]): number {\n let count = 0\n for (const node of nodes) {\n count += 1\n count += countTasks(node.children)\n }\n return count\n}\n\n/**\n * 计算任务树中的总工时\n *\n * @param nodes 任务节点数组\n * @returns 总工时(天)\n */\nexport function sumEstimatedHours(nodes: TaskNode[]): number {\n let total = 0\n for (const node of nodes) {\n if (node.estimatedHours) {\n total += node.estimatedHours\n }\n total += sumEstimatedHours(node.children)\n }\n return total\n}\n\n/**\n * 将任务树转换为树形结构字符串(用于展示)\n *\n * @param nodes 任务节点数组\n * @param prefix 前缀(用于递归)\n * @param isLast 是否是最后一个节点(用于递归)\n * @param showAssignee 是否显示指派人\n * @param assigneeName 指派人名称\n * @returns 树形结构字符串\n */\nexport function formatTaskTree(\n nodes: TaskNode[],\n prefix: string = '',\n _isLast: boolean = true,\n showAssignee: boolean = false,\n assigneeName?: string\n): string {\n const lines: string[] = []\n\n nodes.forEach((node, index) => {\n const isLastNode = index === nodes.length - 1\n const connector = prefix === '' ? '* ' : isLastNode ? '└─ ' : '├─ '\n const hoursStr = node.estimatedHours !== undefined ? ` (${node.estimatedHours}d)` : ''\n const assigneeStr = showAssignee && assigneeName ? ` → ${assigneeName}` : ''\n\n lines.push(`${prefix}${connector}${node.subject}${hoursStr}${assigneeStr}`)\n\n if (node.children.length > 0) {\n const childPrefix = prefix === '' ? ' ' : prefix + (isLastNode ? ' ' : '│ ')\n const childLines = formatTaskTree(node.children, childPrefix, isLastNode, showAssignee, assigneeName)\n lines.push(childLines)\n }\n })\n\n return lines.join('\\n')\n}\n\n/**\n * 将任务树扁平化为数组(用于遍历创建)\n *\n * @param nodes 任务节点数组\n * @param parentPath 父路径(用于调试)\n * @returns 扁平化的任务数组,包含层级信息\n */\nexport function flattenTaskTree(\n nodes: TaskNode[],\n parentPath: string = ''\n): Array<{ node: TaskNode; path: string; depth: number }> {\n const result: Array<{ node: TaskNode; path: string; depth: number }> = []\n\n for (const node of nodes) {\n const currentPath = parentPath ? `${parentPath} > ${node.subject}` : node.subject\n const depth = parentPath ? parentPath.split(' > ').length : 0\n result.push({ node, path: currentPath, depth })\n\n if (node.children.length > 0) {\n result.push(...flattenTaskTree(node.children, currentPath))\n }\n }\n\n return result\n}\n","import { apiClient } from '../client/api-client.js'\nimport type { ApiResponse, Issue, IssueWithChildren, SyncResult, BatchCreateResult } from '../models/types.js'\nimport logger from '../utils/logger.js'\nimport { parseMarkdownToNodes, countTasks, sumEstimatedHours, type TaskNode } from '../utils/markdown-parser.js'\n\n/**\n * 延迟函数,避免 API 限流\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * 问题服务\n */\nexport class IssueService {\n /**\n * 获取问题详情\n */\n async getIssue(\n token: string,\n host: string,\n project: string,\n issueId: number,\n includeChildren?: boolean,\n includeRelations?: boolean\n ): Promise<ApiResponse<Issue>> {\n const params: Record<string, unknown> = {\n token,\n host,\n issue_id: issueId,\n }\n\n // project 参数可选,如果提供则传递\n if (project) params.project = project\n\n // 构建 include 参数,API 需要 JSON 格式的数组\n const includes: string[] = []\n if (includeChildren) includes.push('children')\n if (includeRelations) includes.push('relations')\n if (includes.length > 0) {\n params.include = JSON.stringify(includes)\n }\n\n logger.info('获取问题详情', { host, project, issueId })\n // 使用 GET 请求,接口名是 'issue' 不是 'get_issue'\n return await apiClient.get<Issue>('issue', params)\n }\n\n /**\n * 创建问题\n */\n async createIssue(params: Record<string, unknown>): Promise<ApiResponse<Issue>> {\n logger.info('创建问题', { params })\n return await apiClient.post<Issue>('create_issue', params)\n }\n\n /**\n * 更新问题\n */\n async updateIssue(params: Record<string, unknown>): Promise<ApiResponse<Issue>> {\n logger.info('更新问题', { params })\n return await apiClient.post<Issue>('update_issue', params)\n }\n\n /**\n * 获取问题附件\n */\n async getIssueAttachments(\n token: string,\n host: string,\n project: string,\n issueId: number\n ): Promise<ApiResponse<unknown>> {\n logger.info('获取问题附件', { host, project, issueId })\n return await apiClient.get('get_issue_attachments', {\n token,\n host,\n project,\n issue_id: issueId,\n })\n }\n\n /**\n * 获取问题字段选项\n */\n async getIssueFieldOptions(\n token: string,\n host: string,\n project: string\n ): Promise<ApiResponse<unknown>> {\n logger.info('获取问题字段选项', { host, project })\n return await apiClient.post('get_issue_field_options', { token, host, project })\n }\n\n /**\n * 自定义查询\n */\n async customQuery(\n token: string,\n host: string,\n project: string,\n queryId: number,\n limit?: number,\n offset?: number\n ): Promise<ApiResponse<Issue[]>> {\n const params: Record<string, unknown> = {\n token,\n host,\n project,\n query_id: queryId,\n }\n\n if (limit !== undefined) params.limit = limit\n if (offset !== undefined) params.offset = offset\n\n logger.info('自定义查询', { host, project, queryId })\n return await apiClient.post<Issue[]>('custom_query', params)\n }\n\n /**\n * V6 过滤器查询\n */\n async filterQueryV6(\n token: string,\n host: string,\n project: string,\n mode: 'normal' | 'simple' | 'advanced',\n filterParams: Record<string, unknown>\n ): Promise<ApiResponse<Issue[]>> {\n const params: Record<string, unknown> = {\n token,\n host,\n project,\n mode,\n ...filterParams,\n }\n\n logger.info('V6 过滤器查询', { host, project, mode })\n return await apiClient.post<Issue[]>('filter_query_v6', params)\n }\n\n /**\n * 递归获取问题及其子单(包含完整详情)\n * @param depth 递归深度,默认 10\n * @param currentLevel 当前层级(内部使用)\n *\n * 实现逻辑:\n * 1. 调用 getIssue(includeChildren=true) 获取当前问题详情\n * 2. API 返回的 children 字段只包含简略信息(status 是字符串,缺少 assigned_to 等)\n * 3. 从 children 中提取子单 ID,递归调用 getIssue 获取每个子单的完整详情\n * 4. 用完整详情替换原始的简略 children\n */\n async getIssueWithChildren(\n token: string,\n host: string,\n project: string,\n issueId: number,\n depth: number = 10,\n currentLevel: number = 0\n ): Promise<ApiResponse<IssueWithChildren>> {\n logger.info('递归获取问题详情', { host, project, issueId, depth, currentLevel })\n\n // 获取当前问题详情(包含简略的 children 信息)\n const issueResult = await this.getIssue(token, host, project, issueId, true)\n if (!issueResult.success || !issueResult.data) {\n return issueResult as ApiResponse<IssueWithChildren>\n }\n\n const issue = issueResult.data as IssueWithChildren\n issue.level = currentLevel\n\n // 如果已达到最大深度,保留 API 返回的简略 children 信息\n if (currentLevel >= depth) {\n return { success: true, data: issue }\n }\n\n // 从 API 返回的 children 中提取直接子单 ID\n // API 返回的 children 是嵌套的,但我们只取直接子单(第一层)\n const rawChildren = (issue as unknown as { children?: Array<{ id: number }> }).children\n if (!rawChildren || rawChildren.length === 0) {\n return { success: true, data: issue }\n }\n\n // 提取直接子单 ID(只取有 id 的)\n const directChildrenIds = rawChildren\n .filter(child => child.id)\n .map(child => child.id)\n\n if (directChildrenIds.length === 0) {\n return { success: true, data: issue }\n }\n\n logger.info('获取子单完整详情', { parentId: issueId, childCount: directChildrenIds.length, level: currentLevel })\n\n // 递归获取每个子单的完整详情\n const childrenPromises = directChildrenIds.map(childId =>\n this.getIssueWithChildren(token, host, project, childId, depth, currentLevel + 1)\n )\n\n const childrenResults = await Promise.all(childrenPromises)\n\n // 用完整详情替换原始的简略 children\n issue.children = childrenResults\n .filter(r => r.success && r.data)\n .map(r => r.data!)\n\n return { success: true, data: issue }\n }\n\n /**\n * 获取直接子单的 ID 列表\n */\n async getDirectChildren(\n token: string,\n host: string,\n project: string,\n parentId: number\n ): Promise<ApiResponse<number[]>> {\n // 使用过滤器查询 parent_id = parentId 的问题\n const filters: Record<string, { operator: string; values: string[] }> = {\n parent_id: { operator: '=', values: [parentId.toString()] },\n }\n\n const params: Record<string, unknown> = {\n token,\n host,\n project,\n filter_mode: 'simple',\n filters: JSON.stringify(filters),\n c: JSON.stringify(['id']),\n per_page: 200,\n }\n\n logger.info('查询直接子单', { host, project, parentId })\n const result = await apiClient.get<unknown>('filter_query_v6', params)\n\n if (result.success && result.data) {\n const data = result.data as { data?: { list?: Array<{ id: number }> } }\n if (data.data?.list) {\n const ids = data.data.list.map(item => item.id)\n return { success: true, data: ids }\n }\n }\n\n return { success: true, data: [] }\n }\n\n /**\n * 查询子任务(按根任务和负责人过滤)\n * 使用两步查询:先过滤获取 ID 列表,再批量获取详情\n */\n async queryChildren(\n token: string,\n host: string,\n project: string,\n rootId: number,\n assignedToId?: number,\n perPage: number = 100\n ): Promise<ApiResponse<unknown>> {\n // 构建过滤器参数\n const filters: Record<string, { operator: string; values: string[] }> = {\n root_id: { operator: '=', values: [rootId.toString()] },\n }\n\n if (assignedToId) {\n filters.assigned_to_id = { operator: '=', values: [assignedToId.toString()] }\n }\n\n const params: Record<string, unknown> = {\n token,\n host,\n project,\n filter_mode: 'simple',\n filters: JSON.stringify(filters),\n c: JSON.stringify(['id', 'subject', 'status', 'tracker', 'estimated_hours', 'done_ratio', 'assigned_to', 'parent']),\n per_page: perPage,\n }\n\n logger.info('查询子任务', { host, project, rootId, assignedToId })\n const result = await apiClient.get<unknown>('filter_query_v6', params)\n\n // 如果成功获取到 ID 列表,批量获取详情\n if (result.success && result.data) {\n const data = result.data as { data?: { list?: Array<{ id: number }> } }\n if (data.data?.list && data.data.list.length > 0) {\n const issueIds = data.data.list.map((item) => item.id)\n const detailedIssues = await Promise.all(\n issueIds.map(async (id) => {\n const issueResult = await this.getIssue(token, host, project, id)\n if (issueResult.success && issueResult.data) {\n return {\n id: issueResult.data.id,\n subject: issueResult.data.subject,\n status: issueResult.data.status?.name,\n tracker: issueResult.data.tracker?.name,\n estimated_hours: issueResult.data.estimated_hours,\n done_ratio: issueResult.data.done_ratio,\n assigned_to: issueResult.data.assigned_to?.name,\n parent_id: (issueResult.data as unknown as { parent_id?: number }).parent_id,\n }\n }\n return null\n })\n )\n return {\n success: true,\n data: {\n total: detailedIssues.filter(Boolean).length,\n issues: detailedIssues.filter(Boolean),\n },\n }\n }\n }\n\n return result\n }\n\n /**\n * 批量获取多个问题详情\n * @param issueIds 问题 ID 数组\n * @param options 可选参数\n * @returns 返回所有问题的详情数组(包含成功和失败的结果)\n */\n async getMultipleIssues(\n token: string,\n host: string,\n project: string,\n issueIds: number[],\n options?: {\n includeChildren?: boolean\n includeRelations?: boolean\n depth?: number\n }\n ): Promise<ApiResponse<Array<{ id: number; success: boolean; data?: Issue | IssueWithChildren; error?: string }>>> {\n const { includeChildren = false, includeRelations = false, depth = 0 } = options || {}\n\n logger.info('批量获取问题详情', { host, project, count: issueIds.length, issueIds })\n\n // 并行请求所有问题\n const results = await Promise.all(\n issueIds.map(async (issueId) => {\n try {\n let result: ApiResponse<Issue | IssueWithChildren>\n\n if (includeChildren && depth > 0) {\n // 递归获取子单\n result = await this.getIssueWithChildren(token, host, project, issueId, depth)\n } else {\n // 普通获取\n result = await this.getIssue(token, host, project, issueId, includeChildren, includeRelations)\n }\n\n if (result.success && result.data) {\n return { id: issueId, success: true, data: result.data }\n } else {\n return {\n id: issueId,\n success: false,\n error: result.post_error_msg || result.api_error_msg || result.message || result.msg || '获取失败',\n }\n }\n } catch (error) {\n return {\n id: issueId,\n success: false,\n error: error instanceof Error ? error.message : '未知错误',\n }\n }\n })\n )\n\n const successCount = results.filter((r) => r.success).length\n const failCount = results.filter((r) => !r.success).length\n\n logger.info('批量获取完成', { total: issueIds.length, success: successCount, fail: failCount })\n\n return {\n success: true,\n data: results,\n }\n }\n /**\n * 同步子单:从源父单复制子单到目标父单\n * @param sourceParentId 源父单 ID\n * @param targetParentId 目标父单 ID\n * @param assignedToMail 新子单的指派人邮箱\n * @param options 选项\n */\n async syncChildIssues(\n token: string,\n host: string,\n project: string,\n sourceParentId: number,\n targetParentId: number,\n assignedToMail: string,\n options?: {\n dryRun?: boolean\n depth?: number\n skipExisting?: boolean\n }\n ): Promise<ApiResponse<SyncResult>> {\n const { dryRun = false, depth = 10, skipExisting = true } = options || {}\n\n logger.info('开始同步子单', {\n sourceParentId,\n targetParentId,\n assignedToMail,\n dryRun,\n depth,\n skipExisting,\n })\n\n // 初始化结果\n const result: SyncResult = {\n totalTasks: 0,\n totalCreated: 0,\n totalSkipped: 0,\n totalFailed: 0,\n created: [],\n skipped: [],\n failed: [],\n }\n\n try {\n // 1. 获取源父单的子单树形结构\n logger.info('正在获取源父单的子单树形结构...')\n const sourceResult = await this.getIssueWithChildren(token, host, project, sourceParentId, depth)\n if (!sourceResult.success || !sourceResult.data) {\n return {\n success: false,\n message: `获取源父单 #${sourceParentId} 失败: ${sourceResult.message || '未知错误'}`,\n }\n }\n const sourceIssue = sourceResult.data\n\n // 计算源单的总子任务数\n result.totalTasks = this.countAllChildren(sourceIssue)\n logger.info(`源父单共有 ${result.totalTasks} 个子任务`)\n\n // 2. 获取目标父单的详细信息(作为创建子单的模板)\n logger.info('正在获取目标父单信息...')\n const targetResult = await this.getIssue(token, host, project, targetParentId, false, false)\n if (!targetResult.success || !targetResult.data) {\n return {\n success: false,\n message: `获取目标父单 #${targetParentId} 失败: ${targetResult.message || '未知错误'}`,\n }\n }\n const targetParentInfo = targetResult.data\n\n // 3. 获取目标父单已有的子单名称(用于去重)\n let existingTaskNames: Set<string> = new Set()\n if (skipExisting) {\n logger.info('正在获取目标父单已有的子单...')\n const existingResult = await this.getIssueWithChildren(token, host, project, targetParentId, depth)\n if (existingResult.success && existingResult.data) {\n existingTaskNames = this.collectAllTaskNames(existingResult.data)\n logger.info(`目标父单已有 ${existingTaskNames.size} 个子单`)\n }\n }\n\n // 4. 递归同步子单\n logger.info('开始递归同步子单...')\n await this.syncChildrenRecursive(\n token,\n host,\n project,\n sourceIssue,\n targetParentId,\n targetParentInfo,\n existingTaskNames,\n assignedToMail,\n dryRun,\n result\n )\n\n logger.info('同步完成', {\n totalCreated: result.totalCreated,\n totalSkipped: result.totalSkipped,\n totalFailed: result.totalFailed,\n })\n\n return { success: true, data: result }\n } catch (error) {\n logger.error('同步子单时发生错误', error)\n return {\n success: false,\n message: `同步子单时发生错误: ${error instanceof Error ? error.message : '未知错误'}`,\n }\n }\n }\n\n /**\n * 计算所有子任务数量(递归)\n */\n private countAllChildren(issue: IssueWithChildren): number {\n let count = 0\n for (const child of issue.children || []) {\n count++ // 当前子节点\n count += this.countAllChildren(child) // 递归计算子节点的子节点\n }\n return count\n }\n\n /**\n * 收集所有任务名称(递归)\n */\n private collectAllTaskNames(issue: IssueWithChildren): Set<string> {\n const names = new Set<string>()\n\n // 添加当前节点的名称\n if (issue.subject?.trim()) {\n names.add(issue.subject.trim())\n }\n\n // 递归添加所有子节点的名称\n for (const child of issue.children || []) {\n const childNames = this.collectAllTaskNames(child)\n childNames.forEach((name) => names.add(name))\n }\n\n return names\n }\n\n /**\n * 递归同步子单\n */\n private async syncChildrenRecursive(\n token: string,\n host: string,\n project: string,\n sourceNode: IssueWithChildren,\n targetParentId: number,\n targetParentInfo: Issue,\n existingTaskNames: Set<string>,\n assignedToMail: string,\n dryRun: boolean,\n result: SyncResult\n ): Promise<void> {\n // 遍历源节点的所有子节点\n for (const child of sourceNode.children || []) {\n const taskName = child.subject?.trim() || '未命名任务'\n const sourceId = child.id\n\n // 检查是否已存在同名任务\n if (existingTaskNames.has(taskName)) {\n logger.info(`⏭️ 跳过已存在的任务: ${taskName}`)\n result.totalSkipped++\n result.skipped.push({\n sourceId,\n subject: taskName,\n reason: '目标父单下已存在同名任务',\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalSkipped + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ⏭️ 跳过: #${sourceId} ${taskName} (已存在)`)\n continue\n }\n\n // 准备创建参数\n const isLeafNode = !child.children || child.children.length === 0\n const createParams: Record<string, unknown> = {\n token,\n host,\n project,\n parent_issue_id: targetParentId,\n subject: taskName,\n // 继承自目标父单\n tracker: (targetParentInfo as unknown as { tracker?: { name?: string } }).tracker?.name,\n status: '新建',\n // 覆盖指派人为\"我\"\n assigned_to_mail: assignedToMail,\n // 只有叶子节点才设置工时\n estimated_hours: isLeafNode ? child.estimated_hours : undefined,\n // 保留原任务的优先级\n priority_id: child.priority?.id,\n }\n\n // 继承目标父单的版本\n const targetWithVersion = targetParentInfo as unknown as { fixed_version?: { name?: string } }\n if (targetWithVersion.fixed_version?.name) {\n createParams.version = targetWithVersion.fixed_version.name\n }\n\n // 继承目标父单的自定义字段\n const targetWithCustomFields = targetParentInfo as unknown as {\n custom_fields?: Array<{\n id: number\n name: string\n value: unknown\n identify?: string\n field_format?: string\n multiple?: boolean\n }>\n }\n if (targetWithCustomFields.custom_fields && targetWithCustomFields.custom_fields.length > 0) {\n const customFieldMap: Record<number, unknown> = {}\n const followsMails: string[] = []\n\n for (const field of targetWithCustomFields.custom_fields) {\n if (field.value !== null && field.value !== undefined && field.value !== '') {\n // 特殊处理\"跟进QA\"字段(使用 follows 参数)\n if (field.identify === 'IssuesQCFollow') {\n const followsValue = field.value as Array<{ user?: { mail?: string } }>\n if (Array.isArray(followsValue)) {\n for (const item of followsValue) {\n if (item.user?.mail) {\n followsMails.push(item.user.mail)\n }\n }\n }\n } else if (field.field_format === 'user') {\n // 用户类型字段(如跟进DM):提取用户 ID(API 需要 ID,不是邮箱)\n if (field.multiple && Array.isArray(field.value)) {\n // 多用户字段:提取所有用户 ID\n const userIds: number[] = []\n for (const item of field.value as Array<{ user?: { id?: number } }>) {\n if (item.user?.id) {\n userIds.push(item.user.id)\n }\n }\n if (userIds.length > 0) {\n customFieldMap[field.id] = userIds.join(',')\n }\n } else {\n // 单用户字段:提取用户 ID\n const userValue = field.value as { user?: { id?: number } }\n if (userValue.user?.id) {\n customFieldMap[field.id] = userValue.user.id\n }\n }\n } else {\n customFieldMap[field.id] = field.value\n }\n }\n }\n\n if (Object.keys(customFieldMap).length > 0) {\n createParams.custom_field = JSON.stringify(customFieldMap)\n }\n if (followsMails.length > 0) {\n createParams.follows = followsMails\n }\n }\n\n logger.info(`正在创建子任务: ${taskName}`, { sourceId, targetParentId, isLeafNode })\n\n if (dryRun) {\n // 模拟模式:不实际创建,只记录\n logger.info(`[模拟] 将创建子任务: ${taskName}`)\n result.totalCreated++\n result.created.push({\n sourceId,\n newId: 0, // 模拟模式没有真实 ID\n subject: taskName,\n parentId: targetParentId,\n })\n\n // 递归处理子任务(模拟模式下 parentId 使用 0)\n if (child.children && child.children.length > 0) {\n await this.syncChildrenRecursive(\n token,\n host,\n project,\n child,\n 0, // 模拟模式下没有真实的新 ID\n targetParentInfo,\n existingTaskNames,\n assignedToMail,\n dryRun,\n result\n )\n }\n } else {\n // 实际创建模式\n try {\n const createResult = await this.createIssue(createParams)\n\n if (createResult.success && createResult.data) {\n const newId = createResult.data.id\n logger.info(`✅ 成功创建子任务: ID=${newId}, 标题=${taskName}`)\n result.totalCreated++\n result.created.push({\n sourceId,\n newId,\n subject: taskName,\n parentId: targetParentId,\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalSkipped + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ✅ 同步成功: #${newId} ← #${sourceId} ${taskName}`)\n\n // 递归处理子任务\n if (child.children && child.children.length > 0) {\n logger.info(`📁 发现子任务 ${newId} 有 ${child.children.length} 个子任务,继续递归创建...`)\n await this.syncChildrenRecursive(\n token,\n host,\n project,\n child,\n newId,\n targetParentInfo,\n existingTaskNames,\n assignedToMail,\n dryRun,\n result\n )\n }\n } else {\n const errorMsg = createResult.post_error_msg || createResult.api_error_msg || createResult.message || '创建失败'\n logger.error(`❌ 创建子任务失败: ${taskName}`, errorMsg)\n result.totalFailed++\n result.failed.push({\n sourceId,\n subject: taskName,\n error: errorMsg,\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalSkipped + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ❌ 同步失败: #${sourceId} ${taskName} - ${errorMsg}`)\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : '未知错误'\n logger.error(`❌ 创建子任务时发生错误: ${taskName}`, error)\n result.totalFailed++\n result.failed.push({\n sourceId,\n subject: taskName,\n error: errorMsg,\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalSkipped + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ❌ 同步失败: #${sourceId} ${taskName} - ${errorMsg}`)\n }\n\n // 添加延迟避免 API 限流\n await sleep(500)\n }\n }\n }\n\n /**\n * 从 Markdown 批量创建子单\n * @param parentId 父单 ID\n * @param markdown Markdown 文本\n * @param assignedToMail 指派人邮箱\n * @param options 选项\n */\n async batchCreateFromMarkdown(\n token: string,\n host: string,\n project: string,\n parentId: number,\n markdown: string,\n assignedToMail: string,\n options?: {\n dryRun?: boolean\n interval?: number\n extraCustomFields?: Record<number, string>\n }\n ): Promise<ApiResponse<BatchCreateResult>> {\n const { dryRun = false, interval = 5000, extraCustomFields = {} } = options || {}\n\n logger.info('开始批量创建子单', {\n parentId,\n assignedToMail,\n dryRun,\n interval,\n })\n\n // 初始化结果\n const result: BatchCreateResult = {\n totalCreated: 0,\n totalFailed: 0,\n totalTasks: 0,\n totalEstimatedHours: 0,\n created: [],\n failed: [],\n }\n\n try {\n // 1. 解析 Markdown\n const taskNodes = parseMarkdownToNodes(markdown)\n if (taskNodes.length === 0) {\n return {\n success: false,\n message: '没有解析到任何任务,请检查 Markdown 格式',\n }\n }\n\n result.totalTasks = countTasks(taskNodes)\n result.totalEstimatedHours = sumEstimatedHours(taskNodes)\n\n logger.info('Markdown 解析完成', {\n totalTasks: result.totalTasks,\n totalEstimatedHours: result.totalEstimatedHours,\n })\n\n // 2. 获取父单信息(作为创建子单的模板)\n logger.info('正在获取父单信息...')\n const parentResult = await this.getIssue(token, host, project, parentId, false, false)\n if (!parentResult.success || !parentResult.data) {\n return {\n success: false,\n message: `获取父单 #${parentId} 失败: ${parentResult.message || '未知错误'}`,\n }\n }\n const parentInfo = parentResult.data\n\n // 3. 递归创建子单\n logger.info('开始递归创建子单...')\n await this.batchCreateRecursive(\n token,\n host,\n project,\n taskNodes,\n parentId,\n parentInfo,\n assignedToMail,\n dryRun,\n interval,\n result,\n extraCustomFields\n )\n\n logger.info('批量创建完成', {\n totalCreated: result.totalCreated,\n totalFailed: result.totalFailed,\n })\n\n return { success: true, data: result }\n } catch (error) {\n logger.error('批量创建子单时发生错误', error)\n return {\n success: false,\n message: `批量创建子单时发生错误: ${error instanceof Error ? error.message : '未知错误'}`,\n }\n }\n }\n\n /**\n * 递归批量创建子单\n */\n private async batchCreateRecursive(\n token: string,\n host: string,\n project: string,\n taskNodes: TaskNode[],\n targetParentId: number,\n parentInfo: Issue,\n assignedToMail: string,\n dryRun: boolean,\n interval: number,\n result: BatchCreateResult,\n extraCustomFields: Record<number, string> = {}\n ): Promise<void> {\n for (const node of taskNodes) {\n const taskName = node.subject\n const isLeafNode = node.children.length === 0\n\n // 准备创建参数\n const createParams: Record<string, unknown> = {\n token,\n host,\n project,\n parent_issue_id: targetParentId,\n subject: taskName,\n // 继承自父单\n tracker: (parentInfo as unknown as { tracker?: { name?: string } }).tracker?.name,\n status: '新建',\n // 指派人\n assigned_to_mail: assignedToMail,\n // 只有叶子节点才设置工时\n estimated_hours: isLeafNode ? node.estimatedHours : undefined,\n }\n\n // 继承父单的版本\n const parentWithVersion = parentInfo as unknown as { fixed_version?: { name?: string } }\n if (parentWithVersion.fixed_version?.name) {\n createParams.version = parentWithVersion.fixed_version.name\n }\n\n // 继承父单的自定义字段\n const parentWithCustomFields = parentInfo as unknown as {\n custom_fields?: Array<{\n id: number\n name: string\n value: unknown\n identify?: string\n field_format?: string\n multiple?: boolean\n }>\n }\n if (parentWithCustomFields.custom_fields && parentWithCustomFields.custom_fields.length > 0) {\n const customFieldMap: Record<number, unknown> = {}\n const followsMails: string[] = []\n\n for (const field of parentWithCustomFields.custom_fields) {\n if (field.value !== null && field.value !== undefined && field.value !== '') {\n // 特殊处理\"跟进QA\"字段(使用 follows 参数)\n if (field.identify === 'IssuesQCFollow') {\n const followsValue = field.value as Array<{ user?: { mail?: string } }>\n if (Array.isArray(followsValue)) {\n for (const item of followsValue) {\n if (item.user?.mail) {\n followsMails.push(item.user.mail)\n }\n }\n }\n } else if (field.field_format === 'user') {\n // 用户类型字段(如跟进DM):提取用户 ID(API 需要 ID,不是邮箱)\n if (field.multiple && Array.isArray(field.value)) {\n // 多用户字段:提取所有用户 ID\n const userIds: number[] = []\n for (const item of field.value as Array<{ user?: { id?: number } }>) {\n if (item.user?.id) {\n userIds.push(item.user.id)\n }\n }\n if (userIds.length > 0) {\n customFieldMap[field.id] = userIds.join(',')\n }\n } else {\n // 单用户字段:提取用户 ID\n const userValue = field.value as { user?: { id?: number } }\n if (userValue.user?.id) {\n customFieldMap[field.id] = userValue.user.id\n }\n }\n } else {\n customFieldMap[field.id] = field.value\n }\n }\n }\n\n // 合并额外的自定义字段(用户指定的覆盖继承的)\n const mergedCustomFields = { ...customFieldMap, ...extraCustomFields }\n\n if (Object.keys(mergedCustomFields).length > 0) {\n createParams.custom_field = JSON.stringify(mergedCustomFields)\n }\n if (followsMails.length > 0) {\n createParams.follows = followsMails\n }\n } else if (Object.keys(extraCustomFields).length > 0) {\n // 父单没有自定义字段,但用户指定了额外字段\n createParams.custom_field = JSON.stringify(extraCustomFields)\n }\n\n logger.info(`正在创建子任务: ${taskName}`, { targetParentId, isLeafNode, estimatedHours: node.estimatedHours })\n logger.debug('创建参数', { custom_field: createParams.custom_field, follows: createParams.follows })\n\n if (dryRun) {\n // 模拟模式:不实际创建,只记录\n logger.info(`[模拟] 将创建子任务: ${taskName}`)\n result.totalCreated++\n result.created.push({\n newId: 0, // 模拟模式没有真实 ID\n subject: taskName,\n parentId: targetParentId,\n estimatedHours: node.estimatedHours,\n })\n\n // 递归处理子任务(模拟模式下 parentId 使用 0)\n if (node.children.length > 0) {\n await this.batchCreateRecursive(\n token,\n host,\n project,\n node.children,\n 0, // 模拟模式下没有真实的新 ID\n parentInfo,\n assignedToMail,\n dryRun,\n interval,\n result,\n extraCustomFields\n )\n }\n } else {\n // 实际创建模式\n try {\n const createResult = await this.createIssue(createParams)\n\n if (createResult.success && createResult.data) {\n const newId = createResult.data.id\n logger.info(`✅ 成功创建子任务: ID=${newId}, 标题=${taskName}`)\n result.totalCreated++\n result.created.push({\n newId,\n subject: taskName,\n parentId: targetParentId,\n estimatedHours: node.estimatedHours,\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ✅ 创建成功: #${newId} ${taskName}`)\n\n // 递归处理子任务\n if (node.children.length > 0) {\n logger.info(`📁 发现子任务 ${newId} 有 ${node.children.length} 个子任务,继续递归创建...`)\n await this.batchCreateRecursive(\n token,\n host,\n project,\n node.children,\n newId,\n parentInfo,\n assignedToMail,\n dryRun,\n interval,\n result,\n extraCustomFields\n )\n }\n } else {\n const errorMsg = createResult.post_error_msg || createResult.api_error_msg || createResult.message || '创建失败'\n logger.error(`❌ 创建子任务失败: ${taskName}`, errorMsg)\n result.totalFailed++\n result.failed.push({\n subject: taskName,\n parentId: targetParentId,\n error: errorMsg,\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ❌ 创建失败: ${taskName} - ${errorMsg}`)\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : '未知错误'\n logger.error(`❌ 创建子任务时发生错误: ${taskName}`, error)\n result.totalFailed++\n result.failed.push({\n subject: taskName,\n parentId: targetParentId,\n error: errorMsg,\n })\n // 流式输出进度到 stderr\n const progress = result.totalCreated + result.totalFailed\n console.error(`[${progress}/${result.totalTasks}] ❌ 创建失败: ${taskName} - ${errorMsg}`)\n }\n\n // 添加延迟避免 API 限流\n await sleep(interval)\n }\n }\n }\n}\n\nexport const issueService = new IssueService()\n","import { apiClient } from '../client/api-client.js'\nimport type { ApiResponse, TimeEntry, TimeEntryQueryParams } from '../models/types.js'\nimport logger from '../utils/logger.js'\n\n/**\n * 工时条目服务\n */\nexport class TimeEntryService {\n /**\n * 查询工时条目\n */\n async queryTimeEntries(params: TimeEntryQueryParams): Promise<ApiResponse<TimeEntry[]>> {\n const requestParams: Record<string, unknown> = {\n token: params.token,\n host: params.host,\n project: params.project,\n }\n\n // 添加可选参数\n if (params.from_date) requestParams.from_date = params.from_date\n if (params.to_date) requestParams.to_date = params.to_date\n if (params.user_id) requestParams.user_id = params.user_id\n if (params.activity_id) requestParams.activity_id = params.activity_id\n if (params.member_of_group_id) requestParams.member_of_group_id = params.member_of_group_id\n if (params.tracker_id) requestParams.tracker_id = params.tracker_id\n if (params.version_id) requestParams.version_id = params.version_id\n if (params.offset !== undefined) requestParams.offset = params.offset\n if (params.limit !== undefined) requestParams.limit = params.limit\n\n logger.info('查询工时条目', { host: params.host, project: params.project })\n return await apiClient.get<TimeEntry[]>('query_time_entries', requestParams)\n }\n\n /**\n * 获取工时条目选项(活动类型列表等)\n * 使用 time_entry GET API\n */\n async getTimeEntryOptions(\n token: string,\n host: string,\n project: string,\n issueId?: number\n ): Promise<ApiResponse<unknown>> {\n logger.info('获取工时条目选项', { host, project, issueId })\n const params: Record<string, unknown> = { token, host, project }\n if (issueId) params.issue_id = issueId\n return await apiClient.get('time_entry', params)\n }\n\n /**\n * 创建工时条目\n * 使用 time_entry API(非 save_time_entry)\n */\n async createTimeEntry(params: Record<string, unknown>): Promise<ApiResponse<TimeEntry>> {\n logger.info('创建工时条目', { params })\n return await apiClient.post<TimeEntry>('time_entry', params)\n }\n\n /**\n * 更新工时条目\n */\n async updateTimeEntry(params: Record<string, unknown>): Promise<ApiResponse<TimeEntry>> {\n logger.info('更新工时条目', { params })\n return await apiClient.post<TimeEntry>('save_time_entry', params)\n }\n\n /**\n * 删除工时条目\n */\n async deleteTimeEntry(\n token: string,\n host: string,\n timeEntryId: number\n ): Promise<ApiResponse<unknown>> {\n logger.info('删除工时条目', { host, timeEntryId })\n return await apiClient.get('delete_time_entry', {\n token,\n host,\n id: timeEntryId, // 使用 id 参数,非 time_entry_id\n })\n }\n}\n\nexport const timeEntryService = new TimeEntryService()\n","import { apiClient } from '../client/api-client.js'\nimport type { ApiResponse, Project, User } from '../models/types.js'\nimport logger from '../utils/logger.js'\n\n/**\n * 用户服务\n */\nexport class UserService {\n /**\n * 测试连接 (通过获取项目列表来验证)\n */\n async testConnection(token: string, host: string, project: string): Promise<ApiResponse<unknown>> {\n logger.info('测试连接', { host, project })\n return await apiClient.get('project', { token, host })\n }\n\n /**\n * 获取项目列表\n */\n async getProjects(token: string, host: string): Promise<ApiResponse<Project[]>> {\n logger.info('获取项目列表', { host })\n return await apiClient.get<Project[]>('project', { token, host })\n }\n\n /**\n * 获取项目用户\n */\n async getProjectUsers(token: string, host: string, project: string): Promise<ApiResponse<User[]>> {\n logger.info('获取项目用户', { host, project })\n return await apiClient.get<User[]>('user', { token, host, project })\n }\n\n /**\n * 获取主机信息\n */\n async getHostInfo(token: string, host: string): Promise<ApiResponse<unknown>> {\n logger.info('获取主机信息', { host })\n return await apiClient.get('host', { token, host })\n }\n}\n\nexport const userService = new UserService()\n","import * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\nimport type { CliConfig, Credentials } from '../models/types.js'\n\n/**\n * 默认配置文件路径\n */\nconst DEFAULT_CONFIG_DIR = path.join(os.homedir(), '.config', 'pm-cli')\nconst DEFAULT_CONFIG_FILE = 'config.json'\n\n/**\n * 配置键名映射(命令行风格 -> 配置对象键名)\n * 用于支持 `pm-cli config set default-host xxx` 这种带连字符的写法\n */\nconst CONFIG_KEY_MAP: Record<string, string> = {\n 'default-host': 'host',\n 'default-project': 'project',\n 'default-token': 'token',\n // 用户信息配置\n 'user-id': 'userId',\n 'user-name': 'userName',\n 'user-mail': 'userMail',\n}\n\n/**\n * 规范化配置键名\n */\nfunction normalizeConfigKey(key: string): string {\n return CONFIG_KEY_MAP[key] || key\n}\n\n/**\n * 获取配置文件路径\n */\nexport function getConfigPath(customPath?: string): string {\n // 优先使用自定义路径\n if (customPath) {\n return customPath\n }\n\n // 其次使用环境变量\n if (process.env.PM_CLI_CONFIG) {\n return process.env.PM_CLI_CONFIG\n }\n\n // 最后使用默认路径\n return path.join(DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE)\n}\n\n/**\n * 确保配置目录存在\n */\nfunction ensureConfigDir(configPath: string): void {\n const dir = path.dirname(configPath)\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true })\n }\n}\n\n/**\n * 读取配置文件\n */\nexport function readConfig(customPath?: string): CliConfig {\n const configPath = getConfigPath(customPath)\n\n if (!fs.existsSync(configPath)) {\n return {\n default: {},\n profiles: {},\n }\n }\n\n try {\n const content = fs.readFileSync(configPath, 'utf-8')\n return JSON.parse(content) as CliConfig\n } catch {\n return {\n default: {},\n profiles: {},\n }\n }\n}\n\n/**\n * 写入配置文件\n */\nexport function writeConfig(config: CliConfig, customPath?: string): void {\n const configPath = getConfigPath(customPath)\n ensureConfigDir(configPath)\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8')\n}\n\n/**\n * 获取配置项\n */\nexport function getConfigValue(key: string, customPath?: string): string | undefined {\n const config = readConfig(customPath)\n\n // 支持 default.xxx 和直接 xxx 两种格式\n let normalizedKey: string\n if (key.startsWith('default.')) {\n normalizedKey = normalizeConfigKey(key.replace('default.', ''))\n } else {\n normalizedKey = normalizeConfigKey(key)\n }\n\n // 从 default 中获取\n return config.default[normalizedKey as keyof CliConfig['default']]\n}\n\n/**\n * 设置配置项\n */\nexport function setConfigValue(key: string, value: string, customPath?: string): void {\n const config = readConfig(customPath)\n\n // 支持 default.xxx 和直接 xxx 两种格式,并规范化键名\n let normalizedKey: string\n if (key.startsWith('default.')) {\n normalizedKey = normalizeConfigKey(key.replace('default.', ''))\n } else {\n normalizedKey = normalizeConfigKey(key)\n }\n\n // 设置到 default 中\n config.default[normalizedKey as keyof CliConfig['default']] = value\n\n writeConfig(config, customPath)\n}\n\n/**\n * 获取 profile 配置\n */\nexport function getProfile(name: string, customPath?: string): CliConfig['profiles'][string] | undefined {\n const config = readConfig(customPath)\n return config.profiles[name]\n}\n\n/**\n * 添加或更新 profile\n */\nexport function setProfile(\n name: string,\n profile: CliConfig['profiles'][string],\n customPath?: string\n): void {\n const config = readConfig(customPath)\n config.profiles[name] = profile\n writeConfig(config, customPath)\n}\n\n/**\n * 删除 profile\n */\nexport function removeProfile(name: string, customPath?: string): boolean {\n const config = readConfig(customPath)\n if (config.profiles[name]) {\n delete config.profiles[name]\n writeConfig(config, customPath)\n return true\n }\n return false\n}\n\n/**\n * 列出所有 profiles\n */\nexport function listProfiles(customPath?: string): string[] {\n const config = readConfig(customPath)\n return Object.keys(config.profiles)\n}\n\n/**\n * 合并凭据(按优先级)\n * 优先级: 命令行参数 > 环境变量 > profile 配置 > 默认配置\n */\nexport function resolveCredentials(options: {\n token?: string\n host?: string\n project?: string\n profile?: string\n config?: string\n}): Partial<Credentials> {\n const config = readConfig(options.config)\n\n // 基础配置(从默认配置开始)\n const result: Partial<Credentials> = {\n token: config.default.token,\n host: config.default.host,\n project: config.default.project,\n }\n\n // 如果指定了 profile,覆盖\n if (options.profile && config.profiles[options.profile]) {\n const profile = config.profiles[options.profile]\n if (profile.token) result.token = profile.token\n if (profile.host) result.host = profile.host\n if (profile.project) result.project = profile.project\n }\n\n // 环境变量覆盖\n if (process.env.NETEASE_TOKEN) result.token = process.env.NETEASE_TOKEN\n if (process.env.NETEASE_HOST) result.host = process.env.NETEASE_HOST\n if (process.env.NETEASE_PROJECT) result.project = process.env.NETEASE_PROJECT\n\n // 命令行参数覆盖(最高优先级)\n if (options.token) result.token = options.token\n if (options.host) result.host = options.host\n if (options.project) result.project = options.project\n\n return result\n}\n\n/**\n * 验证凭据是否完整\n */\nexport function validateCredentials(\n creds: Partial<Credentials>,\n requiredFields: (keyof Credentials)[] = ['token', 'host', 'project']\n): { valid: boolean; missing: string[] } {\n const missing: string[] = []\n\n for (const field of requiredFields) {\n if (!creds[field]) {\n missing.push(field)\n }\n }\n\n return {\n valid: missing.length === 0,\n missing,\n }\n}\n","import type { PmLinkInfo } from '../models/types.js'\n\n/**\n * PM 链接正则表达式\n * 格式1: https://{subdomain}.pm.netease.com/v6/issues/{issueId}\n * 格式2: https://{subdomain}.pm.netease.com/v6/issues?...&issue_id={issueId}\n */\nconst PM_URL_PATH_PATTERN = /https?:\\/\\/([^/]+\\.pm\\.netease\\.com)\\/v6\\/issues\\/(\\d+)/\nconst PM_URL_QUERY_PATTERN = /https?:\\/\\/([^/]+\\.pm\\.netease\\.com)\\/v6\\/issues\\?/\n\n/**\n * 解析 PM 链接\n * 支持两种格式:\n * 1. 路径格式: https://a19.pm.netease.com/v6/issues/213112\n * 2. 查询参数格式: https://a19.pm.netease.com/v6/issues?project_id=7&issue_id=254946\n *\n * @param url PM 链接\n * @returns 解析结果,包含 host 和 issueId;如果解析失败返回 null\n */\nexport function parsePmLink(url: string): PmLinkInfo | null {\n // 尝试路径格式\n const pathMatch = url.match(PM_URL_PATH_PATTERN)\n if (pathMatch) {\n return {\n host: pathMatch[1],\n issueId: pathMatch[2],\n }\n }\n\n // 尝试查询参数格式\n const queryMatch = url.match(PM_URL_QUERY_PATTERN)\n if (queryMatch) {\n const host = queryMatch[1]\n // 从 URL 中提取 issue_id 参数\n const issueIdMatch = url.match(/[?&]issue_id=(\\d+)/)\n if (issueIdMatch) {\n return {\n host,\n issueId: issueIdMatch[1],\n }\n }\n }\n\n return null\n}\n\n/**\n * 检查字符串是否是 PM 链接\n */\nexport function isPmLink(str: string): boolean {\n return PM_URL_PATH_PATTERN.test(str) || PM_URL_QUERY_PATTERN.test(str)\n}\n\n/**\n * 从文本中提取所有 PM 链接\n */\nexport function extractPmLinks(text: string): PmLinkInfo[] {\n const results: PmLinkInfo[] = []\n\n // 匹配所有可能的 PM 链接\n const urlPattern = /https?:\\/\\/[^\\s]+\\.pm\\.netease\\.com\\/v6\\/issues[^\\s]*/g\n const urls = text.match(urlPattern) || []\n\n for (const url of urls) {\n const info = parsePmLink(url)\n if (info) {\n results.push(info)\n }\n }\n\n return results\n}\n"],"mappings":"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","names":["currentLevel"]}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { Command as Command6 } from "commander";
7
7
  import { Command } from "commander";
8
8
 
9
9
  // src/utils/logger.ts
10
- var currentLevel = "info";
10
+ var currentLevel = "silent";
11
11
  var levels = {
12
12
  debug: 0,
13
13
  info: 1,