@hangox/pm-cli 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2006,6 +2006,17 @@ var TimeEntryService = class {
|
|
|
2006
2006
|
var timeEntryService = new TimeEntryService();
|
|
2007
2007
|
|
|
2008
2008
|
// src/commands/time/index.ts
|
|
2009
|
+
var WEEKDAY_NAMES = ["\u661F\u671F\u65E5", "\u661F\u671F\u4E00", "\u661F\u671F\u4E8C", "\u661F\u671F\u4E09", "\u661F\u671F\u56DB", "\u661F\u671F\u4E94", "\u661F\u671F\u516D"];
|
|
2010
|
+
function getTodayContext() {
|
|
2011
|
+
const now = /* @__PURE__ */ new Date();
|
|
2012
|
+
const year = now.getFullYear();
|
|
2013
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
2014
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
2015
|
+
return {
|
|
2016
|
+
today: `${year}-${month}-${day}`,
|
|
2017
|
+
todayWeekday: WEEKDAY_NAMES[now.getDay()]
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2009
2020
|
function getWeekDateRange(week) {
|
|
2010
2021
|
const now = /* @__PURE__ */ new Date();
|
|
2011
2022
|
let offset = 0;
|
|
@@ -2106,6 +2117,7 @@ function createTimeCommand() {
|
|
|
2106
2117
|
}
|
|
2107
2118
|
const totalHours = allTimeEntries.reduce((sum, e) => sum + (e.hours || 0), 0);
|
|
2108
2119
|
outputSuccess({
|
|
2120
|
+
...getTodayContext(),
|
|
2109
2121
|
total_count: totalCount,
|
|
2110
2122
|
total_hours: Math.round(totalHours * 100) / 100,
|
|
2111
2123
|
project_summary: projectSummary,
|
|
@@ -2364,6 +2376,7 @@ function createTimeCommand() {
|
|
|
2364
2376
|
const remainingHours = Math.max(0, targetHours - totalHours);
|
|
2365
2377
|
totalHours = Math.round(totalHours * 100) / 100;
|
|
2366
2378
|
const output = {
|
|
2379
|
+
...getTodayContext(),
|
|
2367
2380
|
period: periodLabel,
|
|
2368
2381
|
userId: userId || "\u672A\u6307\u5B9A",
|
|
2369
2382
|
totalHours,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/test.ts","../src/utils/logger.ts","../src/client/api-client.ts","../src/services/user-service.ts","../src/utils/config.ts","../src/utils/output.ts","../src/utils/key-translator.ts","../src/commands/config/index.ts","../src/commands/issue/index.ts","../src/services/issue-service.ts","../src/utils/url-parser.ts","../src/utils/issue-exporter.ts","../src/utils/preset-extractor.ts","../src/commands/time/index.ts","../src/services/time-entry-service.ts","../src/commands/project/index.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { createTestCommand } from './commands/test.js'\nimport { createConfigCommand } from './commands/config/index.js'\nimport { createIssueCommand } from './commands/issue/index.js'\nimport { createTimeCommand } from './commands/time/index.js'\nimport { createProjectCommand } from './commands/project/index.js'\nimport logger from './utils/logger.js'\nimport { setKeyMappingEnabled } from './utils/key-translator.js'\nimport { readFileSync } from 'fs'\nimport { dirname, join } from 'path'\nimport { fileURLToPath } from 'url'\n\n// 获取当前文件的目录\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n// 从 package.json 读取版本号\nconst packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'))\nconst version = packageJson.version\n\nconst program = new Command()\n\nprogram\n .name('pm-cli')\n .description('网易易协作 (PM NetEase) 命令行工具')\n .version(version)\n .option('--verbose', '详细输出')\n .option('--debug', '调试模式')\n .option('--no-mapping', '不输出 key 映射表(默认输出)')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts()\n if (opts.debug) {\n logger.setLevel('debug')\n } else if (opts.verbose) {\n logger.setLevel('info')\n } else {\n logger.setLevel('silent')\n }\n // 设置是否输出 key 映射表(--no-mapping 表示禁用)\n if (opts.mapping === false) {\n setKeyMappingEnabled(false)\n }\n })\n\n// 添加子命令\nprogram.addCommand(createTestCommand())\nprogram.addCommand(createConfigCommand())\nprogram.addCommand(createIssueCommand())\nprogram.addCommand(createTimeCommand())\nprogram.addCommand(createProjectCommand())\n\n// 解析命令行参数\nprogram.parse()\n","import { Command } from 'commander'\nimport { userService } from '../services/user-service.js'\nimport { resolveCredentials, validateCredentials } from '../utils/config.js'\nimport { outputSuccess, outputError } from '../utils/output.js'\n\nexport function createTestCommand(): Command {\n const testCmd = new Command('test')\n .description('测试网易易协作连接')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.testConnection(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (result.success) {\n outputSuccess({\n message: '连接成功',\n host: creds.host,\n project: creds.project,\n data: result.data,\n }, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '连接失败', options.pretty)\n process.exit(1)\n }\n })\n\n return testCmd\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nlet currentLevel: LogLevel = 'info'\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\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","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 { writeFileSync } from 'fs'\nimport type { CliOutput } from '../models/types.js'\nimport { generateKeyMapping, isKeyMappingEnabled } from './key-translator.js'\n\n/**\n * 生成输出文件路径\n * 命名规则: pm-cli_{命令}_{标识}_{时间戳}.json\n * @param command 命令名称,如 \"issue-get\"\n * @param identifier 标识符,如单号 \"250397\"\n */\nexport function generateOutputPath(command: string, identifier: string): string {\n const now = new Date()\n const timestamp = now.getFullYear().toString() +\n (now.getMonth() + 1).toString().padStart(2, '0') +\n now.getDate().toString().padStart(2, '0') +\n now.getHours().toString().padStart(2, '0') +\n now.getMinutes().toString().padStart(2, '0') +\n now.getSeconds().toString().padStart(2, '0')\n return `/tmp/pm-cli_${command}_${identifier}_${timestamp}.json`\n}\n\n/**\n * 为输出数据添加 key 映射表\n * @param result 原始输出结果\n * @returns 添加了 _key_mapping 的结果\n */\nfunction addKeyMappingToResult<T>(result: CliOutput<T>): CliOutput<T> & { _key_mapping?: Record<string, string> } {\n if (!isKeyMappingEnabled()) {\n return result\n }\n\n const mapping = generateKeyMapping(result)\n return {\n ...result,\n _key_mapping: mapping,\n }\n}\n\n/**\n * 输出到文件\n * @param data 要输出的数据\n * @param filePath 文件路径\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n * @returns 输出的文件路径\n */\nexport function outputToFile<T>(data: T, filePath: string, pretty = false): string {\n const result: CliOutput<T> = {\n success: true,\n data,\n }\n const finalResult = addKeyMappingToResult(result)\n writeFileSync(filePath, JSON.stringify(finalResult, null, pretty ? 2 : 0), 'utf-8')\n return filePath\n}\n\n/**\n * 智能输出 - 根据参数决定输出到文件还是控制台\n * @param data 要输出的数据\n * @param options 输出选项\n * @returns 如果输出到文件,返回文件路径;否则返回 undefined\n */\nexport function smartOutput<T>(\n data: T,\n options: {\n stdout?: boolean\n output?: string\n command: string\n identifier: string\n pretty?: boolean\n }\n): string | undefined {\n // 如果指定了 --stdout,输出到控制台\n if (options.stdout) {\n outputSuccess(data, options.pretty)\n return undefined\n }\n\n // 确定输出文件路径\n const filePath = options.output || generateOutputPath(options.command, options.identifier)\n\n // 输出到文件\n outputToFile(data, filePath, options.pretty)\n\n // 在控制台显示文件路径\n console.log(JSON.stringify({ success: true, output: filePath }, null, options.pretty ? 2 : 0))\n\n return filePath\n}\n\n/**\n * 输出成功结果\n * @param data 要输出的数据\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n */\nexport function outputSuccess<T>(data: T, pretty = false): void {\n const result: CliOutput<T> = {\n success: true,\n data,\n }\n const finalResult = addKeyMappingToResult(result)\n console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0))\n}\n\n/**\n * 输出错误结果\n * @param error 错误信息\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n */\nexport function outputError(error: string, pretty = false): void {\n const result: CliOutput = {\n success: false,\n error,\n }\n // 错误输出也添加映射表,方便理解 error 字段\n const finalResult = addKeyMappingToResult(result)\n console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0))\n}\n\n/**\n * 输出原始 API 响应\n * @param data 要输出的数据\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n */\nexport function outputRaw(data: unknown, pretty = false): void {\n console.log(JSON.stringify(data, null, pretty ? 2 : 0))\n}\n\n// 导出 key-translator 的函数,方便外部使用\nexport { setKeyMappingEnabled, isKeyMappingEnabled } from './key-translator.js'\n","/**\n * JSON Key 映射表生成器\n * 生成 key 到中文的映射表,附加到 JSON 输出中帮助 AI 理解\n */\n\n// 英文 key 到中文 key 的映射表\nconst KEY_MAP: Record<string, string> = {\n // 问题 (Issue) 相关字段\n id: '编号',\n subject: '标题',\n description: '描述',\n status: '状态',\n tracker: '跟踪器',\n priority: '优先级',\n author: '创建者',\n assigned_to: '负责人',\n project: '项目',\n parent: '父单',\n parent_id: '父单编号',\n parent_issue_id: '父单编号',\n root_id: '根单编号',\n children: '子单列表',\n level: '层级',\n fixed_version: '目标版本',\n version: '版本',\n start_date: '开始日期',\n due_date: '截止日期',\n estimated_hours: '预估工时',\n spent_hours: '已用工时',\n done_ratio: '完成度',\n created_on: '创建时间',\n updated_on: '更新时间',\n closed_on: '关闭时间',\n relations: '关联问题',\n custom_fields: '自定义字段',\n attachments: '附件',\n journals: '历史记录',\n watchers: '跟踪者',\n watcher_user_ids: '跟踪者编号',\n\n // 通用对象字段\n name: '名称',\n value: '值',\n mail: '邮箱',\n login: '登录名',\n firstname: '名',\n lastname: '姓',\n identifier: '标识符',\n is_public: '是否公开',\n\n // 工时 (TimeEntry) 相关字段\n hours: '工时',\n spent_on: '工时日期',\n activity: '活动类型',\n activity_id: '活动类型编号',\n comments: '备注',\n time_entries: '工时条目',\n time_entry: '工时条目',\n total_count: '总数',\n total_hours: '总工时',\n\n // 用户 (User) 相关字段\n user: '用户',\n user_id: '用户编号',\n author_mail: '作者邮箱',\n assigned_to_mail: '负责人邮箱',\n assigned_to_id: '负责人编号',\n follows: '跟进QA',\n last_login_on: '最后登录时间',\n\n // API 响应字段\n success: '成功',\n data: '数据',\n error: '错误',\n message: '消息',\n msg: '消息',\n api_error_msg: '接口错误',\n post_error_msg: '请求错误',\n output: '输出路径',\n issues: '问题列表',\n list: '列表',\n total: '总数',\n\n // 查询/统计相关字段\n offset: '偏移量',\n limit: '限制数',\n page: '页码',\n per_page: '每页数量',\n period: '时间段',\n targetHours: '目标工时',\n remainingHours: '剩余工时',\n isFilled: '是否填满',\n projectBreakdown: '项目分布',\n project_summary: '项目汇总',\n\n // 版本 (Version) 相关字段\n effective_date: '有效日期',\n sharing: '共享范围',\n wiki_page_title: 'Wiki页面',\n\n // 自定义字段相关\n custom_field: '自定义字段',\n identify: '标识',\n\n // 选项/配置相关\n options: '选项',\n activities: '活动类型列表',\n trackers: '跟踪器列表',\n statuses: '状态列表',\n priorities: '优先级列表',\n versions: '版本列表',\n users: '用户列表',\n projects: '项目列表',\n\n // 其他字段\n notes: '说明',\n is_pending_version: '是否待定版本',\n count: '数量',\n category: '分类',\n category_id: '分类编号',\n}\n\n// 全局开关:是否输出 key 映射表(默认开启)\nlet keyMappingEnabled = true\n\n/**\n * 设置是否输出 key 映射表\n */\nexport function setKeyMappingEnabled(enabled: boolean): void {\n keyMappingEnabled = enabled\n}\n\n/**\n * 获取当前是否输出 key 映射表\n */\nexport function isKeyMappingEnabled(): boolean {\n return keyMappingEnabled\n}\n\n/**\n * 递归收集对象中所有使用的 key\n * @param obj 要分析的对象\n * @param keys 收集到的 key 集合\n */\nfunction collectKeysRecursive(obj: unknown, keys: Set<string>): void {\n if (obj === null || obj === undefined) return\n if (typeof obj !== 'object') return\n\n if (Array.isArray(obj)) {\n for (const item of obj) {\n collectKeysRecursive(item, keys)\n }\n return\n }\n\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n keys.add(key)\n collectKeysRecursive(value, keys)\n }\n}\n\n/**\n * 收集对象中所有使用的 key\n * @param obj 要分析的对象\n * @returns 使用的 key 数组\n */\nexport function collectUsedKeys(obj: unknown): string[] {\n const keys = new Set<string>()\n collectKeysRecursive(obj, keys)\n return Array.from(keys)\n}\n\n/**\n * 为对象生成 key 映射表\n * 只包含对象中实际使用的 key,且在映射表中有对应中文的\n * @param obj 要分析的对象\n * @returns key 映射表 { englishKey: chineseKey }\n */\nexport function generateKeyMapping(obj: unknown): Record<string, string> {\n const usedKeys = collectUsedKeys(obj)\n const mapping: Record<string, string> = {}\n\n for (const key of usedKeys) {\n if (KEY_MAP[key]) {\n mapping[key] = KEY_MAP[key]\n }\n }\n\n return mapping\n}\n\n/**\n * 获取完整的 key 映射表(用于调试或文档)\n */\nexport function getFullKeyMap(): Record<string, string> {\n return { ...KEY_MAP }\n}\n\n/**\n * 添加自定义 key 映射\n */\nexport function addKeyMapping(englishKey: string, chineseKey: string): void {\n KEY_MAP[englishKey] = chineseKey\n}\n","import { Command } from 'commander'\nimport {\n readConfig,\n setConfigValue,\n getConfigValue,\n setProfile,\n removeProfile,\n listProfiles,\n getProfile,\n getConfigPath,\n} from '../../utils/config.js'\nimport { outputSuccess, outputError } from '../../utils/output.js'\n\nexport function createConfigCommand(): Command {\n const configCmd = new Command('config').description('配置管理')\n\n // config set <key> <value>\n configCmd\n .command('set <key> <value>')\n .description('设置配置项')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((key: string, value: string, options: { config?: string; pretty?: boolean }) => {\n setConfigValue(key, value, options.config)\n outputSuccess({\n message: `配置项 ${key} 已设置`,\n key,\n value,\n configPath: getConfigPath(options.config),\n }, options.pretty)\n })\n\n // config get <key>\n configCmd\n .command('get <key>')\n .description('获取配置项')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((key: string, options: { config?: string; pretty?: boolean }) => {\n const value = getConfigValue(key, options.config)\n if (value !== undefined) {\n outputSuccess({ key, value }, options.pretty)\n } else {\n outputError(`配置项 ${key} 不存在`, options.pretty)\n process.exit(1)\n }\n })\n\n // config list\n configCmd\n .command('list')\n .description('列出所有配置')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((options: { config?: string; pretty?: boolean }) => {\n const config = readConfig(options.config)\n outputSuccess({\n configPath: getConfigPath(options.config),\n config,\n }, options.pretty)\n })\n\n // config profile 子命令组\n const profileCmd = new Command('profile').description('Profile 管理')\n\n // config profile add <name>\n profileCmd\n .command('add <name>')\n .description('添加 profile')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--token <token>', 'API Token')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(\n (\n name: string,\n options: { host?: string; project?: string; token?: string; config?: string; pretty?: boolean }\n ) => {\n const profile: Record<string, string> = {}\n if (options.host) profile.host = options.host\n if (options.project) profile.project = options.project\n if (options.token) profile.token = options.token\n\n if (Object.keys(profile).length === 0) {\n outputError('请至少指定一个配置项 (--host, --project, --token)', options.pretty)\n process.exit(1)\n }\n\n setProfile(name, profile, options.config)\n outputSuccess({\n message: `Profile ${name} 已添加`,\n name,\n profile,\n }, options.pretty)\n }\n )\n\n // config profile remove <name>\n profileCmd\n .command('remove <name>')\n .description('删除 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((name: string, options: { config?: string; pretty?: boolean }) => {\n const removed = removeProfile(name, options.config)\n if (removed) {\n outputSuccess({ message: `Profile ${name} 已删除`, name }, options.pretty)\n } else {\n outputError(`Profile ${name} 不存在`, options.pretty)\n process.exit(1)\n }\n })\n\n // config profile list\n profileCmd\n .command('list')\n .description('列出所有 profiles')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((options: { config?: string; pretty?: boolean }) => {\n const profiles = listProfiles(options.config)\n const profileDetails: Record<string, unknown> = {}\n\n for (const name of profiles) {\n profileDetails[name] = getProfile(name, options.config)\n }\n\n outputSuccess({\n profiles: profileDetails,\n count: profiles.length,\n }, options.pretty)\n })\n\n configCmd.addCommand(profileCmd)\n\n return configCmd\n}\n","import { Command } from 'commander'\nimport { issueService } from '../../services/issue-service.js'\nimport { userService } from '../../services/user-service.js'\nimport { resolveCredentials, validateCredentials, readConfig } from '../../utils/config.js'\nimport { parsePmLink } from '../../utils/url-parser.js'\nimport { outputSuccess, outputError, smartOutput } from '../../utils/output.js'\nimport { exportIssuePresets, generateExportDir, generateAIGuideOutput } from '../../utils/issue-exporter.js'\n\nexport function createIssueCommand(): Command {\n const issueCmd = new Command('issue').description('问题管理')\n\n // issue get <id> 或 --url\n issueCmd\n .command('get [id]')\n .description('获取问题详情(默认分级导出,--raw 输出完整 JSON)')\n .option('--url <url>', 'PM 链接')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--depth <depth>', '递归获取子单的深度(0 表示不获取子单)', '10')\n .option('-o, --output <path>', '输出目录或文件路径(默认 /tmp)')\n .option('--stdout', '强制输出到控制台而非文件')\n .option('--raw', '输出完整 JSON 文件(不分级,原行为)')\n .option('--include-relations', '包含关联问题')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string | undefined, options) => {\n let issueId: number\n let host: string | undefined\n\n // 解析 URL 或 ID\n if (options.url) {\n const linkInfo = parsePmLink(options.url)\n if (!linkInfo) {\n outputError('无效的 PM 链接格式', options.pretty)\n process.exit(1)\n }\n issueId = parseInt(linkInfo.issueId, 10)\n host = linkInfo.host\n } else if (id) {\n // 支持 #123456 格式\n const cleanId = id.replace(/^#/, '')\n issueId = parseInt(cleanId, 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n } else {\n outputError('请提供问题 ID 或 --url 参数', options.pretty)\n process.exit(1)\n }\n\n // 合并凭据,URL 中的 host 优先\n const creds = resolveCredentials({\n ...options,\n host: host || options.host,\n })\n\n // 对于 get 操作,只需要 token 和 host,project 可选\n const validation = validateCredentials(creds, ['token', 'host'])\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const depth = parseInt(options.depth, 10)\n\n // 根据 depth 决定是否递归获取子单\n if (depth > 0) {\n // 递归获取子单\n const result = await issueService.getIssueWithChildren(\n creds.token!,\n creds.host!,\n creds.project || '',\n issueId,\n depth\n )\n\n if (result.success && result.data) {\n // 判断是使用分级导出还是原始输出\n if (options.raw || options.stdout) {\n // --raw 或 --stdout: 使用原来的单文件输出\n smartOutput(result.data, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-get',\n identifier: issueId.toString(),\n pretty: options.pretty,\n })\n } else {\n // 默认: 分级导出到目录\n const exportDir = options.output || generateExportDir('issue-get', issueId.toString())\n const exportResult = await exportIssuePresets(result.data, exportDir, {\n depth,\n pretty: options.pretty,\n })\n\n if (exportResult.success) {\n // 输出 AI 读取指南\n console.log(generateAIGuideOutput(\n exportResult.exportDir,\n exportResult.fileCount,\n exportResult.totalSize\n ))\n } else {\n outputError('导出失败', options.pretty)\n process.exit(1)\n }\n }\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取问题失败', options.pretty)\n process.exit(1)\n }\n } else {\n // 不递归,只获取当前问题\n const result = await issueService.getIssue(\n creds.token!,\n creds.host!,\n creds.project || '',\n issueId,\n false,\n options.includeRelations\n )\n\n if (result.success && result.data) {\n smartOutput(result.data, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-get',\n identifier: issueId.toString(),\n pretty: options.pretty,\n })\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取问题失败', options.pretty)\n process.exit(1)\n }\n }\n })\n\n // issue create\n issueCmd\n .command('create')\n .description('创建问题')\n .requiredOption('--subject <subject>', '问题标题')\n .option('--description <description>', '问题描述')\n .option('--tracker <tracker>', '跟踪器名称')\n .option('--tracker-id <id>', '跟踪器 ID')\n .option('--priority-id <id>', '优先级 ID')\n .option('--assigned-to-mail <email>', '指派人邮箱')\n .option('--assigned-to-id <id>', '指派人 ID')\n .option('--parent-id <id>', '父问题 ID(子单会继承父单的 tracker、version、assigned_to 等信息)')\n .option('--version <version>', '目标版本名称')\n .option('--start-date <date>', '开始日期')\n .option('--due-date <date>', '截止日期')\n .option('--estimated-hours <hours>', '预估工时')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n subject: options.subject,\n }\n\n // 如果提供了父单 ID,先查询父单信息以继承相关字段\n if (options.parentId) {\n const parentId = parseInt(options.parentId, 10)\n const parentResult = await issueService.getIssue(\n creds.token!,\n creds.host!,\n creds.project || '',\n parentId,\n false,\n false\n )\n\n if (parentResult.success && parentResult.data) {\n const parent = parentResult.data as unknown as {\n tracker?: { name?: string }\n assigned_to?: { mail?: string }\n fixed_version?: { name?: string }\n priority?: { id?: number }\n custom_fields?: Array<{ id: number; name: string; value: unknown }>\n }\n\n // 继承父单的信息(如果用户未明确指定)\n if (!options.tracker && !options.trackerId && parent.tracker?.name) {\n params.tracker = parent.tracker.name\n }\n if (!options.assignedToMail && !options.assignedToId && parent.assigned_to?.mail) {\n params.assigned_to_mail = parent.assigned_to.mail\n }\n if (!options.version && parent.fixed_version?.name) {\n params.version = parent.fixed_version.name\n }\n if (!options.priorityId && parent.priority?.id) {\n params.priority_id = parent.priority.id\n }\n // 默认状态为\"新建\"\n params.status = '新建'\n\n // 继承自定义字段\n if (parent.custom_fields && parent.custom_fields.length > 0) {\n const customFieldMap: Record<number, unknown> = {}\n const followsMails: string[] = []\n\n for (const field of parent.custom_fields) {\n // 跳过空值字段\n if (field.value !== null && field.value !== undefined && field.value !== '') {\n // 特殊处理\"跟进QA\"字段 (identify: IssuesQCFollow)\n const fieldWithIdentify = field as { id: number; name: string; value: unknown; identify?: string }\n if (fieldWithIdentify.identify === 'IssuesQCFollow') {\n // 提取跟进QA的邮箱\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 {\n // 其他自定义字段正常继承\n customFieldMap[field.id] = field.value\n }\n }\n }\n\n if (Object.keys(customFieldMap).length > 0) {\n params.custom_field = JSON.stringify(customFieldMap)\n }\n if (followsMails.length > 0) {\n params.follows = followsMails\n }\n }\n } else {\n outputError(`无法获取父单 #${parentId} 的信息: ${parentResult.message || '未知错误'}`, options.pretty)\n process.exit(1)\n }\n\n params.parent_issue_id = parentId\n }\n\n // 用户明确提供的参数(优先级最高)\n if (options.description) params.description = options.description\n if (options.tracker) params.tracker = options.tracker\n if (options.trackerId) params.tracker_id = parseInt(options.trackerId, 10)\n if (options.priorityId) params.priority_id = parseInt(options.priorityId, 10)\n if (options.assignedToMail) params.assigned_to_mail = options.assignedToMail\n if (options.assignedToId) params.assigned_to_id = parseInt(options.assignedToId, 10)\n if (options.version) params.version = options.version\n if (options.startDate) params.start_date = options.startDate\n if (options.dueDate) params.due_date = options.dueDate\n if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours)\n\n const result = await issueService.createIssue(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '创建问题失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue update <id>\n issueCmd\n .command('update <id>')\n .description('更新问题')\n .option('--subject <subject>', '问题标题')\n .option('--description <description>', '问题描述')\n .option('--status <status>', '状态名称 (如: 新建、开发中、已解决)')\n .option('--tracker <tracker>', '跟踪标签名称 (如: BUG、任务)')\n .option('--version <version>', '目标版本名称')\n .option('--assigned-to-mail <email>', '指派人邮箱')\n .option('--notes <notes>', '更新说明/备注')\n .option('--start-date <date>', '开始日期 (格式: YYYY-MM-DD)')\n .option('--due-date <date>', '截止日期 (格式: YYYY-MM-DD)')\n .option('--estimated-hours <hours>', '预估工时')\n .option('--follows <email>', '跟进QA邮箱')\n .option('--custom-field <key=value>', '自定义字段 (格式: 字段ID=值 或 字段名=值,可多次使用)')\n .option('--cf <key=value>', '自定义字段的简写别名')\n .option('--url <url>', 'PM 链接 (自动解析 host 和 issue_id)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n let issueId: number\n let urlHost: string | undefined\n\n // 支持 --url 参数\n if (options.url) {\n const linkInfo = parsePmLink(options.url)\n if (!linkInfo) {\n outputError('无效的 PM 链接格式', options.pretty)\n process.exit(1)\n }\n issueId = parseInt(linkInfo.issueId, 10)\n urlHost = linkInfo.host\n } else {\n issueId = parseInt(id.replace(/^#/, ''), 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 合并凭据,URL 中的 host 优先\n const creds = resolveCredentials({\n ...options,\n host: urlHost || options.host,\n })\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 构建 API 参数(使用 API 文档定义的参数名)\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n issue_id: issueId,\n }\n\n // 使用 API 文档定义的参数名\n if (options.subject) params.subject = options.subject\n if (options.description) params.description = options.description\n if (options.status) params.status = options.status // 状态名称\n if (options.tracker) params.tracker = options.tracker // 跟踪标签名称\n if (options.version) params.version = options.version // 版本名称\n if (options.assignedToMail) params.assigned_to_mail = options.assignedToMail // 指派人邮箱\n if (options.notes) params.notes = options.notes // 更新说明\n if (options.startDate) params.start_date = options.startDate\n if (options.dueDate) params.due_date = options.dueDate\n if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours)\n if (options.follows) params.follows = options.follows // 跟进QA\n\n // 处理自定义字段(支持 --custom-field 和 --cf)\n const customFieldInputs = []\n if (options.customField) {\n customFieldInputs.push(...(Array.isArray(options.customField) ? options.customField : [options.customField]))\n }\n if (options.cf) {\n customFieldInputs.push(...(Array.isArray(options.cf) ? options.cf : [options.cf]))\n }\n\n if (customFieldInputs.length > 0) {\n try {\n const customFieldData: Record<string, string> = {}\n\n // 解析所有 key=value 输入\n for (const input of customFieldInputs) {\n const match = input.match(/^([^=]+)=(.*)$/)\n if (!match) {\n outputError(`自定义字段格式错误: ${input},正确格式为 \"字段ID=值\" 或 \"字段名=值\"`, options.pretty)\n process.exit(1)\n }\n const [, key, value] = match\n customFieldData[key.trim()] = value.trim()\n }\n\n // 检查是否所有键都是数字(字段ID)\n const hasNonNumericKey = Object.keys(customFieldData).some(key => isNaN(Number(key)))\n\n if (hasNonNumericKey) {\n // 获取字段选项以映射字段名到字段ID\n const fieldOptionsResult = await issueService.getIssueFieldOptions(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (fieldOptionsResult.success && fieldOptionsResult.data) {\n const fieldOptions = fieldOptionsResult.data as {\n data?: { custom_fields?: Array<{ id: number; name: string }> }\n }\n\n if (fieldOptions.data?.custom_fields) {\n const fieldMap = new Map<string, number>()\n for (const field of fieldOptions.data.custom_fields) {\n fieldMap.set(field.name, field.id)\n }\n\n // 转换字段名为字段ID\n const convertedData: Record<number, string> = {}\n for (const [key, value] of Object.entries(customFieldData)) {\n const fieldId = isNaN(Number(key)) ? fieldMap.get(key) : Number(key)\n if (fieldId !== undefined) {\n convertedData[fieldId] = value\n } else {\n outputError(`未找到自定义字段: ${key}`, options.pretty)\n process.exit(1)\n }\n }\n\n params.custom_field = JSON.stringify(convertedData)\n } else {\n outputError('无法获取自定义字段列表', options.pretty)\n process.exit(1)\n }\n } else {\n outputError('获取自定义字段列表失败', options.pretty)\n process.exit(1)\n }\n } else {\n // 所有键都是数字,直接使用\n const convertedData: Record<number, string> = {}\n for (const [key, value] of Object.entries(customFieldData)) {\n convertedData[Number(key)] = value\n }\n params.custom_field = JSON.stringify(convertedData)\n }\n } catch (error) {\n outputError(\n `自定义字段解析错误: ${error instanceof Error ? error.message : '未知错误'}`,\n options.pretty\n )\n process.exit(1)\n }\n }\n\n const result = await issueService.updateIssue(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '更新问题失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue query\n issueCmd\n .command('query')\n .description('自定义查询')\n .requiredOption('--query-id <id>', '查询 ID')\n .option('--limit <limit>', '返回数量限制')\n .option('--offset <offset>', '偏移量')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const queryId = parseInt(options.queryId, 10)\n const limit = options.limit ? parseInt(options.limit, 10) : undefined\n const offset = options.offset ? parseInt(options.offset, 10) : undefined\n\n const result = await issueService.customQuery(\n creds.token!,\n creds.host!,\n creds.project!,\n queryId,\n limit,\n offset\n )\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue filter\n issueCmd\n .command('filter')\n .description('V6 过滤器查询')\n .option('--mode <mode>', '查询模式 (normal/simple/advanced)', 'normal')\n .option('--status <status>', '状态过滤')\n .option('--tracker <tracker>', '跟踪器过滤')\n .option('--assigned-to <user>', '指派人过滤')\n .option('--limit <limit>', '返回数量限制')\n .option('--offset <offset>', '偏移量')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const filterParams: Record<string, unknown> = {}\n if (options.status) filterParams.status = options.status\n if (options.tracker) filterParams.tracker = options.tracker\n if (options.assignedTo) filterParams.assigned_to = options.assignedTo\n if (options.limit) filterParams.limit = parseInt(options.limit, 10)\n if (options.offset) filterParams.offset = parseInt(options.offset, 10)\n\n const result = await issueService.filterQueryV6(\n creds.token!,\n creds.host!,\n creds.project!,\n options.mode as 'normal' | 'simple' | 'advanced',\n filterParams\n )\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue field-options\n issueCmd\n .command('field-options')\n .description('获取问题字段选项')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await issueService.getIssueFieldOptions(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (result.success) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取字段选项失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue children <id> - 查询子任务\n issueCmd\n .command('children <id>')\n .description('查询子任务(支持按负责人过滤)')\n .option('--url <url>', 'PM 链接')\n .option('--assigned-to <name>', '负责人姓名(支持模糊匹配)')\n .option('--assigned-to-id <id>', '负责人 ID')\n .option('--limit <limit>', '返回数量限制', '100')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n let issueId: number\n let host: string | undefined\n\n // 解析 URL 或 ID\n if (options.url) {\n const linkInfo = parsePmLink(options.url)\n if (!linkInfo) {\n outputError('无效的 PM 链接格式', options.pretty)\n process.exit(1)\n }\n issueId = parseInt(linkInfo.issueId, 10)\n host = linkInfo.host\n } else {\n const cleanId = id.replace(/^#/, '')\n issueId = parseInt(cleanId, 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 合并凭据\n const creds = resolveCredentials({\n ...options,\n host: host || options.host,\n })\n\n const validation = validateCredentials(creds)\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n let assignedToId: number | undefined\n\n // 如果提供了负责人姓名,查找对应的用户 ID\n if (options.assignedTo && !options.assignedToId) {\n const usersResult = await userService.getProjectUsers(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (usersResult.success && usersResult.data) {\n const users = usersResult.data as unknown as Array<{ id: number; name?: string; firstname?: string; lastname?: string }>\n const searchName = options.assignedTo.toLowerCase()\n const matchedUser = users.find(\n (u) =>\n u.name?.toLowerCase().includes(searchName) ||\n u.firstname?.toLowerCase().includes(searchName) ||\n u.lastname?.toLowerCase().includes(searchName)\n )\n\n if (matchedUser) {\n assignedToId = matchedUser.id\n } else {\n outputError(`未找到匹配的用户: ${options.assignedTo}`, options.pretty)\n process.exit(1)\n }\n } else {\n outputError('获取用户列表失败', options.pretty)\n process.exit(1)\n }\n } else if (options.assignedToId) {\n assignedToId = parseInt(options.assignedToId, 10)\n }\n\n const perPage = parseInt(options.limit, 10) || 100\n\n const result = await issueService.queryChildren(\n creds.token!,\n creds.host!,\n creds.project!,\n issueId,\n assignedToId,\n perPage\n )\n\n if (result.success && result.data) {\n // 提取并格式化结果\n const data = result.data as { data?: { list?: unknown[] } }\n if (data.data?.list) {\n outputSuccess({\n total: data.data.list.length,\n issues: data.data.list,\n }, options.pretty)\n } else {\n outputSuccess(result.data, options.pretty)\n }\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询子任务失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue mget <id1> <id2> ... - 批量获取多个问题\n issueCmd\n .command('mget [ids...]')\n .description('批量获取多个问题详情')\n .option('--ids <ids>', '逗号分隔的问题 ID 列表')\n .option('--depth <depth>', '递归获取子单的深度(0 表示不获取子单)', '0')\n .option('--include-relations', '包含关联问题')\n .option('-o, --output <path>', '输出 JSON 到指定文件(默认输出到 /tmp)')\n .option('--stdout', '强制输出到控制台而非文件')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (ids: string[], options) => {\n // 收集所有 ID(支持多种输入格式)\n let issueIds: number[] = []\n\n // 1. 从位置参数收集 ID(支持 #123 格式)\n if (ids && ids.length > 0) {\n const parsedIds = ids\n .map((id) => parseInt(id.replace(/^#/, ''), 10))\n .filter((id) => !isNaN(id))\n issueIds.push(...parsedIds)\n }\n\n // 2. 从 --ids 选项收集(逗号分隔)\n if (options.ids) {\n const idsFromOption = options.ids\n .split(',')\n .map((id: string) => parseInt(id.trim().replace(/^#/, ''), 10))\n .filter((id: number) => !isNaN(id))\n issueIds.push(...idsFromOption)\n }\n\n // 去重\n issueIds = [...new Set(issueIds)]\n\n if (issueIds.length === 0) {\n outputError('请提供至少一个问题 ID', options.pretty)\n process.exit(1)\n }\n\n // 合并凭据\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const depth = parseInt(options.depth, 10)\n const includeChildren = depth > 0\n const includeRelations = options.includeRelations\n\n const result = await issueService.getMultipleIssues(\n creds.token!,\n creds.host!,\n creds.project || '',\n issueIds,\n { includeChildren, includeRelations, depth }\n )\n\n if (result.success && result.data) {\n // 统计结果\n const successItems = result.data.filter((item) => item.success)\n const failedItems = result.data.filter((item) => !item.success)\n\n const output = {\n success: true,\n summary: {\n total: issueIds.length,\n success: successItems.length,\n failed: failedItems.length,\n },\n issues: successItems.map((item) => item.data),\n errors: failedItems.length > 0 ? failedItems.map((item) => ({ id: item.id, error: item.error })) : undefined,\n }\n\n smartOutput(output, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-mget',\n identifier: issueIds.join('_'),\n pretty: options.pretty,\n })\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '批量获取问题失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue sync - 同步子单\n issueCmd\n .command('sync')\n .description('同步子单:从源父单复制子单到目标父单')\n .option('--from <id>', '源父单 ID')\n .option('--to <id>', '目标父单 ID')\n .option('--from-url <url>', '源父单 PM 链接')\n .option('--to-url <url>', '目标父单 PM 链接')\n .option('--assigned-to-mail <email>', '指派人邮箱(默认使用配置的 userMail)')\n .option('--depth <depth>', '递归深度', '10')\n .option('--dry-run', '模拟运行,不实际创建')\n .option('--no-skip-existing', '不跳过同名任务')\n .option('-o, --output <path>', '输出 JSON 到指定文件')\n .option('--stdout', '强制输出到控制台而非文件')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n let sourceId: number | undefined\n let targetId: number | undefined\n let sourceHost: string | undefined\n let targetHost: string | undefined\n\n // 解析源父单 ID\n if (options.fromUrl) {\n const linkInfo = parsePmLink(options.fromUrl)\n if (!linkInfo) {\n outputError('无效的源父单 PM 链接格式', options.pretty)\n process.exit(1)\n }\n sourceId = parseInt(linkInfo.issueId, 10)\n sourceHost = linkInfo.host\n } else if (options.from) {\n const cleanId = options.from.replace(/^#/, '')\n sourceId = parseInt(cleanId, 10)\n if (isNaN(sourceId)) {\n outputError('无效的源父单 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 解析目标父单 ID\n if (options.toUrl) {\n const linkInfo = parsePmLink(options.toUrl)\n if (!linkInfo) {\n outputError('无效的目标父单 PM 链接格式', options.pretty)\n process.exit(1)\n }\n targetId = parseInt(linkInfo.issueId, 10)\n targetHost = linkInfo.host\n } else if (options.to) {\n const cleanId = options.to.replace(/^#/, '')\n targetId = parseInt(cleanId, 10)\n if (isNaN(targetId)) {\n outputError('无效的目标父单 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 验证必填参数\n if (!sourceId) {\n outputError('请提供源父单 ID(--from)或 PM 链接(--from-url)', options.pretty)\n process.exit(1)\n }\n if (!targetId) {\n outputError('请提供目标父单 ID(--to)或 PM 链接(--to-url)', options.pretty)\n process.exit(1)\n }\n\n // 合并凭据,URL 中的 host 优先\n const creds = resolveCredentials({\n ...options,\n host: sourceHost || targetHost || options.host,\n })\n\n const validation = validateCredentials(creds, ['token', 'host'])\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 获取指派人邮箱\n let assignedToMail = options.assignedToMail\n if (!assignedToMail) {\n // 从配置中获取 userMail\n const config = readConfig(options.config)\n assignedToMail = config.default?.userMail\n if (options.profile && config.profiles[options.profile]?.userMail) {\n assignedToMail = config.profiles[options.profile].userMail\n }\n }\n\n if (!assignedToMail) {\n outputError('请提供指派人邮箱(--assigned-to-mail)或配置 userMail(pm-cli config set user-mail xxx)', options.pretty)\n process.exit(1)\n }\n\n const depth = parseInt(options.depth, 10)\n const dryRun = options.dryRun || false\n const skipExisting = options.skipExisting !== false\n\n // 执行同步\n const result = await issueService.syncChildIssues(\n creds.token!,\n creds.host!,\n creds.project || '',\n sourceId,\n targetId,\n assignedToMail,\n { dryRun, depth, skipExisting }\n )\n\n if (result.success && result.data) {\n const output = {\n success: true,\n dryRun,\n sourceParentId: sourceId,\n targetParentId: targetId,\n assignedToMail,\n summary: {\n totalCreated: result.data.totalCreated,\n totalSkipped: result.data.totalSkipped,\n totalFailed: result.data.totalFailed,\n },\n created: result.data.created,\n skipped: result.data.skipped.length > 0 ? result.data.skipped : undefined,\n failed: result.data.failed.length > 0 ? result.data.failed : undefined,\n }\n\n smartOutput(output, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-sync',\n identifier: `${sourceId}_to_${targetId}`,\n pretty: options.pretty,\n })\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '同步子单失败', options.pretty)\n process.exit(1)\n }\n })\n\n return issueCmd\n}\n","import { apiClient } from '../client/api-client.js'\nimport type { ApiResponse, Issue, IssueWithChildren, SyncResult } from '../models/types.js'\nimport logger from '../utils/logger.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.message || result.msg || result.api_error_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 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 // 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 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 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<{ id: number; name: string; value: unknown; identify?: string }>\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\"字段\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 {\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\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 logger.error(`❌ 创建子任务失败: ${taskName}`, createResult.message || createResult.api_error_msg)\n result.totalFailed++\n result.failed.push({\n sourceId,\n subject: taskName,\n error: createResult.message || createResult.api_error_msg || '创建失败',\n })\n }\n } catch (error) {\n logger.error(`❌ 创建子任务时发生错误: ${taskName}`, error)\n result.totalFailed++\n result.failed.push({\n sourceId,\n subject: taskName,\n error: error instanceof Error ? error.message : '未知错误',\n })\n }\n\n // 添加延迟避免 API 限流\n await sleep(500)\n }\n }\n }\n}\n\nexport const issueService = new IssueService()\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","/**\n * Issue 分级导出器\n * 将 Issue 数据导出为分级目录结构\n *\n * 输出结构:\n * /tmp/pm-cli_issue-get_{id}_{timestamp}/\n * ├── tree.summary.json # 完整树形结构,summary 级别\n * ├── standard/\n * │ └── {id}.json # 扁平单任务,standard 级别\n * └── complete/\n * └── {id}.json # 扁平单任务,complete 级别\n */\n\nimport { writeFileSync, mkdirSync, statSync } from 'fs'\nimport { join } from 'path'\nimport type { IssueWithChildren } from '../models/types.js'\nimport {\n generateSummaryTree,\n extractStandardFields,\n extractCompleteFields,\n flattenIssues,\n} from './preset-extractor.js'\n\n// ==================== 类型定义 ====================\n\nexport interface ExportResult {\n success: boolean\n exportDir: string\n fileCount: number\n totalSize: number\n files: {\n path: string\n size: number\n type: 'tree' | 'standard' | 'complete'\n }[]\n}\n\nexport interface ExportOptions {\n /** 树的最大深度,-1 表示无限 */\n depth?: number\n /** 是否格式化 JSON 输出 */\n pretty?: boolean\n}\n\n// ==================== 工具函数 ====================\n\n/**\n * 生成导出目录路径\n */\nexport function generateExportDir(command: string, identifier: string): string {\n const now = new Date()\n const timestamp = now.getFullYear().toString() +\n (now.getMonth() + 1).toString().padStart(2, '0') +\n now.getDate().toString().padStart(2, '0') +\n now.getHours().toString().padStart(2, '0') +\n now.getMinutes().toString().padStart(2, '0') +\n now.getSeconds().toString().padStart(2, '0')\n return `/tmp/pm-cli_${command}_${identifier}_${timestamp}`\n}\n\n// ==================== 导出函数 ====================\n\n/**\n * 导出 Issue 为分级目录结构\n */\nexport async function exportIssuePresets(\n issue: IssueWithChildren,\n exportDir: string,\n options: ExportOptions = {}\n): Promise<ExportResult> {\n const { depth = -1, pretty = false } = options\n const indent = pretty ? 2 : 0\n\n // 创建目录结构\n mkdirSync(exportDir, { recursive: true })\n mkdirSync(join(exportDir, 'standard'), { recursive: true })\n mkdirSync(join(exportDir, 'complete'), { recursive: true })\n\n const resultFiles: ExportResult['files'] = []\n\n // 1. 生成 tree.summary.json(唯一包含 children 的文件)\n const summaryTree = generateSummaryTree(issue, 0, depth)\n const summaryPath = join(exportDir, 'tree.summary.json')\n writeFileSync(summaryPath, JSON.stringify(summaryTree, null, indent), 'utf-8')\n const summarySize = statSync(summaryPath).size\n resultFiles.push({ path: 'tree.summary.json', size: summarySize, type: 'tree' })\n\n // 2. 收集所有任务(扁平化)\n const allIssues = flattenIssues(issue)\n\n // 3. 生成 standard/*.json(扁平单任务)\n for (const iss of allIssues) {\n const standardData = extractStandardFields(iss)\n const standardPath = join(exportDir, 'standard', `${iss.id}.json`)\n writeFileSync(standardPath, JSON.stringify(standardData, null, indent), 'utf-8')\n const standardSize = statSync(standardPath).size\n resultFiles.push({ path: `standard/${iss.id}.json`, size: standardSize, type: 'standard' })\n }\n\n // 4. 生成 complete/*.json(扁平单任务)\n for (const iss of allIssues) {\n const completeData = extractCompleteFields(iss)\n const completePath = join(exportDir, 'complete', `${iss.id}.json`)\n writeFileSync(completePath, JSON.stringify(completeData, null, indent), 'utf-8')\n const completeSize = statSync(completePath).size\n resultFiles.push({ path: `complete/${iss.id}.json`, size: completeSize, type: 'complete' })\n }\n\n // 计算总大小\n const totalSize = resultFiles.reduce((sum, f) => sum + f.size, 0)\n\n return {\n success: true,\n exportDir,\n fileCount: resultFiles.length,\n totalSize,\n files: resultFiles,\n }\n}\n\n/**\n * 生成 AI 读取指南输出\n */\nexport function generateAIGuideOutput(\n exportDir: string,\n fileCount: number,\n totalSize: number\n): string {\n const sizeKB = (totalSize / 1024).toFixed(2)\n\n return `\n✅ 导出完成!\n 📂 ${exportDir}/\n 📊 ${fileCount} 个文件, ${sizeKB} KB\n\n🤖 AI 读取指南:\n 1️⃣ 先读 tree.summary.json → 获取任务树 + 父子关系 + 基本信息\n 2️⃣ 需要详情时 → 读 standard/{id}.json 或 complete/{id}.json\n\n 📖 字段级别:\n • summary = id, subject, status, assigned_to, 工时, 预计提测时间\n • standard = summary + priority, tracker, project, 时间戳, 8个自定义字段\n • complete = 全部字段(含 description, author, journals 等)\n\n 💡 Token 优化:\n • 只看任务列表/进度 → tree.summary.json (~5k tokens)\n • 单任务优先级/来源 → standard/{id}.json (~3k tokens)\n • 任务详细描述/历史 → complete/{id}.json (~11k tokens)\n`.trim()\n}\n","/**\n * 预设级别字段提取器\n * 用于将完整的 Issue 数据按不同预设级别提取字段\n *\n * 三个预设级别:\n * - summary: 核心字段(10个)+ 1个关键自定义字段(预计提测时间)\n * - standard: summary + 扩展字段 + 8个常用自定义字段\n * - complete: 所有字段\n */\n\nimport type { IssueWithChildren } from '../models/types.js'\n\n// ==================== 类型定义 ====================\n\n/**\n * Summary 级别的 Issue 类型(最精简)\n */\nexport interface SummaryIssue {\n id: number\n subject: string\n status: {\n id: number\n name: string\n }\n assigned_to?: {\n id: number\n name: string\n }\n estimated_hours?: number\n spent_hours?: number\n done_ratio: number\n custom_fields: Array<{\n id: number\n name: string\n value: unknown\n }>\n children?: SummaryIssue[]\n}\n\n/**\n * Standard 级别的 Issue 类型(日常使用)\n * 不包含 children(扁平文件)\n */\nexport interface StandardIssue {\n id: number\n subject: string\n status: {\n id: number\n name: string\n is_closed?: boolean\n }\n assigned_to?: {\n id: number\n name: string\n mail?: string\n }\n estimated_hours?: number\n spent_hours?: number\n done_ratio: number\n priority?: {\n id: number\n name: string\n }\n tracker?: {\n id: number\n name: string\n }\n project?: {\n id: number\n name: string\n }\n created_on?: string\n updated_on?: string\n custom_fields: Array<{\n id: number\n name: string\n value: unknown\n }>\n}\n\n/**\n * Complete 级别的 Issue 类型(完整信息)\n * 不包含 children(扁平文件)\n */\nexport interface CompleteIssue {\n [key: string]: unknown\n}\n\n// ==================== 常量定义 ====================\n\n/**\n * Summary 级别包含的自定义字段 ID\n */\nconst SUMMARY_CUSTOM_FIELD_IDS = [84] // 预计提测时间\n\n/**\n * Standard 级别包含的自定义字段 ID\n */\nconst STANDARD_CUSTOM_FIELD_IDS = [\n 84, // 预计提测时间\n 286, // 需求大类\n 186, // 业务目标\n 77, // 需求来源\n 274, // 开发结束时间\n 19, // 跟进QA\n 86, // 需求延误\n 87, // 需求变更\n]\n\n// ==================== 提取函数 ====================\n\n/**\n * 提取 Summary 级别字段(单个任务,不含 children)\n */\nexport function extractSummaryFields(issue: IssueWithChildren): Omit<SummaryIssue, 'children'> {\n const rawIssue = issue as unknown as Record<string, unknown>\n\n // 提取 status\n const rawStatus = rawIssue.status as { id?: number; name?: string } | undefined\n const status = rawStatus\n ? { id: rawStatus.id ?? 0, name: rawStatus.name ?? '' }\n : { id: 0, name: '' }\n\n // 提取 assigned_to\n const rawAssignedTo = rawIssue.assigned_to as { id?: number; name?: string } | undefined\n const assigned_to = rawAssignedTo\n ? { id: rawAssignedTo.id ?? 0, name: rawAssignedTo.name ?? '' }\n : undefined\n\n // 提取自定义字段(仅保留 summary 级别的)\n const rawCustomFields = rawIssue.custom_fields as Array<{ id: number; name: string; value: unknown }> | undefined\n const custom_fields = (rawCustomFields || [])\n .filter(cf => SUMMARY_CUSTOM_FIELD_IDS.includes(cf.id))\n .map(cf => ({ id: cf.id, name: cf.name, value: cf.value }))\n\n return {\n id: issue.id,\n subject: issue.subject,\n status,\n assigned_to,\n estimated_hours: issue.estimated_hours,\n spent_hours: issue.spent_hours,\n done_ratio: issue.done_ratio,\n custom_fields,\n }\n}\n\n/**\n * 提取 Standard 级别字段(单个任务,不含 children)\n */\nexport function extractStandardFields(issue: IssueWithChildren): StandardIssue {\n const rawIssue = issue as unknown as Record<string, unknown>\n\n // 提取 status(包含 is_closed)\n const rawStatus = rawIssue.status as { id?: number; name?: string; is_closed?: boolean } | undefined\n const status = rawStatus\n ? { id: rawStatus.id ?? 0, name: rawStatus.name ?? '', is_closed: rawStatus.is_closed }\n : { id: 0, name: '' }\n\n // 提取 assigned_to(包含 mail)\n const rawAssignedTo = rawIssue.assigned_to as { id?: number; name?: string; mail?: string } | undefined\n const assigned_to = rawAssignedTo\n ? { id: rawAssignedTo.id ?? 0, name: rawAssignedTo.name ?? '', mail: rawAssignedTo.mail }\n : undefined\n\n // 提取 priority\n const rawPriority = rawIssue.priority as { id?: number; name?: string } | undefined\n const priority = rawPriority\n ? { id: rawPriority.id ?? 0, name: rawPriority.name ?? '' }\n : undefined\n\n // 提取 tracker\n const rawTracker = rawIssue.tracker as { id?: number; name?: string } | undefined\n const tracker = rawTracker\n ? { id: rawTracker.id ?? 0, name: rawTracker.name ?? '' }\n : undefined\n\n // 提取 project\n const rawProject = rawIssue.project as { id?: number; name?: string } | undefined\n const project = rawProject\n ? { id: rawProject.id ?? 0, name: rawProject.name ?? '' }\n : undefined\n\n // 提取自定义字段(仅保留 standard 级别的)\n const rawCustomFields = rawIssue.custom_fields as Array<{ id: number; name: string; value: unknown }> | undefined\n const custom_fields = (rawCustomFields || [])\n .filter(cf => STANDARD_CUSTOM_FIELD_IDS.includes(cf.id))\n .map(cf => ({ id: cf.id, name: cf.name, value: cf.value }))\n\n return {\n id: issue.id,\n subject: issue.subject,\n status,\n assigned_to,\n estimated_hours: issue.estimated_hours,\n spent_hours: issue.spent_hours,\n done_ratio: issue.done_ratio,\n priority,\n tracker,\n project,\n created_on: issue.created_on,\n updated_on: issue.updated_on,\n custom_fields,\n }\n}\n\n/**\n * 提取 Complete 级别字段(单个任务,不含 children)\n * 保留所有原始字段,但移除 children\n */\nexport function extractCompleteFields(issue: IssueWithChildren): CompleteIssue {\n const rawIssue = issue as unknown as Record<string, unknown>\n\n // 复制所有字段,但排除 children\n const result: CompleteIssue = {}\n for (const key of Object.keys(rawIssue)) {\n if (key !== 'children') {\n result[key] = rawIssue[key]\n }\n }\n\n return result\n}\n\n// ==================== 树形结构生成 ====================\n\n/**\n * 生成 Summary 级别的树形结构(唯一包含 children 的输出)\n * @param issue 根任务\n * @param currentDepth 当前深度\n * @param maxDepth 最大深度(-1 表示无限)\n */\nexport function generateSummaryTree(\n issue: IssueWithChildren,\n currentDepth: number = 0,\n maxDepth: number = -1\n): SummaryIssue {\n const node = extractSummaryFields(issue) as SummaryIssue\n\n // 检查是否达到深度限制\n if (maxDepth !== -1 && currentDepth >= maxDepth) {\n // 达到深度限制,children 为空数组\n node.children = []\n return node\n }\n\n // 递归处理子任务\n if (issue.children && issue.children.length > 0) {\n node.children = issue.children.map(child =>\n generateSummaryTree(child, currentDepth + 1, maxDepth)\n )\n } else {\n // 叶子节点,children 为空数组\n node.children = []\n }\n\n return node\n}\n\n// ==================== 扁平化工具 ====================\n\n/**\n * 收集树中所有任务(扁平化)\n */\nexport function flattenIssues(issue: IssueWithChildren, result: IssueWithChildren[] = []): IssueWithChildren[] {\n result.push(issue)\n if (issue.children) {\n for (const child of issue.children) {\n flattenIssues(child, result)\n }\n }\n return result\n}\n","import { Command } from 'commander'\nimport { timeEntryService } from '../../services/time-entry-service.js'\nimport { userService } from '../../services/user-service.js'\nimport { resolveCredentials, validateCredentials, getConfigValue } from '../../utils/config.js'\nimport { outputSuccess, outputError } from '../../utils/output.js'\nimport logger from '../../utils/logger.js'\n\n/**\n * 获取周的日期范围(周一到周五)\n * @param week 'current' | 'last' | 数字(相对当前周的偏移,负数表示之前的周)\n * @returns { from: string, to: string } 日期范围\n */\nfunction getWeekDateRange(week: string): { from: string; to: string; weekLabel: string } {\n const now = new Date()\n let offset = 0\n\n if (week === 'current') {\n offset = 0\n } else if (week === 'last') {\n offset = -1\n } else {\n offset = parseInt(week, 10) || 0\n }\n\n // 获取本周周一\n const dayOfWeek = now.getDay()\n const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek // 周日是0,需要特殊处理\n const monday = new Date(now)\n monday.setDate(now.getDate() + diffToMonday + offset * 7)\n monday.setHours(0, 0, 0, 0)\n\n // 获取本周周五\n const friday = new Date(monday)\n friday.setDate(monday.getDate() + 4)\n\n // 使用本地时间格式化,避免 UTC 时区偏差\n const formatDate = (d: Date) => {\n const year = d.getFullYear()\n const month = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n }\n const weekLabel = offset === 0 ? '本周' : offset === -1 ? '上周' : `${Math.abs(offset)}周前`\n\n return {\n from: formatDate(monday),\n to: formatDate(friday),\n weekLabel,\n }\n}\n\nexport function createTimeCommand(): Command {\n const timeCmd = new Command('time').description('工时管理')\n\n // time list\n timeCmd\n .command('list')\n .description('查询工时条目')\n .option('--from <date>', '开始日期 (YYYY-MM-DD)')\n .option('--to <date>', '结束日期 (YYYY-MM-DD)')\n .option('--user-id <id>', '用户 ID')\n .option('--activity-id <id>', '活动类型 ID')\n .option('--limit <limit>', '返回数量限制')\n .option('--offset <offset>', '偏移量')\n .option('--all-projects', '查询所有项目的工时')\n .option('--all-user', '查询所有用户的工时(不筛选,默认只查询当前用户)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n\n // --all-projects 模式只需要 token 和 host\n const requiredFields = options.allProjects ? ['token', 'host'] : ['token', 'host', 'project']\n const validation = validateCredentials(creds, requiredFields as ('token' | 'host' | 'project')[])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 如果使用 --all-projects,则查询所有项目的工时\n if (options.allProjects) {\n logger.info('查询所有项目的工时...')\n\n // 1. 获取所有项目列表\n const projectsResult = await userService.getProjects(creds.token!, creds.host!)\n if (!projectsResult.success || !projectsResult.data) {\n outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || '获取项目列表失败', options.pretty)\n process.exit(1)\n }\n\n const projects = projectsResult.data as unknown as Record<string, { name: string; id: number }>\n const projectNames = Object.values(projects).map((p) => p.name)\n logger.info(`找到 ${projectNames.length} 个项目`)\n\n // 处理用户筛选逻辑\n let userId: number | undefined\n if (!options.allUser) {\n // 默认使用配置中的用户 ID 进行筛选\n if (options.userId) {\n userId = parseInt(options.userId, 10)\n } else {\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n logger.info(`使用配置中的用户 ID: ${userId}`)\n } else {\n outputError('未配置用户 ID,请先设置: pm-cli config set user-id <您的用户ID>,或使用 --all-user 查询所有用户', options.pretty)\n process.exit(1)\n }\n }\n } else if (options.userId) {\n // 如果同时指定了 --all-user 和 --user-id,使用 --user-id\n userId = parseInt(options.userId, 10)\n logger.info(`使用指定的用户 ID: ${userId}`)\n }\n\n // 2. 逐个项目查询工时\n const allTimeEntries: unknown[] = []\n let totalCount = 0\n const projectSummary: { project: string; count: number; hours: number }[] = []\n\n for (const projectName of projectNames) {\n const result = await timeEntryService.queryTimeEntries({\n token: creds.token!,\n host: creds.host!,\n project: projectName,\n from_date: options.from,\n to_date: options.to,\n user_id: userId,\n activity_id: options.activityId ? parseInt(options.activityId, 10) : undefined,\n limit: options.limit ? parseInt(options.limit, 10) : 100,\n offset: options.offset ? parseInt(options.offset, 10) : undefined,\n })\n\n if (result.success && result.data) {\n const data = result.data as { time_entries?: { hours: number }[]; total_count?: number }\n if (data.time_entries && data.time_entries.length > 0) {\n allTimeEntries.push(...data.time_entries)\n const projectHours = data.time_entries.reduce((sum, e) => sum + (e.hours || 0), 0)\n projectSummary.push({\n project: projectName,\n count: data.time_entries.length,\n hours: projectHours,\n })\n totalCount += data.total_count || data.time_entries.length\n logger.info(`${projectName}: ${data.time_entries.length} 条工时记录`)\n }\n }\n }\n\n // 3. 汇总结果\n const totalHours = allTimeEntries.reduce<number>((sum, e) => sum + ((e as { hours: number }).hours || 0), 0)\n outputSuccess({\n total_count: totalCount,\n total_hours: Math.round(totalHours * 100) / 100,\n project_summary: projectSummary,\n time_entries: allTimeEntries,\n })\n } else {\n // 普通模式:查询单个项目的工时\n const result = await timeEntryService.queryTimeEntries({\n token: creds.token!,\n host: creds.host!,\n project: creds.project!,\n from_date: options.from,\n to_date: options.to,\n user_id: options.userId ? parseInt(options.userId, 10) : undefined,\n activity_id: options.activityId ? parseInt(options.activityId, 10) : undefined,\n limit: options.limit ? parseInt(options.limit, 10) : undefined,\n offset: options.offset ? parseInt(options.offset, 10) : undefined,\n })\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询工时失败', options.pretty)\n process.exit(1)\n }\n }\n })\n\n // time create\n timeCmd\n .command('create')\n .description('创建工时条目')\n .requiredOption('--issue <id>', '问题 ID')\n .requiredOption('--days <days>', '工时(天)')\n .requiredOption('--activity <id>', '活动类型 ID(使用 pm-cli time options 获取可用值)')\n .option('--date <date>', '日期 (YYYY-MM-DD),默认今天')\n .option('--comments <comments>', '备注')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 获取用户 ID\n let userId: number | undefined\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n }\n\n if (!userId) {\n outputError('缺少用户 ID,请先设置: pm-cli config set user-id <您的用户ID>', options.pretty)\n process.exit(1)\n }\n\n const issueId = parseInt(options.issue.replace(/^#/, ''), 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n\n const days = parseFloat(options.days)\n if (isNaN(days) || days <= 0) {\n outputError('无效的工时数', options.pretty)\n process.exit(1)\n }\n\n const activityId = parseInt(options.activity, 10)\n if (isNaN(activityId)) {\n outputError('无效的活动类型 ID,使用 pm-cli time options 获取可用值', options.pretty)\n process.exit(1)\n }\n\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n issue_id: issueId,\n hours: days, // API 参数名为 hours,但单位是天\n spent_on: options.date || new Date().toISOString().split('T')[0],\n user_id: userId, // 用户 ID(必填)\n activity_id: activityId, // 活动类型 ID(必填)\n bulk_create: 'true', // 固定为 true\n }\n\n if (options.comments) params.comments = options.comments\n\n const result = await timeEntryService.createTimeEntry(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '创建工时失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time update <id>\n timeCmd\n .command('update <id>')\n .description('更新工时条目')\n .requiredOption('--issue <id>', '问题 ID(必填)')\n .requiredOption('--days <days>', '工时(天)(必填)')\n .requiredOption('--activity <id>', '活动类型 ID(必填)')\n .requiredOption('--date <date>', '日期 (YYYY-MM-DD)(必填)')\n .option('--comments <comments>', '备注')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 获取用户 ID\n let userId: number | undefined\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n }\n\n if (!userId) {\n outputError('缺少用户 ID,请先设置: pm-cli config set user-id <您的用户ID>', options.pretty)\n process.exit(1)\n }\n\n const timeEntryId = parseInt(id, 10)\n if (isNaN(timeEntryId)) {\n outputError('无效的工时条目 ID', options.pretty)\n process.exit(1)\n }\n\n const issueId = parseInt(options.issue.replace(/^#/, ''), 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n id: timeEntryId, // 工时 ID\n issue_id: issueId, // 问题 ID(必填)\n hours: parseFloat(options.days), // 工时(必填)\n activity_id: parseInt(options.activity, 10), // 活动类型(必填)\n spent_on: options.date, // 日期(必填)\n user_id: userId, // 用户 ID(必填)\n updated_by: userId, // 操作用户 ID(必填)\n }\n\n if (options.comments) params.comments = options.comments\n\n const result = await timeEntryService.updateTimeEntry(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '更新工时失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time delete <id>\n timeCmd\n .command('delete <id>')\n .description('删除工时条目')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n const creds = resolveCredentials(options)\n // 删除工时只需要 token 和 host\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const timeEntryId = parseInt(id, 10)\n if (isNaN(timeEntryId)) {\n outputError('无效的工时条目 ID', options.pretty)\n process.exit(1)\n }\n\n const result = await timeEntryService.deleteTimeEntry(\n creds.token!,\n creds.host!,\n timeEntryId\n )\n\n if (result.success) {\n outputSuccess({ message: '工时条目已删除', id: timeEntryId }, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '删除工时失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time options\n timeCmd\n .command('options')\n .description('获取工时条目选项(活动类型列表等)')\n .option('--issue <id>', '问题 ID(可选,用于获取特定问题的工时选项)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const issueId = options.issue ? parseInt(options.issue.replace(/^#/, ''), 10) : undefined\n\n const result = await timeEntryService.getTimeEntryOptions(\n creds.token!,\n creds.host!,\n creds.project!,\n issueId\n )\n\n if (result.success && result.data) {\n // 格式化输出活动类型列表\n const data = result.data as { options?: { activities?: [string, number, number[]][] } }\n if (data.options?.activities) {\n const activities = data.options.activities.map(([name, id]) => ({ id, name }))\n outputSuccess({ activities }, options.pretty)\n } else {\n outputSuccess(result.data, options.pretty)\n }\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取选项失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time summary\n timeCmd\n .command('summary')\n .description('工时统计汇总(查询指定时间段的工时并计算缺口)')\n .option('--week <week>', '周选择: current (本周), last (上周), 或数字偏移', 'current')\n .option('--from <date>', '开始日期 (YYYY-MM-DD),与 --week 互斥')\n .option('--to <date>', '结束日期 (YYYY-MM-DD),与 --week 互斥')\n .option('--target <days>', '目标工时(天),默认 5', '5')\n .option('--detail', '显示完整工时条目详情')\n .option('--user-id <id>', '用户 ID(默认使用配置中的 user-id)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 确定日期范围\n let fromDate: string\n let toDate: string\n let periodLabel: string\n\n if (options.from && options.to) {\n // 使用自定义日期范围\n fromDate = options.from\n toDate = options.to\n periodLabel = `${fromDate} ~ ${toDate}`\n } else {\n // 使用周选择\n const weekRange = getWeekDateRange(options.week)\n fromDate = weekRange.from\n toDate = weekRange.to\n periodLabel = `${weekRange.weekLabel} (${fromDate} ~ ${toDate})`\n }\n\n // 确定用户 ID\n let userId: number | undefined\n if (options.userId) {\n userId = parseInt(options.userId, 10)\n } else {\n // 从配置中获取用户 ID\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n }\n }\n\n logger.info(`查询工时统计: ${periodLabel}`)\n if (userId) {\n logger.info(`用户 ID: ${userId}`)\n }\n\n // 获取所有项目列表\n const projectsResult = await userService.getProjects(creds.token!, creds.host!)\n if (!projectsResult.success || !projectsResult.data) {\n outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || '获取项目列表失败', options.pretty)\n process.exit(1)\n }\n\n const projects = projectsResult.data as unknown as Record<string, { name: string; id: number }>\n const projectNames = Object.values(projects).map((p) => p.name)\n logger.info(`查询 ${projectNames.length} 个项目...`)\n\n // 逐个项目查询工时\n const projectSummary: { project: string; hours: number }[] = []\n const allTimeEntries: unknown[] = []\n let totalHours = 0\n\n for (const projectName of projectNames) {\n const result = await timeEntryService.queryTimeEntries({\n token: creds.token!,\n host: creds.host!,\n project: projectName,\n from_date: fromDate,\n to_date: toDate,\n user_id: userId,\n limit: 1000,\n })\n\n if (result.success && result.data) {\n const data = result.data as { time_entries?: { hours: number }[] }\n if (data.time_entries && data.time_entries.length > 0) {\n const projectHours = data.time_entries.reduce((sum, e) => sum + (e.hours || 0), 0)\n if (projectHours > 0) {\n projectSummary.push({\n project: projectName,\n hours: Math.round(projectHours * 100) / 100,\n })\n totalHours += projectHours\n }\n // 收集完整工时条目用于 --detail 选项\n allTimeEntries.push(...data.time_entries)\n }\n }\n }\n\n // 计算目标和缺口\n const targetHours = parseFloat(options.target) || 5\n const remainingHours = Math.max(0, targetHours - totalHours)\n totalHours = Math.round(totalHours * 100) / 100\n\n // 输出结果\n const output: Record<string, unknown> = {\n period: periodLabel,\n userId: userId || '未指定',\n totalHours,\n targetHours,\n remainingHours: Math.round(remainingHours * 100) / 100,\n isFilled: remainingHours === 0,\n projectBreakdown: projectSummary.sort((a, b) => b.hours - a.hours),\n }\n\n // 如果指定了 --detail 选项,添加完整工时条目详情\n if (options.detail) {\n // 按日期排序\n output.timeEntries = allTimeEntries.sort((a, b) => {\n const dateA = (a as { spent_on?: string }).spent_on || ''\n const dateB = (b as { spent_on?: string }).spent_on || ''\n return dateA.localeCompare(dateB)\n })\n }\n\n outputSuccess(output, options.pretty)\n })\n\n return timeCmd\n}\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 { Command } from 'commander'\nimport { userService } from '../../services/user-service.js'\nimport { resolveCredentials, validateCredentials } from '../../utils/config.js'\nimport { outputSuccess, outputError } from '../../utils/output.js'\n\nexport function createProjectCommand(): Command {\n const projectCmd = new Command('project').description('项目管理')\n\n // project list\n projectCmd\n .command('list')\n .description('获取项目列表')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.getProjects(creds.token!, creds.host!)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取项目列表失败', options.pretty)\n process.exit(1)\n }\n })\n\n // project users\n projectCmd\n .command('users')\n .description('获取项目用户列表')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.getProjectUsers(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取用户列表失败', options.pretty)\n process.exit(1)\n }\n })\n\n // project info\n projectCmd\n .command('info')\n .description('获取主机信息')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.getHostInfo(creds.token!, creds.host!)\n\n if (result.success) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取主机信息失败', options.pretty)\n process.exit(1)\n }\n })\n\n return projectCmd\n}\n"],"mappings":";;;AAAA,SAAS,WAAAA,gBAAe;;;ACAxB,SAAS,eAAe;;;ACExB,IAAI,eAAyB;AAE7B,IAAM,SAAmC;AAAA,EACvC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;AAEA,SAAS,UAAU,OAA0B;AAC3C,SAAO,OAAO,KAAK,KAAK,OAAO,YAAY;AAC7C;AAEA,SAAS,cAAc,OAAe,SAAiB,MAAwB;AAC7E,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,UAAU,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AACpD,SAAO,IAAI,SAAS,MAAM,MAAM,YAAY,CAAC,KAAK,OAAO,GAAG,OAAO;AACrE;AAEO,IAAM,SAAS;AAAA,EACpB,SAAS,OAAuB;AAC9B,mBAAe;AAAA,EACjB;AAAA,EAEA,WAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,SAAiB,MAAsB;AAC3C,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ,MAAM,cAAc,SAAS,SAAS,IAAI,CAAC;AAAA,IACrD;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,MAAsB;AAC1C,QAAI,UAAU,MAAM,GAAG;AACrB,cAAQ,MAAM,cAAc,QAAQ,SAAS,IAAI,CAAC;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,MAAsB;AAC1C,QAAI,UAAU,MAAM,GAAG;AACrB,cAAQ,MAAM,cAAc,QAAQ,SAAS,IAAI,CAAC;AAAA,IACpD;AAAA,EACF;AAAA,EAEA,MAAM,SAAiB,MAAsB;AAC3C,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ,MAAM,cAAc,SAAS,SAAS,IAAI,CAAC;AAAA,IACrD;AAAA,EACF;AACF;AAEA,IAAO,iBAAQ;;;AClDR,IAAM,YAAN,MAAM,WAAU;AAAA,EACrB,OAAwB,WAAW;AAAA,EACnC,OAAwB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAK1C,MAAM,IAAO,UAAkB,SAAkC,CAAC,GAA4B;AAC5F,QAAI;AACF,qBAAO,MAAM,yBAAe,QAAQ,IAAI,EAAE,OAAO,CAAC;AAElD,YAAM,cAAc,KAAK,iBAAiB,MAAM;AAChD,YAAM,MAAM,GAAG,WAAU,QAAQ,IAAI,QAAQ,GAAG,cAAc,IAAI,WAAW,KAAK,EAAE;AAEpF,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,WAAU,eAAe;AAEhF,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAChC,QAAQ;AAAA,UACR,QAAQ,WAAW;AAAA,QACrB,CAAC;AAED,qBAAa,SAAS;AAEtB,YAAI,CAAC,SAAS,IAAI;AAChB,yBAAO,MAAM,kCAAc,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AACnE,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,mDAAgB,SAAS,MAAM;AAAA,UAC1C;AAAA,QACF;AAEA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,uBAAO,MAAM,qBAAW,IAAI;AAC5B,eAAO;AAAA,MACT,UAAE;AACA,qBAAa,SAAS;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,aAAO,KAAK,YAAY,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAQ,UAAkB,SAAkC,CAAC,GAA4B;AAC7F,QAAI;AACF,qBAAO,MAAM,0BAAgB,QAAQ,IAAI,EAAE,OAAO,CAAC;AAEnD,YAAM,MAAM,GAAG,WAAU,QAAQ,IAAI,QAAQ;AAC7C,YAAM,WAAW,KAAK,cAAc,MAAM;AAE1C,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,WAAU,eAAe;AAEhF,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAChC,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,UAClB;AAAA,UACA,MAAM;AAAA,UACN,QAAQ,WAAW;AAAA,QACrB,CAAC;AAED,qBAAa,SAAS;AAEtB,YAAI,CAAC,SAAS,IAAI;AAChB,yBAAO,MAAM,kCAAc,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AACnE,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,mDAAgB,SAAS,MAAM;AAAA,UAC1C;AAAA,QACF;AAEA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,uBAAO,MAAM,qBAAW,IAAI;AAC5B,eAAO;AAAA,MACT,UAAE;AACA,qBAAa,SAAS;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,aAAO,KAAK,YAAY,KAAK;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAe,OAAgC;AACrD,QAAI,iBAAiB,OAAO;AAC1B,UAAI,MAAM,SAAS,cAAc;AAC/B,uBAAO,MAAM,0BAAM;AACnB,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS;AAAA,QACX;AAAA,MACF;AACA,qBAAO,MAAM,6BAAS,KAAK;AAC3B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,4BAAQ,MAAM,OAAO;AAAA,MAChC;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,QAAyC;AAChE,WAAO,OAAO,QAAQ,MAAM,EACzB,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,QAAQ,UAAU,MAAS,EAC3D,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,OAAO,KAAK,CAAC,CAAC,EAAE,EACvF,KAAK,GAAG;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,QAAyC;AAC7D,WAAO,OAAO,QAAQ,MAAM,EACzB,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,QAAQ,UAAU,MAAS,EAC3D,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,OAAO,KAAK,CAAC,CAAC,EAAE,EACvF,KAAK,GAAG;AAAA,EACb;AACF;AAGO,IAAM,YAAY,IAAI,UAAU;;;ACrIhC,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA,EAIvB,MAAM,eAAe,OAAe,MAAc,SAAgD;AAChG,mBAAO,KAAK,4BAAQ,EAAE,MAAM,QAAQ,CAAC;AACrC,WAAO,MAAM,UAAU,IAAI,WAAW,EAAE,OAAO,KAAK,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,OAAe,MAA+C;AAC9E,mBAAO,KAAK,wCAAU,EAAE,KAAK,CAAC;AAC9B,WAAO,MAAM,UAAU,IAAe,WAAW,EAAE,OAAO,KAAK,CAAC;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,OAAe,MAAc,SAA+C;AAChG,mBAAO,KAAK,wCAAU,EAAE,MAAM,QAAQ,CAAC;AACvC,WAAO,MAAM,UAAU,IAAY,QAAQ,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,OAAe,MAA6C;AAC5E,mBAAO,KAAK,wCAAU,EAAE,KAAK,CAAC;AAC9B,WAAO,MAAM,UAAU,IAAI,QAAQ,EAAE,OAAO,KAAK,CAAC;AAAA,EACpD;AACF;AAEO,IAAM,cAAc,IAAI,YAAY;;;ACzC3C,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,YAAY,QAAQ;AAMpB,IAAM,qBAA0B,UAAQ,WAAQ,GAAG,WAAW,QAAQ;AACtE,IAAM,sBAAsB;AAM5B,IAAM,iBAAyC;AAAA,EAC7C,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA;AAAA,EAEjB,WAAW;AAAA,EACX,aAAa;AAAA,EACb,aAAa;AACf;AAKA,SAAS,mBAAmB,KAAqB;AAC/C,SAAO,eAAe,GAAG,KAAK;AAChC;AAKO,SAAS,cAAc,YAA6B;AAEzD,MAAI,YAAY;AACd,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,IAAI,eAAe;AAC7B,WAAO,QAAQ,IAAI;AAAA,EACrB;AAGA,SAAY,UAAK,oBAAoB,mBAAmB;AAC1D;AAKA,SAAS,gBAAgB,YAA0B;AACjD,QAAM,MAAW,aAAQ,UAAU;AACnC,MAAI,CAAI,cAAW,GAAG,GAAG;AACvB,IAAG,aAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACvC;AACF;AAKO,SAAS,WAAW,YAAgC;AACzD,QAAM,aAAa,cAAc,UAAU;AAE3C,MAAI,CAAI,cAAW,UAAU,GAAG;AAC9B,WAAO;AAAA,MACL,SAAS,CAAC;AAAA,MACV,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAEA,MAAI;AACF,UAAM,UAAa,gBAAa,YAAY,OAAO;AACnD,WAAO,KAAK,MAAM,OAAO;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,MACL,SAAS,CAAC;AAAA,MACV,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AACF;AAKO,SAAS,YAAY,QAAmB,YAA2B;AACxE,QAAM,aAAa,cAAc,UAAU;AAC3C,kBAAgB,UAAU;AAC1B,EAAG,iBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC9E;AAKO,SAAS,eAAe,KAAa,YAAyC;AACnF,QAAM,SAAS,WAAW,UAAU;AAGpC,MAAI;AACJ,MAAI,IAAI,WAAW,UAAU,GAAG;AAC9B,oBAAgB,mBAAmB,IAAI,QAAQ,YAAY,EAAE,CAAC;AAAA,EAChE,OAAO;AACL,oBAAgB,mBAAmB,GAAG;AAAA,EACxC;AAGA,SAAO,OAAO,QAAQ,aAA2C;AACnE;AAKO,SAAS,eAAe,KAAa,OAAe,YAA2B;AACpF,QAAM,SAAS,WAAW,UAAU;AAGpC,MAAI;AACJ,MAAI,IAAI,WAAW,UAAU,GAAG;AAC9B,oBAAgB,mBAAmB,IAAI,QAAQ,YAAY,EAAE,CAAC;AAAA,EAChE,OAAO;AACL,oBAAgB,mBAAmB,GAAG;AAAA,EACxC;AAGA,SAAO,QAAQ,aAA2C,IAAI;AAE9D,cAAY,QAAQ,UAAU;AAChC;AAKO,SAAS,WAAW,MAAc,YAAgE;AACvG,QAAM,SAAS,WAAW,UAAU;AACpC,SAAO,OAAO,SAAS,IAAI;AAC7B;AAKO,SAAS,WACd,MACA,SACA,YACM;AACN,QAAM,SAAS,WAAW,UAAU;AACpC,SAAO,SAAS,IAAI,IAAI;AACxB,cAAY,QAAQ,UAAU;AAChC;AAKO,SAAS,cAAc,MAAc,YAA8B;AACxE,QAAM,SAAS,WAAW,UAAU;AACpC,MAAI,OAAO,SAAS,IAAI,GAAG;AACzB,WAAO,OAAO,SAAS,IAAI;AAC3B,gBAAY,QAAQ,UAAU;AAC9B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAKO,SAAS,aAAa,YAA+B;AAC1D,QAAM,SAAS,WAAW,UAAU;AACpC,SAAO,OAAO,KAAK,OAAO,QAAQ;AACpC;AAMO,SAAS,mBAAmB,SAMV;AACvB,QAAM,SAAS,WAAW,QAAQ,MAAM;AAGxC,QAAM,SAA+B;AAAA,IACnC,OAAO,OAAO,QAAQ;AAAA,IACtB,MAAM,OAAO,QAAQ;AAAA,IACrB,SAAS,OAAO,QAAQ;AAAA,EAC1B;AAGA,MAAI,QAAQ,WAAW,OAAO,SAAS,QAAQ,OAAO,GAAG;AACvD,UAAM,UAAU,OAAO,SAAS,QAAQ,OAAO;AAC/C,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAC1C,QAAI,QAAQ,KAAM,QAAO,OAAO,QAAQ;AACxC,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAAA,EAChD;AAGA,MAAI,QAAQ,IAAI,cAAe,QAAO,QAAQ,QAAQ,IAAI;AAC1D,MAAI,QAAQ,IAAI,aAAc,QAAO,OAAO,QAAQ,IAAI;AACxD,MAAI,QAAQ,IAAI,gBAAiB,QAAO,UAAU,QAAQ,IAAI;AAG9D,MAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAC1C,MAAI,QAAQ,KAAM,QAAO,OAAO,QAAQ;AACxC,MAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAE9C,SAAO;AACT;AAKO,SAAS,oBACd,OACA,iBAAwC,CAAC,SAAS,QAAQ,SAAS,GAC5B;AACvC,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,gBAAgB;AAClC,QAAI,CAAC,MAAM,KAAK,GAAG;AACjB,cAAQ,KAAK,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,QAAQ,WAAW;AAAA,IAC1B;AAAA,EACF;AACF;;;ACzOA,SAAS,iBAAAC,sBAAqB;;;ACM9B,IAAM,UAAkC;AAAA;AAAA,EAEtC,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,eAAe;AAAA,EACf,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,WAAW;AAAA,EACX,eAAe;AAAA,EACf,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,kBAAkB;AAAA;AAAA,EAGlB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,WAAW;AAAA,EACX,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA;AAAA,EAGX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,UAAU;AAAA,EACV,aAAa;AAAA,EACb,UAAU;AAAA,EACV,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,aAAa;AAAA;AAAA,EAGb,MAAM;AAAA,EACN,SAAS;AAAA,EACT,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,eAAe;AAAA;AAAA,EAGf,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,SAAS;AAAA,EACT,KAAK;AAAA,EACL,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA;AAAA,EAGP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,iBAAiB;AAAA;AAAA,EAGjB,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,iBAAiB;AAAA;AAAA,EAGjB,cAAc;AAAA,EACd,UAAU;AAAA;AAAA,EAGV,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AAAA;AAAA,EAGV,OAAO;AAAA,EACP,oBAAoB;AAAA,EACpB,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AACf;AAGA,IAAI,oBAAoB;AAKjB,SAAS,qBAAqB,SAAwB;AAC3D,sBAAoB;AACtB;AAKO,SAAS,sBAA+B;AAC7C,SAAO;AACT;AAOA,SAAS,qBAAqB,KAAc,MAAyB;AACnE,MAAI,QAAQ,QAAQ,QAAQ,OAAW;AACvC,MAAI,OAAO,QAAQ,SAAU;AAE7B,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,eAAW,QAAQ,KAAK;AACtB,2BAAqB,MAAM,IAAI;AAAA,IACjC;AACA;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAA8B,GAAG;AACzE,SAAK,IAAI,GAAG;AACZ,yBAAqB,OAAO,IAAI;AAAA,EAClC;AACF;AAOO,SAAS,gBAAgB,KAAwB;AACtD,QAAM,OAAO,oBAAI,IAAY;AAC7B,uBAAqB,KAAK,IAAI;AAC9B,SAAO,MAAM,KAAK,IAAI;AACxB;AAQO,SAAS,mBAAmB,KAAsC;AACvE,QAAM,WAAW,gBAAgB,GAAG;AACpC,QAAM,UAAkC,CAAC;AAEzC,aAAW,OAAO,UAAU;AAC1B,QAAI,QAAQ,GAAG,GAAG;AAChB,cAAQ,GAAG,IAAI,QAAQ,GAAG;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO;AACT;;;ADnLO,SAAS,mBAAmB,SAAiB,YAA4B;AAC9E,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,YAAY,IAAI,YAAY,EAAE,SAAS,KAC1C,IAAI,SAAS,IAAI,GAAG,SAAS,EAAE,SAAS,GAAG,GAAG,IAC/C,IAAI,QAAQ,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,IACxC,IAAI,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,IACzC,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,IAC3C,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AAC7C,SAAO,eAAe,OAAO,IAAI,UAAU,IAAI,SAAS;AAC1D;AAOA,SAAS,sBAAyB,QAAgF;AAChH,MAAI,CAAC,oBAAoB,GAAG;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,mBAAmB,MAAM;AACzC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc;AAAA,EAChB;AACF;AASO,SAAS,aAAgB,MAAS,UAAkB,SAAS,OAAe;AACjF,QAAM,SAAuB;AAAA,IAC3B,SAAS;AAAA,IACT;AAAA,EACF;AACA,QAAM,cAAc,sBAAsB,MAAM;AAChD,EAAAC,eAAc,UAAU,KAAK,UAAU,aAAa,MAAM,SAAS,IAAI,CAAC,GAAG,OAAO;AAClF,SAAO;AACT;AAQO,SAAS,YACd,MACA,SAOoB;AAEpB,MAAI,QAAQ,QAAQ;AAClB,kBAAc,MAAM,QAAQ,MAAM;AAClC,WAAO;AAAA,EACT;AAGA,QAAM,WAAW,QAAQ,UAAU,mBAAmB,QAAQ,SAAS,QAAQ,UAAU;AAGzF,eAAa,MAAM,UAAU,QAAQ,MAAM;AAG3C,UAAQ,IAAI,KAAK,UAAU,EAAE,SAAS,MAAM,QAAQ,SAAS,GAAG,MAAM,QAAQ,SAAS,IAAI,CAAC,CAAC;AAE7F,SAAO;AACT;AAOO,SAAS,cAAiB,MAAS,SAAS,OAAa;AAC9D,QAAM,SAAuB;AAAA,IAC3B,SAAS;AAAA,IACT;AAAA,EACF;AACA,QAAM,cAAc,sBAAsB,MAAM;AAChD,UAAQ,IAAI,KAAK,UAAU,aAAa,MAAM,SAAS,IAAI,CAAC,CAAC;AAC/D;AAOO,SAAS,YAAY,OAAe,SAAS,OAAa;AAC/D,QAAM,SAAoB;AAAA,IACxB,SAAS;AAAA,IACT;AAAA,EACF;AAEA,QAAM,cAAc,sBAAsB,MAAM;AAChD,UAAQ,IAAI,KAAK,UAAU,aAAa,MAAM,SAAS,IAAI,CAAC,CAAC;AAC/D;;;AL/GO,SAAS,oBAA6B;AAC3C,QAAM,UAAU,IAAI,QAAQ,MAAM,EAC/B,YAAY,wDAAW,EACvB,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,YAAY;AAAA,MAC/B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAEA,QAAI,OAAO,SAAS;AAClB,oBAAc;AAAA,QACZ,SAAS;AAAA,QACT,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,MAAM,OAAO;AAAA,MACf,GAAG,QAAQ,MAAM;AAAA,IACnB,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,4BAAQ,QAAQ,MAAM;AAC1F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,SAAO;AACT;;;AO3CA,SAAS,WAAAC,gBAAe;AAajB,SAAS,sBAA+B;AAC7C,QAAM,YAAY,IAAIC,SAAQ,QAAQ,EAAE,YAAY,0BAAM;AAG1D,YACG,QAAQ,mBAAmB,EAC3B,YAAY,gCAAO,EACnB,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,CAAC,KAAa,OAAe,YAAmD;AACtF,mBAAe,KAAK,OAAO,QAAQ,MAAM;AACzC,kBAAc;AAAA,MACZ,SAAS,sBAAO,GAAG;AAAA,MACnB;AAAA,MACA;AAAA,MACA,YAAY,cAAc,QAAQ,MAAM;AAAA,IAC1C,GAAG,QAAQ,MAAM;AAAA,EACnB,CAAC;AAGH,YACG,QAAQ,WAAW,EACnB,YAAY,gCAAO,EACnB,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,CAAC,KAAa,YAAmD;AACvE,UAAM,QAAQ,eAAe,KAAK,QAAQ,MAAM;AAChD,QAAI,UAAU,QAAW;AACvB,oBAAc,EAAE,KAAK,MAAM,GAAG,QAAQ,MAAM;AAAA,IAC9C,OAAO;AACL,kBAAY,sBAAO,GAAG,uBAAQ,QAAQ,MAAM;AAC5C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,YACG,QAAQ,MAAM,EACd,YAAY,sCAAQ,EACpB,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,CAAC,YAAmD;AAC1D,UAAM,SAAS,WAAW,QAAQ,MAAM;AACxC,kBAAc;AAAA,MACZ,YAAY,cAAc,QAAQ,MAAM;AAAA,MACxC;AAAA,IACF,GAAG,QAAQ,MAAM;AAAA,EACnB,CAAC;AAGH,QAAM,aAAa,IAAIA,SAAQ,SAAS,EAAE,YAAY,sBAAY;AAGlE,aACG,QAAQ,YAAY,EACpB,YAAY,sBAAY,EACxB,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,mBAAmB,WAAW,EACrC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC;AAAA,IACC,CACE,MACA,YACG;AACH,YAAM,UAAkC,CAAC;AACzC,UAAI,QAAQ,KAAM,SAAQ,OAAO,QAAQ;AACzC,UAAI,QAAQ,QAAS,SAAQ,UAAU,QAAQ;AAC/C,UAAI,QAAQ,MAAO,SAAQ,QAAQ,QAAQ;AAE3C,UAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,oBAAY,6FAA2C,QAAQ,MAAM;AACrE,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAEA,iBAAW,MAAM,SAAS,QAAQ,MAAM;AACxC,oBAAc;AAAA,QACZ,SAAS,WAAW,IAAI;AAAA,QACxB;AAAA,QACA;AAAA,MACF,GAAG,QAAQ,MAAM;AAAA,IACnB;AAAA,EACF;AAGF,aACG,QAAQ,eAAe,EACvB,YAAY,sBAAY,EACxB,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,CAAC,MAAc,YAAmD;AACxE,UAAM,UAAU,cAAc,MAAM,QAAQ,MAAM;AAClD,QAAI,SAAS;AACX,oBAAc,EAAE,SAAS,WAAW,IAAI,uBAAQ,KAAK,GAAG,QAAQ,MAAM;AAAA,IACxE,OAAO;AACL,kBAAY,WAAW,IAAI,uBAAQ,QAAQ,MAAM;AACjD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,aACG,QAAQ,MAAM,EACd,YAAY,mCAAe,EAC3B,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,CAAC,YAAmD;AAC1D,UAAM,WAAW,aAAa,QAAQ,MAAM;AAC5C,UAAM,iBAA0C,CAAC;AAEjD,eAAW,QAAQ,UAAU;AAC3B,qBAAe,IAAI,IAAI,WAAW,MAAM,QAAQ,MAAM;AAAA,IACxD;AAEA,kBAAc;AAAA,MACZ,UAAU;AAAA,MACV,OAAO,SAAS;AAAA,IAClB,GAAG,QAAQ,MAAM;AAAA,EACnB,CAAC;AAEH,YAAU,WAAW,UAAU;AAE/B,SAAO;AACT;;;ACzIA,SAAS,WAAAC,gBAAe;;;ACOxB,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAKO,IAAM,eAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAIxB,MAAM,SACJ,OACA,MACA,SACA,SACA,iBACA,kBAC6B;AAC7B,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ;AAGA,QAAI,QAAS,QAAO,UAAU;AAG9B,UAAM,WAAqB,CAAC;AAC5B,QAAI,gBAAiB,UAAS,KAAK,UAAU;AAC7C,QAAI,iBAAkB,UAAS,KAAK,WAAW;AAC/C,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO,UAAU,KAAK,UAAU,QAAQ;AAAA,IAC1C;AAEA,mBAAO,KAAK,wCAAU,EAAE,MAAM,SAAS,QAAQ,CAAC;AAEhD,WAAO,MAAM,UAAU,IAAW,SAAS,MAAM;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,QAA8D;AAC9E,mBAAO,KAAK,4BAAQ,EAAE,OAAO,CAAC;AAC9B,WAAO,MAAM,UAAU,KAAY,gBAAgB,MAAM;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,QAA8D;AAC9E,mBAAO,KAAK,4BAAQ,EAAE,OAAO,CAAC;AAC9B,WAAO,MAAM,UAAU,KAAY,gBAAgB,MAAM;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,OACA,MACA,SACA,SAC+B;AAC/B,mBAAO,KAAK,wCAAU,EAAE,MAAM,SAAS,QAAQ,CAAC;AAChD,WAAO,MAAM,UAAU,IAAI,yBAAyB;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBACJ,OACA,MACA,SAC+B;AAC/B,mBAAO,KAAK,oDAAY,EAAE,MAAM,QAAQ,CAAC;AACzC,WAAO,MAAM,UAAU,KAAK,2BAA2B,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YACJ,OACA,MACA,SACA,SACA,OACA,QAC+B;AAC/B,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,IACZ;AAEA,QAAI,UAAU,OAAW,QAAO,QAAQ;AACxC,QAAI,WAAW,OAAW,QAAO,SAAS;AAE1C,mBAAO,KAAK,kCAAS,EAAE,MAAM,SAAS,QAAQ,CAAC;AAC/C,WAAO,MAAM,UAAU,KAAc,gBAAgB,MAAM;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,OACA,MACA,SACA,MACA,cAC+B;AAC/B,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL;AAEA,mBAAO,KAAK,qCAAY,EAAE,MAAM,SAAS,KAAK,CAAC;AAC/C,WAAO,MAAM,UAAU,KAAc,mBAAmB,MAAM;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,qBACJ,OACA,MACA,SACA,SACA,QAAgB,IAChBC,gBAAuB,GACkB;AACzC,mBAAO,KAAK,oDAAY,EAAE,MAAM,SAAS,SAAS,OAAO,cAAAA,cAAa,CAAC;AAGvE,UAAM,cAAc,MAAM,KAAK,SAAS,OAAO,MAAM,SAAS,SAAS,IAAI;AAC3E,QAAI,CAAC,YAAY,WAAW,CAAC,YAAY,MAAM;AAC7C,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,YAAY;AAC1B,UAAM,QAAQA;AAGd,QAAIA,iBAAgB,OAAO;AACzB,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM;AAAA,IACtC;AAIA,UAAM,cAAe,MAA0D;AAC/E,QAAI,CAAC,eAAe,YAAY,WAAW,GAAG;AAC5C,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM;AAAA,IACtC;AAGA,UAAM,oBAAoB,YACvB,OAAO,WAAS,MAAM,EAAE,EACxB,IAAI,WAAS,MAAM,EAAE;AAExB,QAAI,kBAAkB,WAAW,GAAG;AAClC,aAAO,EAAE,SAAS,MAAM,MAAM,MAAM;AAAA,IACtC;AAEA,mBAAO,KAAK,oDAAY,EAAE,UAAU,SAAS,YAAY,kBAAkB,QAAQ,OAAOA,cAAa,CAAC;AAGxG,UAAM,mBAAmB,kBAAkB;AAAA,MAAI,aAC7C,KAAK,qBAAqB,OAAO,MAAM,SAAS,SAAS,OAAOA,gBAAe,CAAC;AAAA,IAClF;AAEA,UAAM,kBAAkB,MAAM,QAAQ,IAAI,gBAAgB;AAG1D,UAAM,WAAW,gBACd,OAAO,OAAK,EAAE,WAAW,EAAE,IAAI,EAC/B,IAAI,OAAK,EAAE,IAAK;AAEnB,WAAO,EAAE,SAAS,MAAM,MAAM,MAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,kBACJ,OACA,MACA,SACA,UACgC;AAEhC,UAAM,UAAkE;AAAA,MACtE,WAAW,EAAE,UAAU,KAAK,QAAQ,CAAC,SAAS,SAAS,CAAC,EAAE;AAAA,IAC5D;AAEA,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,SAAS,KAAK,UAAU,OAAO;AAAA,MAC/B,GAAG,KAAK,UAAU,CAAC,IAAI,CAAC;AAAA,MACxB,UAAU;AAAA,IACZ;AAEA,mBAAO,KAAK,wCAAU,EAAE,MAAM,SAAS,SAAS,CAAC;AACjD,UAAM,SAAS,MAAM,UAAU,IAAa,mBAAmB,MAAM;AAErE,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,YAAM,OAAO,OAAO;AACpB,UAAI,KAAK,MAAM,MAAM;AACnB,cAAM,MAAM,KAAK,KAAK,KAAK,IAAI,UAAQ,KAAK,EAAE;AAC9C,eAAO,EAAE,SAAS,MAAM,MAAM,IAAI;AAAA,MACpC;AAAA,IACF;AAEA,WAAO,EAAE,SAAS,MAAM,MAAM,CAAC,EAAE;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cACJ,OACA,MACA,SACA,QACA,cACA,UAAkB,KACa;AAE/B,UAAM,UAAkE;AAAA,MACtE,SAAS,EAAE,UAAU,KAAK,QAAQ,CAAC,OAAO,SAAS,CAAC,EAAE;AAAA,IACxD;AAEA,QAAI,cAAc;AAChB,cAAQ,iBAAiB,EAAE,UAAU,KAAK,QAAQ,CAAC,aAAa,SAAS,CAAC,EAAE;AAAA,IAC9E;AAEA,UAAM,SAAkC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,SAAS,KAAK,UAAU,OAAO;AAAA,MAC/B,GAAG,KAAK,UAAU,CAAC,MAAM,WAAW,UAAU,WAAW,mBAAmB,cAAc,eAAe,QAAQ,CAAC;AAAA,MAClH,UAAU;AAAA,IACZ;AAEA,mBAAO,KAAK,kCAAS,EAAE,MAAM,SAAS,QAAQ,aAAa,CAAC;AAC5D,UAAM,SAAS,MAAM,UAAU,IAAa,mBAAmB,MAAM;AAGrE,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,YAAM,OAAO,OAAO;AACpB,UAAI,KAAK,MAAM,QAAQ,KAAK,KAAK,KAAK,SAAS,GAAG;AAChD,cAAM,WAAW,KAAK,KAAK,KAAK,IAAI,CAAC,SAAS,KAAK,EAAE;AACrD,cAAM,iBAAiB,MAAM,QAAQ;AAAA,UACnC,SAAS,IAAI,OAAO,OAAO;AACzB,kBAAM,cAAc,MAAM,KAAK,SAAS,OAAO,MAAM,SAAS,EAAE;AAChE,gBAAI,YAAY,WAAW,YAAY,MAAM;AAC3C,qBAAO;AAAA,gBACL,IAAI,YAAY,KAAK;AAAA,gBACrB,SAAS,YAAY,KAAK;AAAA,gBAC1B,QAAQ,YAAY,KAAK,QAAQ;AAAA,gBACjC,SAAS,YAAY,KAAK,SAAS;AAAA,gBACnC,iBAAiB,YAAY,KAAK;AAAA,gBAClC,YAAY,YAAY,KAAK;AAAA,gBAC7B,aAAa,YAAY,KAAK,aAAa;AAAA,gBAC3C,WAAY,YAAY,KAA2C;AAAA,cACrE;AAAA,YACF;AACA,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AACA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,OAAO,eAAe,OAAO,OAAO,EAAE;AAAA,YACtC,QAAQ,eAAe,OAAO,OAAO;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBACJ,OACA,MACA,SACA,UACA,SAKiH;AACjH,UAAM,EAAE,kBAAkB,OAAO,mBAAmB,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AAErF,mBAAO,KAAK,oDAAY,EAAE,MAAM,SAAS,OAAO,SAAS,QAAQ,SAAS,CAAC;AAG3E,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,SAAS,IAAI,OAAO,YAAY;AAC9B,YAAI;AACF,cAAI;AAEJ,cAAI,mBAAmB,QAAQ,GAAG;AAEhC,qBAAS,MAAM,KAAK,qBAAqB,OAAO,MAAM,SAAS,SAAS,KAAK;AAAA,UAC/E,OAAO;AAEL,qBAAS,MAAM,KAAK,SAAS,OAAO,MAAM,SAAS,SAAS,iBAAiB,gBAAgB;AAAA,UAC/F;AAEA,cAAI,OAAO,WAAW,OAAO,MAAM;AACjC,mBAAO,EAAE,IAAI,SAAS,SAAS,MAAM,MAAM,OAAO,KAAK;AAAA,UACzD,OAAO;AACL,mBAAO;AAAA,cACL,IAAI;AAAA,cACJ,SAAS;AAAA,cACT,OAAO,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB;AAAA,YACjE;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,IAAI;AAAA,YACJ,SAAS;AAAA,YACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAClD;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,eAAe,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACtD,UAAM,YAAY,QAAQ,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE;AAEpD,mBAAO,KAAK,wCAAU,EAAE,OAAO,SAAS,QAAQ,SAAS,cAAc,MAAM,UAAU,CAAC;AAExF,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBACJ,OACA,MACA,SACA,gBACA,gBACA,gBACA,SAKkC;AAClC,UAAM,EAAE,SAAS,OAAO,QAAQ,IAAI,eAAe,KAAK,IAAI,WAAW,CAAC;AAExE,mBAAO,KAAK,wCAAU;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,SAAqB;AAAA,MACzB,cAAc;AAAA,MACd,cAAc;AAAA,MACd,aAAa;AAAA,MACb,SAAS,CAAC;AAAA,MACV,SAAS,CAAC;AAAA,MACV,QAAQ,CAAC;AAAA,IACX;AAEA,QAAI;AAEF,qBAAO,KAAK,yFAAmB;AAC/B,YAAM,eAAe,MAAM,KAAK,qBAAqB,OAAO,MAAM,SAAS,gBAAgB,KAAK;AAChG,UAAI,CAAC,aAAa,WAAW,CAAC,aAAa,MAAM;AAC/C,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS,mCAAU,cAAc,kBAAQ,aAAa,WAAW,0BAAM;AAAA,QACzE;AAAA,MACF;AACA,YAAM,cAAc,aAAa;AAGjC,qBAAO,KAAK,iEAAe;AAC3B,YAAM,eAAe,MAAM,KAAK,SAAS,OAAO,MAAM,SAAS,gBAAgB,OAAO,KAAK;AAC3F,UAAI,CAAC,aAAa,WAAW,CAAC,aAAa,MAAM;AAC/C,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS,yCAAW,cAAc,kBAAQ,aAAa,WAAW,0BAAM;AAAA,QAC1E;AAAA,MACF;AACA,YAAM,mBAAmB,aAAa;AAGtC,UAAI,oBAAiC,oBAAI,IAAI;AAC7C,UAAI,cAAc;AAChB,uBAAO,KAAK,mFAAkB;AAC9B,cAAM,iBAAiB,MAAM,KAAK,qBAAqB,OAAO,MAAM,SAAS,gBAAgB,KAAK;AAClG,YAAI,eAAe,WAAW,eAAe,MAAM;AACjD,8BAAoB,KAAK,oBAAoB,eAAe,IAAI;AAChE,yBAAO,KAAK,wCAAU,kBAAkB,IAAI,qBAAM;AAAA,QACpD;AAAA,MACF;AAGA,qBAAO,KAAK,qDAAa;AACzB,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,qBAAO,KAAK,4BAAQ;AAAA,QAClB,cAAc,OAAO;AAAA,QACrB,cAAc,OAAO;AAAA,QACrB,aAAa,OAAO;AAAA,MACtB,CAAC;AAED,aAAO,EAAE,SAAS,MAAM,MAAM,OAAO;AAAA,IACvC,SAAS,OAAO;AACd,qBAAO,MAAM,0DAAa,KAAK;AAC/B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,2DAAc,iBAAiB,QAAQ,MAAM,UAAU,0BAAM;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,OAAuC;AACjE,UAAM,QAAQ,oBAAI,IAAY;AAG9B,QAAI,MAAM,SAAS,KAAK,GAAG;AACzB,YAAM,IAAI,MAAM,QAAQ,KAAK,CAAC;AAAA,IAChC;AAGA,eAAW,SAAS,MAAM,YAAY,CAAC,GAAG;AACxC,YAAM,aAAa,KAAK,oBAAoB,KAAK;AACjD,iBAAW,QAAQ,CAAC,SAAS,MAAM,IAAI,IAAI,CAAC;AAAA,IAC9C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,sBACZ,OACA,MACA,SACA,YACA,gBACA,kBACA,mBACA,gBACA,QACA,QACe;AAEf,eAAW,SAAS,WAAW,YAAY,CAAC,GAAG;AAC7C,YAAM,WAAW,MAAM,SAAS,KAAK,KAAK;AAC1C,YAAM,WAAW,MAAM;AAGvB,UAAI,kBAAkB,IAAI,QAAQ,GAAG;AACnC,uBAAO,KAAK,mEAAiB,QAAQ,EAAE;AACvC,eAAO;AACP,eAAO,QAAQ,KAAK;AAAA,UAClB;AAAA,UACA,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AACD;AAAA,MACF;AAGA,YAAM,aAAa,CAAC,MAAM,YAAY,MAAM,SAAS,WAAW;AAChE,YAAM,eAAwC;AAAA,QAC5C;AAAA,QACA;AAAA,QACA;AAAA,QACA,iBAAiB;AAAA,QACjB,SAAS;AAAA;AAAA,QAET,SAAU,iBAAgE,SAAS;AAAA,QACnF,QAAQ;AAAA;AAAA,QAER,kBAAkB;AAAA;AAAA,QAElB,iBAAiB,aAAa,MAAM,kBAAkB;AAAA;AAAA,QAEtD,aAAa,MAAM,UAAU;AAAA,MAC/B;AAGA,YAAM,oBAAoB;AAC1B,UAAI,kBAAkB,eAAe,MAAM;AACzC,qBAAa,UAAU,kBAAkB,cAAc;AAAA,MACzD;AAGA,YAAM,yBAAyB;AAG/B,UAAI,uBAAuB,iBAAiB,uBAAuB,cAAc,SAAS,GAAG;AAC3F,cAAM,iBAA0C,CAAC;AACjD,cAAM,eAAyB,CAAC;AAEhC,mBAAW,SAAS,uBAAuB,eAAe;AACxD,cAAI,MAAM,UAAU,QAAQ,MAAM,UAAU,UAAa,MAAM,UAAU,IAAI;AAE3E,gBAAI,MAAM,aAAa,kBAAkB;AACvC,oBAAM,eAAe,MAAM;AAC3B,kBAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,2BAAW,QAAQ,cAAc;AAC/B,sBAAI,KAAK,MAAM,MAAM;AACnB,iCAAa,KAAK,KAAK,KAAK,IAAI;AAAA,kBAClC;AAAA,gBACF;AAAA,cACF;AAAA,YACF,OAAO;AACL,6BAAe,MAAM,EAAE,IAAI,MAAM;AAAA,YACnC;AAAA,UACF;AAAA,QACF;AAEA,YAAI,OAAO,KAAK,cAAc,EAAE,SAAS,GAAG;AAC1C,uBAAa,eAAe,KAAK,UAAU,cAAc;AAAA,QAC3D;AACA,YAAI,aAAa,SAAS,GAAG;AAC3B,uBAAa,UAAU;AAAA,QACzB;AAAA,MACF;AAEA,qBAAO,KAAK,+CAAY,QAAQ,IAAI,EAAE,UAAU,gBAAgB,WAAW,CAAC;AAE5E,UAAI,QAAQ;AAEV,uBAAO,KAAK,wDAAgB,QAAQ,EAAE;AACtC,eAAO;AACP,eAAO,QAAQ,KAAK;AAAA,UAClB;AAAA,UACA,OAAO;AAAA;AAAA,UACP,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAGD,YAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,gBAAM,KAAK;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF,OAAO;AAEL,YAAI;AACF,gBAAM,eAAe,MAAM,KAAK,YAAY,YAAY;AAExD,cAAI,aAAa,WAAW,aAAa,MAAM;AAC7C,kBAAM,QAAQ,aAAa,KAAK;AAChC,2BAAO,KAAK,yDAAiB,KAAK,kBAAQ,QAAQ,EAAE;AACpD,mBAAO;AACP,mBAAO,QAAQ,KAAK;AAAA,cAClB;AAAA,cACA;AAAA,cACA,SAAS;AAAA,cACT,UAAU;AAAA,YACZ,CAAC;AAGD,gBAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,6BAAO,KAAK,4CAAY,KAAK,WAAM,MAAM,SAAS,MAAM,wEAAiB;AACzE,oBAAM,KAAK;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,YACF;AAAA,UACF,OAAO;AACL,2BAAO,MAAM,sDAAc,QAAQ,IAAI,aAAa,WAAW,aAAa,aAAa;AACzF,mBAAO;AACP,mBAAO,OAAO,KAAK;AAAA,cACjB;AAAA,cACA,SAAS;AAAA,cACT,OAAO,aAAa,WAAW,aAAa,iBAAiB;AAAA,YAC/D,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,yBAAO,MAAM,wEAAiB,QAAQ,IAAI,KAAK;AAC/C,iBAAO;AACP,iBAAO,OAAO,KAAK;AAAA,YACjB;AAAA,YACA,SAAS;AAAA,YACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAClD,CAAC;AAAA,QACH;AAGA,cAAM,MAAM,GAAG;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,eAAe,IAAI,aAAa;;;ACrqB7C,IAAM,sBAAsB;AAC5B,IAAM,uBAAuB;AAWtB,SAAS,YAAY,KAAgC;AAE1D,QAAM,YAAY,IAAI,MAAM,mBAAmB;AAC/C,MAAI,WAAW;AACb,WAAO;AAAA,MACL,MAAM,UAAU,CAAC;AAAA,MACjB,SAAS,UAAU,CAAC;AAAA,IACtB;AAAA,EACF;AAGA,QAAM,aAAa,IAAI,MAAM,oBAAoB;AACjD,MAAI,YAAY;AACd,UAAM,OAAO,WAAW,CAAC;AAEzB,UAAM,eAAe,IAAI,MAAM,oBAAoB;AACnD,QAAI,cAAc;AAChB,aAAO;AAAA,QACL;AAAA,QACA,SAAS,aAAa,CAAC;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AC/BA,SAAS,iBAAAC,gBAAe,aAAAC,YAAW,gBAAgB;AACnD,SAAS,QAAAC,aAAY;;;AC+ErB,IAAM,2BAA2B,CAAC,EAAE;AAKpC,IAAM,4BAA4B;AAAA,EAChC;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAOO,SAAS,qBAAqB,OAA0D;AAC7F,QAAM,WAAW;AAGjB,QAAM,YAAY,SAAS;AAC3B,QAAM,SAAS,YACX,EAAE,IAAI,UAAU,MAAM,GAAG,MAAM,UAAU,QAAQ,GAAG,IACpD,EAAE,IAAI,GAAG,MAAM,GAAG;AAGtB,QAAM,gBAAgB,SAAS;AAC/B,QAAM,cAAc,gBAChB,EAAE,IAAI,cAAc,MAAM,GAAG,MAAM,cAAc,QAAQ,GAAG,IAC5D;AAGJ,QAAM,kBAAkB,SAAS;AACjC,QAAM,iBAAiB,mBAAmB,CAAC,GACxC,OAAO,QAAM,yBAAyB,SAAS,GAAG,EAAE,CAAC,EACrD,IAAI,SAAO,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM,EAAE;AAE5D,SAAO;AAAA,IACL,IAAI,MAAM;AAAA,IACV,SAAS,MAAM;AAAA,IACf;AAAA,IACA;AAAA,IACA,iBAAiB,MAAM;AAAA,IACvB,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM;AAAA,IAClB;AAAA,EACF;AACF;AAKO,SAAS,sBAAsB,OAAyC;AAC7E,QAAM,WAAW;AAGjB,QAAM,YAAY,SAAS;AAC3B,QAAM,SAAS,YACX,EAAE,IAAI,UAAU,MAAM,GAAG,MAAM,UAAU,QAAQ,IAAI,WAAW,UAAU,UAAU,IACpF,EAAE,IAAI,GAAG,MAAM,GAAG;AAGtB,QAAM,gBAAgB,SAAS;AAC/B,QAAM,cAAc,gBAChB,EAAE,IAAI,cAAc,MAAM,GAAG,MAAM,cAAc,QAAQ,IAAI,MAAM,cAAc,KAAK,IACtF;AAGJ,QAAM,cAAc,SAAS;AAC7B,QAAM,WAAW,cACb,EAAE,IAAI,YAAY,MAAM,GAAG,MAAM,YAAY,QAAQ,GAAG,IACxD;AAGJ,QAAM,aAAa,SAAS;AAC5B,QAAM,UAAU,aACZ,EAAE,IAAI,WAAW,MAAM,GAAG,MAAM,WAAW,QAAQ,GAAG,IACtD;AAGJ,QAAM,aAAa,SAAS;AAC5B,QAAM,UAAU,aACZ,EAAE,IAAI,WAAW,MAAM,GAAG,MAAM,WAAW,QAAQ,GAAG,IACtD;AAGJ,QAAM,kBAAkB,SAAS;AACjC,QAAM,iBAAiB,mBAAmB,CAAC,GACxC,OAAO,QAAM,0BAA0B,SAAS,GAAG,EAAE,CAAC,EACtD,IAAI,SAAO,EAAE,IAAI,GAAG,IAAI,MAAM,GAAG,MAAM,OAAO,GAAG,MAAM,EAAE;AAE5D,SAAO;AAAA,IACL,IAAI,MAAM;AAAA,IACV,SAAS,MAAM;AAAA,IACf;AAAA,IACA;AAAA,IACA,iBAAiB,MAAM;AAAA,IACvB,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,MAAM;AAAA,IAClB,YAAY,MAAM;AAAA,IAClB;AAAA,EACF;AACF;AAMO,SAAS,sBAAsB,OAAyC;AAC7E,QAAM,WAAW;AAGjB,QAAM,SAAwB,CAAC;AAC/B,aAAW,OAAO,OAAO,KAAK,QAAQ,GAAG;AACvC,QAAI,QAAQ,YAAY;AACtB,aAAO,GAAG,IAAI,SAAS,GAAG;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO;AACT;AAUO,SAAS,oBACd,OACA,eAAuB,GACvB,WAAmB,IACL;AACd,QAAM,OAAO,qBAAqB,KAAK;AAGvC,MAAI,aAAa,MAAM,gBAAgB,UAAU;AAE/C,SAAK,WAAW,CAAC;AACjB,WAAO;AAAA,EACT;AAGA,MAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,SAAK,WAAW,MAAM,SAAS;AAAA,MAAI,WACjC,oBAAoB,OAAO,eAAe,GAAG,QAAQ;AAAA,IACvD;AAAA,EACF,OAAO;AAEL,SAAK,WAAW,CAAC;AAAA,EACnB;AAEA,SAAO;AACT;AAOO,SAAS,cAAc,OAA0B,SAA8B,CAAC,GAAwB;AAC7G,SAAO,KAAK,KAAK;AACjB,MAAI,MAAM,UAAU;AAClB,eAAW,SAAS,MAAM,UAAU;AAClC,oBAAc,OAAO,MAAM;AAAA,IAC7B;AAAA,EACF;AACA,SAAO;AACT;;;AD/NO,SAAS,kBAAkB,SAAiB,YAA4B;AAC7E,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,YAAY,IAAI,YAAY,EAAE,SAAS,KAC1C,IAAI,SAAS,IAAI,GAAG,SAAS,EAAE,SAAS,GAAG,GAAG,IAC/C,IAAI,QAAQ,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,IACxC,IAAI,SAAS,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,IACzC,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG,IAC3C,IAAI,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AAC7C,SAAO,eAAe,OAAO,IAAI,UAAU,IAAI,SAAS;AAC1D;AAOA,eAAsB,mBACpB,OACA,WACA,UAAyB,CAAC,GACH;AACvB,QAAM,EAAE,QAAQ,IAAI,SAAS,MAAM,IAAI;AACvC,QAAM,SAAS,SAAS,IAAI;AAG5B,EAAAC,WAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,EAAAA,WAAUC,MAAK,WAAW,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,EAAAD,WAAUC,MAAK,WAAW,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE1D,QAAM,cAAqC,CAAC;AAG5C,QAAM,cAAc,oBAAoB,OAAO,GAAG,KAAK;AACvD,QAAM,cAAcA,MAAK,WAAW,mBAAmB;AACvD,EAAAC,eAAc,aAAa,KAAK,UAAU,aAAa,MAAM,MAAM,GAAG,OAAO;AAC7E,QAAM,cAAc,SAAS,WAAW,EAAE;AAC1C,cAAY,KAAK,EAAE,MAAM,qBAAqB,MAAM,aAAa,MAAM,OAAO,CAAC;AAG/E,QAAM,YAAY,cAAc,KAAK;AAGrC,aAAW,OAAO,WAAW;AAC3B,UAAM,eAAe,sBAAsB,GAAG;AAC9C,UAAM,eAAeD,MAAK,WAAW,YAAY,GAAG,IAAI,EAAE,OAAO;AACjE,IAAAC,eAAc,cAAc,KAAK,UAAU,cAAc,MAAM,MAAM,GAAG,OAAO;AAC/E,UAAM,eAAe,SAAS,YAAY,EAAE;AAC5C,gBAAY,KAAK,EAAE,MAAM,YAAY,IAAI,EAAE,SAAS,MAAM,cAAc,MAAM,WAAW,CAAC;AAAA,EAC5F;AAGA,aAAW,OAAO,WAAW;AAC3B,UAAM,eAAe,sBAAsB,GAAG;AAC9C,UAAM,eAAeD,MAAK,WAAW,YAAY,GAAG,IAAI,EAAE,OAAO;AACjE,IAAAC,eAAc,cAAc,KAAK,UAAU,cAAc,MAAM,MAAM,GAAG,OAAO;AAC/E,UAAM,eAAe,SAAS,YAAY,EAAE;AAC5C,gBAAY,KAAK,EAAE,MAAM,YAAY,IAAI,EAAE,SAAS,MAAM,cAAc,MAAM,WAAW,CAAC;AAAA,EAC5F;AAGA,QAAM,YAAY,YAAY,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,MAAM,CAAC;AAEhE,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,WAAW,YAAY;AAAA,IACvB;AAAA,IACA,OAAO;AAAA,EACT;AACF;AAKO,SAAS,sBACd,WACA,WACA,WACQ;AACR,QAAM,UAAU,YAAY,MAAM,QAAQ,CAAC;AAE3C,SAAO;AAAA;AAAA,cAEF,SAAS;AAAA,cACT,SAAS,wBAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAe7B,KAAK;AACP;;;AH7IO,SAAS,qBAA8B;AAC5C,QAAM,WAAW,IAAIC,SAAQ,OAAO,EAAE,YAAY,0BAAM;AAGxD,WACG,QAAQ,UAAU,EAClB,YAAY,+HAAgC,EAC5C,OAAO,eAAe,iBAAO,EAC7B,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,mBAAmB,kHAAwB,IAAI,EACtD,OAAO,uBAAuB,qFAAoB,EAClD,OAAO,YAAY,0EAAc,EACjC,OAAO,SAAS,kGAAuB,EACvC,OAAO,uBAAuB,sCAAQ,EACtC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,IAAwB,YAAY;AACjD,QAAI;AACJ,QAAI;AAGJ,QAAI,QAAQ,KAAK;AACf,YAAM,WAAW,YAAY,QAAQ,GAAG;AACxC,UAAI,CAAC,UAAU;AACb,oBAAY,kDAAe,QAAQ,MAAM;AACzC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,gBAAU,SAAS,SAAS,SAAS,EAAE;AACvC,aAAO,SAAS;AAAA,IAClB,WAAW,IAAI;AAEb,YAAM,UAAU,GAAG,QAAQ,MAAM,EAAE;AACnC,gBAAU,SAAS,SAAS,EAAE;AAC9B,UAAI,MAAM,OAAO,GAAG;AAClB,oBAAY,qCAAY,QAAQ,MAAM;AACtC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,OAAO;AACL,kBAAY,+DAAuB,QAAQ,MAAM;AACjD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,QAAQ,mBAAmB;AAAA,MAC/B,GAAG;AAAA,MACH,MAAM,QAAQ,QAAQ;AAAA,IACxB,CAAC;AAGD,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAC/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,QAAQ,SAAS,QAAQ,OAAO,EAAE;AAGxC,QAAI,QAAQ,GAAG;AAEb,YAAM,SAAS,MAAM,aAAa;AAAA,QAChC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,WAAW;AAAA,QACjB;AAAA,QACA;AAAA,MACF;AAEA,UAAI,OAAO,WAAW,OAAO,MAAM;AAEjC,YAAI,QAAQ,OAAO,QAAQ,QAAQ;AAEjC,sBAAY,OAAO,MAAM;AAAA,YACvB,QAAQ,QAAQ;AAAA,YAChB,QAAQ,QAAQ;AAAA,YAChB,SAAS;AAAA,YACT,YAAY,QAAQ,SAAS;AAAA,YAC7B,QAAQ,QAAQ;AAAA,UAClB,CAAC;AAAA,QACH,OAAO;AAEL,gBAAM,YAAY,QAAQ,UAAU,kBAAkB,aAAa,QAAQ,SAAS,CAAC;AACrF,gBAAM,eAAe,MAAM,mBAAmB,OAAO,MAAM,WAAW;AAAA,YACpE;AAAA,YACA,QAAQ,QAAQ;AAAA,UAClB,CAAC;AAED,cAAI,aAAa,SAAS;AAExB,oBAAQ,IAAI;AAAA,cACV,aAAa;AAAA,cACb,aAAa;AAAA,cACb,aAAa;AAAA,YACf,CAAC;AAAA,UACH,OAAO;AACL,wBAAY,4BAAQ,QAAQ,MAAM;AAClC,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AAAA,MACF,OAAO;AACL,oBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,OAAO;AAEL,YAAM,SAAS,MAAM,aAAa;AAAA,QAChC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,WAAW;AAAA,QACjB;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AAEA,UAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAY,OAAO,MAAM;AAAA,UACvB,QAAQ,QAAQ;AAAA,UAChB,QAAQ,QAAQ;AAAA,UAChB,SAAS;AAAA,UACT,YAAY,QAAQ,SAAS;AAAA,UAC7B,QAAQ,QAAQ;AAAA,QAClB,CAAC;AAAA,MACH,OAAO;AACL,oBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,QAAQ,EAChB,YAAY,0BAAM,EAClB,eAAe,uBAAuB,0BAAM,EAC5C,OAAO,+BAA+B,0BAAM,EAC5C,OAAO,uBAAuB,gCAAO,EACrC,OAAO,qBAAqB,uBAAQ,EACpC,OAAO,sBAAsB,uBAAQ,EACrC,OAAO,8BAA8B,gCAAO,EAC5C,OAAO,yBAAyB,uBAAQ,EACxC,OAAO,oBAAoB,4IAAkD,EAC7E,OAAO,uBAAuB,sCAAQ,EACtC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,qBAAqB,0BAAM,EAClC,OAAO,6BAA6B,0BAAM,EAC1C,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAkC;AAAA,MACtC,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,SAAS,QAAQ;AAAA,IACnB;AAGA,QAAI,QAAQ,UAAU;AACpB,YAAM,WAAW,SAAS,QAAQ,UAAU,EAAE;AAC9C,YAAM,eAAe,MAAM,aAAa;AAAA,QACtC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM,WAAW;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,UAAI,aAAa,WAAW,aAAa,MAAM;AAC7C,cAAM,SAAS,aAAa;AAS5B,YAAI,CAAC,QAAQ,WAAW,CAAC,QAAQ,aAAa,OAAO,SAAS,MAAM;AAClE,iBAAO,UAAU,OAAO,QAAQ;AAAA,QAClC;AACA,YAAI,CAAC,QAAQ,kBAAkB,CAAC,QAAQ,gBAAgB,OAAO,aAAa,MAAM;AAChF,iBAAO,mBAAmB,OAAO,YAAY;AAAA,QAC/C;AACA,YAAI,CAAC,QAAQ,WAAW,OAAO,eAAe,MAAM;AAClD,iBAAO,UAAU,OAAO,cAAc;AAAA,QACxC;AACA,YAAI,CAAC,QAAQ,cAAc,OAAO,UAAU,IAAI;AAC9C,iBAAO,cAAc,OAAO,SAAS;AAAA,QACvC;AAEA,eAAO,SAAS;AAGhB,YAAI,OAAO,iBAAiB,OAAO,cAAc,SAAS,GAAG;AAC3D,gBAAM,iBAA0C,CAAC;AACjD,gBAAM,eAAyB,CAAC;AAEhC,qBAAW,SAAS,OAAO,eAAe;AAExC,gBAAI,MAAM,UAAU,QAAQ,MAAM,UAAU,UAAa,MAAM,UAAU,IAAI;AAE3E,oBAAM,oBAAoB;AAC1B,kBAAI,kBAAkB,aAAa,kBAAkB;AAEnD,sBAAM,eAAe,MAAM;AAC3B,oBAAI,MAAM,QAAQ,YAAY,GAAG;AAC/B,6BAAW,QAAQ,cAAc;AAC/B,wBAAI,KAAK,MAAM,MAAM;AACnB,mCAAa,KAAK,KAAK,KAAK,IAAI;AAAA,oBAClC;AAAA,kBACF;AAAA,gBACF;AAAA,cACF,OAAO;AAEL,+BAAe,MAAM,EAAE,IAAI,MAAM;AAAA,cACnC;AAAA,YACF;AAAA,UACF;AAEA,cAAI,OAAO,KAAK,cAAc,EAAE,SAAS,GAAG;AAC1C,mBAAO,eAAe,KAAK,UAAU,cAAc;AAAA,UACrD;AACA,cAAI,aAAa,SAAS,GAAG;AAC3B,mBAAO,UAAU;AAAA,UACnB;AAAA,QACF;AAAA,MACF,OAAO;AACL,oBAAY,yCAAW,QAAQ,wBAAS,aAAa,WAAW,0BAAM,IAAI,QAAQ,MAAM;AACxF,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAEA,aAAO,kBAAkB;AAAA,IAC3B;AAGA,QAAI,QAAQ,YAAa,QAAO,cAAc,QAAQ;AACtD,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAC9C,QAAI,QAAQ,UAAW,QAAO,aAAa,SAAS,QAAQ,WAAW,EAAE;AACzE,QAAI,QAAQ,WAAY,QAAO,cAAc,SAAS,QAAQ,YAAY,EAAE;AAC5E,QAAI,QAAQ,eAAgB,QAAO,mBAAmB,QAAQ;AAC9D,QAAI,QAAQ,aAAc,QAAO,iBAAiB,SAAS,QAAQ,cAAc,EAAE;AACnF,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAC9C,QAAI,QAAQ,UAAW,QAAO,aAAa,QAAQ;AACnD,QAAI,QAAQ,QAAS,QAAO,WAAW,QAAQ;AAC/C,QAAI,QAAQ,eAAgB,QAAO,kBAAkB,WAAW,QAAQ,cAAc;AAEtF,UAAM,SAAS,MAAM,aAAa,YAAY,MAAM;AAEpD,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,aAAa,EACrB,YAAY,0BAAM,EAClB,OAAO,uBAAuB,0BAAM,EACpC,OAAO,+BAA+B,0BAAM,EAC5C,OAAO,qBAAqB,iGAAsB,EAClD,OAAO,uBAAuB,sEAAoB,EAClD,OAAO,uBAAuB,sCAAQ,EACtC,OAAO,8BAA8B,gCAAO,EAC5C,OAAO,mBAAmB,uCAAS,EACnC,OAAO,uBAAuB,qDAAuB,EACrD,OAAO,qBAAqB,qDAAuB,EACnD,OAAO,6BAA6B,0BAAM,EAC1C,OAAO,qBAAqB,4BAAQ,EACpC,OAAO,8BAA8B,2IAAkC,EACvE,OAAO,oBAAoB,8DAAY,EACvC,OAAO,eAAe,iEAA8B,EACpD,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,IAAY,YAAY;AACrC,QAAI;AACJ,QAAI;AAGJ,QAAI,QAAQ,KAAK;AACf,YAAM,WAAW,YAAY,QAAQ,GAAG;AACxC,UAAI,CAAC,UAAU;AACb,oBAAY,kDAAe,QAAQ,MAAM;AACzC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,gBAAU,SAAS,SAAS,SAAS,EAAE;AACvC,gBAAU,SAAS;AAAA,IACrB,OAAO;AACL,gBAAU,SAAS,GAAG,QAAQ,MAAM,EAAE,GAAG,EAAE;AAC3C,UAAI,MAAM,OAAO,GAAG;AAClB,oBAAY,qCAAY,QAAQ,MAAM;AACtC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,QAAQ,mBAAmB;AAAA,MAC/B,GAAG;AAAA,MACH,MAAM,WAAW,QAAQ;AAAA,IAC3B,CAAC;AACD,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,SAAkC;AAAA,MACtC,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,UAAU;AAAA,IACZ;AAGA,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAC9C,QAAI,QAAQ,YAAa,QAAO,cAAc,QAAQ;AACtD,QAAI,QAAQ,OAAQ,QAAO,SAAS,QAAQ;AAC5C,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAC9C,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAC9C,QAAI,QAAQ,eAAgB,QAAO,mBAAmB,QAAQ;AAC9D,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAC1C,QAAI,QAAQ,UAAW,QAAO,aAAa,QAAQ;AACnD,QAAI,QAAQ,QAAS,QAAO,WAAW,QAAQ;AAC/C,QAAI,QAAQ,eAAgB,QAAO,kBAAkB,WAAW,QAAQ,cAAc;AACtF,QAAI,QAAQ,QAAS,QAAO,UAAU,QAAQ;AAG9C,UAAM,oBAAoB,CAAC;AAC3B,QAAI,QAAQ,aAAa;AACvB,wBAAkB,KAAK,GAAI,MAAM,QAAQ,QAAQ,WAAW,IAAI,QAAQ,cAAc,CAAC,QAAQ,WAAW,CAAE;AAAA,IAC9G;AACA,QAAI,QAAQ,IAAI;AACd,wBAAkB,KAAK,GAAI,MAAM,QAAQ,QAAQ,EAAE,IAAI,QAAQ,KAAK,CAAC,QAAQ,EAAE,CAAE;AAAA,IACnF;AAEA,QAAI,kBAAkB,SAAS,GAAG;AAChC,UAAI;AACF,cAAM,kBAA0C,CAAC;AAGjD,mBAAW,SAAS,mBAAmB;AACrC,gBAAM,QAAQ,MAAM,MAAM,gBAAgB;AAC1C,cAAI,CAAC,OAAO;AACV,wBAAY,2DAAc,KAAK,mGAA6B,QAAQ,MAAM;AAC1E,oBAAQ,KAAK,CAAC;AAAA,UAChB;AACA,gBAAM,CAAC,EAAE,KAAK,KAAK,IAAI;AACvB,0BAAgB,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK;AAAA,QAC3C;AAGA,cAAM,mBAAmB,OAAO,KAAK,eAAe,EAAE,KAAK,SAAO,MAAM,OAAO,GAAG,CAAC,CAAC;AAEpF,YAAI,kBAAkB;AAEpB,gBAAM,qBAAqB,MAAM,aAAa;AAAA,YAC5C,MAAM;AAAA,YACN,MAAM;AAAA,YACN,MAAM;AAAA,UACR;AAEA,cAAI,mBAAmB,WAAW,mBAAmB,MAAM;AACzD,kBAAM,eAAe,mBAAmB;AAIxC,gBAAI,aAAa,MAAM,eAAe;AACpC,oBAAM,WAAW,oBAAI,IAAoB;AACzC,yBAAW,SAAS,aAAa,KAAK,eAAe;AACnD,yBAAS,IAAI,MAAM,MAAM,MAAM,EAAE;AAAA,cACnC;AAGA,oBAAM,gBAAwC,CAAC;AAC/C,yBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,eAAe,GAAG;AAC1D,sBAAM,UAAU,MAAM,OAAO,GAAG,CAAC,IAAI,SAAS,IAAI,GAAG,IAAI,OAAO,GAAG;AACnE,oBAAI,YAAY,QAAW;AACzB,gCAAc,OAAO,IAAI;AAAA,gBAC3B,OAAO;AACL,8BAAY,qDAAa,GAAG,IAAI,QAAQ,MAAM;AAC9C,0BAAQ,KAAK,CAAC;AAAA,gBAChB;AAAA,cACF;AAEA,qBAAO,eAAe,KAAK,UAAU,aAAa;AAAA,YACpD,OAAO;AACL,0BAAY,sEAAe,QAAQ,MAAM;AACzC,sBAAQ,KAAK,CAAC;AAAA,YAChB;AAAA,UACF,OAAO;AACL,wBAAY,sEAAe,QAAQ,MAAM;AACzC,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF,OAAO;AAEL,gBAAM,gBAAwC,CAAC;AAC/C,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,eAAe,GAAG;AAC1D,0BAAc,OAAO,GAAG,CAAC,IAAI;AAAA,UAC/B;AACA,iBAAO,eAAe,KAAK,UAAU,aAAa;AAAA,QACpD;AAAA,MACF,SAAS,OAAO;AACd;AAAA,UACE,2DAAc,iBAAiB,QAAQ,MAAM,UAAU,0BAAM;AAAA,UAC7D,QAAQ;AAAA,QACV;AACA,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,aAAa,YAAY,MAAM;AAEpD,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,OAAO,EACf,YAAY,gCAAO,EACnB,eAAe,mBAAmB,iBAAO,EACzC,OAAO,mBAAmB,sCAAQ,EAClC,OAAO,qBAAqB,oBAAK,EACjC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,UAAU,SAAS,QAAQ,SAAS,EAAE;AAC5C,UAAM,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,OAAO,EAAE,IAAI;AAC5D,UAAM,SAAS,QAAQ,SAAS,SAAS,QAAQ,QAAQ,EAAE,IAAI;AAE/D,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,4BAAQ,QAAQ,MAAM;AAC1F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,QAAQ,EAChB,YAAY,mCAAU,EACtB,OAAO,iBAAiB,qDAAiC,QAAQ,EACjE,OAAO,qBAAqB,0BAAM,EAClC,OAAO,uBAAuB,gCAAO,EACrC,OAAO,wBAAwB,gCAAO,EACtC,OAAO,mBAAmB,sCAAQ,EAClC,OAAO,qBAAqB,oBAAK,EACjC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,eAAwC,CAAC;AAC/C,QAAI,QAAQ,OAAQ,cAAa,SAAS,QAAQ;AAClD,QAAI,QAAQ,QAAS,cAAa,UAAU,QAAQ;AACpD,QAAI,QAAQ,WAAY,cAAa,cAAc,QAAQ;AAC3D,QAAI,QAAQ,MAAO,cAAa,QAAQ,SAAS,QAAQ,OAAO,EAAE;AAClE,QAAI,QAAQ,OAAQ,cAAa,SAAS,SAAS,QAAQ,QAAQ,EAAE;AAErE,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,4BAAQ,QAAQ,MAAM;AAC1F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,eAAe,EACvB,YAAY,kDAAU,EACtB,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAEA,QAAI,OAAO,SAAS;AAClB,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,oDAAY,QAAQ,MAAM;AAC9F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,eAAe,EACvB,YAAY,4FAAiB,EAC7B,OAAO,eAAe,iBAAO,EAC7B,OAAO,wBAAwB,gFAAe,EAC9C,OAAO,yBAAyB,uBAAQ,EACxC,OAAO,mBAAmB,wCAAU,KAAK,EACzC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,IAAY,YAAY;AACrC,QAAI;AACJ,QAAI;AAGJ,QAAI,QAAQ,KAAK;AACf,YAAM,WAAW,YAAY,QAAQ,GAAG;AACxC,UAAI,CAAC,UAAU;AACb,oBAAY,kDAAe,QAAQ,MAAM;AACzC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,gBAAU,SAAS,SAAS,SAAS,EAAE;AACvC,aAAO,SAAS;AAAA,IAClB,OAAO;AACL,YAAM,UAAU,GAAG,QAAQ,MAAM,EAAE;AACnC,gBAAU,SAAS,SAAS,EAAE;AAC9B,UAAI,MAAM,OAAO,GAAG;AAClB,oBAAY,qCAAY,QAAQ,MAAM;AACtC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,QAAQ,mBAAmB;AAAA,MAC/B,GAAG;AAAA,MACH,MAAM,QAAQ,QAAQ;AAAA,IACxB,CAAC;AAED,UAAM,aAAa,oBAAoB,KAAK;AAC5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,QAAI;AAGJ,QAAI,QAAQ,cAAc,CAAC,QAAQ,cAAc;AAC/C,YAAM,cAAc,MAAM,YAAY;AAAA,QACpC,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAEA,UAAI,YAAY,WAAW,YAAY,MAAM;AAC3C,cAAM,QAAQ,YAAY;AAC1B,cAAM,aAAa,QAAQ,WAAW,YAAY;AAClD,cAAM,cAAc,MAAM;AAAA,UACxB,CAAC,MACC,EAAE,MAAM,YAAY,EAAE,SAAS,UAAU,KACzC,EAAE,WAAW,YAAY,EAAE,SAAS,UAAU,KAC9C,EAAE,UAAU,YAAY,EAAE,SAAS,UAAU;AAAA,QACjD;AAEA,YAAI,aAAa;AACf,yBAAe,YAAY;AAAA,QAC7B,OAAO;AACL,sBAAY,qDAAa,QAAQ,UAAU,IAAI,QAAQ,MAAM;AAC7D,kBAAQ,KAAK,CAAC;AAAA,QAChB;AAAA,MACF,OAAO;AACL,oBAAY,oDAAY,QAAQ,MAAM;AACtC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,WAAW,QAAQ,cAAc;AAC/B,qBAAe,SAAS,QAAQ,cAAc,EAAE;AAAA,IAClD;AAEA,UAAM,UAAU,SAAS,QAAQ,OAAO,EAAE,KAAK;AAE/C,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AAEjC,YAAM,OAAO,OAAO;AACpB,UAAI,KAAK,MAAM,MAAM;AACnB,sBAAc;AAAA,UACZ,OAAO,KAAK,KAAK,KAAK;AAAA,UACtB,QAAQ,KAAK,KAAK;AAAA,QACpB,GAAG,QAAQ,MAAM;AAAA,MACnB,OAAO;AACL,sBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,MAC3C;AAAA,IACF,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,8CAAW,QAAQ,MAAM;AAC7F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,eAAe,EACvB,YAAY,8DAAY,EACxB,OAAO,eAAe,4DAAe,EACrC,OAAO,mBAAmB,kHAAwB,GAAG,EACrD,OAAO,uBAAuB,sCAAQ,EACtC,OAAO,uBAAuB,iGAA2B,EACzD,OAAO,YAAY,0EAAc,EACjC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,KAAe,YAAY;AAExC,QAAI,WAAqB,CAAC;AAG1B,QAAI,OAAO,IAAI,SAAS,GAAG;AACzB,YAAM,YAAY,IACf,IAAI,CAAC,OAAO,SAAS,GAAG,QAAQ,MAAM,EAAE,GAAG,EAAE,CAAC,EAC9C,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;AAC5B,eAAS,KAAK,GAAG,SAAS;AAAA,IAC5B;AAGA,QAAI,QAAQ,KAAK;AACf,YAAM,gBAAgB,QAAQ,IAC3B,MAAM,GAAG,EACT,IAAI,CAAC,OAAe,SAAS,GAAG,KAAK,EAAE,QAAQ,MAAM,EAAE,GAAG,EAAE,CAAC,EAC7D,OAAO,CAAC,OAAe,CAAC,MAAM,EAAE,CAAC;AACpC,eAAS,KAAK,GAAG,aAAa;AAAA,IAChC;AAGA,eAAW,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC;AAEhC,QAAI,SAAS,WAAW,GAAG;AACzB,kBAAY,6DAAgB,QAAQ,MAAM;AAC1C,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAC/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,QAAQ,SAAS,QAAQ,OAAO,EAAE;AACxC,UAAM,kBAAkB,QAAQ;AAChC,UAAM,mBAAmB,QAAQ;AAEjC,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,WAAW;AAAA,MACjB;AAAA,MACA,EAAE,iBAAiB,kBAAkB,MAAM;AAAA,IAC7C;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AAEjC,YAAM,eAAe,OAAO,KAAK,OAAO,CAAC,SAAS,KAAK,OAAO;AAC9D,YAAM,cAAc,OAAO,KAAK,OAAO,CAAC,SAAS,CAAC,KAAK,OAAO;AAE9D,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT,SAAS;AAAA,UACP,OAAO,SAAS;AAAA,UAChB,SAAS,aAAa;AAAA,UACtB,QAAQ,YAAY;AAAA,QACtB;AAAA,QACA,QAAQ,aAAa,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,QAC5C,QAAQ,YAAY,SAAS,IAAI,YAAY,IAAI,CAAC,UAAU,EAAE,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,EAAE,IAAI;AAAA,MACrG;AAEA,kBAAY,QAAQ;AAAA,QAChB,QAAQ,QAAQ;AAAA,QACN,QAAQ,QAAQ;AAAA,QAChB,SAAS;AAAA,QACT,YAAY,SAAS,KAAK,GAAG;AAAA,QAC3B,QAAQ,QAAQ;AAAA,MAC9B,CAAC;AAAA,IACL,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,oDAAY,QAAQ,MAAM;AAC9F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,WACG,QAAQ,MAAM,EACd,YAAY,8GAAoB,EAChC,OAAO,eAAe,uBAAQ,EAC9B,OAAO,aAAa,6BAAS,EAC7B,OAAO,oBAAoB,oCAAW,EACtC,OAAO,kBAAkB,0CAAY,EACrC,OAAO,8BAA8B,+FAAyB,EAC9D,OAAO,mBAAmB,4BAAQ,IAAI,EACtC,OAAO,aAAa,8DAAY,EAChC,OAAO,sBAAsB,4CAAS,EACtC,OAAO,uBAAuB,kDAAe,EAC7C,OAAO,YAAY,0EAAc,EACjC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAGJ,QAAI,QAAQ,SAAS;AACnB,YAAM,WAAW,YAAY,QAAQ,OAAO;AAC5C,UAAI,CAAC,UAAU;AACb,oBAAY,oEAAkB,QAAQ,MAAM;AAC5C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,iBAAW,SAAS,SAAS,SAAS,EAAE;AACxC,mBAAa,SAAS;AAAA,IACxB,WAAW,QAAQ,MAAM;AACvB,YAAM,UAAU,QAAQ,KAAK,QAAQ,MAAM,EAAE;AAC7C,iBAAW,SAAS,SAAS,EAAE;AAC/B,UAAI,MAAM,QAAQ,GAAG;AACnB,oBAAY,2CAAa,QAAQ,MAAM;AACvC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,QAAI,QAAQ,OAAO;AACjB,YAAM,WAAW,YAAY,QAAQ,KAAK;AAC1C,UAAI,CAAC,UAAU;AACb,oBAAY,0EAAmB,QAAQ,MAAM;AAC7C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,iBAAW,SAAS,SAAS,SAAS,EAAE;AACxC,mBAAa,SAAS;AAAA,IACxB,WAAW,QAAQ,IAAI;AACrB,YAAM,UAAU,QAAQ,GAAG,QAAQ,MAAM,EAAE;AAC3C,iBAAW,SAAS,SAAS,EAAE;AAC/B,UAAI,MAAM,QAAQ,GAAG;AACnB,oBAAY,iDAAc,QAAQ,MAAM;AACxC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAGA,QAAI,CAAC,UAAU;AACb,kBAAY,yGAAwC,QAAQ,MAAM;AAClE,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,QAAI,CAAC,UAAU;AACb,kBAAY,2GAAqC,QAAQ,MAAM;AAC/D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,UAAM,QAAQ,mBAAmB;AAAA,MAC/B,GAAG;AAAA,MACH,MAAM,cAAc,cAAc,QAAQ;AAAA,IAC5C,CAAC;AAED,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAC/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI,iBAAiB,QAAQ;AAC7B,QAAI,CAAC,gBAAgB;AAEnB,YAAM,SAAS,WAAW,QAAQ,MAAM;AACxC,uBAAiB,OAAO,SAAS;AACjC,UAAI,QAAQ,WAAW,OAAO,SAAS,QAAQ,OAAO,GAAG,UAAU;AACjE,yBAAiB,OAAO,SAAS,QAAQ,OAAO,EAAE;AAAA,MACpD;AAAA,IACF;AAEA,QAAI,CAAC,gBAAgB;AACnB,kBAAY,wJAA6E,QAAQ,MAAM;AACvG,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,QAAQ,SAAS,QAAQ,OAAO,EAAE;AACxC,UAAM,SAAS,QAAQ,UAAU;AACjC,UAAM,eAAe,QAAQ,iBAAiB;AAG9C,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM,WAAW;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,OAAO,aAAa;AAAA,IAChC;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,YAAM,SAAS;AAAA,QACb,SAAS;AAAA,QACT;AAAA,QACA,gBAAgB;AAAA,QAChB,gBAAgB;AAAA,QAChB;AAAA,QACA,SAAS;AAAA,UACP,cAAc,OAAO,KAAK;AAAA,UAC1B,cAAc,OAAO,KAAK;AAAA,UAC1B,aAAa,OAAO,KAAK;AAAA,QAC3B;AAAA,QACA,SAAS,OAAO,KAAK;AAAA,QACrB,SAAS,OAAO,KAAK,QAAQ,SAAS,IAAI,OAAO,KAAK,UAAU;AAAA,QAChE,QAAQ,OAAO,KAAK,OAAO,SAAS,IAAI,OAAO,KAAK,SAAS;AAAA,MAC/D;AAEA,kBAAY,QAAQ;AAAA,QAClB,QAAQ,QAAQ;AAAA,QAChB,QAAQ,QAAQ;AAAA,QAChB,SAAS;AAAA,QACT,YAAY,GAAG,QAAQ,OAAO,QAAQ;AAAA,QACtC,QAAQ,QAAQ;AAAA,MAClB,CAAC;AAAA,IACH,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,SAAO;AACT;;;AK55BA,SAAS,WAAAC,gBAAe;;;ACOjB,IAAM,mBAAN,MAAuB;AAAA;AAAA;AAAA;AAAA,EAI5B,MAAM,iBAAiB,QAAiE;AACtF,UAAM,gBAAyC;AAAA,MAC7C,OAAO,OAAO;AAAA,MACd,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,IAClB;AAGA,QAAI,OAAO,UAAW,eAAc,YAAY,OAAO;AACvD,QAAI,OAAO,QAAS,eAAc,UAAU,OAAO;AACnD,QAAI,OAAO,QAAS,eAAc,UAAU,OAAO;AACnD,QAAI,OAAO,YAAa,eAAc,cAAc,OAAO;AAC3D,QAAI,OAAO,mBAAoB,eAAc,qBAAqB,OAAO;AACzE,QAAI,OAAO,WAAY,eAAc,aAAa,OAAO;AACzD,QAAI,OAAO,WAAY,eAAc,aAAa,OAAO;AACzD,QAAI,OAAO,WAAW,OAAW,eAAc,SAAS,OAAO;AAC/D,QAAI,OAAO,UAAU,OAAW,eAAc,QAAQ,OAAO;AAE7D,mBAAO,KAAK,wCAAU,EAAE,MAAM,OAAO,MAAM,SAAS,OAAO,QAAQ,CAAC;AACpE,WAAO,MAAM,UAAU,IAAiB,sBAAsB,aAAa;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBACJ,OACA,MACA,SACA,SAC+B;AAC/B,mBAAO,KAAK,oDAAY,EAAE,MAAM,SAAS,QAAQ,CAAC;AAClD,UAAM,SAAkC,EAAE,OAAO,MAAM,QAAQ;AAC/D,QAAI,QAAS,QAAO,WAAW;AAC/B,WAAO,MAAM,UAAU,IAAI,cAAc,MAAM;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,gBAAgB,QAAkE;AACtF,mBAAO,KAAK,wCAAU,EAAE,OAAO,CAAC;AAChC,WAAO,MAAM,UAAU,KAAgB,cAAc,MAAM;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,QAAkE;AACtF,mBAAO,KAAK,wCAAU,EAAE,OAAO,CAAC;AAChC,WAAO,MAAM,UAAU,KAAgB,mBAAmB,MAAM;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBACJ,OACA,MACA,aAC+B;AAC/B,mBAAO,KAAK,wCAAU,EAAE,MAAM,YAAY,CAAC;AAC3C,WAAO,MAAM,UAAU,IAAI,qBAAqB;AAAA,MAC9C;AAAA,MACA;AAAA,MACA,IAAI;AAAA;AAAA,IACN,CAAC;AAAA,EACH;AACF;AAEO,IAAM,mBAAmB,IAAI,iBAAiB;;;ADvErD,SAAS,iBAAiB,MAA+D;AACvF,QAAM,MAAM,oBAAI,KAAK;AACrB,MAAI,SAAS;AAEb,MAAI,SAAS,WAAW;AACtB,aAAS;AAAA,EACX,WAAW,SAAS,QAAQ;AAC1B,aAAS;AAAA,EACX,OAAO;AACL,aAAS,SAAS,MAAM,EAAE,KAAK;AAAA,EACjC;AAGA,QAAM,YAAY,IAAI,OAAO;AAC7B,QAAM,eAAe,cAAc,IAAI,KAAK,IAAI;AAChD,QAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,SAAO,QAAQ,IAAI,QAAQ,IAAI,eAAe,SAAS,CAAC;AACxD,SAAO,SAAS,GAAG,GAAG,GAAG,CAAC;AAG1B,QAAM,SAAS,IAAI,KAAK,MAAM;AAC9B,SAAO,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAGnC,QAAM,aAAa,CAAC,MAAY;AAC9B,UAAM,OAAO,EAAE,YAAY;AAC3B,UAAM,QAAQ,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AACtD,UAAM,MAAM,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AAC/C,WAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAAA,EAChC;AACA,QAAM,YAAY,WAAW,IAAI,iBAAO,WAAW,KAAK,iBAAO,GAAG,KAAK,IAAI,MAAM,CAAC;AAElF,SAAO;AAAA,IACL,MAAM,WAAW,MAAM;AAAA,IACvB,IAAI,WAAW,MAAM;AAAA,IACrB;AAAA,EACF;AACF;AAEO,SAAS,oBAA6B;AAC3C,QAAM,UAAU,IAAIC,SAAQ,MAAM,EAAE,YAAY,0BAAM;AAGtD,UACG,QAAQ,MAAM,EACd,YAAY,sCAAQ,EACpB,OAAO,iBAAiB,uCAAmB,EAC3C,OAAO,eAAe,uCAAmB,EACzC,OAAO,kBAAkB,iBAAO,EAChC,OAAO,sBAAsB,6BAAS,EACtC,OAAO,mBAAmB,sCAAQ,EAClC,OAAO,qBAAqB,oBAAK,EACjC,OAAO,kBAAkB,wDAAW,EACpC,OAAO,cAAc,kJAA0B,EAC/C,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AAGxC,UAAM,iBAAiB,QAAQ,cAAc,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,QAAQ,SAAS;AAC5F,UAAM,aAAa,oBAAoB,OAAO,cAAkD;AAEhG,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI,QAAQ,aAAa;AACvB,qBAAO,KAAK,2DAAc;AAG1B,YAAM,iBAAiB,MAAM,YAAY,YAAY,MAAM,OAAQ,MAAM,IAAK;AAC9E,UAAI,CAAC,eAAe,WAAW,CAAC,eAAe,MAAM;AACnD,oBAAY,eAAe,WAAW,eAAe,OAAO,eAAe,iBAAiB,oDAAY,QAAQ,MAAM;AACtH,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAEA,YAAM,WAAW,eAAe;AAChC,YAAM,eAAe,OAAO,OAAO,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAC9D,qBAAO,KAAK,gBAAM,aAAa,MAAM,qBAAM;AAG3C,UAAI;AACJ,UAAI,CAAC,QAAQ,SAAS;AAEpB,YAAI,QAAQ,QAAQ;AAClB,mBAAS,SAAS,QAAQ,QAAQ,EAAE;AAAA,QACtC,OAAO;AACL,gBAAM,eAAe,eAAe,UAAU,QAAQ,MAAM;AAC5D,cAAI,cAAc;AAChB,qBAAS,SAAS,cAAc,EAAE;AAClC,2BAAO,KAAK,wDAAgB,MAAM,EAAE;AAAA,UACtC,OAAO;AACL,wBAAY,mMAA2E,QAAQ,MAAM;AACrG,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AAAA,MACF,WAAW,QAAQ,QAAQ;AAEzB,iBAAS,SAAS,QAAQ,QAAQ,EAAE;AACpC,uBAAO,KAAK,kDAAe,MAAM,EAAE;AAAA,MACrC;AAGA,YAAM,iBAA4B,CAAC;AACnC,UAAI,aAAa;AACjB,YAAM,iBAAsE,CAAC;AAE7E,iBAAW,eAAe,cAAc;AACtC,cAAM,SAAS,MAAM,iBAAiB,iBAAiB;AAAA,UACrD,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,UACZ,SAAS;AAAA,UACT,WAAW,QAAQ;AAAA,UACnB,SAAS,QAAQ;AAAA,UACjB,SAAS;AAAA,UACT,aAAa,QAAQ,aAAa,SAAS,QAAQ,YAAY,EAAE,IAAI;AAAA,UACrE,OAAO,QAAQ,QAAQ,SAAS,QAAQ,OAAO,EAAE,IAAI;AAAA,UACrD,QAAQ,QAAQ,SAAS,SAAS,QAAQ,QAAQ,EAAE,IAAI;AAAA,QAC1D,CAAC;AAED,YAAI,OAAO,WAAW,OAAO,MAAM;AACjC,gBAAM,OAAO,OAAO;AACpB,cAAI,KAAK,gBAAgB,KAAK,aAAa,SAAS,GAAG;AACrD,2BAAe,KAAK,GAAG,KAAK,YAAY;AACxC,kBAAM,eAAe,KAAK,aAAa,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,SAAS,IAAI,CAAC;AACjF,2BAAe,KAAK;AAAA,cAClB,SAAS;AAAA,cACT,OAAO,KAAK,aAAa;AAAA,cACzB,OAAO;AAAA,YACT,CAAC;AACD,0BAAc,KAAK,eAAe,KAAK,aAAa;AACpD,2BAAO,KAAK,GAAG,WAAW,KAAK,KAAK,aAAa,MAAM,iCAAQ;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAGA,YAAM,aAAa,eAAe,OAAe,CAAC,KAAK,MAAM,OAAQ,EAAwB,SAAS,IAAI,CAAC;AAC3G,oBAAc;AAAA,QACZ,aAAa;AAAA,QACb,aAAa,KAAK,MAAM,aAAa,GAAG,IAAI;AAAA,QAC5C,iBAAiB;AAAA,QACjB,cAAc;AAAA,MAChB,CAAC;AAAA,IACH,OAAO;AAEL,YAAM,SAAS,MAAM,iBAAiB,iBAAiB;AAAA,QACrD,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,QACf,WAAW,QAAQ;AAAA,QACnB,SAAS,QAAQ;AAAA,QACjB,SAAS,QAAQ,SAAS,SAAS,QAAQ,QAAQ,EAAE,IAAI;AAAA,QACzD,aAAa,QAAQ,aAAa,SAAS,QAAQ,YAAY,EAAE,IAAI;AAAA,QACrE,OAAO,QAAQ,QAAQ,SAAS,QAAQ,OAAO,EAAE,IAAI;AAAA,QACrD,QAAQ,QAAQ,SAAS,SAAS,QAAQ,QAAQ,EAAE,IAAI;AAAA,MAC1D,CAAC;AAED,UAAI,OAAO,WAAW,OAAO,MAAM;AACjC,sBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,MAC3C,OAAO;AACL,oBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,CAAC;AAGH,UACG,QAAQ,QAAQ,EAChB,YAAY,sCAAQ,EACpB,eAAe,gBAAgB,iBAAO,EACtC,eAAe,iBAAiB,gCAAO,EACvC,eAAe,mBAAmB,wGAAuC,EACzE,OAAO,iBAAiB,yDAAsB,EAC9C,OAAO,yBAAyB,cAAI,EACpC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI;AACJ,UAAM,eAAe,eAAe,UAAU,QAAQ,MAAM;AAC5D,QAAI,cAAc;AAChB,eAAS,SAAS,cAAc,EAAE;AAAA,IACpC;AAEA,QAAI,CAAC,QAAQ;AACX,kBAAY,qHAAoD,QAAQ,MAAM;AAC9E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,UAAU,SAAS,QAAQ,MAAM,QAAQ,MAAM,EAAE,GAAG,EAAE;AAC5D,QAAI,MAAM,OAAO,GAAG;AAClB,kBAAY,qCAAY,QAAQ,MAAM;AACtC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,OAAO,WAAW,QAAQ,IAAI;AACpC,QAAI,MAAM,IAAI,KAAK,QAAQ,GAAG;AAC5B,kBAAY,wCAAU,QAAQ,MAAM;AACpC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,aAAa,SAAS,QAAQ,UAAU,EAAE;AAChD,QAAI,MAAM,UAAU,GAAG;AACrB,kBAAY,sHAA2C,QAAQ,MAAM;AACrE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAkC;AAAA,MACtC,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,UAAU;AAAA,MACV,OAAO;AAAA;AAAA,MACP,UAAU,QAAQ,SAAQ,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/D,SAAS;AAAA;AAAA,MACT,aAAa;AAAA;AAAA,MACb,aAAa;AAAA;AAAA,IACf;AAEA,QAAI,QAAQ,SAAU,QAAO,WAAW,QAAQ;AAEhD,UAAM,SAAS,MAAM,iBAAiB,gBAAgB,MAAM;AAE5D,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,UACG,QAAQ,aAAa,EACrB,YAAY,sCAAQ,EACpB,eAAe,gBAAgB,yCAAW,EAC1C,eAAe,iBAAiB,wDAAW,EAC3C,eAAe,mBAAmB,qDAAa,EAC/C,eAAe,iBAAiB,mDAAqB,EACrD,OAAO,yBAAyB,cAAI,EACpC,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,IAAY,YAAY;AACrC,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI;AACJ,UAAM,eAAe,eAAe,UAAU,QAAQ,MAAM;AAC5D,QAAI,cAAc;AAChB,eAAS,SAAS,cAAc,EAAE;AAAA,IACpC;AAEA,QAAI,CAAC,QAAQ;AACX,kBAAY,qHAAoD,QAAQ,MAAM;AAC9E,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc,SAAS,IAAI,EAAE;AACnC,QAAI,MAAM,WAAW,GAAG;AACtB,kBAAY,iDAAc,QAAQ,MAAM;AACxC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,UAAU,SAAS,QAAQ,MAAM,QAAQ,MAAM,EAAE,GAAG,EAAE;AAC5D,QAAI,MAAM,OAAO,GAAG;AAClB,kBAAY,qCAAY,QAAQ,MAAM;AACtC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAkC;AAAA,MACtC,OAAO,MAAM;AAAA,MACb,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,IAAI;AAAA;AAAA,MACJ,UAAU;AAAA;AAAA,MACV,OAAO,WAAW,QAAQ,IAAI;AAAA;AAAA,MAC9B,aAAa,SAAS,QAAQ,UAAU,EAAE;AAAA;AAAA,MAC1C,UAAU,QAAQ;AAAA;AAAA,MAClB,SAAS;AAAA;AAAA,MACT,YAAY;AAAA;AAAA,IACd;AAEA,QAAI,QAAQ,SAAU,QAAO,WAAW,QAAQ;AAEhD,UAAM,SAAS,MAAM,iBAAiB,gBAAgB,MAAM;AAE5D,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,UACG,QAAQ,aAAa,EACrB,YAAY,sCAAQ,EACpB,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,IAAY,YAAY;AACrC,UAAM,QAAQ,mBAAmB,OAAO;AAExC,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAE/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,cAAc,SAAS,IAAI,EAAE;AACnC,QAAI,MAAM,WAAW,GAAG;AACtB,kBAAY,iDAAc,QAAQ,MAAM;AACxC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,iBAAiB;AAAA,MACpC,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,IACF;AAEA,QAAI,OAAO,SAAS;AAClB,oBAAc,EAAE,SAAS,8CAAW,IAAI,YAAY,GAAG,QAAQ,MAAM;AAAA,IACvE,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,UACG,QAAQ,SAAS,EACjB,YAAY,wGAAmB,EAC/B,OAAO,gBAAgB,6HAAyB,EAChD,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,UAAU,QAAQ,QAAQ,SAAS,QAAQ,MAAM,QAAQ,MAAM,EAAE,GAAG,EAAE,IAAI;AAEhF,UAAM,SAAS,MAAM,iBAAiB;AAAA,MACpC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AAEjC,YAAM,OAAO,OAAO;AACpB,UAAI,KAAK,SAAS,YAAY;AAC5B,cAAM,aAAa,KAAK,QAAQ,WAAW,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,KAAK,EAAE;AAC7E,sBAAc,EAAE,WAAW,GAAG,QAAQ,MAAM;AAAA,MAC9C,OAAO;AACL,sBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,MAC3C;AAAA,IACF,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,wCAAU,QAAQ,MAAM;AAC5F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,UACG,QAAQ,SAAS,EACjB,YAAY,4IAAyB,EACrC,OAAO,iBAAiB,mGAAuC,SAAS,EACxE,OAAO,iBAAiB,uEAA+B,EACvD,OAAO,eAAe,uEAA+B,EACrD,OAAO,mBAAmB,kEAAgB,GAAG,EAC7C,OAAO,YAAY,8DAAY,EAC/B,OAAO,kBAAkB,qFAAyB,EAClD,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAE/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAGA,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,QAAQ,QAAQ,QAAQ,IAAI;AAE9B,iBAAW,QAAQ;AACnB,eAAS,QAAQ;AACjB,oBAAc,GAAG,QAAQ,MAAM,MAAM;AAAA,IACvC,OAAO;AAEL,YAAM,YAAY,iBAAiB,QAAQ,IAAI;AAC/C,iBAAW,UAAU;AACrB,eAAS,UAAU;AACnB,oBAAc,GAAG,UAAU,SAAS,KAAK,QAAQ,MAAM,MAAM;AAAA,IAC/D;AAGA,QAAI;AACJ,QAAI,QAAQ,QAAQ;AAClB,eAAS,SAAS,QAAQ,QAAQ,EAAE;AAAA,IACtC,OAAO;AAEL,YAAM,eAAe,eAAe,UAAU,QAAQ,MAAM;AAC5D,UAAI,cAAc;AAChB,iBAAS,SAAS,cAAc,EAAE;AAAA,MACpC;AAAA,IACF;AAEA,mBAAO,KAAK,yCAAW,WAAW,EAAE;AACpC,QAAI,QAAQ;AACV,qBAAO,KAAK,oBAAU,MAAM,EAAE;AAAA,IAChC;AAGA,UAAM,iBAAiB,MAAM,YAAY,YAAY,MAAM,OAAQ,MAAM,IAAK;AAC9E,QAAI,CAAC,eAAe,WAAW,CAAC,eAAe,MAAM;AACnD,kBAAY,eAAe,WAAW,eAAe,OAAO,eAAe,iBAAiB,oDAAY,QAAQ,MAAM;AACtH,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,WAAW,eAAe;AAChC,UAAM,eAAe,OAAO,OAAO,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAC9D,mBAAO,KAAK,gBAAM,aAAa,MAAM,wBAAS;AAG9C,UAAM,iBAAuD,CAAC;AAC9D,UAAM,iBAA4B,CAAC;AACnC,QAAI,aAAa;AAEjB,eAAW,eAAe,cAAc;AACtC,YAAM,SAAS,MAAM,iBAAiB,iBAAiB;AAAA,QACrD,OAAO,MAAM;AAAA,QACb,MAAM,MAAM;AAAA,QACZ,SAAS;AAAA,QACT,WAAW;AAAA,QACX,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAED,UAAI,OAAO,WAAW,OAAO,MAAM;AACjC,cAAM,OAAO,OAAO;AACpB,YAAI,KAAK,gBAAgB,KAAK,aAAa,SAAS,GAAG;AACrD,gBAAM,eAAe,KAAK,aAAa,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,SAAS,IAAI,CAAC;AACjF,cAAI,eAAe,GAAG;AACpB,2BAAe,KAAK;AAAA,cAClB,SAAS;AAAA,cACT,OAAO,KAAK,MAAM,eAAe,GAAG,IAAI;AAAA,YAC1C,CAAC;AACD,0BAAc;AAAA,UAChB;AAEA,yBAAe,KAAK,GAAG,KAAK,YAAY;AAAA,QAC1C;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAAc,WAAW,QAAQ,MAAM,KAAK;AAClD,UAAM,iBAAiB,KAAK,IAAI,GAAG,cAAc,UAAU;AAC3D,iBAAa,KAAK,MAAM,aAAa,GAAG,IAAI;AAG5C,UAAM,SAAkC;AAAA,MACtC,QAAQ;AAAA,MACR,QAAQ,UAAU;AAAA,MAClB;AAAA,MACA;AAAA,MACA,gBAAgB,KAAK,MAAM,iBAAiB,GAAG,IAAI;AAAA,MACnD,UAAU,mBAAmB;AAAA,MAC7B,kBAAkB,eAAe,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAAA,IACnE;AAGA,QAAI,QAAQ,QAAQ;AAElB,aAAO,cAAc,eAAe,KAAK,CAAC,GAAG,MAAM;AACjD,cAAM,QAAS,EAA4B,YAAY;AACvD,cAAM,QAAS,EAA4B,YAAY;AACvD,eAAO,MAAM,cAAc,KAAK;AAAA,MAClC,CAAC;AAAA,IACH;AAEA,kBAAc,QAAQ,QAAQ,MAAM;AAAA,EACtC,CAAC;AAEH,SAAO;AACT;;;AE3iBA,SAAS,WAAAC,gBAAe;AAKjB,SAAS,uBAAgC;AAC9C,QAAM,aAAa,IAAIC,SAAQ,SAAS,EAAE,YAAY,0BAAM;AAG5D,aACG,QAAQ,MAAM,EACd,YAAY,sCAAQ,EACpB,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAE/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,YAAY,YAAY,MAAM,OAAQ,MAAM,IAAK;AAEtE,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,oDAAY,QAAQ,MAAM;AAC9F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,aACG,QAAQ,OAAO,EACf,YAAY,kDAAU,EACtB,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,uBAAuB,0BAAM,EACpC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,KAAK;AAE5C,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,YAAY;AAAA,MAC/B,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AAEA,QAAI,OAAO,WAAW,OAAO,MAAM;AACjC,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,oDAAY,QAAQ,MAAM;AAC9F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAGH,aACG,QAAQ,MAAM,EACd,YAAY,sCAAQ,EACpB,OAAO,mBAAmB,WAAW,EACrC,OAAO,iBAAiB,6BAAS,EACjC,OAAO,oBAAoB,kCAAc,EACzC,OAAO,mBAAmB,wDAAW,EACrC,OAAO,YAAY,yEAAkB,EACrC,OAAO,OAAO,YAAY;AACzB,UAAM,QAAQ,mBAAmB,OAAO;AACxC,UAAM,aAAa,oBAAoB,OAAO,CAAC,SAAS,MAAM,CAAC;AAE/D,QAAI,CAAC,WAAW,OAAO;AACrB,kBAAY,yCAAW,WAAW,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,MAAM;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,SAAS,MAAM,YAAY,YAAY,MAAM,OAAQ,MAAM,IAAK;AAEtE,QAAI,OAAO,SAAS;AAClB,oBAAc,OAAO,MAAM,QAAQ,MAAM;AAAA,IAC3C,OAAO;AACL,kBAAY,OAAO,WAAW,OAAO,OAAO,OAAO,iBAAiB,oDAAY,QAAQ,MAAM;AAC9F,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,CAAC;AAEH,SAAO;AACT;;;AhB1FA,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,qBAAqB;AAG9B,IAAM,aAAa,cAAc,YAAY,GAAG;AAChD,IAAM,YAAYD,SAAQ,UAAU;AAGpC,IAAM,cAAc,KAAK,MAAMD,cAAaE,MAAK,WAAW,iBAAiB,GAAG,OAAO,CAAC;AACxF,IAAM,UAAU,YAAY;AAE5B,IAAM,UAAU,IAAIC,SAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,4EAA0B,EACtC,QAAQ,OAAO,EACf,OAAO,aAAa,0BAAM,EAC1B,OAAO,WAAW,0BAAM,EACxB,OAAO,gBAAgB,+EAAmB,EAC1C,KAAK,aAAa,CAAC,gBAAgB;AAClC,QAAM,OAAO,YAAY,KAAK;AAC9B,MAAI,KAAK,OAAO;AACd,mBAAO,SAAS,OAAO;AAAA,EACzB,WAAW,KAAK,SAAS;AACvB,mBAAO,SAAS,MAAM;AAAA,EACxB,OAAO;AACL,mBAAO,SAAS,QAAQ;AAAA,EAC1B;AAEA,MAAI,KAAK,YAAY,OAAO;AAC1B,yBAAqB,KAAK;AAAA,EAC5B;AACF,CAAC;AAGH,QAAQ,WAAW,kBAAkB,CAAC;AACtC,QAAQ,WAAW,oBAAoB,CAAC;AACxC,QAAQ,WAAW,mBAAmB,CAAC;AACvC,QAAQ,WAAW,kBAAkB,CAAC;AACtC,QAAQ,WAAW,qBAAqB,CAAC;AAGzC,QAAQ,MAAM;","names":["Command","writeFileSync","writeFileSync","Command","Command","Command","currentLevel","writeFileSync","mkdirSync","join","mkdirSync","join","writeFileSync","Command","Command","Command","Command","Command","readFileSync","dirname","join","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/test.ts","../src/utils/logger.ts","../src/client/api-client.ts","../src/services/user-service.ts","../src/utils/config.ts","../src/utils/output.ts","../src/utils/key-translator.ts","../src/commands/config/index.ts","../src/commands/issue/index.ts","../src/services/issue-service.ts","../src/utils/url-parser.ts","../src/utils/issue-exporter.ts","../src/utils/preset-extractor.ts","../src/commands/time/index.ts","../src/services/time-entry-service.ts","../src/commands/project/index.ts"],"sourcesContent":["import { Command } from 'commander'\nimport { createTestCommand } from './commands/test.js'\nimport { createConfigCommand } from './commands/config/index.js'\nimport { createIssueCommand } from './commands/issue/index.js'\nimport { createTimeCommand } from './commands/time/index.js'\nimport { createProjectCommand } from './commands/project/index.js'\nimport logger from './utils/logger.js'\nimport { setKeyMappingEnabled } from './utils/key-translator.js'\nimport { readFileSync } from 'fs'\nimport { dirname, join } from 'path'\nimport { fileURLToPath } from 'url'\n\n// 获取当前文件的目录\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n// 从 package.json 读取版本号\nconst packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'))\nconst version = packageJson.version\n\nconst program = new Command()\n\nprogram\n .name('pm-cli')\n .description('网易易协作 (PM NetEase) 命令行工具')\n .version(version)\n .option('--verbose', '详细输出')\n .option('--debug', '调试模式')\n .option('--no-mapping', '不输出 key 映射表(默认输出)')\n .hook('preAction', (thisCommand) => {\n const opts = thisCommand.opts()\n if (opts.debug) {\n logger.setLevel('debug')\n } else if (opts.verbose) {\n logger.setLevel('info')\n } else {\n logger.setLevel('silent')\n }\n // 设置是否输出 key 映射表(--no-mapping 表示禁用)\n if (opts.mapping === false) {\n setKeyMappingEnabled(false)\n }\n })\n\n// 添加子命令\nprogram.addCommand(createTestCommand())\nprogram.addCommand(createConfigCommand())\nprogram.addCommand(createIssueCommand())\nprogram.addCommand(createTimeCommand())\nprogram.addCommand(createProjectCommand())\n\n// 解析命令行参数\nprogram.parse()\n","import { Command } from 'commander'\nimport { userService } from '../services/user-service.js'\nimport { resolveCredentials, validateCredentials } from '../utils/config.js'\nimport { outputSuccess, outputError } from '../utils/output.js'\n\nexport function createTestCommand(): Command {\n const testCmd = new Command('test')\n .description('测试网易易协作连接')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.testConnection(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (result.success) {\n outputSuccess({\n message: '连接成功',\n host: creds.host,\n project: creds.project,\n data: result.data,\n }, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '连接失败', options.pretty)\n process.exit(1)\n }\n })\n\n return testCmd\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent'\n\nlet currentLevel: LogLevel = 'info'\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\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","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 { writeFileSync } from 'fs'\nimport type { CliOutput } from '../models/types.js'\nimport { generateKeyMapping, isKeyMappingEnabled } from './key-translator.js'\n\n/**\n * 生成输出文件路径\n * 命名规则: pm-cli_{命令}_{标识}_{时间戳}.json\n * @param command 命令名称,如 \"issue-get\"\n * @param identifier 标识符,如单号 \"250397\"\n */\nexport function generateOutputPath(command: string, identifier: string): string {\n const now = new Date()\n const timestamp = now.getFullYear().toString() +\n (now.getMonth() + 1).toString().padStart(2, '0') +\n now.getDate().toString().padStart(2, '0') +\n now.getHours().toString().padStart(2, '0') +\n now.getMinutes().toString().padStart(2, '0') +\n now.getSeconds().toString().padStart(2, '0')\n return `/tmp/pm-cli_${command}_${identifier}_${timestamp}.json`\n}\n\n/**\n * 为输出数据添加 key 映射表\n * @param result 原始输出结果\n * @returns 添加了 _key_mapping 的结果\n */\nfunction addKeyMappingToResult<T>(result: CliOutput<T>): CliOutput<T> & { _key_mapping?: Record<string, string> } {\n if (!isKeyMappingEnabled()) {\n return result\n }\n\n const mapping = generateKeyMapping(result)\n return {\n ...result,\n _key_mapping: mapping,\n }\n}\n\n/**\n * 输出到文件\n * @param data 要输出的数据\n * @param filePath 文件路径\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n * @returns 输出的文件路径\n */\nexport function outputToFile<T>(data: T, filePath: string, pretty = false): string {\n const result: CliOutput<T> = {\n success: true,\n data,\n }\n const finalResult = addKeyMappingToResult(result)\n writeFileSync(filePath, JSON.stringify(finalResult, null, pretty ? 2 : 0), 'utf-8')\n return filePath\n}\n\n/**\n * 智能输出 - 根据参数决定输出到文件还是控制台\n * @param data 要输出的数据\n * @param options 输出选项\n * @returns 如果输出到文件,返回文件路径;否则返回 undefined\n */\nexport function smartOutput<T>(\n data: T,\n options: {\n stdout?: boolean\n output?: string\n command: string\n identifier: string\n pretty?: boolean\n }\n): string | undefined {\n // 如果指定了 --stdout,输出到控制台\n if (options.stdout) {\n outputSuccess(data, options.pretty)\n return undefined\n }\n\n // 确定输出文件路径\n const filePath = options.output || generateOutputPath(options.command, options.identifier)\n\n // 输出到文件\n outputToFile(data, filePath, options.pretty)\n\n // 在控制台显示文件路径\n console.log(JSON.stringify({ success: true, output: filePath }, null, options.pretty ? 2 : 0))\n\n return filePath\n}\n\n/**\n * 输出成功结果\n * @param data 要输出的数据\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n */\nexport function outputSuccess<T>(data: T, pretty = false): void {\n const result: CliOutput<T> = {\n success: true,\n data,\n }\n const finalResult = addKeyMappingToResult(result)\n console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0))\n}\n\n/**\n * 输出错误结果\n * @param error 错误信息\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n */\nexport function outputError(error: string, pretty = false): void {\n const result: CliOutput = {\n success: false,\n error,\n }\n // 错误输出也添加映射表,方便理解 error 字段\n const finalResult = addKeyMappingToResult(result)\n console.log(JSON.stringify(finalResult, null, pretty ? 2 : 0))\n}\n\n/**\n * 输出原始 API 响应\n * @param data 要输出的数据\n * @param pretty 是否格式化输出(默认 false,即压缩格式)\n */\nexport function outputRaw(data: unknown, pretty = false): void {\n console.log(JSON.stringify(data, null, pretty ? 2 : 0))\n}\n\n// 导出 key-translator 的函数,方便外部使用\nexport { setKeyMappingEnabled, isKeyMappingEnabled } from './key-translator.js'\n","/**\n * JSON Key 映射表生成器\n * 生成 key 到中文的映射表,附加到 JSON 输出中帮助 AI 理解\n */\n\n// 英文 key 到中文 key 的映射表\nconst KEY_MAP: Record<string, string> = {\n // 问题 (Issue) 相关字段\n id: '编号',\n subject: '标题',\n description: '描述',\n status: '状态',\n tracker: '跟踪器',\n priority: '优先级',\n author: '创建者',\n assigned_to: '负责人',\n project: '项目',\n parent: '父单',\n parent_id: '父单编号',\n parent_issue_id: '父单编号',\n root_id: '根单编号',\n children: '子单列表',\n level: '层级',\n fixed_version: '目标版本',\n version: '版本',\n start_date: '开始日期',\n due_date: '截止日期',\n estimated_hours: '预估工时',\n spent_hours: '已用工时',\n done_ratio: '完成度',\n created_on: '创建时间',\n updated_on: '更新时间',\n closed_on: '关闭时间',\n relations: '关联问题',\n custom_fields: '自定义字段',\n attachments: '附件',\n journals: '历史记录',\n watchers: '跟踪者',\n watcher_user_ids: '跟踪者编号',\n\n // 通用对象字段\n name: '名称',\n value: '值',\n mail: '邮箱',\n login: '登录名',\n firstname: '名',\n lastname: '姓',\n identifier: '标识符',\n is_public: '是否公开',\n\n // 工时 (TimeEntry) 相关字段\n hours: '工时',\n spent_on: '工时日期',\n activity: '活动类型',\n activity_id: '活动类型编号',\n comments: '备注',\n time_entries: '工时条目',\n time_entry: '工时条目',\n total_count: '总数',\n total_hours: '总工时',\n\n // 用户 (User) 相关字段\n user: '用户',\n user_id: '用户编号',\n author_mail: '作者邮箱',\n assigned_to_mail: '负责人邮箱',\n assigned_to_id: '负责人编号',\n follows: '跟进QA',\n last_login_on: '最后登录时间',\n\n // API 响应字段\n success: '成功',\n data: '数据',\n error: '错误',\n message: '消息',\n msg: '消息',\n api_error_msg: '接口错误',\n post_error_msg: '请求错误',\n output: '输出路径',\n issues: '问题列表',\n list: '列表',\n total: '总数',\n\n // 查询/统计相关字段\n offset: '偏移量',\n limit: '限制数',\n page: '页码',\n per_page: '每页数量',\n period: '时间段',\n targetHours: '目标工时',\n remainingHours: '剩余工时',\n isFilled: '是否填满',\n projectBreakdown: '项目分布',\n project_summary: '项目汇总',\n\n // 版本 (Version) 相关字段\n effective_date: '有效日期',\n sharing: '共享范围',\n wiki_page_title: 'Wiki页面',\n\n // 自定义字段相关\n custom_field: '自定义字段',\n identify: '标识',\n\n // 选项/配置相关\n options: '选项',\n activities: '活动类型列表',\n trackers: '跟踪器列表',\n statuses: '状态列表',\n priorities: '优先级列表',\n versions: '版本列表',\n users: '用户列表',\n projects: '项目列表',\n\n // 其他字段\n notes: '说明',\n is_pending_version: '是否待定版本',\n count: '数量',\n category: '分类',\n category_id: '分类编号',\n}\n\n// 全局开关:是否输出 key 映射表(默认开启)\nlet keyMappingEnabled = true\n\n/**\n * 设置是否输出 key 映射表\n */\nexport function setKeyMappingEnabled(enabled: boolean): void {\n keyMappingEnabled = enabled\n}\n\n/**\n * 获取当前是否输出 key 映射表\n */\nexport function isKeyMappingEnabled(): boolean {\n return keyMappingEnabled\n}\n\n/**\n * 递归收集对象中所有使用的 key\n * @param obj 要分析的对象\n * @param keys 收集到的 key 集合\n */\nfunction collectKeysRecursive(obj: unknown, keys: Set<string>): void {\n if (obj === null || obj === undefined) return\n if (typeof obj !== 'object') return\n\n if (Array.isArray(obj)) {\n for (const item of obj) {\n collectKeysRecursive(item, keys)\n }\n return\n }\n\n for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {\n keys.add(key)\n collectKeysRecursive(value, keys)\n }\n}\n\n/**\n * 收集对象中所有使用的 key\n * @param obj 要分析的对象\n * @returns 使用的 key 数组\n */\nexport function collectUsedKeys(obj: unknown): string[] {\n const keys = new Set<string>()\n collectKeysRecursive(obj, keys)\n return Array.from(keys)\n}\n\n/**\n * 为对象生成 key 映射表\n * 只包含对象中实际使用的 key,且在映射表中有对应中文的\n * @param obj 要分析的对象\n * @returns key 映射表 { englishKey: chineseKey }\n */\nexport function generateKeyMapping(obj: unknown): Record<string, string> {\n const usedKeys = collectUsedKeys(obj)\n const mapping: Record<string, string> = {}\n\n for (const key of usedKeys) {\n if (KEY_MAP[key]) {\n mapping[key] = KEY_MAP[key]\n }\n }\n\n return mapping\n}\n\n/**\n * 获取完整的 key 映射表(用于调试或文档)\n */\nexport function getFullKeyMap(): Record<string, string> {\n return { ...KEY_MAP }\n}\n\n/**\n * 添加自定义 key 映射\n */\nexport function addKeyMapping(englishKey: string, chineseKey: string): void {\n KEY_MAP[englishKey] = chineseKey\n}\n","import { Command } from 'commander'\nimport {\n readConfig,\n setConfigValue,\n getConfigValue,\n setProfile,\n removeProfile,\n listProfiles,\n getProfile,\n getConfigPath,\n} from '../../utils/config.js'\nimport { outputSuccess, outputError } from '../../utils/output.js'\n\nexport function createConfigCommand(): Command {\n const configCmd = new Command('config').description('配置管理')\n\n // config set <key> <value>\n configCmd\n .command('set <key> <value>')\n .description('设置配置项')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((key: string, value: string, options: { config?: string; pretty?: boolean }) => {\n setConfigValue(key, value, options.config)\n outputSuccess({\n message: `配置项 ${key} 已设置`,\n key,\n value,\n configPath: getConfigPath(options.config),\n }, options.pretty)\n })\n\n // config get <key>\n configCmd\n .command('get <key>')\n .description('获取配置项')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((key: string, options: { config?: string; pretty?: boolean }) => {\n const value = getConfigValue(key, options.config)\n if (value !== undefined) {\n outputSuccess({ key, value }, options.pretty)\n } else {\n outputError(`配置项 ${key} 不存在`, options.pretty)\n process.exit(1)\n }\n })\n\n // config list\n configCmd\n .command('list')\n .description('列出所有配置')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((options: { config?: string; pretty?: boolean }) => {\n const config = readConfig(options.config)\n outputSuccess({\n configPath: getConfigPath(options.config),\n config,\n }, options.pretty)\n })\n\n // config profile 子命令组\n const profileCmd = new Command('profile').description('Profile 管理')\n\n // config profile add <name>\n profileCmd\n .command('add <name>')\n .description('添加 profile')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--token <token>', 'API Token')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(\n (\n name: string,\n options: { host?: string; project?: string; token?: string; config?: string; pretty?: boolean }\n ) => {\n const profile: Record<string, string> = {}\n if (options.host) profile.host = options.host\n if (options.project) profile.project = options.project\n if (options.token) profile.token = options.token\n\n if (Object.keys(profile).length === 0) {\n outputError('请至少指定一个配置项 (--host, --project, --token)', options.pretty)\n process.exit(1)\n }\n\n setProfile(name, profile, options.config)\n outputSuccess({\n message: `Profile ${name} 已添加`,\n name,\n profile,\n }, options.pretty)\n }\n )\n\n // config profile remove <name>\n profileCmd\n .command('remove <name>')\n .description('删除 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((name: string, options: { config?: string; pretty?: boolean }) => {\n const removed = removeProfile(name, options.config)\n if (removed) {\n outputSuccess({ message: `Profile ${name} 已删除`, name }, options.pretty)\n } else {\n outputError(`Profile ${name} 不存在`, options.pretty)\n process.exit(1)\n }\n })\n\n // config profile list\n profileCmd\n .command('list')\n .description('列出所有 profiles')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action((options: { config?: string; pretty?: boolean }) => {\n const profiles = listProfiles(options.config)\n const profileDetails: Record<string, unknown> = {}\n\n for (const name of profiles) {\n profileDetails[name] = getProfile(name, options.config)\n }\n\n outputSuccess({\n profiles: profileDetails,\n count: profiles.length,\n }, options.pretty)\n })\n\n configCmd.addCommand(profileCmd)\n\n return configCmd\n}\n","import { Command } from 'commander'\nimport { issueService } from '../../services/issue-service.js'\nimport { userService } from '../../services/user-service.js'\nimport { resolveCredentials, validateCredentials, readConfig } from '../../utils/config.js'\nimport { parsePmLink } from '../../utils/url-parser.js'\nimport { outputSuccess, outputError, smartOutput } from '../../utils/output.js'\nimport { exportIssuePresets, generateExportDir, generateAIGuideOutput } from '../../utils/issue-exporter.js'\n\nexport function createIssueCommand(): Command {\n const issueCmd = new Command('issue').description('问题管理')\n\n // issue get <id> 或 --url\n issueCmd\n .command('get [id]')\n .description('获取问题详情(默认分级导出,--raw 输出完整 JSON)')\n .option('--url <url>', 'PM 链接')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--depth <depth>', '递归获取子单的深度(0 表示不获取子单)', '10')\n .option('-o, --output <path>', '输出目录或文件路径(默认 /tmp)')\n .option('--stdout', '强制输出到控制台而非文件')\n .option('--raw', '输出完整 JSON 文件(不分级,原行为)')\n .option('--include-relations', '包含关联问题')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string | undefined, options) => {\n let issueId: number\n let host: string | undefined\n\n // 解析 URL 或 ID\n if (options.url) {\n const linkInfo = parsePmLink(options.url)\n if (!linkInfo) {\n outputError('无效的 PM 链接格式', options.pretty)\n process.exit(1)\n }\n issueId = parseInt(linkInfo.issueId, 10)\n host = linkInfo.host\n } else if (id) {\n // 支持 #123456 格式\n const cleanId = id.replace(/^#/, '')\n issueId = parseInt(cleanId, 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n } else {\n outputError('请提供问题 ID 或 --url 参数', options.pretty)\n process.exit(1)\n }\n\n // 合并凭据,URL 中的 host 优先\n const creds = resolveCredentials({\n ...options,\n host: host || options.host,\n })\n\n // 对于 get 操作,只需要 token 和 host,project 可选\n const validation = validateCredentials(creds, ['token', 'host'])\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const depth = parseInt(options.depth, 10)\n\n // 根据 depth 决定是否递归获取子单\n if (depth > 0) {\n // 递归获取子单\n const result = await issueService.getIssueWithChildren(\n creds.token!,\n creds.host!,\n creds.project || '',\n issueId,\n depth\n )\n\n if (result.success && result.data) {\n // 判断是使用分级导出还是原始输出\n if (options.raw || options.stdout) {\n // --raw 或 --stdout: 使用原来的单文件输出\n smartOutput(result.data, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-get',\n identifier: issueId.toString(),\n pretty: options.pretty,\n })\n } else {\n // 默认: 分级导出到目录\n const exportDir = options.output || generateExportDir('issue-get', issueId.toString())\n const exportResult = await exportIssuePresets(result.data, exportDir, {\n depth,\n pretty: options.pretty,\n })\n\n if (exportResult.success) {\n // 输出 AI 读取指南\n console.log(generateAIGuideOutput(\n exportResult.exportDir,\n exportResult.fileCount,\n exportResult.totalSize\n ))\n } else {\n outputError('导出失败', options.pretty)\n process.exit(1)\n }\n }\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取问题失败', options.pretty)\n process.exit(1)\n }\n } else {\n // 不递归,只获取当前问题\n const result = await issueService.getIssue(\n creds.token!,\n creds.host!,\n creds.project || '',\n issueId,\n false,\n options.includeRelations\n )\n\n if (result.success && result.data) {\n smartOutput(result.data, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-get',\n identifier: issueId.toString(),\n pretty: options.pretty,\n })\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取问题失败', options.pretty)\n process.exit(1)\n }\n }\n })\n\n // issue create\n issueCmd\n .command('create')\n .description('创建问题')\n .requiredOption('--subject <subject>', '问题标题')\n .option('--description <description>', '问题描述')\n .option('--tracker <tracker>', '跟踪器名称')\n .option('--tracker-id <id>', '跟踪器 ID')\n .option('--priority-id <id>', '优先级 ID')\n .option('--assigned-to-mail <email>', '指派人邮箱')\n .option('--assigned-to-id <id>', '指派人 ID')\n .option('--parent-id <id>', '父问题 ID(子单会继承父单的 tracker、version、assigned_to 等信息)')\n .option('--version <version>', '目标版本名称')\n .option('--start-date <date>', '开始日期')\n .option('--due-date <date>', '截止日期')\n .option('--estimated-hours <hours>', '预估工时')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n subject: options.subject,\n }\n\n // 如果提供了父单 ID,先查询父单信息以继承相关字段\n if (options.parentId) {\n const parentId = parseInt(options.parentId, 10)\n const parentResult = await issueService.getIssue(\n creds.token!,\n creds.host!,\n creds.project || '',\n parentId,\n false,\n false\n )\n\n if (parentResult.success && parentResult.data) {\n const parent = parentResult.data as unknown as {\n tracker?: { name?: string }\n assigned_to?: { mail?: string }\n fixed_version?: { name?: string }\n priority?: { id?: number }\n custom_fields?: Array<{ id: number; name: string; value: unknown }>\n }\n\n // 继承父单的信息(如果用户未明确指定)\n if (!options.tracker && !options.trackerId && parent.tracker?.name) {\n params.tracker = parent.tracker.name\n }\n if (!options.assignedToMail && !options.assignedToId && parent.assigned_to?.mail) {\n params.assigned_to_mail = parent.assigned_to.mail\n }\n if (!options.version && parent.fixed_version?.name) {\n params.version = parent.fixed_version.name\n }\n if (!options.priorityId && parent.priority?.id) {\n params.priority_id = parent.priority.id\n }\n // 默认状态为\"新建\"\n params.status = '新建'\n\n // 继承自定义字段\n if (parent.custom_fields && parent.custom_fields.length > 0) {\n const customFieldMap: Record<number, unknown> = {}\n const followsMails: string[] = []\n\n for (const field of parent.custom_fields) {\n // 跳过空值字段\n if (field.value !== null && field.value !== undefined && field.value !== '') {\n // 特殊处理\"跟进QA\"字段 (identify: IssuesQCFollow)\n const fieldWithIdentify = field as { id: number; name: string; value: unknown; identify?: string }\n if (fieldWithIdentify.identify === 'IssuesQCFollow') {\n // 提取跟进QA的邮箱\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 {\n // 其他自定义字段正常继承\n customFieldMap[field.id] = field.value\n }\n }\n }\n\n if (Object.keys(customFieldMap).length > 0) {\n params.custom_field = JSON.stringify(customFieldMap)\n }\n if (followsMails.length > 0) {\n params.follows = followsMails\n }\n }\n } else {\n outputError(`无法获取父单 #${parentId} 的信息: ${parentResult.message || '未知错误'}`, options.pretty)\n process.exit(1)\n }\n\n params.parent_issue_id = parentId\n }\n\n // 用户明确提供的参数(优先级最高)\n if (options.description) params.description = options.description\n if (options.tracker) params.tracker = options.tracker\n if (options.trackerId) params.tracker_id = parseInt(options.trackerId, 10)\n if (options.priorityId) params.priority_id = parseInt(options.priorityId, 10)\n if (options.assignedToMail) params.assigned_to_mail = options.assignedToMail\n if (options.assignedToId) params.assigned_to_id = parseInt(options.assignedToId, 10)\n if (options.version) params.version = options.version\n if (options.startDate) params.start_date = options.startDate\n if (options.dueDate) params.due_date = options.dueDate\n if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours)\n\n const result = await issueService.createIssue(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '创建问题失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue update <id>\n issueCmd\n .command('update <id>')\n .description('更新问题')\n .option('--subject <subject>', '问题标题')\n .option('--description <description>', '问题描述')\n .option('--status <status>', '状态名称 (如: 新建、开发中、已解决)')\n .option('--tracker <tracker>', '跟踪标签名称 (如: BUG、任务)')\n .option('--version <version>', '目标版本名称')\n .option('--assigned-to-mail <email>', '指派人邮箱')\n .option('--notes <notes>', '更新说明/备注')\n .option('--start-date <date>', '开始日期 (格式: YYYY-MM-DD)')\n .option('--due-date <date>', '截止日期 (格式: YYYY-MM-DD)')\n .option('--estimated-hours <hours>', '预估工时')\n .option('--follows <email>', '跟进QA邮箱')\n .option('--custom-field <key=value>', '自定义字段 (格式: 字段ID=值 或 字段名=值,可多次使用)')\n .option('--cf <key=value>', '自定义字段的简写别名')\n .option('--url <url>', 'PM 链接 (自动解析 host 和 issue_id)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n let issueId: number\n let urlHost: string | undefined\n\n // 支持 --url 参数\n if (options.url) {\n const linkInfo = parsePmLink(options.url)\n if (!linkInfo) {\n outputError('无效的 PM 链接格式', options.pretty)\n process.exit(1)\n }\n issueId = parseInt(linkInfo.issueId, 10)\n urlHost = linkInfo.host\n } else {\n issueId = parseInt(id.replace(/^#/, ''), 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 合并凭据,URL 中的 host 优先\n const creds = resolveCredentials({\n ...options,\n host: urlHost || options.host,\n })\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 构建 API 参数(使用 API 文档定义的参数名)\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n issue_id: issueId,\n }\n\n // 使用 API 文档定义的参数名\n if (options.subject) params.subject = options.subject\n if (options.description) params.description = options.description\n if (options.status) params.status = options.status // 状态名称\n if (options.tracker) params.tracker = options.tracker // 跟踪标签名称\n if (options.version) params.version = options.version // 版本名称\n if (options.assignedToMail) params.assigned_to_mail = options.assignedToMail // 指派人邮箱\n if (options.notes) params.notes = options.notes // 更新说明\n if (options.startDate) params.start_date = options.startDate\n if (options.dueDate) params.due_date = options.dueDate\n if (options.estimatedHours) params.estimated_hours = parseFloat(options.estimatedHours)\n if (options.follows) params.follows = options.follows // 跟进QA\n\n // 处理自定义字段(支持 --custom-field 和 --cf)\n const customFieldInputs = []\n if (options.customField) {\n customFieldInputs.push(...(Array.isArray(options.customField) ? options.customField : [options.customField]))\n }\n if (options.cf) {\n customFieldInputs.push(...(Array.isArray(options.cf) ? options.cf : [options.cf]))\n }\n\n if (customFieldInputs.length > 0) {\n try {\n const customFieldData: Record<string, string> = {}\n\n // 解析所有 key=value 输入\n for (const input of customFieldInputs) {\n const match = input.match(/^([^=]+)=(.*)$/)\n if (!match) {\n outputError(`自定义字段格式错误: ${input},正确格式为 \"字段ID=值\" 或 \"字段名=值\"`, options.pretty)\n process.exit(1)\n }\n const [, key, value] = match\n customFieldData[key.trim()] = value.trim()\n }\n\n // 检查是否所有键都是数字(字段ID)\n const hasNonNumericKey = Object.keys(customFieldData).some(key => isNaN(Number(key)))\n\n if (hasNonNumericKey) {\n // 获取字段选项以映射字段名到字段ID\n const fieldOptionsResult = await issueService.getIssueFieldOptions(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (fieldOptionsResult.success && fieldOptionsResult.data) {\n const fieldOptions = fieldOptionsResult.data as {\n data?: { custom_fields?: Array<{ id: number; name: string }> }\n }\n\n if (fieldOptions.data?.custom_fields) {\n const fieldMap = new Map<string, number>()\n for (const field of fieldOptions.data.custom_fields) {\n fieldMap.set(field.name, field.id)\n }\n\n // 转换字段名为字段ID\n const convertedData: Record<number, string> = {}\n for (const [key, value] of Object.entries(customFieldData)) {\n const fieldId = isNaN(Number(key)) ? fieldMap.get(key) : Number(key)\n if (fieldId !== undefined) {\n convertedData[fieldId] = value\n } else {\n outputError(`未找到自定义字段: ${key}`, options.pretty)\n process.exit(1)\n }\n }\n\n params.custom_field = JSON.stringify(convertedData)\n } else {\n outputError('无法获取自定义字段列表', options.pretty)\n process.exit(1)\n }\n } else {\n outputError('获取自定义字段列表失败', options.pretty)\n process.exit(1)\n }\n } else {\n // 所有键都是数字,直接使用\n const convertedData: Record<number, string> = {}\n for (const [key, value] of Object.entries(customFieldData)) {\n convertedData[Number(key)] = value\n }\n params.custom_field = JSON.stringify(convertedData)\n }\n } catch (error) {\n outputError(\n `自定义字段解析错误: ${error instanceof Error ? error.message : '未知错误'}`,\n options.pretty\n )\n process.exit(1)\n }\n }\n\n const result = await issueService.updateIssue(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '更新问题失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue query\n issueCmd\n .command('query')\n .description('自定义查询')\n .requiredOption('--query-id <id>', '查询 ID')\n .option('--limit <limit>', '返回数量限制')\n .option('--offset <offset>', '偏移量')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const queryId = parseInt(options.queryId, 10)\n const limit = options.limit ? parseInt(options.limit, 10) : undefined\n const offset = options.offset ? parseInt(options.offset, 10) : undefined\n\n const result = await issueService.customQuery(\n creds.token!,\n creds.host!,\n creds.project!,\n queryId,\n limit,\n offset\n )\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue filter\n issueCmd\n .command('filter')\n .description('V6 过滤器查询')\n .option('--mode <mode>', '查询模式 (normal/simple/advanced)', 'normal')\n .option('--status <status>', '状态过滤')\n .option('--tracker <tracker>', '跟踪器过滤')\n .option('--assigned-to <user>', '指派人过滤')\n .option('--limit <limit>', '返回数量限制')\n .option('--offset <offset>', '偏移量')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const filterParams: Record<string, unknown> = {}\n if (options.status) filterParams.status = options.status\n if (options.tracker) filterParams.tracker = options.tracker\n if (options.assignedTo) filterParams.assigned_to = options.assignedTo\n if (options.limit) filterParams.limit = parseInt(options.limit, 10)\n if (options.offset) filterParams.offset = parseInt(options.offset, 10)\n\n const result = await issueService.filterQueryV6(\n creds.token!,\n creds.host!,\n creds.project!,\n options.mode as 'normal' | 'simple' | 'advanced',\n filterParams\n )\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue field-options\n issueCmd\n .command('field-options')\n .description('获取问题字段选项')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await issueService.getIssueFieldOptions(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (result.success) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取字段选项失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue children <id> - 查询子任务\n issueCmd\n .command('children <id>')\n .description('查询子任务(支持按负责人过滤)')\n .option('--url <url>', 'PM 链接')\n .option('--assigned-to <name>', '负责人姓名(支持模糊匹配)')\n .option('--assigned-to-id <id>', '负责人 ID')\n .option('--limit <limit>', '返回数量限制', '100')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n let issueId: number\n let host: string | undefined\n\n // 解析 URL 或 ID\n if (options.url) {\n const linkInfo = parsePmLink(options.url)\n if (!linkInfo) {\n outputError('无效的 PM 链接格式', options.pretty)\n process.exit(1)\n }\n issueId = parseInt(linkInfo.issueId, 10)\n host = linkInfo.host\n } else {\n const cleanId = id.replace(/^#/, '')\n issueId = parseInt(cleanId, 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 合并凭据\n const creds = resolveCredentials({\n ...options,\n host: host || options.host,\n })\n\n const validation = validateCredentials(creds)\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n let assignedToId: number | undefined\n\n // 如果提供了负责人姓名,查找对应的用户 ID\n if (options.assignedTo && !options.assignedToId) {\n const usersResult = await userService.getProjectUsers(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (usersResult.success && usersResult.data) {\n const users = usersResult.data as unknown as Array<{ id: number; name?: string; firstname?: string; lastname?: string }>\n const searchName = options.assignedTo.toLowerCase()\n const matchedUser = users.find(\n (u) =>\n u.name?.toLowerCase().includes(searchName) ||\n u.firstname?.toLowerCase().includes(searchName) ||\n u.lastname?.toLowerCase().includes(searchName)\n )\n\n if (matchedUser) {\n assignedToId = matchedUser.id\n } else {\n outputError(`未找到匹配的用户: ${options.assignedTo}`, options.pretty)\n process.exit(1)\n }\n } else {\n outputError('获取用户列表失败', options.pretty)\n process.exit(1)\n }\n } else if (options.assignedToId) {\n assignedToId = parseInt(options.assignedToId, 10)\n }\n\n const perPage = parseInt(options.limit, 10) || 100\n\n const result = await issueService.queryChildren(\n creds.token!,\n creds.host!,\n creds.project!,\n issueId,\n assignedToId,\n perPage\n )\n\n if (result.success && result.data) {\n // 提取并格式化结果\n const data = result.data as { data?: { list?: unknown[] } }\n if (data.data?.list) {\n outputSuccess({\n total: data.data.list.length,\n issues: data.data.list,\n }, options.pretty)\n } else {\n outputSuccess(result.data, options.pretty)\n }\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询子任务失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue mget <id1> <id2> ... - 批量获取多个问题\n issueCmd\n .command('mget [ids...]')\n .description('批量获取多个问题详情')\n .option('--ids <ids>', '逗号分隔的问题 ID 列表')\n .option('--depth <depth>', '递归获取子单的深度(0 表示不获取子单)', '0')\n .option('--include-relations', '包含关联问题')\n .option('-o, --output <path>', '输出 JSON 到指定文件(默认输出到 /tmp)')\n .option('--stdout', '强制输出到控制台而非文件')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (ids: string[], options) => {\n // 收集所有 ID(支持多种输入格式)\n let issueIds: number[] = []\n\n // 1. 从位置参数收集 ID(支持 #123 格式)\n if (ids && ids.length > 0) {\n const parsedIds = ids\n .map((id) => parseInt(id.replace(/^#/, ''), 10))\n .filter((id) => !isNaN(id))\n issueIds.push(...parsedIds)\n }\n\n // 2. 从 --ids 选项收集(逗号分隔)\n if (options.ids) {\n const idsFromOption = options.ids\n .split(',')\n .map((id: string) => parseInt(id.trim().replace(/^#/, ''), 10))\n .filter((id: number) => !isNaN(id))\n issueIds.push(...idsFromOption)\n }\n\n // 去重\n issueIds = [...new Set(issueIds)]\n\n if (issueIds.length === 0) {\n outputError('请提供至少一个问题 ID', options.pretty)\n process.exit(1)\n }\n\n // 合并凭据\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const depth = parseInt(options.depth, 10)\n const includeChildren = depth > 0\n const includeRelations = options.includeRelations\n\n const result = await issueService.getMultipleIssues(\n creds.token!,\n creds.host!,\n creds.project || '',\n issueIds,\n { includeChildren, includeRelations, depth }\n )\n\n if (result.success && result.data) {\n // 统计结果\n const successItems = result.data.filter((item) => item.success)\n const failedItems = result.data.filter((item) => !item.success)\n\n const output = {\n success: true,\n summary: {\n total: issueIds.length,\n success: successItems.length,\n failed: failedItems.length,\n },\n issues: successItems.map((item) => item.data),\n errors: failedItems.length > 0 ? failedItems.map((item) => ({ id: item.id, error: item.error })) : undefined,\n }\n\n smartOutput(output, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-mget',\n identifier: issueIds.join('_'),\n pretty: options.pretty,\n })\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '批量获取问题失败', options.pretty)\n process.exit(1)\n }\n })\n\n // issue sync - 同步子单\n issueCmd\n .command('sync')\n .description('同步子单:从源父单复制子单到目标父单')\n .option('--from <id>', '源父单 ID')\n .option('--to <id>', '目标父单 ID')\n .option('--from-url <url>', '源父单 PM 链接')\n .option('--to-url <url>', '目标父单 PM 链接')\n .option('--assigned-to-mail <email>', '指派人邮箱(默认使用配置的 userMail)')\n .option('--depth <depth>', '递归深度', '10')\n .option('--dry-run', '模拟运行,不实际创建')\n .option('--no-skip-existing', '不跳过同名任务')\n .option('-o, --output <path>', '输出 JSON 到指定文件')\n .option('--stdout', '强制输出到控制台而非文件')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n let sourceId: number | undefined\n let targetId: number | undefined\n let sourceHost: string | undefined\n let targetHost: string | undefined\n\n // 解析源父单 ID\n if (options.fromUrl) {\n const linkInfo = parsePmLink(options.fromUrl)\n if (!linkInfo) {\n outputError('无效的源父单 PM 链接格式', options.pretty)\n process.exit(1)\n }\n sourceId = parseInt(linkInfo.issueId, 10)\n sourceHost = linkInfo.host\n } else if (options.from) {\n const cleanId = options.from.replace(/^#/, '')\n sourceId = parseInt(cleanId, 10)\n if (isNaN(sourceId)) {\n outputError('无效的源父单 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 解析目标父单 ID\n if (options.toUrl) {\n const linkInfo = parsePmLink(options.toUrl)\n if (!linkInfo) {\n outputError('无效的目标父单 PM 链接格式', options.pretty)\n process.exit(1)\n }\n targetId = parseInt(linkInfo.issueId, 10)\n targetHost = linkInfo.host\n } else if (options.to) {\n const cleanId = options.to.replace(/^#/, '')\n targetId = parseInt(cleanId, 10)\n if (isNaN(targetId)) {\n outputError('无效的目标父单 ID', options.pretty)\n process.exit(1)\n }\n }\n\n // 验证必填参数\n if (!sourceId) {\n outputError('请提供源父单 ID(--from)或 PM 链接(--from-url)', options.pretty)\n process.exit(1)\n }\n if (!targetId) {\n outputError('请提供目标父单 ID(--to)或 PM 链接(--to-url)', options.pretty)\n process.exit(1)\n }\n\n // 合并凭据,URL 中的 host 优先\n const creds = resolveCredentials({\n ...options,\n host: sourceHost || targetHost || options.host,\n })\n\n const validation = validateCredentials(creds, ['token', 'host'])\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 获取指派人邮箱\n let assignedToMail = options.assignedToMail\n if (!assignedToMail) {\n // 从配置中获取 userMail\n const config = readConfig(options.config)\n assignedToMail = config.default?.userMail\n if (options.profile && config.profiles[options.profile]?.userMail) {\n assignedToMail = config.profiles[options.profile].userMail\n }\n }\n\n if (!assignedToMail) {\n outputError('请提供指派人邮箱(--assigned-to-mail)或配置 userMail(pm-cli config set user-mail xxx)', options.pretty)\n process.exit(1)\n }\n\n const depth = parseInt(options.depth, 10)\n const dryRun = options.dryRun || false\n const skipExisting = options.skipExisting !== false\n\n // 执行同步\n const result = await issueService.syncChildIssues(\n creds.token!,\n creds.host!,\n creds.project || '',\n sourceId,\n targetId,\n assignedToMail,\n { dryRun, depth, skipExisting }\n )\n\n if (result.success && result.data) {\n const output = {\n success: true,\n dryRun,\n sourceParentId: sourceId,\n targetParentId: targetId,\n assignedToMail,\n summary: {\n totalCreated: result.data.totalCreated,\n totalSkipped: result.data.totalSkipped,\n totalFailed: result.data.totalFailed,\n },\n created: result.data.created,\n skipped: result.data.skipped.length > 0 ? result.data.skipped : undefined,\n failed: result.data.failed.length > 0 ? result.data.failed : undefined,\n }\n\n smartOutput(output, {\n stdout: options.stdout,\n output: options.output,\n command: 'issue-sync',\n identifier: `${sourceId}_to_${targetId}`,\n pretty: options.pretty,\n })\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '同步子单失败', options.pretty)\n process.exit(1)\n }\n })\n\n return issueCmd\n}\n","import { apiClient } from '../client/api-client.js'\nimport type { ApiResponse, Issue, IssueWithChildren, SyncResult } from '../models/types.js'\nimport logger from '../utils/logger.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.message || result.msg || result.api_error_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 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 // 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 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 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<{ id: number; name: string; value: unknown; identify?: string }>\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\"字段\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 {\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\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 logger.error(`❌ 创建子任务失败: ${taskName}`, createResult.message || createResult.api_error_msg)\n result.totalFailed++\n result.failed.push({\n sourceId,\n subject: taskName,\n error: createResult.message || createResult.api_error_msg || '创建失败',\n })\n }\n } catch (error) {\n logger.error(`❌ 创建子任务时发生错误: ${taskName}`, error)\n result.totalFailed++\n result.failed.push({\n sourceId,\n subject: taskName,\n error: error instanceof Error ? error.message : '未知错误',\n })\n }\n\n // 添加延迟避免 API 限流\n await sleep(500)\n }\n }\n }\n}\n\nexport const issueService = new IssueService()\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","/**\n * Issue 分级导出器\n * 将 Issue 数据导出为分级目录结构\n *\n * 输出结构:\n * /tmp/pm-cli_issue-get_{id}_{timestamp}/\n * ├── tree.summary.json # 完整树形结构,summary 级别\n * ├── standard/\n * │ └── {id}.json # 扁平单任务,standard 级别\n * └── complete/\n * └── {id}.json # 扁平单任务,complete 级别\n */\n\nimport { writeFileSync, mkdirSync, statSync } from 'fs'\nimport { join } from 'path'\nimport type { IssueWithChildren } from '../models/types.js'\nimport {\n generateSummaryTree,\n extractStandardFields,\n extractCompleteFields,\n flattenIssues,\n} from './preset-extractor.js'\n\n// ==================== 类型定义 ====================\n\nexport interface ExportResult {\n success: boolean\n exportDir: string\n fileCount: number\n totalSize: number\n files: {\n path: string\n size: number\n type: 'tree' | 'standard' | 'complete'\n }[]\n}\n\nexport interface ExportOptions {\n /** 树的最大深度,-1 表示无限 */\n depth?: number\n /** 是否格式化 JSON 输出 */\n pretty?: boolean\n}\n\n// ==================== 工具函数 ====================\n\n/**\n * 生成导出目录路径\n */\nexport function generateExportDir(command: string, identifier: string): string {\n const now = new Date()\n const timestamp = now.getFullYear().toString() +\n (now.getMonth() + 1).toString().padStart(2, '0') +\n now.getDate().toString().padStart(2, '0') +\n now.getHours().toString().padStart(2, '0') +\n now.getMinutes().toString().padStart(2, '0') +\n now.getSeconds().toString().padStart(2, '0')\n return `/tmp/pm-cli_${command}_${identifier}_${timestamp}`\n}\n\n// ==================== 导出函数 ====================\n\n/**\n * 导出 Issue 为分级目录结构\n */\nexport async function exportIssuePresets(\n issue: IssueWithChildren,\n exportDir: string,\n options: ExportOptions = {}\n): Promise<ExportResult> {\n const { depth = -1, pretty = false } = options\n const indent = pretty ? 2 : 0\n\n // 创建目录结构\n mkdirSync(exportDir, { recursive: true })\n mkdirSync(join(exportDir, 'standard'), { recursive: true })\n mkdirSync(join(exportDir, 'complete'), { recursive: true })\n\n const resultFiles: ExportResult['files'] = []\n\n // 1. 生成 tree.summary.json(唯一包含 children 的文件)\n const summaryTree = generateSummaryTree(issue, 0, depth)\n const summaryPath = join(exportDir, 'tree.summary.json')\n writeFileSync(summaryPath, JSON.stringify(summaryTree, null, indent), 'utf-8')\n const summarySize = statSync(summaryPath).size\n resultFiles.push({ path: 'tree.summary.json', size: summarySize, type: 'tree' })\n\n // 2. 收集所有任务(扁平化)\n const allIssues = flattenIssues(issue)\n\n // 3. 生成 standard/*.json(扁平单任务)\n for (const iss of allIssues) {\n const standardData = extractStandardFields(iss)\n const standardPath = join(exportDir, 'standard', `${iss.id}.json`)\n writeFileSync(standardPath, JSON.stringify(standardData, null, indent), 'utf-8')\n const standardSize = statSync(standardPath).size\n resultFiles.push({ path: `standard/${iss.id}.json`, size: standardSize, type: 'standard' })\n }\n\n // 4. 生成 complete/*.json(扁平单任务)\n for (const iss of allIssues) {\n const completeData = extractCompleteFields(iss)\n const completePath = join(exportDir, 'complete', `${iss.id}.json`)\n writeFileSync(completePath, JSON.stringify(completeData, null, indent), 'utf-8')\n const completeSize = statSync(completePath).size\n resultFiles.push({ path: `complete/${iss.id}.json`, size: completeSize, type: 'complete' })\n }\n\n // 计算总大小\n const totalSize = resultFiles.reduce((sum, f) => sum + f.size, 0)\n\n return {\n success: true,\n exportDir,\n fileCount: resultFiles.length,\n totalSize,\n files: resultFiles,\n }\n}\n\n/**\n * 生成 AI 读取指南输出\n */\nexport function generateAIGuideOutput(\n exportDir: string,\n fileCount: number,\n totalSize: number\n): string {\n const sizeKB = (totalSize / 1024).toFixed(2)\n\n return `\n✅ 导出完成!\n 📂 ${exportDir}/\n 📊 ${fileCount} 个文件, ${sizeKB} KB\n\n🤖 AI 读取指南:\n 1️⃣ 先读 tree.summary.json → 获取任务树 + 父子关系 + 基本信息\n 2️⃣ 需要详情时 → 读 standard/{id}.json 或 complete/{id}.json\n\n 📖 字段级别:\n • summary = id, subject, status, assigned_to, 工时, 预计提测时间\n • standard = summary + priority, tracker, project, 时间戳, 8个自定义字段\n • complete = 全部字段(含 description, author, journals 等)\n\n 💡 Token 优化:\n • 只看任务列表/进度 → tree.summary.json (~5k tokens)\n • 单任务优先级/来源 → standard/{id}.json (~3k tokens)\n • 任务详细描述/历史 → complete/{id}.json (~11k tokens)\n`.trim()\n}\n","/**\n * 预设级别字段提取器\n * 用于将完整的 Issue 数据按不同预设级别提取字段\n *\n * 三个预设级别:\n * - summary: 核心字段(10个)+ 1个关键自定义字段(预计提测时间)\n * - standard: summary + 扩展字段 + 8个常用自定义字段\n * - complete: 所有字段\n */\n\nimport type { IssueWithChildren } from '../models/types.js'\n\n// ==================== 类型定义 ====================\n\n/**\n * Summary 级别的 Issue 类型(最精简)\n */\nexport interface SummaryIssue {\n id: number\n subject: string\n status: {\n id: number\n name: string\n }\n assigned_to?: {\n id: number\n name: string\n }\n estimated_hours?: number\n spent_hours?: number\n done_ratio: number\n custom_fields: Array<{\n id: number\n name: string\n value: unknown\n }>\n children?: SummaryIssue[]\n}\n\n/**\n * Standard 级别的 Issue 类型(日常使用)\n * 不包含 children(扁平文件)\n */\nexport interface StandardIssue {\n id: number\n subject: string\n status: {\n id: number\n name: string\n is_closed?: boolean\n }\n assigned_to?: {\n id: number\n name: string\n mail?: string\n }\n estimated_hours?: number\n spent_hours?: number\n done_ratio: number\n priority?: {\n id: number\n name: string\n }\n tracker?: {\n id: number\n name: string\n }\n project?: {\n id: number\n name: string\n }\n created_on?: string\n updated_on?: string\n custom_fields: Array<{\n id: number\n name: string\n value: unknown\n }>\n}\n\n/**\n * Complete 级别的 Issue 类型(完整信息)\n * 不包含 children(扁平文件)\n */\nexport interface CompleteIssue {\n [key: string]: unknown\n}\n\n// ==================== 常量定义 ====================\n\n/**\n * Summary 级别包含的自定义字段 ID\n */\nconst SUMMARY_CUSTOM_FIELD_IDS = [84] // 预计提测时间\n\n/**\n * Standard 级别包含的自定义字段 ID\n */\nconst STANDARD_CUSTOM_FIELD_IDS = [\n 84, // 预计提测时间\n 286, // 需求大类\n 186, // 业务目标\n 77, // 需求来源\n 274, // 开发结束时间\n 19, // 跟进QA\n 86, // 需求延误\n 87, // 需求变更\n]\n\n// ==================== 提取函数 ====================\n\n/**\n * 提取 Summary 级别字段(单个任务,不含 children)\n */\nexport function extractSummaryFields(issue: IssueWithChildren): Omit<SummaryIssue, 'children'> {\n const rawIssue = issue as unknown as Record<string, unknown>\n\n // 提取 status\n const rawStatus = rawIssue.status as { id?: number; name?: string } | undefined\n const status = rawStatus\n ? { id: rawStatus.id ?? 0, name: rawStatus.name ?? '' }\n : { id: 0, name: '' }\n\n // 提取 assigned_to\n const rawAssignedTo = rawIssue.assigned_to as { id?: number; name?: string } | undefined\n const assigned_to = rawAssignedTo\n ? { id: rawAssignedTo.id ?? 0, name: rawAssignedTo.name ?? '' }\n : undefined\n\n // 提取自定义字段(仅保留 summary 级别的)\n const rawCustomFields = rawIssue.custom_fields as Array<{ id: number; name: string; value: unknown }> | undefined\n const custom_fields = (rawCustomFields || [])\n .filter(cf => SUMMARY_CUSTOM_FIELD_IDS.includes(cf.id))\n .map(cf => ({ id: cf.id, name: cf.name, value: cf.value }))\n\n return {\n id: issue.id,\n subject: issue.subject,\n status,\n assigned_to,\n estimated_hours: issue.estimated_hours,\n spent_hours: issue.spent_hours,\n done_ratio: issue.done_ratio,\n custom_fields,\n }\n}\n\n/**\n * 提取 Standard 级别字段(单个任务,不含 children)\n */\nexport function extractStandardFields(issue: IssueWithChildren): StandardIssue {\n const rawIssue = issue as unknown as Record<string, unknown>\n\n // 提取 status(包含 is_closed)\n const rawStatus = rawIssue.status as { id?: number; name?: string; is_closed?: boolean } | undefined\n const status = rawStatus\n ? { id: rawStatus.id ?? 0, name: rawStatus.name ?? '', is_closed: rawStatus.is_closed }\n : { id: 0, name: '' }\n\n // 提取 assigned_to(包含 mail)\n const rawAssignedTo = rawIssue.assigned_to as { id?: number; name?: string; mail?: string } | undefined\n const assigned_to = rawAssignedTo\n ? { id: rawAssignedTo.id ?? 0, name: rawAssignedTo.name ?? '', mail: rawAssignedTo.mail }\n : undefined\n\n // 提取 priority\n const rawPriority = rawIssue.priority as { id?: number; name?: string } | undefined\n const priority = rawPriority\n ? { id: rawPriority.id ?? 0, name: rawPriority.name ?? '' }\n : undefined\n\n // 提取 tracker\n const rawTracker = rawIssue.tracker as { id?: number; name?: string } | undefined\n const tracker = rawTracker\n ? { id: rawTracker.id ?? 0, name: rawTracker.name ?? '' }\n : undefined\n\n // 提取 project\n const rawProject = rawIssue.project as { id?: number; name?: string } | undefined\n const project = rawProject\n ? { id: rawProject.id ?? 0, name: rawProject.name ?? '' }\n : undefined\n\n // 提取自定义字段(仅保留 standard 级别的)\n const rawCustomFields = rawIssue.custom_fields as Array<{ id: number; name: string; value: unknown }> | undefined\n const custom_fields = (rawCustomFields || [])\n .filter(cf => STANDARD_CUSTOM_FIELD_IDS.includes(cf.id))\n .map(cf => ({ id: cf.id, name: cf.name, value: cf.value }))\n\n return {\n id: issue.id,\n subject: issue.subject,\n status,\n assigned_to,\n estimated_hours: issue.estimated_hours,\n spent_hours: issue.spent_hours,\n done_ratio: issue.done_ratio,\n priority,\n tracker,\n project,\n created_on: issue.created_on,\n updated_on: issue.updated_on,\n custom_fields,\n }\n}\n\n/**\n * 提取 Complete 级别字段(单个任务,不含 children)\n * 保留所有原始字段,但移除 children\n */\nexport function extractCompleteFields(issue: IssueWithChildren): CompleteIssue {\n const rawIssue = issue as unknown as Record<string, unknown>\n\n // 复制所有字段,但排除 children\n const result: CompleteIssue = {}\n for (const key of Object.keys(rawIssue)) {\n if (key !== 'children') {\n result[key] = rawIssue[key]\n }\n }\n\n return result\n}\n\n// ==================== 树形结构生成 ====================\n\n/**\n * 生成 Summary 级别的树形结构(唯一包含 children 的输出)\n * @param issue 根任务\n * @param currentDepth 当前深度\n * @param maxDepth 最大深度(-1 表示无限)\n */\nexport function generateSummaryTree(\n issue: IssueWithChildren,\n currentDepth: number = 0,\n maxDepth: number = -1\n): SummaryIssue {\n const node = extractSummaryFields(issue) as SummaryIssue\n\n // 检查是否达到深度限制\n if (maxDepth !== -1 && currentDepth >= maxDepth) {\n // 达到深度限制,children 为空数组\n node.children = []\n return node\n }\n\n // 递归处理子任务\n if (issue.children && issue.children.length > 0) {\n node.children = issue.children.map(child =>\n generateSummaryTree(child, currentDepth + 1, maxDepth)\n )\n } else {\n // 叶子节点,children 为空数组\n node.children = []\n }\n\n return node\n}\n\n// ==================== 扁平化工具 ====================\n\n/**\n * 收集树中所有任务(扁平化)\n */\nexport function flattenIssues(issue: IssueWithChildren, result: IssueWithChildren[] = []): IssueWithChildren[] {\n result.push(issue)\n if (issue.children) {\n for (const child of issue.children) {\n flattenIssues(child, result)\n }\n }\n return result\n}\n","import { Command } from 'commander'\nimport { timeEntryService } from '../../services/time-entry-service.js'\nimport { userService } from '../../services/user-service.js'\nimport { resolveCredentials, validateCredentials, getConfigValue } from '../../utils/config.js'\nimport { outputSuccess, outputError } from '../../utils/output.js'\nimport logger from '../../utils/logger.js'\n\nconst WEEKDAY_NAMES = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']\n\n/**\n * 获取今天的日期上下文(用于 AI 推算日期星期对应关系)\n */\nfunction getTodayContext() {\n const now = new Date()\n const year = now.getFullYear()\n const month = String(now.getMonth() + 1).padStart(2, '0')\n const day = String(now.getDate()).padStart(2, '0')\n return {\n today: `${year}-${month}-${day}`,\n todayWeekday: WEEKDAY_NAMES[now.getDay()],\n }\n}\n\n/**\n * 获取周的日期范围(周一到周五)\n * @param week 'current' | 'last' | 数字(相对当前周的偏移,负数表示之前的周)\n * @returns { from: string, to: string } 日期范围\n */\nfunction getWeekDateRange(week: string): { from: string; to: string; weekLabel: string } {\n const now = new Date()\n let offset = 0\n\n if (week === 'current') {\n offset = 0\n } else if (week === 'last') {\n offset = -1\n } else {\n offset = parseInt(week, 10) || 0\n }\n\n // 获取本周周一\n const dayOfWeek = now.getDay()\n const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek // 周日是0,需要特殊处理\n const monday = new Date(now)\n monday.setDate(now.getDate() + diffToMonday + offset * 7)\n monday.setHours(0, 0, 0, 0)\n\n // 获取本周周五\n const friday = new Date(monday)\n friday.setDate(monday.getDate() + 4)\n\n // 使用本地时间格式化,避免 UTC 时区偏差\n const formatDate = (d: Date) => {\n const year = d.getFullYear()\n const month = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n }\n const weekLabel = offset === 0 ? '本周' : offset === -1 ? '上周' : `${Math.abs(offset)}周前`\n\n return {\n from: formatDate(monday),\n to: formatDate(friday),\n weekLabel,\n }\n}\n\nexport function createTimeCommand(): Command {\n const timeCmd = new Command('time').description('工时管理')\n\n // time list\n timeCmd\n .command('list')\n .description('查询工时条目')\n .option('--from <date>', '开始日期 (YYYY-MM-DD)')\n .option('--to <date>', '结束日期 (YYYY-MM-DD)')\n .option('--user-id <id>', '用户 ID')\n .option('--activity-id <id>', '活动类型 ID')\n .option('--limit <limit>', '返回数量限制')\n .option('--offset <offset>', '偏移量')\n .option('--all-projects', '查询所有项目的工时')\n .option('--all-user', '查询所有用户的工时(不筛选,默认只查询当前用户)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n\n // --all-projects 模式只需要 token 和 host\n const requiredFields = options.allProjects ? ['token', 'host'] : ['token', 'host', 'project']\n const validation = validateCredentials(creds, requiredFields as ('token' | 'host' | 'project')[])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 如果使用 --all-projects,则查询所有项目的工时\n if (options.allProjects) {\n logger.info('查询所有项目的工时...')\n\n // 1. 获取所有项目列表\n const projectsResult = await userService.getProjects(creds.token!, creds.host!)\n if (!projectsResult.success || !projectsResult.data) {\n outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || '获取项目列表失败', options.pretty)\n process.exit(1)\n }\n\n const projects = projectsResult.data as unknown as Record<string, { name: string; id: number }>\n const projectNames = Object.values(projects).map((p) => p.name)\n logger.info(`找到 ${projectNames.length} 个项目`)\n\n // 处理用户筛选逻辑\n let userId: number | undefined\n if (!options.allUser) {\n // 默认使用配置中的用户 ID 进行筛选\n if (options.userId) {\n userId = parseInt(options.userId, 10)\n } else {\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n logger.info(`使用配置中的用户 ID: ${userId}`)\n } else {\n outputError('未配置用户 ID,请先设置: pm-cli config set user-id <您的用户ID>,或使用 --all-user 查询所有用户', options.pretty)\n process.exit(1)\n }\n }\n } else if (options.userId) {\n // 如果同时指定了 --all-user 和 --user-id,使用 --user-id\n userId = parseInt(options.userId, 10)\n logger.info(`使用指定的用户 ID: ${userId}`)\n }\n\n // 2. 逐个项目查询工时\n const allTimeEntries: unknown[] = []\n let totalCount = 0\n const projectSummary: { project: string; count: number; hours: number }[] = []\n\n for (const projectName of projectNames) {\n const result = await timeEntryService.queryTimeEntries({\n token: creds.token!,\n host: creds.host!,\n project: projectName,\n from_date: options.from,\n to_date: options.to,\n user_id: userId,\n activity_id: options.activityId ? parseInt(options.activityId, 10) : undefined,\n limit: options.limit ? parseInt(options.limit, 10) : 100,\n offset: options.offset ? parseInt(options.offset, 10) : undefined,\n })\n\n if (result.success && result.data) {\n const data = result.data as { time_entries?: { hours: number }[]; total_count?: number }\n if (data.time_entries && data.time_entries.length > 0) {\n allTimeEntries.push(...data.time_entries)\n const projectHours = data.time_entries.reduce((sum, e) => sum + (e.hours || 0), 0)\n projectSummary.push({\n project: projectName,\n count: data.time_entries.length,\n hours: projectHours,\n })\n totalCount += data.total_count || data.time_entries.length\n logger.info(`${projectName}: ${data.time_entries.length} 条工时记录`)\n }\n }\n }\n\n // 3. 汇总结果\n const totalHours = allTimeEntries.reduce<number>((sum, e) => sum + ((e as { hours: number }).hours || 0), 0)\n outputSuccess({\n ...getTodayContext(),\n total_count: totalCount,\n total_hours: Math.round(totalHours * 100) / 100,\n project_summary: projectSummary,\n time_entries: allTimeEntries,\n })\n } else {\n // 普通模式:查询单个项目的工时\n const result = await timeEntryService.queryTimeEntries({\n token: creds.token!,\n host: creds.host!,\n project: creds.project!,\n from_date: options.from,\n to_date: options.to,\n user_id: options.userId ? parseInt(options.userId, 10) : undefined,\n activity_id: options.activityId ? parseInt(options.activityId, 10) : undefined,\n limit: options.limit ? parseInt(options.limit, 10) : undefined,\n offset: options.offset ? parseInt(options.offset, 10) : undefined,\n })\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '查询工时失败', options.pretty)\n process.exit(1)\n }\n }\n })\n\n // time create\n timeCmd\n .command('create')\n .description('创建工时条目')\n .requiredOption('--issue <id>', '问题 ID')\n .requiredOption('--days <days>', '工时(天)')\n .requiredOption('--activity <id>', '活动类型 ID(使用 pm-cli time options 获取可用值)')\n .option('--date <date>', '日期 (YYYY-MM-DD),默认今天')\n .option('--comments <comments>', '备注')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 获取用户 ID\n let userId: number | undefined\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n }\n\n if (!userId) {\n outputError('缺少用户 ID,请先设置: pm-cli config set user-id <您的用户ID>', options.pretty)\n process.exit(1)\n }\n\n const issueId = parseInt(options.issue.replace(/^#/, ''), 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n\n const days = parseFloat(options.days)\n if (isNaN(days) || days <= 0) {\n outputError('无效的工时数', options.pretty)\n process.exit(1)\n }\n\n const activityId = parseInt(options.activity, 10)\n if (isNaN(activityId)) {\n outputError('无效的活动类型 ID,使用 pm-cli time options 获取可用值', options.pretty)\n process.exit(1)\n }\n\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n issue_id: issueId,\n hours: days, // API 参数名为 hours,但单位是天\n spent_on: options.date || new Date().toISOString().split('T')[0],\n user_id: userId, // 用户 ID(必填)\n activity_id: activityId, // 活动类型 ID(必填)\n bulk_create: 'true', // 固定为 true\n }\n\n if (options.comments) params.comments = options.comments\n\n const result = await timeEntryService.createTimeEntry(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '创建工时失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time update <id>\n timeCmd\n .command('update <id>')\n .description('更新工时条目')\n .requiredOption('--issue <id>', '问题 ID(必填)')\n .requiredOption('--days <days>', '工时(天)(必填)')\n .requiredOption('--activity <id>', '活动类型 ID(必填)')\n .requiredOption('--date <date>', '日期 (YYYY-MM-DD)(必填)')\n .option('--comments <comments>', '备注')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 获取用户 ID\n let userId: number | undefined\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n }\n\n if (!userId) {\n outputError('缺少用户 ID,请先设置: pm-cli config set user-id <您的用户ID>', options.pretty)\n process.exit(1)\n }\n\n const timeEntryId = parseInt(id, 10)\n if (isNaN(timeEntryId)) {\n outputError('无效的工时条目 ID', options.pretty)\n process.exit(1)\n }\n\n const issueId = parseInt(options.issue.replace(/^#/, ''), 10)\n if (isNaN(issueId)) {\n outputError('无效的问题 ID', options.pretty)\n process.exit(1)\n }\n\n const params: Record<string, unknown> = {\n token: creds.token,\n host: creds.host,\n project: creds.project,\n id: timeEntryId, // 工时 ID\n issue_id: issueId, // 问题 ID(必填)\n hours: parseFloat(options.days), // 工时(必填)\n activity_id: parseInt(options.activity, 10), // 活动类型(必填)\n spent_on: options.date, // 日期(必填)\n user_id: userId, // 用户 ID(必填)\n updated_by: userId, // 操作用户 ID(必填)\n }\n\n if (options.comments) params.comments = options.comments\n\n const result = await timeEntryService.updateTimeEntry(params)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '更新工时失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time delete <id>\n timeCmd\n .command('delete <id>')\n .description('删除工时条目')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (id: string, options) => {\n const creds = resolveCredentials(options)\n // 删除工时只需要 token 和 host\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const timeEntryId = parseInt(id, 10)\n if (isNaN(timeEntryId)) {\n outputError('无效的工时条目 ID', options.pretty)\n process.exit(1)\n }\n\n const result = await timeEntryService.deleteTimeEntry(\n creds.token!,\n creds.host!,\n timeEntryId\n )\n\n if (result.success) {\n outputSuccess({ message: '工时条目已删除', id: timeEntryId }, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '删除工时失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time options\n timeCmd\n .command('options')\n .description('获取工时条目选项(活动类型列表等)')\n .option('--issue <id>', '问题 ID(可选,用于获取特定问题的工时选项)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const issueId = options.issue ? parseInt(options.issue.replace(/^#/, ''), 10) : undefined\n\n const result = await timeEntryService.getTimeEntryOptions(\n creds.token!,\n creds.host!,\n creds.project!,\n issueId\n )\n\n if (result.success && result.data) {\n // 格式化输出活动类型列表\n const data = result.data as { options?: { activities?: [string, number, number[]][] } }\n if (data.options?.activities) {\n const activities = data.options.activities.map(([name, id]) => ({ id, name }))\n outputSuccess({ activities }, options.pretty)\n } else {\n outputSuccess(result.data, options.pretty)\n }\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取选项失败', options.pretty)\n process.exit(1)\n }\n })\n\n // time summary\n timeCmd\n .command('summary')\n .description('工时统计汇总(查询指定时间段的工时并计算缺口)')\n .option('--week <week>', '周选择: current (本周), last (上周), 或数字偏移', 'current')\n .option('--from <date>', '开始日期 (YYYY-MM-DD),与 --week 互斥')\n .option('--to <date>', '结束日期 (YYYY-MM-DD),与 --week 互斥')\n .option('--target <days>', '目标工时(天),默认 5', '5')\n .option('--detail', '显示完整工时条目详情')\n .option('--user-id <id>', '用户 ID(默认使用配置中的 user-id)')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n // 确定日期范围\n let fromDate: string\n let toDate: string\n let periodLabel: string\n\n if (options.from && options.to) {\n // 使用自定义日期范围\n fromDate = options.from\n toDate = options.to\n periodLabel = `${fromDate} ~ ${toDate}`\n } else {\n // 使用周选择\n const weekRange = getWeekDateRange(options.week)\n fromDate = weekRange.from\n toDate = weekRange.to\n periodLabel = `${weekRange.weekLabel} (${fromDate} ~ ${toDate})`\n }\n\n // 确定用户 ID\n let userId: number | undefined\n if (options.userId) {\n userId = parseInt(options.userId, 10)\n } else {\n // 从配置中获取用户 ID\n const configUserId = getConfigValue('userId', options.config)\n if (configUserId) {\n userId = parseInt(configUserId, 10)\n }\n }\n\n logger.info(`查询工时统计: ${periodLabel}`)\n if (userId) {\n logger.info(`用户 ID: ${userId}`)\n }\n\n // 获取所有项目列表\n const projectsResult = await userService.getProjects(creds.token!, creds.host!)\n if (!projectsResult.success || !projectsResult.data) {\n outputError(projectsResult.message || projectsResult.msg || projectsResult.api_error_msg || '获取项目列表失败', options.pretty)\n process.exit(1)\n }\n\n const projects = projectsResult.data as unknown as Record<string, { name: string; id: number }>\n const projectNames = Object.values(projects).map((p) => p.name)\n logger.info(`查询 ${projectNames.length} 个项目...`)\n\n // 逐个项目查询工时\n const projectSummary: { project: string; hours: number }[] = []\n const allTimeEntries: unknown[] = []\n let totalHours = 0\n\n for (const projectName of projectNames) {\n const result = await timeEntryService.queryTimeEntries({\n token: creds.token!,\n host: creds.host!,\n project: projectName,\n from_date: fromDate,\n to_date: toDate,\n user_id: userId,\n limit: 1000,\n })\n\n if (result.success && result.data) {\n const data = result.data as { time_entries?: { hours: number }[] }\n if (data.time_entries && data.time_entries.length > 0) {\n const projectHours = data.time_entries.reduce((sum, e) => sum + (e.hours || 0), 0)\n if (projectHours > 0) {\n projectSummary.push({\n project: projectName,\n hours: Math.round(projectHours * 100) / 100,\n })\n totalHours += projectHours\n }\n // 收集完整工时条目用于 --detail 选项\n allTimeEntries.push(...data.time_entries)\n }\n }\n }\n\n // 计算目标和缺口\n const targetHours = parseFloat(options.target) || 5\n const remainingHours = Math.max(0, targetHours - totalHours)\n totalHours = Math.round(totalHours * 100) / 100\n\n // 输出结果\n const output: Record<string, unknown> = {\n ...getTodayContext(),\n period: periodLabel,\n userId: userId || '未指定',\n totalHours,\n targetHours,\n remainingHours: Math.round(remainingHours * 100) / 100,\n isFilled: remainingHours === 0,\n projectBreakdown: projectSummary.sort((a, b) => b.hours - a.hours),\n }\n\n // 如果指定了 --detail 选项,添加完整工时条目详情\n if (options.detail) {\n // 按日期排序\n output.timeEntries = allTimeEntries.sort((a, b) => {\n const dateA = (a as { spent_on?: string }).spent_on || ''\n const dateB = (b as { spent_on?: string }).spent_on || ''\n return dateA.localeCompare(dateB)\n })\n }\n\n outputSuccess(output, options.pretty)\n })\n\n return timeCmd\n}\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 { Command } from 'commander'\nimport { userService } from '../../services/user-service.js'\nimport { resolveCredentials, validateCredentials } from '../../utils/config.js'\nimport { outputSuccess, outputError } from '../../utils/output.js'\n\nexport function createProjectCommand(): Command {\n const projectCmd = new Command('project').description('项目管理')\n\n // project list\n projectCmd\n .command('list')\n .description('获取项目列表')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.getProjects(creds.token!, creds.host!)\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取项目列表失败', options.pretty)\n process.exit(1)\n }\n })\n\n // project users\n projectCmd\n .command('users')\n .description('获取项目用户列表')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--project <project>', '项目名称')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds)\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.getProjectUsers(\n creds.token!,\n creds.host!,\n creds.project!\n )\n\n if (result.success && result.data) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取用户列表失败', options.pretty)\n process.exit(1)\n }\n })\n\n // project info\n projectCmd\n .command('info')\n .description('获取主机信息')\n .option('--token <token>', 'API Token')\n .option('--host <host>', 'PM 主机地址')\n .option('--profile <name>', '使用配置 profile')\n .option('--config <path>', '自定义配置文件路径')\n .option('--pretty', '格式化输出 JSON(默认压缩)')\n .action(async (options) => {\n const creds = resolveCredentials(options)\n const validation = validateCredentials(creds, ['token', 'host'])\n\n if (!validation.valid) {\n outputError(`缺少必要参数: ${validation.missing.join(', ')}`, options.pretty)\n process.exit(1)\n }\n\n const result = await userService.getHostInfo(creds.token!, creds.host!)\n\n if (result.success) {\n outputSuccess(result.data, options.pretty)\n } else {\n outputError(result.message || result.msg || result.api_error_msg || '获取主机信息失败', options.pretty)\n process.exit(1)\n }\n })\n\n return projectCmd\n}\n"],"mappings":"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","names":["Command","writeFileSync","writeFileSync","Command","Command","Command","currentLevel","writeFileSync","mkdirSync","join","mkdirSync","join","writeFileSync","Command","Command","Command","Command","Command","readFileSync","dirname","join","Command"]}
|