@comate/zulu 1.4.0-beta.2 → 1.4.0-beta.3
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/comate-engine/assets/skills/auto-commit/SKILL.md +241 -0
- package/comate-engine/assets/skills/auto-commit/references/data_structures.md +183 -0
- package/comate-engine/assets/skills/auto-commit/references/interaction_instruction.md +220 -0
- package/comate-engine/assets/skills/auto-commit/references/issue_type_mapping.json +19 -0
- package/comate-engine/assets/skills/auto-commit/references/query_reference.md +176 -0
- package/comate-engine/assets/skills/auto-commit/scripts/build_git_commit_payload.py +195 -0
- package/comate-engine/assets/skills/auto-commit/scripts/build_icafe_cards_payload.py +80 -0
- package/comate-engine/assets/skills/auto-commit/scripts/cache_manager.py +69 -0
- package/comate-engine/assets/skills/auto-commit/scripts/create_card_cli.py +67 -0
- package/comate-engine/assets/skills/auto-commit/scripts/git_diff_cli.py +201 -0
- package/comate-engine/assets/skills/auto-commit/scripts/git_utils.py +230 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/__init__.py +66 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/client.py +473 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/farseer.py +52 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/matching.py +784 -0
- package/comate-engine/assets/skills/auto-commit/scripts/logger.py +32 -0
- package/comate-engine/assets/skills/auto-commit/scripts/match_card_cli.py +41 -0
- package/comate-engine/assets/skills/auto-commit/scripts/payload_validators.py +309 -0
- package/comate-engine/assets/skills/auto-commit/scripts/recognize_card_cli.py +63 -0
- package/comate-engine/assets/skills/{automation-browser-comate → automation-browser}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{cnap-comate → cnap}/SKILL.md +1 -0
- package/comate-engine/assets/skills/code-review/SKILL.md +202 -0
- package/comate-engine/assets/skills/code-review/agents/correctness-reviewer.md +62 -0
- package/comate-engine/assets/skills/code-review/agents/custom-reviewer.md +53 -0
- package/comate-engine/assets/skills/code-review/agents/meta-reviewer.md +84 -0
- package/comate-engine/assets/skills/code-review/agents/reliability-reviewer.md +72 -0
- package/comate-engine/assets/skills/code-review/agents/reuse-reviewer.md +101 -0
- package/comate-engine/assets/skills/code-review/agents/style-reviewer.md +65 -0
- package/comate-engine/assets/skills/code-review/evals/SKILL.md +334 -0
- package/comate-engine/assets/skills/code-review/evals/agents/gt-generator.md +76 -0
- package/comate-engine/assets/skills/code-review/evals/agents/miner.md +87 -0
- package/comate-engine/assets/skills/code-review/evals/agents/score-judge.md +168 -0
- package/comate-engine/assets/skills/code-review/evals/references/cli-query-template.md +114 -0
- package/comate-engine/assets/skills/code-review/evals/references/gt-schema.md +77 -0
- package/comate-engine/assets/skills/code-review/references/custom-rules/RULE_TEMPLATE.md +141 -0
- package/comate-engine/assets/skills/code-review/references/dispatch-template.md +142 -0
- package/comate-engine/assets/skills/code-review/references/output-schema.md +197 -0
- package/comate-engine/assets/skills/code-review/references/report-format.md +41 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_AUTH_RULES.md +29 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_CORRECTNESS_RULES.md +111 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_RESOURCE_CONCURRENCY_RULES.md +190 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_STYLE_RULES.md +354 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_AUTH_RULES.md +34 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_CORRECTNESS_RULES.md +207 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_RESOURCE_CONCURRENCY_RULES.md +220 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_STYLE_RULES.md +306 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_AUTH_RULES.md +48 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_CORRECTNESS_RULES.md +364 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_RESOURCE_CONCURRENCY_RULES.md +180 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_STYLE_RULES.md +350 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_AUTH_RULES.md +38 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_CORRECTNESS_RULES.md +255 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_RESOURCE_CONCURRENCY_RULES.md +180 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_STYLE_RULES.md +195 -0
- package/comate-engine/assets/skills/code-review/references/telemetry.md +27 -0
- package/comate-engine/assets/skills/{code-security-comate → code-security}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/SKILL.md +1 -1
- package/comate-engine/assets/skills/create-automation-tasks-comate/SKILL.md +300 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/backend_dev.md +109 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/env_setup.md +130 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/frontend_dev.md +74 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/git_operations.md +88 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/long_running_task.md +96 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/testing_strategy.md +94 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/scripts/check_config.py +397 -0
- package/comate-engine/assets/skills/{create-rule-comate → create-rule}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{create-skill-comate → create-skill}/SKILL.md +1 -1
- package/comate-engine/assets/skills/{figma2code-comate → figma2code}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{icafe-comate → icafe}/SKILL.md +1 -13
- package/comate-engine/assets/skills/{icode-comate → icode}/SKILL.md +1 -0
- package/comate-engine/node_modules/@comate/plugin-shared-internals/dist/index.js +3 -3
- package/comate-engine/server.js +136 -82
- package/dist/bundle/index.js +20 -9
- package/package.json +1 -1
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/cases.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/deploy-troubleshoot.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/install.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/kubectl.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/login.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/oncall.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/scripts/install_cnap_cli.sh +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/credential_hosting.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-go_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-java_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-php_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-python_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair_sensitive.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/credential_hosting.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/credential_poll.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/http_client.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/parse_scan_result.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/repair_vulnerability.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/report_chat.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/scan_vulnerability.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/utils.py +0 -0
- /package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/references/doc-map-extended.md +0 -0
- /package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/references/models-and-billing.md +0 -0
- /package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/references/product-overview.md +0 -0
- /package/comate-engine/assets/skills/{create-image-comate → create-image}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/LICENSE.txt +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/agents/analyzer.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/agents/comparator.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/agents/grader.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/assets/eval_review.html +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/eval-viewer/generate_review.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/eval-viewer/viewer.html +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/references/schemas.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/__init__.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/__pycache__/aggregate_benchmark.cpython-311.pyc +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/aggregate_benchmark.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/generate_report.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/package_skill.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/quick_validate.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/utils.py +0 -0
- /package/comate-engine/assets/skills/{create-subagent-comate → create-subagent}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{figma2code-comate → figma2code}/references/codeConnect.md +0 -0
- /package/comate-engine/assets/skills/{figma2code-comate → figma2code}/references/designToken.md +0 -0
- /package/comate-engine/assets/skills/{figma2code-comate → figma2code}/references/image2design.md +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/fetch_skills.py +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/get_download_url.py +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/install_skill.py +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/preview_skill.py +0 -0
- /package/comate-engine/assets/skills/{get-ugate-token-comate → get-ugate-token}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{get-ugate-token-comate → get-ugate-token}/getUgateToken.py +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/ai-workflows.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/commands.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/error-handling.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/git-auto-bindcard-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/git-bindcard-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/iql-syntax.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/platform-concepts.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/smart-create-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/smart-find-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/smart-update-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/add_reviewers.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/build_fetch_command.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/check_repo_permission.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/create_branch.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/create_draft_comment.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_ai_cr_result.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_ai_review.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_diff_content.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_diff_file.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_machine_check.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_my_reviews.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_person_commit.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_person_repo.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_branch.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_config.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_members.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_reviews.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_review_comments.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_review_info.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_submit_settings.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/icode-api.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/publish_comments.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/set_review_score.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/start_ai_review.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/submit_review.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/trigger_ai_cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/add-reviewer.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/fix-machine-check.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/merge-cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/ssh-setup.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/submit-acr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/submit-cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/clone.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/icode-git.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/push.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/push_cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/install.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/login.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/add-reviewer.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/common.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/fix-machine-check.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/merge-cr.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/ssh-setup.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/submit-acr.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/submit-cr.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/test-preflight.sh +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/examples.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/add_member.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/change_scope.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/copy_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/create_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/delete_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/edit_content.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/move_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_comment.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_content.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_flowchart.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_permission.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_recent_view.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_repo.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_user_info.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/update_member.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/upload_attachment.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/scripts/ku_operator.py +0 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
"""iCafe API 客户端
|
|
2
|
+
|
|
3
|
+
包含常量、配置类和 API 客户端。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json as _json
|
|
7
|
+
import os
|
|
8
|
+
import urllib.error
|
|
9
|
+
import urllib.parse
|
|
10
|
+
import urllib.request
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Dict, List, Any, Optional
|
|
14
|
+
|
|
15
|
+
from logger import get_logger
|
|
16
|
+
|
|
17
|
+
logger = get_logger()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ========================================
|
|
21
|
+
# 常量配置
|
|
22
|
+
# ========================================
|
|
23
|
+
|
|
24
|
+
# iCafe API base URL
|
|
25
|
+
ICAFE_BASE_URL = "http://10.11.152.208:8701/api/process/icafe"
|
|
26
|
+
|
|
27
|
+
# Farseer API base URL
|
|
28
|
+
FARSEER_BASE_URL = "https://farseer.baidu.com/api/v1"
|
|
29
|
+
|
|
30
|
+
# 默认超时时间(秒)
|
|
31
|
+
DEFAULT_TIMEOUT = 30
|
|
32
|
+
|
|
33
|
+
# 默认查询参数
|
|
34
|
+
DEFAULT_LOOKBACK_DAYS = 14
|
|
35
|
+
DEFAULT_MAX_RECORDS = 20
|
|
36
|
+
|
|
37
|
+
# Git log 提取时的最大提交数
|
|
38
|
+
DEFAULT_MAX_COMMITS = 200
|
|
39
|
+
|
|
40
|
+
# Git diff 输出的最大行数
|
|
41
|
+
DEFAULT_MAX_DIFF_LINES = 500
|
|
42
|
+
|
|
43
|
+
# 不是 iCafe 空间 ID 的常见前缀(小写)
|
|
44
|
+
GIT_LOG_FALSE_POSITIVE_PREFIXES = {
|
|
45
|
+
'v', 'sha', 'utf', 'iso', 'rfc', 'p', 'rc', 'pr', 'mr',
|
|
46
|
+
'http', 'https', 'feat', 'fix', 'chore', 'docs', 'style',
|
|
47
|
+
'refactor', 'perf', 'test', 'build', 'ci', 'revert',
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# 卡片类型 ID -> 名称映射表
|
|
51
|
+
ISSUE_TYPE_MAP = {
|
|
52
|
+
56075: "Epic",
|
|
53
|
+
54443: "Feature",
|
|
54
|
+
5007: "Story",
|
|
55
|
+
54444: "Task",
|
|
56
|
+
54621: "Tech Feature",
|
|
57
|
+
54622: "Tech Task",
|
|
58
|
+
5009: "Bug",
|
|
59
|
+
5811: "Bug(线上)",
|
|
60
|
+
5011: "Case",
|
|
61
|
+
65085: "非研发任务",
|
|
62
|
+
49460: "项目",
|
|
63
|
+
5008: "需求",
|
|
64
|
+
5010: "任务",
|
|
65
|
+
88805: "Prompt Story",
|
|
66
|
+
88806: "Prompt Task",
|
|
67
|
+
88807: "GenAI Data Story",
|
|
68
|
+
88808: "GenAI Data Task",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# 反向映射:名称 -> issueTypeId
|
|
72
|
+
ISSUE_TYPE_NAME_MAP = {v: k for k, v in ISSUE_TYPE_MAP.items()}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
# ========================================
|
|
76
|
+
# 配置类
|
|
77
|
+
# ========================================
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class ICafeQueryConfig:
|
|
81
|
+
"""iCafe 查询 API 配置"""
|
|
82
|
+
|
|
83
|
+
base_url: str = ICAFE_BASE_URL
|
|
84
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _get_auth_token() -> Optional[str]:
|
|
88
|
+
"""获取认证 token
|
|
89
|
+
|
|
90
|
+
优先从环境变量 COMATE_AUTH_TOKEN 读取,
|
|
91
|
+
如果没有则从 ~/.comate/login 文件读取
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
认证 token 字符串,如果都未找到则返回 None
|
|
95
|
+
"""
|
|
96
|
+
token = os.environ.get("COMATE_AUTH_TOKEN")
|
|
97
|
+
if token:
|
|
98
|
+
logger.info("Token 来源: env")
|
|
99
|
+
return token
|
|
100
|
+
|
|
101
|
+
login_file = Path.home() / ".comate" / "login"
|
|
102
|
+
if login_file.exists():
|
|
103
|
+
try:
|
|
104
|
+
content = login_file.read_text().strip()
|
|
105
|
+
if content:
|
|
106
|
+
logger.info("Token 来源: file (~/.comate/login)")
|
|
107
|
+
return content
|
|
108
|
+
except Exception:
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
logger.warning("Token 来源: none, 未找到认证 token")
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# ========================================
|
|
116
|
+
# API 客户端
|
|
117
|
+
# ========================================
|
|
118
|
+
|
|
119
|
+
class ICafeQueryClient:
|
|
120
|
+
"""iCafe 卡片查询客户端
|
|
121
|
+
|
|
122
|
+
专注于卡片查询场景,支持:
|
|
123
|
+
- 通过 IQL 条件表达式查询卡片
|
|
124
|
+
- 通过卡片 ID 获取卡片详情
|
|
125
|
+
- 查询空间列表和空间计划
|
|
126
|
+
- 分页、排序、展示选项等高级查询能力
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __init__(self, config: Optional[ICafeQueryConfig] = None):
|
|
130
|
+
self.config = config or ICafeQueryConfig()
|
|
131
|
+
self._default_headers: Dict[str, str] = {
|
|
132
|
+
"Content-Type": "application/json",
|
|
133
|
+
"User-Agent": "iAPI/1.0.0 (http://iapi.baidu-int.com)",
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
auth_token = self.config._get_auth_token()
|
|
137
|
+
if auth_token:
|
|
138
|
+
self._default_headers["x-ac-Authorization"] = auth_token
|
|
139
|
+
|
|
140
|
+
def _http_request(
|
|
141
|
+
self,
|
|
142
|
+
url: str,
|
|
143
|
+
params: Optional[dict] = None,
|
|
144
|
+
data: Optional[dict] = None,
|
|
145
|
+
headers: Optional[Dict[str, str]] = None,
|
|
146
|
+
timeout: Optional[int] = None,
|
|
147
|
+
) -> Any:
|
|
148
|
+
"""发送 HTTP 请求并返回解析后的 JSON
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
url: 请求 URL
|
|
152
|
+
params: GET 查询参数
|
|
153
|
+
data: POST 请求体(dict,会被序列化为 JSON)
|
|
154
|
+
headers: 额外 headers(会合并到默认 headers)
|
|
155
|
+
timeout: 超时时间(秒)
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
解析后的 JSON 对象(dict 或 list)
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
urllib.error.HTTPError: HTTP 状态码非 2xx 时
|
|
162
|
+
urllib.error.URLError: 网络连接失败时
|
|
163
|
+
"""
|
|
164
|
+
if timeout is None:
|
|
165
|
+
timeout = self.config.timeout
|
|
166
|
+
|
|
167
|
+
# 合并 headers
|
|
168
|
+
merged_headers = dict(self._default_headers)
|
|
169
|
+
if headers:
|
|
170
|
+
merged_headers.update(headers)
|
|
171
|
+
|
|
172
|
+
# 构建完整 URL
|
|
173
|
+
if params:
|
|
174
|
+
query_string = urllib.parse.urlencode(params, doseq=True)
|
|
175
|
+
url = f"{url}?{query_string}" if "?" not in url else f"{url}&{query_string}"
|
|
176
|
+
|
|
177
|
+
# 构建 Request
|
|
178
|
+
body = None
|
|
179
|
+
if data is not None:
|
|
180
|
+
body = _json.dumps(data).encode("utf-8")
|
|
181
|
+
|
|
182
|
+
req = urllib.request.Request(url, data=body, headers=merged_headers)
|
|
183
|
+
|
|
184
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
185
|
+
return _json.loads(resp.read().decode("utf-8"))
|
|
186
|
+
|
|
187
|
+
def _get_with_retry(self, url: str, params: dict, max_retries: int = 3) -> Any:
|
|
188
|
+
"""带重试的 GET 请求,返回解析后的 JSON"""
|
|
189
|
+
import time
|
|
190
|
+
last_exc = None
|
|
191
|
+
for attempt in range(max_retries + 1):
|
|
192
|
+
try:
|
|
193
|
+
return self._http_request(url, params=params)
|
|
194
|
+
except Exception as e:
|
|
195
|
+
last_exc = e
|
|
196
|
+
if attempt < max_retries:
|
|
197
|
+
logger.warning("API 请求失败 (attempt %d/%d): %s, error=%s", attempt + 1, max_retries, url, e)
|
|
198
|
+
time.sleep(0.5 * (attempt + 1))
|
|
199
|
+
logger.error("API 请求最终失败: %s, error=%s", url, last_exc)
|
|
200
|
+
raise last_exc
|
|
201
|
+
|
|
202
|
+
# ========================================
|
|
203
|
+
# 1. 通过 IQL 条件查询卡片
|
|
204
|
+
# ========================================
|
|
205
|
+
def query_cards(self, space_id: str, iql: str,
|
|
206
|
+
page: Optional[int] = None,
|
|
207
|
+
show_detail: bool = False,
|
|
208
|
+
show_associations: bool = False,
|
|
209
|
+
is_desc: bool = False,
|
|
210
|
+
order: Optional[str] = None,
|
|
211
|
+
show_children: bool = False,
|
|
212
|
+
max_records: Optional[int] = None,
|
|
213
|
+
show_okr: bool = False,
|
|
214
|
+
show_accumulate: bool = False) -> Dict[str, Any]:
|
|
215
|
+
"""通过 IQL 条件表达式查询卡片
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
space_id: 空间 ID,如 "joytest"
|
|
219
|
+
iql: IQL 查询表达式,如 "类型 = Bug AND 负责人 = currentUser"
|
|
220
|
+
page: 页码(从 1 开始),可选
|
|
221
|
+
show_detail: 是否显示卡片详情内容,默认 False
|
|
222
|
+
show_associations: 是否显示关联信息,默认 False
|
|
223
|
+
is_desc: 是否降序排列,默认 False
|
|
224
|
+
order: 排序字段(如 "创建时间"、"更新时间"),可选
|
|
225
|
+
show_children: 是否显示子卡片,默认 False
|
|
226
|
+
max_records: 最大返回记录数,可选
|
|
227
|
+
show_okr: 是否显示 OKR 信息,默认 False
|
|
228
|
+
show_accumulate: 是否显示累计信息,默认 False
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
查询结果字典,包含卡片列表和分页信息
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
ValueError: 当 space_id 或 iql 为空时
|
|
235
|
+
urllib.error.URLError: 请求失败时
|
|
236
|
+
|
|
237
|
+
IQL 表达式示例:
|
|
238
|
+
- "类型 = Bug"
|
|
239
|
+
- "负责人 = currentUser"
|
|
240
|
+
- "负责人 = dongkexin01"
|
|
241
|
+
- "类型 = Bug AND 负责人 = currentUser"
|
|
242
|
+
- "创建时间 > \\"2025-01-01\\""
|
|
243
|
+
- "类型 in (Bug, Epic, Story)"
|
|
244
|
+
- "标题 ~ 测试"
|
|
245
|
+
- "流程状态 in (新建, 开发中)"
|
|
246
|
+
"""
|
|
247
|
+
if not space_id:
|
|
248
|
+
raise ValueError("space_id 不能为空")
|
|
249
|
+
if not iql:
|
|
250
|
+
raise ValueError("iql 查询表达式不能为空")
|
|
251
|
+
|
|
252
|
+
params = {'iql': iql}
|
|
253
|
+
if page is not None:
|
|
254
|
+
params['page'] = page
|
|
255
|
+
if show_detail:
|
|
256
|
+
params['showDetail'] = ''
|
|
257
|
+
if show_associations:
|
|
258
|
+
params['showAssociations'] = ''
|
|
259
|
+
if is_desc:
|
|
260
|
+
params['isDesc'] = ''
|
|
261
|
+
if order:
|
|
262
|
+
params['order'] = order
|
|
263
|
+
if show_children:
|
|
264
|
+
params['showChildren'] = ''
|
|
265
|
+
if max_records:
|
|
266
|
+
params['maxRecords'] = max_records
|
|
267
|
+
if show_okr:
|
|
268
|
+
params['showOkr'] = ''
|
|
269
|
+
if show_accumulate:
|
|
270
|
+
params['showAccumulate'] = ''
|
|
271
|
+
|
|
272
|
+
url = f"{self.config.base_url}/api/spaces/{space_id}/cards"
|
|
273
|
+
logger.info("查询卡片: space=%s, iql=%s", space_id, iql)
|
|
274
|
+
result = self._get_with_retry(url, params)
|
|
275
|
+
cards_count = len(result.get("cards", []))
|
|
276
|
+
logger.info("查询卡片返回: %d 张", cards_count)
|
|
277
|
+
return result
|
|
278
|
+
|
|
279
|
+
# ========================================
|
|
280
|
+
# 2. 通过卡片 ID 获取卡片详情
|
|
281
|
+
# ========================================
|
|
282
|
+
def get_card_by_id(self, space_id: str, card_id: str,
|
|
283
|
+
show_associations: bool = False,
|
|
284
|
+
show_children: bool = False,
|
|
285
|
+
show_okr: bool = False,
|
|
286
|
+
show_accumulate: bool = False) -> Dict[str, Any]:
|
|
287
|
+
"""根据空间 ID 和卡片 ID 获取单张卡片详情
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
space_id: 空间 ID
|
|
291
|
+
card_id: 卡片 ID
|
|
292
|
+
show_associations: 是否显示关联信息
|
|
293
|
+
show_children: 是否显示子卡片
|
|
294
|
+
show_okr: 是否显示 OKR 信息
|
|
295
|
+
show_accumulate: 是否显示累计信息
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
卡片信息字典,包含 id, title, description, status, assignee 等
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
ValueError: 当 space_id 或 card_id 为空时
|
|
302
|
+
urllib.error.URLError: 请求失败时
|
|
303
|
+
"""
|
|
304
|
+
if not space_id or not card_id:
|
|
305
|
+
raise ValueError("space_id 和 card_id 不能为空")
|
|
306
|
+
|
|
307
|
+
params = {}
|
|
308
|
+
if show_associations:
|
|
309
|
+
params['showAssociations'] = ''
|
|
310
|
+
if show_children:
|
|
311
|
+
params['showChildren'] = ''
|
|
312
|
+
if show_okr:
|
|
313
|
+
params['showOkr'] = ''
|
|
314
|
+
if show_accumulate:
|
|
315
|
+
params['showAccumulate'] = ''
|
|
316
|
+
|
|
317
|
+
url = f"{self.config.base_url}/api/spaces/{space_id}/cards/{card_id}"
|
|
318
|
+
return self._get_with_retry(url, params)
|
|
319
|
+
|
|
320
|
+
# ========================================
|
|
321
|
+
# 3. 查询最近访问的空间列表
|
|
322
|
+
# ========================================
|
|
323
|
+
def get_latest_spaces(self, current_user: str, limit: int = 3) -> List[Dict[str, Any]]:
|
|
324
|
+
"""查询用户最近访问的空间列表
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
current_user: 用户 ID,如 "dongkexin01"
|
|
328
|
+
limit: 最多返回的空间数量,默认 3
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
空间列表,每个空间包含 id, name, prefix_code, access_time 等
|
|
332
|
+
|
|
333
|
+
Raises:
|
|
334
|
+
ValueError: 当 current_user 为空时
|
|
335
|
+
urllib.error.URLError: 请求失败时
|
|
336
|
+
"""
|
|
337
|
+
if not current_user:
|
|
338
|
+
raise ValueError("current_user 不能为空")
|
|
339
|
+
|
|
340
|
+
logger.info("查询最近访问空间: user=%s, limit=%d", current_user, limit)
|
|
341
|
+
url = f"{self.config.base_url}/api/v2/space/latest"
|
|
342
|
+
params = {'currentUser': current_user}
|
|
343
|
+
data = self._get_with_retry(url, params)
|
|
344
|
+
|
|
345
|
+
# 对返回的空间列表截取前 limit 个
|
|
346
|
+
if isinstance(data, dict) and 'result' in data:
|
|
347
|
+
data['result'] = data['result'][:limit]
|
|
348
|
+
elif isinstance(data, list):
|
|
349
|
+
data = data[:limit]
|
|
350
|
+
|
|
351
|
+
return data
|
|
352
|
+
|
|
353
|
+
# ========================================
|
|
354
|
+
# 4. 获取空间内所有计划
|
|
355
|
+
# ========================================
|
|
356
|
+
def get_space_plans(self, space_id: str) -> List[Dict[str, Any]]:
|
|
357
|
+
"""获取空间内所有计划
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
space_id: 空间 ID
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
计划列表,每个计划包含 id, name, start_date, end_date, status 等
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
ValueError: 当 space_id 为空时
|
|
367
|
+
urllib.error.URLError: 请求失败时
|
|
368
|
+
"""
|
|
369
|
+
if not space_id:
|
|
370
|
+
raise ValueError("space_id 不能为空")
|
|
371
|
+
|
|
372
|
+
url = f"{self.config.base_url}/api/v2/space/{space_id}/plans"
|
|
373
|
+
return self._get_with_retry(url, {})
|
|
374
|
+
|
|
375
|
+
# ========================================
|
|
376
|
+
# 5. 获取空间卡片类型列表
|
|
377
|
+
# ========================================
|
|
378
|
+
def get_space_issue_types(self, space_prefix: str) -> List[Dict[str, Any]]:
|
|
379
|
+
"""获取空间的卡片类型列表
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
space_prefix: 空间前缀(如 "dkx")
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
卡片类型列表,每个类型包含 id, name 等
|
|
386
|
+
|
|
387
|
+
Raises:
|
|
388
|
+
ValueError: 当 space_prefix 为空时
|
|
389
|
+
urllib.error.URLError: 请求失败时
|
|
390
|
+
"""
|
|
391
|
+
if not space_prefix:
|
|
392
|
+
raise ValueError("space_prefix 不能为空")
|
|
393
|
+
|
|
394
|
+
url = f"{self.config.base_url}/api/v2/space/{space_prefix}/issueTypes"
|
|
395
|
+
return self._get_with_retry(url, {})
|
|
396
|
+
|
|
397
|
+
# ========================================
|
|
398
|
+
# 6. 创建卡片
|
|
399
|
+
# ========================================
|
|
400
|
+
def create_card(
|
|
401
|
+
self,
|
|
402
|
+
title: str,
|
|
403
|
+
space_id: str,
|
|
404
|
+
card_type: str,
|
|
405
|
+
description: str = "",
|
|
406
|
+
assignee_id: Optional[str] = None,
|
|
407
|
+
) -> Optional[Dict[str, Any]]:
|
|
408
|
+
"""创建 iCafe 卡片
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
title: 卡片标题
|
|
412
|
+
space_id: 空间前缀(字符串类型,如 "dkx"),注意不是数字 ID
|
|
413
|
+
card_type: 卡片类型名称(如 "Bug", "Story")
|
|
414
|
+
description: 卡片描述,默认为空
|
|
415
|
+
assignee_id: 负责人 ID,默认为当前用户
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
创建成功的卡片信息,包含:id, title, type, sequence 等。
|
|
419
|
+
失败时返回 None。
|
|
420
|
+
|
|
421
|
+
Note:
|
|
422
|
+
此实现完全参考 icafe-card-assistant 的 icafe_client.py create_card 方法
|
|
423
|
+
重要: space_id 参数应该是空间前缀(如 "dkx"),而不是数字 ID
|
|
424
|
+
"""
|
|
425
|
+
try:
|
|
426
|
+
if not title or not space_id:
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
logger.info("创建卡片: title=%s, space=%s, type=%s, assignee=%s", title, space_id, card_type, assignee_id)
|
|
430
|
+
issue = {
|
|
431
|
+
'title': title,
|
|
432
|
+
'detail': description, # 简化处理,不做 HTML 转换
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if card_type:
|
|
436
|
+
issue['type'] = card_type
|
|
437
|
+
|
|
438
|
+
# 构建自定义字段
|
|
439
|
+
fields = {}
|
|
440
|
+
if assignee_id:
|
|
441
|
+
fields['负责人'] = assignee_id
|
|
442
|
+
|
|
443
|
+
if fields:
|
|
444
|
+
issue['fields'] = fields
|
|
445
|
+
|
|
446
|
+
data = {
|
|
447
|
+
'issues': [issue]
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
url = f"{self.config.base_url}/api/v2/space/{space_id}/issue/new"
|
|
451
|
+
headers = {
|
|
452
|
+
'User-Agent': 'iAPI/1.0.0 (http://iapi.baidu-int.com)',
|
|
453
|
+
'Content-Type': 'application/json'
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
result = self._http_request(url, data=data, headers=headers)
|
|
457
|
+
|
|
458
|
+
# 检查返回的状态
|
|
459
|
+
if isinstance(result, dict):
|
|
460
|
+
response_status = result.get('status')
|
|
461
|
+
if response_status != 200:
|
|
462
|
+
logger.error("创建卡片失败: API 返回业务状态码 %s, 消息: %s", response_status, result.get('message', 'Unknown'))
|
|
463
|
+
print(f"错误: API 返回业务状态码 {response_status}, 消息: {result.get('message', 'Unknown')}", file=__import__('sys').stderr)
|
|
464
|
+
return None
|
|
465
|
+
|
|
466
|
+
issues = result.get("issues", [])
|
|
467
|
+
seq = issues[0].get("sequence") if issues else "unknown"
|
|
468
|
+
logger.info("创建卡片成功: sequence=%s", seq)
|
|
469
|
+
return result
|
|
470
|
+
except Exception as e:
|
|
471
|
+
logger.error("创建卡片异常: %s: %s", type(e).__name__, e)
|
|
472
|
+
print(f"创建卡片异常: {type(e).__name__}: {e}", file=__import__('sys').stderr)
|
|
473
|
+
return None
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Farseer API 客户端"""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import urllib.parse
|
|
5
|
+
import urllib.request
|
|
6
|
+
from typing import Dict, List, Any, Optional
|
|
7
|
+
|
|
8
|
+
from .client import FARSEER_BASE_URL, DEFAULT_TIMEOUT, ISSUE_TYPE_MAP
|
|
9
|
+
from logger import get_logger
|
|
10
|
+
|
|
11
|
+
logger = get_logger()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_binding_card_types(space_id: int, timeout: int = DEFAULT_TIMEOUT) -> List[Dict[str, Any]]:
|
|
15
|
+
"""查询空间可绑定的卡片类型列表(含 ID 和名称)
|
|
16
|
+
|
|
17
|
+
调用 farseer storyRule 接口获取空间的 engineeringBindingTypes,
|
|
18
|
+
返回包含 issueTypeId 和类型名称的完整列表。
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
space_id: 空间数字 ID(如 56355)
|
|
22
|
+
timeout: 请求超时时间(秒)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
卡片类型列表,如 [{"id": "5009", "name": "Bug"}, {"id": "5007", "name": "Story"}]。
|
|
26
|
+
接口调用失败或无配置时返回空列表。
|
|
27
|
+
"""
|
|
28
|
+
logger.info("查询 farseer 可绑定类型: space_id=%s", space_id)
|
|
29
|
+
try:
|
|
30
|
+
query_string = urllib.parse.urlencode({'icafeSpaceId': space_id})
|
|
31
|
+
url = f"{FARSEER_BASE_URL}/storyRule/getRule?{query_string}"
|
|
32
|
+
req = urllib.request.Request(url)
|
|
33
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
34
|
+
data = json.loads(resp.read().decode("utf-8"))
|
|
35
|
+
|
|
36
|
+
rule_data = data.get('data', {}).get('data', {})
|
|
37
|
+
binding_types = rule_data.get('engineeringBindingTypes', [])
|
|
38
|
+
|
|
39
|
+
result = []
|
|
40
|
+
for item in binding_types:
|
|
41
|
+
type_id = item.get('issueTypeId')
|
|
42
|
+
if type_id and type_id in ISSUE_TYPE_MAP:
|
|
43
|
+
result.append({
|
|
44
|
+
'id': str(type_id),
|
|
45
|
+
'name': ISSUE_TYPE_MAP[type_id]
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
logger.info("获取到 %d 种可绑定卡片类型", len(result))
|
|
49
|
+
return result
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.warning("farseer 查询失败: %s", e)
|
|
52
|
+
return []
|