@comate/zulu 1.2.1-beta.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/comate-engine/assets/skills/auto-commit-comate/SKILL.md +260 -0
- package/comate-engine/assets/skills/auto-commit-comate/references/data_structures.md +189 -0
- package/comate-engine/assets/skills/auto-commit-comate/references/new_version_instruction.md +209 -0
- package/comate-engine/assets/skills/auto-commit-comate/references/old_version_instruction.md +208 -0
- package/comate-engine/assets/skills/auto-commit-comate/scripts/git_diff_cli.py +196 -0
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/git_utils.py +20 -10
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/client.py +69 -40
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/farseer.py +8 -9
- package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/matching.py +65 -9
- package/comate-engine/assets/skills/auto-commit-comate/scripts/match_card_cli.py +37 -0
- package/comate-engine/assets/skills/cnap-comate/SKILL.md +157 -0
- package/comate-engine/assets/skills/cnap-comate/references/cases.md +198 -0
- package/comate-engine/assets/skills/cnap-comate/references/deploy-troubleshoot.md +15 -0
- package/comate-engine/assets/skills/cnap-comate/references/install.md +43 -0
- package/comate-engine/assets/skills/cnap-comate/references/kubectl.md +55 -0
- package/comate-engine/assets/skills/cnap-comate/references/login.md +125 -0
- package/comate-engine/assets/skills/cnap-comate/references/oncall.md +24 -0
- package/comate-engine/assets/skills/cnap-comate/scripts/install_cnap_cli.sh +36 -0
- package/comate-engine/assets/skills/code-security/SKILL.md +176 -0
- package/comate-engine/assets/skills/code-security/references/credential_hosting.md +102 -0
- package/comate-engine/assets/skills/code-security/references/vul_repair_sensitive.md +219 -0
- package/comate-engine/assets/skills/code-security/scripts/build_repair_info.py +0 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_hosting.py +99 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_poll.py +350 -0
- package/comate-engine/assets/skills/code-security/scripts/http_client.py +173 -0
- package/comate-engine/assets/skills/code-security/scripts/parse_scan_result.py +301 -0
- package/comate-engine/assets/skills/code-security/scripts/repair_vulnerability.py +261 -0
- package/comate-engine/assets/skills/code-security/scripts/report_chat.py +198 -0
- package/comate-engine/assets/skills/code-security/scripts/scan_vulnerability.py +316 -0
- package/comate-engine/assets/skills/code-security-comate/SKILL.md +219 -0
- package/comate-engine/assets/skills/code-security-comate/references/credential_hosting.md +102 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-go_sql_injection.md +399 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-java_sql_injection.md +591 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-php_sql_injection.md +318 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair-python_sql_injection.md +198 -0
- package/comate-engine/assets/skills/code-security-comate/references/vul_repair_sensitive.md +219 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/credential_hosting.py +87 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/credential_poll.py +345 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/http_client.py +173 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/parse_scan_result.py +392 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/repair_vulnerability.py +245 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/report_chat.py +145 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/scan_vulnerability.py +444 -0
- package/comate-engine/assets/skills/code-security-comate/scripts/utils.py +153 -0
- package/comate-engine/assets/skills/comate-docs-comate/SKILL.md +148 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/doc-map-extended.md +78 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/models-and-billing.md +51 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/product-overview.md +73 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/query_content.md +83 -0
- package/comate-engine/assets/skills/comate-docs-comate/references/query_repo.md +57 -0
- package/comate-engine/assets/skills/comate-docs-comate/scripts/ku_operator.py +1575 -0
- package/comate-engine/assets/skills/create-image-comate/SKILL.md +278 -0
- package/comate-engine/assets/skills/create-skill-comate/SKILL.md +308 -217
- package/comate-engine/assets/skills/create-skill-comate/agents/analyzer.md +274 -0
- package/comate-engine/assets/skills/create-skill-comate/agents/comparator.md +202 -0
- package/comate-engine/assets/skills/create-skill-comate/agents/grader.md +223 -0
- package/comate-engine/assets/skills/create-skill-comate/assets/eval_review.html +146 -0
- package/comate-engine/assets/skills/create-skill-comate/eval-viewer/generate_review.py +489 -0
- package/comate-engine/assets/skills/create-skill-comate/eval-viewer/viewer.html +1325 -0
- package/comate-engine/assets/skills/create-skill-comate/references/schemas.md +430 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/__init__.py +0 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/__pycache__/aggregate_benchmark.cpython-311.pyc +0 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/aggregate_benchmark.py +412 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/generate_report.py +334 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/package_skill.py +140 -0
- package/comate-engine/assets/skills/create-skill-comate/scripts/utils.py +53 -0
- package/comate-engine/assets/skills/find-skills-comate/SKILL.md +15 -12
- package/comate-engine/assets/skills/find-skills-comate/scripts/fetch_skills.py +32 -3
- package/comate-engine/assets/skills/get-ugate-token-comate/SKILL.md +159 -0
- package/comate-engine/assets/skills/get-ugate-token-comate/getUgateToken.py +150 -0
- package/comate-engine/assets/skills/icafe-comate/SKILL.md +240 -0
- package/comate-engine/assets/skills/icafe-comate/references/ai-workflows.md +233 -0
- package/comate-engine/assets/skills/icafe-comate/references/commands.md +1147 -0
- package/comate-engine/assets/skills/icafe-comate/references/error-handling.md +164 -0
- package/comate-engine/assets/skills/icafe-comate/references/git-auto-bindcard-workflow.md +201 -0
- package/comate-engine/assets/skills/icafe-comate/references/git-bindcard-workflow.md +327 -0
- package/comate-engine/assets/skills/icafe-comate/references/iql-syntax.md +327 -0
- package/comate-engine/assets/skills/icafe-comate/references/platform-concepts.md +317 -0
- package/comate-engine/assets/skills/icafe-comate/references/smart-create-workflow.md +171 -0
- package/comate-engine/assets/skills/icafe-comate/references/smart-find-workflow.md +127 -0
- package/comate-engine/assets/skills/icafe-comate/references/smart-update-workflow.md +118 -0
- package/comate-engine/assets/skills/icode-comate/SKILL.md +366 -0
- package/comate-engine/assets/skills/icode-comate/references/api/add_reviewers.md +44 -0
- package/comate-engine/assets/skills/icode-comate/references/api/build_fetch_command.md +89 -0
- package/comate-engine/assets/skills/icode-comate/references/api/check_repo_permission.md +89 -0
- package/comate-engine/assets/skills/icode-comate/references/api/create_branch.md +79 -0
- package/comate-engine/assets/skills/icode-comate/references/api/create_draft_comment.md +109 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_ai_cr_result.md +190 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_ai_review.md +97 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_diff_content.md +92 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_diff_file.md +88 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_machine_check.md +73 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_my_reviews.md +115 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_person_commit.md +89 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_person_repo.md +63 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_branch.md +62 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_config.md +91 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_members.md +118 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_repo_reviews.md +91 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_review_comments.md +87 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_review_info.md +81 -0
- package/comate-engine/assets/skills/icode-comate/references/api/get_submit_settings.md +105 -0
- package/comate-engine/assets/skills/icode-comate/references/api/icode-api.md +86 -0
- package/comate-engine/assets/skills/icode-comate/references/api/publish_comments.md +72 -0
- package/comate-engine/assets/skills/icode-comate/references/api/set_review_score.md +58 -0
- package/comate-engine/assets/skills/icode-comate/references/api/start_ai_review.md +77 -0
- package/comate-engine/assets/skills/icode-comate/references/api/submit_review.md +50 -0
- package/comate-engine/assets/skills/icode-comate/references/api/trigger_ai_cr.md +63 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/add-reviewer.md +92 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/fix-machine-check.md +144 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/merge-cr.md +100 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/ssh-setup.md +106 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/submit-acr.md +135 -0
- package/comate-engine/assets/skills/icode-comate/references/feature/submit-cr.md +123 -0
- package/comate-engine/assets/skills/icode-comate/references/git/clone.md +67 -0
- package/comate-engine/assets/skills/icode-comate/references/git/icode-git.md +68 -0
- package/comate-engine/assets/skills/icode-comate/references/git/push.md +64 -0
- package/comate-engine/assets/skills/icode-comate/references/git/push_cr.md +103 -0
- package/comate-engine/assets/skills/icode-comate/references/install.md +144 -0
- package/comate-engine/assets/skills/icode-comate/references/login.md +111 -0
- package/comate-engine/assets/skills/icode-comate/scripts/add-reviewer.sh +154 -0
- package/comate-engine/assets/skills/icode-comate/scripts/common.sh +145 -0
- package/comate-engine/assets/skills/icode-comate/scripts/fix-machine-check.sh +131 -0
- package/comate-engine/assets/skills/icode-comate/scripts/merge-cr.sh +105 -0
- package/comate-engine/assets/skills/icode-comate/scripts/ssh-setup.sh +159 -0
- package/comate-engine/assets/skills/icode-comate/scripts/submit-acr.sh +236 -0
- package/comate-engine/assets/skills/icode-comate/scripts/submit-cr.sh +104 -0
- package/comate-engine/assets/skills/icode-comate/scripts/test-preflight.sh +89 -0
- package/comate-engine/assets/skills/ku-operator-comate/SKILL.md +121 -0
- package/comate-engine/assets/skills/ku-operator-comate/examples.md +190 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/add_member.md +49 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/change_scope.md +38 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/copy_doc.md +50 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/create_doc.md +61 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/delete_doc.md +31 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/edit_content.md +568 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/move_doc.md +45 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_comment.md +79 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_content.md +83 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_flowchart.md +84 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_permission.md +38 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_recent_view.md +67 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_repo.md +57 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/query_user_info.md +37 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/update_member.md +41 -0
- package/comate-engine/assets/skills/ku-operator-comate/references/upload_attachment.md +52 -0
- package/comate-engine/assets/skills/ku-operator-comate/scripts/ku_operator.py +1575 -0
- package/comate-engine/node_modules/better-sqlite3/node_modules/.bin/prebuild-install +2 -2
- package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build +2 -2
- package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build-optional +2 -2
- package/comate-engine/node_modules/tree-sitter-bash/node_modules/.bin/node-gyp-build-test +2 -2
- package/comate-engine/package.json +2 -0
- package/comate-engine/server.js +263 -79
- package/dist/bundle/index.js +3 -3
- package/package.json +1 -1
- package/comate-engine/assets/skills/figma2code-comate/codeConnect.md +0 -37
- package/comate-engine/assets/skills/figma2code-comate/designToken.md +0 -3
- package/comate-engine/assets/skills/figma2code-comate/f2cMcp.md +0 -59
- package/comate-engine/assets/skills/smart-commit/SKILL.md +0 -646
- package/comate-engine/node_modules/@comate/plugin-host/dist/index-AZIho4HV.js +0 -1
- package/comate-engine/node_modules/@comate/plugin-host/dist/user-BIpzRUfb.js +0 -44
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/references/issue_type_mapping.json +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/references/query_reference.md +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/compat.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/create_card_cli.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/icafe/__init__.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/logger.py +0 -0
- /package/comate-engine/assets/skills/{smart-commit → auto-commit-comate}/scripts/recognize_card_cli.py +0 -0
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
凭证托管工具 - 将凭证托管到第三方平台(iPipe/CNAP/datamanage)。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python3 credential_hosting.py --poll-result <poll_result.json路径> --username <用户名>
|
|
7
|
+
|
|
8
|
+
也可手动指定各参数(不推荐,仅向后兼容):
|
|
9
|
+
python3 credential_hosting.py --chat-id <chatID> --platform <platformName> --username <用户名> --credentials '<JSON>'
|
|
10
|
+
|
|
11
|
+
使用 --poll-result 时,自动从 credential_poll.py 的输出文件中提取 chatUUID、deployment.platformName、data.credentials。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import hashlib
|
|
16
|
+
import json
|
|
17
|
+
import logging
|
|
18
|
+
import sys
|
|
19
|
+
import uuid
|
|
20
|
+
|
|
21
|
+
import http_client # noqa: F401 (triggers shared logging config)
|
|
22
|
+
|
|
23
|
+
HOST = "https://comate-sec.baidu-int.com"
|
|
24
|
+
USER_ID = ""
|
|
25
|
+
USERNAME = ""
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger("credential_hosting")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def build_headers(chat_id):
|
|
31
|
+
return {
|
|
32
|
+
"Comate-User-Id": USER_ID,
|
|
33
|
+
"Comate-Username": USERNAME,
|
|
34
|
+
"SAST-Chat-ID": chat_id,
|
|
35
|
+
"SAST-Request-ID": str(uuid.uuid4()),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def hosting_credentials(chat_id, platform, credentials):
|
|
40
|
+
"""将凭证托管到指定平台。"""
|
|
41
|
+
url = "{}/api/v1/deployments/{}/credentials".format(HOST, platform)
|
|
42
|
+
payload = {
|
|
43
|
+
"configs": credentials,
|
|
44
|
+
"scanChatID": chat_id,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
logger.info("credential_hosting: chat_id=%s, platform=%s, credentials_count=%d",
|
|
48
|
+
chat_id, platform, len(credentials))
|
|
49
|
+
print("正在托管凭证到 {} 平台...".format(platform), file=sys.stderr)
|
|
50
|
+
return http_client.post(url, headers=build_headers(chat_id), json_body=payload)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def main():
|
|
54
|
+
global USER_ID, USERNAME
|
|
55
|
+
parser = argparse.ArgumentParser(description="凭证托管工具")
|
|
56
|
+
parser.add_argument("--poll-result", default=None, help="credential_poll.py 输出的结果文件路径,自动提取所需参数")
|
|
57
|
+
parser.add_argument("--chat-id", default=None, help="scanChatID(使用 --poll-result 时可省略)")
|
|
58
|
+
parser.add_argument("--platform", default=None, help="托管平台名称(使用 --poll-result 时可省略)")
|
|
59
|
+
parser.add_argument("--username", required=True, help="Comate 用户名")
|
|
60
|
+
parser.add_argument("--credentials", default=None, help="凭证配置 JSON(使用 --poll-result 时可省略)")
|
|
61
|
+
args = parser.parse_args()
|
|
62
|
+
|
|
63
|
+
USERNAME = args.username
|
|
64
|
+
USER_ID = hashlib.md5(args.username.encode("utf-8")).hexdigest()[:12]
|
|
65
|
+
|
|
66
|
+
chat_id = args.chat_id
|
|
67
|
+
platform = args.platform
|
|
68
|
+
credentials = None
|
|
69
|
+
|
|
70
|
+
if args.poll_result:
|
|
71
|
+
# 从 poll_result 文件提取所有参数
|
|
72
|
+
try:
|
|
73
|
+
with open(args.poll_result, "r", encoding="utf-8") as f:
|
|
74
|
+
poll_data = json.load(f)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print("错误: 读取 poll-result 失败 {}: {}".format(args.poll_result, e), file=sys.stderr)
|
|
77
|
+
sys.exit(1)
|
|
78
|
+
|
|
79
|
+
chat_id = chat_id or poll_data.get("chatUUID", "")
|
|
80
|
+
data = poll_data.get("data", {})
|
|
81
|
+
platform = platform or data.get("deployment", {}).get("platformName", "")
|
|
82
|
+
credentials = data.get("credentials")
|
|
83
|
+
elif args.credentials:
|
|
84
|
+
try:
|
|
85
|
+
credentials = json.loads(args.credentials)
|
|
86
|
+
except json.JSONDecodeError as e:
|
|
87
|
+
print("错误: 凭证 JSON 解析失败: {}".format(e), file=sys.stderr)
|
|
88
|
+
sys.exit(1)
|
|
89
|
+
|
|
90
|
+
if not chat_id or not platform or credentials is None:
|
|
91
|
+
print("错误: 缺少必要参数。使用 --poll-result 或同时提供 --chat-id、--platform、--credentials", file=sys.stderr)
|
|
92
|
+
sys.exit(1)
|
|
93
|
+
|
|
94
|
+
result = hosting_credentials(chat_id, platform, credentials)
|
|
95
|
+
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
凭证配置轮询工具 - 通过 WebSocket 监听凭证托管助手网页的配置结果。
|
|
4
|
+
|
|
5
|
+
用法:
|
|
6
|
+
python3 credential_poll.py --chat-id <chatID> --username <用户名>
|
|
7
|
+
|
|
8
|
+
流程:
|
|
9
|
+
1. 连接 WebSocket 端点
|
|
10
|
+
2. 等待用户在网页完成凭证配置并点击「生成代码」
|
|
11
|
+
3. 收到 msgType=repair 消息后输出配置数据
|
|
12
|
+
|
|
13
|
+
注意: 使用 Python 标准库实现 WebSocket 客户端,无需第三方依赖。
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import argparse
|
|
17
|
+
import hashlib
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
import os
|
|
21
|
+
import socket
|
|
22
|
+
import struct
|
|
23
|
+
import sys
|
|
24
|
+
import time
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
import ssl
|
|
28
|
+
_SSL_AVAILABLE = True
|
|
29
|
+
except ImportError:
|
|
30
|
+
_SSL_AVAILABLE = False
|
|
31
|
+
|
|
32
|
+
# 触发共享日志配置
|
|
33
|
+
import http_client # noqa: F401
|
|
34
|
+
|
|
35
|
+
WS_HOST = "wss://comate-sec-test.baidu-int.com"
|
|
36
|
+
USER_ID = ""
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger("credential_poll")
|
|
39
|
+
|
|
40
|
+
WS_TIMEOUT = 30 * 60 # 30 分钟超时
|
|
41
|
+
RECV_TIMEOUT = 5 # 每次 recv 超时秒数
|
|
42
|
+
MAX_RECONNECT = 10
|
|
43
|
+
RECONNECT_BASE_INTERVAL = 1 # 秒
|
|
44
|
+
|
|
45
|
+
# WebSocket opcodes
|
|
46
|
+
OP_TEXT = 0x1
|
|
47
|
+
OP_CLOSE = 0x8
|
|
48
|
+
OP_PING = 0x9
|
|
49
|
+
OP_PONG = 0xA
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _generate_ws_key():
|
|
53
|
+
"""生成随机的 Sec-WebSocket-Key。"""
|
|
54
|
+
import base64
|
|
55
|
+
return base64.b64encode(os.urandom(16)).decode("ascii")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _ws_connect(host, port, path, headers, use_ssl=True, timeout=10):
|
|
59
|
+
"""
|
|
60
|
+
建立 WebSocket 连接,返回 socket 对象。
|
|
61
|
+
执行 HTTP Upgrade 握手,验证 101 响应。
|
|
62
|
+
"""
|
|
63
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
64
|
+
sock.settimeout(timeout)
|
|
65
|
+
|
|
66
|
+
if use_ssl:
|
|
67
|
+
if not _SSL_AVAILABLE:
|
|
68
|
+
raise RuntimeError(
|
|
69
|
+
"WebSocket 需要 SSL 但 ssl 模块不可用。"
|
|
70
|
+
"请安装 libssl1.1 或重新编译 Python 以支持 ssl。"
|
|
71
|
+
)
|
|
72
|
+
ctx = ssl.create_default_context()
|
|
73
|
+
ctx.check_hostname = False
|
|
74
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
75
|
+
sock = ctx.wrap_socket(sock, server_hostname=host)
|
|
76
|
+
|
|
77
|
+
sock.connect((host, port))
|
|
78
|
+
|
|
79
|
+
# 构造握手请求
|
|
80
|
+
ws_key = _generate_ws_key()
|
|
81
|
+
lines = [
|
|
82
|
+
"GET {} HTTP/1.1".format(path),
|
|
83
|
+
"Host: {}".format(host),
|
|
84
|
+
"Upgrade: websocket",
|
|
85
|
+
"Connection: Upgrade",
|
|
86
|
+
"Sec-WebSocket-Version: 13",
|
|
87
|
+
"Sec-WebSocket-Key: {}".format(ws_key),
|
|
88
|
+
]
|
|
89
|
+
for k, v in headers.items():
|
|
90
|
+
lines.append("{}: {}".format(k, v))
|
|
91
|
+
lines.append("")
|
|
92
|
+
lines.append("")
|
|
93
|
+
|
|
94
|
+
request = "\r\n".join(lines)
|
|
95
|
+
sock.sendall(request.encode("utf-8"))
|
|
96
|
+
|
|
97
|
+
# 读取响应头
|
|
98
|
+
response = b""
|
|
99
|
+
while b"\r\n\r\n" not in response:
|
|
100
|
+
chunk = sock.recv(4096)
|
|
101
|
+
if not chunk:
|
|
102
|
+
raise ConnectionError("连接在握手期间被关闭")
|
|
103
|
+
response += chunk
|
|
104
|
+
|
|
105
|
+
header_end = response.index(b"\r\n\r\n")
|
|
106
|
+
header_part = response[:header_end].decode("utf-8")
|
|
107
|
+
status_line = header_part.split("\r\n")[0]
|
|
108
|
+
|
|
109
|
+
# 验证 101 状态码
|
|
110
|
+
parts = status_line.split(" ", 2)
|
|
111
|
+
status_code = int(parts[1])
|
|
112
|
+
if status_code != 101:
|
|
113
|
+
# 尝试读取 body 获取错误信息
|
|
114
|
+
body_part = response[header_end + 4:]
|
|
115
|
+
error_msg = body_part.decode("utf-8", errors="replace") if body_part else ""
|
|
116
|
+
raise ConnectionError("WebSocket 握手失败: HTTP {} - {}".format(status_code, error_msg))
|
|
117
|
+
|
|
118
|
+
return sock
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _ws_recv_frame(sock):
|
|
122
|
+
"""
|
|
123
|
+
接收一个 WebSocket 帧,返回 (opcode, payload_bytes)。
|
|
124
|
+
"""
|
|
125
|
+
def _recv_exact(n):
|
|
126
|
+
data = b""
|
|
127
|
+
while len(data) < n:
|
|
128
|
+
chunk = sock.recv(n - len(data))
|
|
129
|
+
if not chunk:
|
|
130
|
+
raise ConnectionError("连接被关闭")
|
|
131
|
+
data += chunk
|
|
132
|
+
return data
|
|
133
|
+
|
|
134
|
+
# 读取 2 字节头
|
|
135
|
+
head = _recv_exact(2)
|
|
136
|
+
opcode = head[0] & 0x0F
|
|
137
|
+
masked = (head[1] & 0x80) != 0
|
|
138
|
+
payload_len = head[1] & 0x7F
|
|
139
|
+
|
|
140
|
+
# 扩展长度
|
|
141
|
+
if payload_len == 126:
|
|
142
|
+
payload_len = struct.unpack("!H", _recv_exact(2))[0]
|
|
143
|
+
elif payload_len == 127:
|
|
144
|
+
payload_len = struct.unpack("!Q", _recv_exact(8))[0]
|
|
145
|
+
|
|
146
|
+
# mask (服务端发给客户端通常不 mask,但保留处理)
|
|
147
|
+
mask_key = None
|
|
148
|
+
if masked:
|
|
149
|
+
mask_key = _recv_exact(4)
|
|
150
|
+
|
|
151
|
+
# payload
|
|
152
|
+
payload = _recv_exact(payload_len)
|
|
153
|
+
|
|
154
|
+
if mask_key:
|
|
155
|
+
payload = bytes(b ^ mask_key[i % 4] for i, b in enumerate(payload))
|
|
156
|
+
|
|
157
|
+
return opcode, payload
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _ws_send_frame(sock, opcode, payload=b""):
|
|
161
|
+
"""
|
|
162
|
+
发送一个 WebSocket 帧(客户端发送需要 mask)。
|
|
163
|
+
"""
|
|
164
|
+
mask_key = os.urandom(4)
|
|
165
|
+
masked_payload = bytes(b ^ mask_key[i % 4] for i, b in enumerate(payload))
|
|
166
|
+
|
|
167
|
+
frame = bytearray()
|
|
168
|
+
frame.append(0x80 | opcode) # FIN + opcode
|
|
169
|
+
|
|
170
|
+
length = len(payload)
|
|
171
|
+
if length < 126:
|
|
172
|
+
frame.append(0x80 | length) # MASK bit + length
|
|
173
|
+
elif length < 65536:
|
|
174
|
+
frame.append(0x80 | 126)
|
|
175
|
+
frame.extend(struct.pack("!H", length))
|
|
176
|
+
else:
|
|
177
|
+
frame.append(0x80 | 127)
|
|
178
|
+
frame.extend(struct.pack("!Q", length))
|
|
179
|
+
|
|
180
|
+
frame.extend(mask_key)
|
|
181
|
+
frame.extend(masked_payload)
|
|
182
|
+
|
|
183
|
+
sock.sendall(bytes(frame))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _ws_send_pong(sock, payload=b""):
|
|
187
|
+
"""回复 pong 帧。"""
|
|
188
|
+
_ws_send_frame(sock, OP_PONG, payload)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _ws_send_close(sock, code=1000, reason=""):
|
|
192
|
+
"""发送 close 帧。"""
|
|
193
|
+
payload = struct.pack("!H", code) + reason.encode("utf-8")
|
|
194
|
+
_ws_send_frame(sock, OP_CLOSE, payload)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _parse_ws_url(url):
|
|
198
|
+
"""解析 wss://host/path,返回 (host, port, path, use_ssl)。"""
|
|
199
|
+
if url.startswith("wss://"):
|
|
200
|
+
use_ssl = True
|
|
201
|
+
default_port = 443
|
|
202
|
+
rest = url[6:]
|
|
203
|
+
elif url.startswith("ws://"):
|
|
204
|
+
use_ssl = False
|
|
205
|
+
default_port = 80
|
|
206
|
+
rest = url[5:]
|
|
207
|
+
else:
|
|
208
|
+
raise ValueError("不支持的 URL scheme: {}".format(url))
|
|
209
|
+
|
|
210
|
+
if "/" in rest:
|
|
211
|
+
host_port, path = rest.split("/", 1)
|
|
212
|
+
path = "/" + path
|
|
213
|
+
else:
|
|
214
|
+
host_port = rest
|
|
215
|
+
path = "/"
|
|
216
|
+
|
|
217
|
+
if ":" in host_port:
|
|
218
|
+
host, port_str = host_port.split(":", 1)
|
|
219
|
+
port = int(port_str)
|
|
220
|
+
else:
|
|
221
|
+
host = host_port
|
|
222
|
+
port = default_port
|
|
223
|
+
|
|
224
|
+
return host, port, path, use_ssl
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def poll_credential_config(chat_id):
|
|
228
|
+
"""通过 WebSocket 监听凭证配置,返回配置数据 dict 或 None。"""
|
|
229
|
+
url = "{}/api/v1/ws/credential/events".format(WS_HOST)
|
|
230
|
+
host, port, path, use_ssl = _parse_ws_url(url)
|
|
231
|
+
logger.info("credential_poll start: chat_id=%s", chat_id)
|
|
232
|
+
|
|
233
|
+
ws_headers = {
|
|
234
|
+
"SAST-Chat-ID": chat_id,
|
|
235
|
+
"Comate-User-Id": USER_ID,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
reconnect_attempts = 0
|
|
239
|
+
start_time = time.time()
|
|
240
|
+
|
|
241
|
+
while time.time() - start_time < WS_TIMEOUT:
|
|
242
|
+
sock = None
|
|
243
|
+
try:
|
|
244
|
+
sock = _ws_connect(host, port, path, ws_headers, use_ssl, timeout=RECV_TIMEOUT)
|
|
245
|
+
print("WebSocket 已连接,等待凭证配置...", file=sys.stderr)
|
|
246
|
+
reconnect_attempts = 0 # 连接成功,重置重连计数
|
|
247
|
+
|
|
248
|
+
while time.time() - start_time < WS_TIMEOUT:
|
|
249
|
+
try:
|
|
250
|
+
opcode, payload = _ws_recv_frame(sock)
|
|
251
|
+
|
|
252
|
+
if opcode == OP_TEXT:
|
|
253
|
+
msg = payload.decode("utf-8")
|
|
254
|
+
if not msg:
|
|
255
|
+
continue
|
|
256
|
+
data = json.loads(msg)
|
|
257
|
+
msg_type = data.get("msgType", "")
|
|
258
|
+
|
|
259
|
+
if msg_type == "repair":
|
|
260
|
+
print("收到凭证配置数据", file=sys.stderr)
|
|
261
|
+
logger.info("credential_poll received repair data")
|
|
262
|
+
try:
|
|
263
|
+
_ws_send_close(sock)
|
|
264
|
+
except Exception:
|
|
265
|
+
pass
|
|
266
|
+
return data
|
|
267
|
+
elif msg_type == "openFile":
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
elif opcode == OP_PING:
|
|
271
|
+
_ws_send_pong(sock, payload)
|
|
272
|
+
|
|
273
|
+
elif opcode == OP_CLOSE:
|
|
274
|
+
print("服务端关闭连接,尝试重连...", file=sys.stderr)
|
|
275
|
+
break
|
|
276
|
+
|
|
277
|
+
except socket.timeout:
|
|
278
|
+
# recv 超时,继续等待
|
|
279
|
+
continue
|
|
280
|
+
except (ConnectionError, OSError):
|
|
281
|
+
print("WebSocket 连接断开,尝试重连...", file=sys.stderr)
|
|
282
|
+
break
|
|
283
|
+
except ValueError as e:
|
|
284
|
+
print("JSON 解析失败: {}".format(e), file=sys.stderr)
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
except ConnectionError as e:
|
|
288
|
+
err_msg = str(e)
|
|
289
|
+
print("WebSocket 连接失败: {}".format(err_msg), file=sys.stderr)
|
|
290
|
+
# 400004 表示会话不存在(用户还未打开网页),继续重试等待用户打开
|
|
291
|
+
# 其他业务错误直接退出
|
|
292
|
+
if "400004" in err_msg:
|
|
293
|
+
print("会话不存在,等待用户打开凭证托管网页...", file=sys.stderr)
|
|
294
|
+
elif "HTTP 200" in err_msg or "400" in err_msg:
|
|
295
|
+
print("服务端拒绝连接,请检查 chatID 是否有效", file=sys.stderr)
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
except Exception as e:
|
|
299
|
+
print("WebSocket 连接失败: {}".format(e), file=sys.stderr)
|
|
300
|
+
|
|
301
|
+
finally:
|
|
302
|
+
if sock:
|
|
303
|
+
try:
|
|
304
|
+
sock.close()
|
|
305
|
+
except Exception:
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
# 重连逻辑:指数退避
|
|
309
|
+
reconnect_attempts += 1
|
|
310
|
+
if reconnect_attempts > MAX_RECONNECT:
|
|
311
|
+
print("达到最大重连次数,退出", file=sys.stderr)
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
delay = RECONNECT_BASE_INTERVAL * (2 ** (reconnect_attempts - 1))
|
|
315
|
+
print("{}秒后重连 (第{}/{})次...".format(delay, reconnect_attempts, MAX_RECONNECT), file=sys.stderr)
|
|
316
|
+
time.sleep(delay)
|
|
317
|
+
|
|
318
|
+
print("超时,未收到凭证配置", file=sys.stderr)
|
|
319
|
+
logger.warning("credential_poll timeout: chat_id=%s", chat_id)
|
|
320
|
+
return None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def main():
|
|
324
|
+
"""解析命令行参数并启动凭证配置轮询,将结果输出到标准输出(可选保存到文件)。"""
|
|
325
|
+
global USER_ID
|
|
326
|
+
parser = argparse.ArgumentParser(description="凭证配置轮询工具")
|
|
327
|
+
parser.add_argument("--chat-id", required=True, help="scanChatID,用于关联扫描会话")
|
|
328
|
+
parser.add_argument("--username", required=True, help="Comate 用户名")
|
|
329
|
+
parser.add_argument("--output", default=None, help="将结果保存到指定文件路径(同时仍输出到标准输出)")
|
|
330
|
+
args = parser.parse_args()
|
|
331
|
+
|
|
332
|
+
USER_ID = hashlib.md5(args.username.encode("utf-8")).hexdigest()[:12]
|
|
333
|
+
|
|
334
|
+
result = poll_credential_config(args.chat_id)
|
|
335
|
+
if result:
|
|
336
|
+
output_json = json.dumps(result, ensure_ascii=False, indent=2)
|
|
337
|
+
print(output_json)
|
|
338
|
+
if args.output:
|
|
339
|
+
output_dir = os.path.dirname(os.path.abspath(args.output))
|
|
340
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
341
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
342
|
+
f.write(output_json)
|
|
343
|
+
print("已保存: {}".format(args.output), file=sys.stderr)
|
|
344
|
+
else:
|
|
345
|
+
print(json.dumps({"error": "未收到凭证配置数据"}, ensure_ascii=False))
|
|
346
|
+
sys.exit(1)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
if __name__ == "__main__":
|
|
350
|
+
main()
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
基于 urllib 的轻量 HTTP 客户端,零第三方依赖。
|
|
4
|
+
提供 GET / POST / PUT 方法,支持 JSON 请求和响应。
|
|
5
|
+
当 ssl 模块不可用时(如 libssl 版本不匹配),自动回退到 curl 子进程。
|
|
6
|
+
所有脚本共用日志配置,写入 skill/logs/<日期>.log,自动清理 7 天前的日志。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import datetime
|
|
10
|
+
import glob
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import subprocess
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
import ssl
|
|
18
|
+
_SSL_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
_SSL_AVAILABLE = False
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from urllib.request import Request, urlopen
|
|
24
|
+
from urllib.error import HTTPError, URLError
|
|
25
|
+
except ImportError:
|
|
26
|
+
from urllib2 import Request, urlopen, HTTPError, URLError
|
|
27
|
+
|
|
28
|
+
# ---- 统一日志配置 ----
|
|
29
|
+
_scripts_dir = os.path.dirname(os.path.abspath(__file__))
|
|
30
|
+
_skill_dir = os.path.dirname(_scripts_dir)
|
|
31
|
+
_log_dir = os.path.join(_skill_dir, "logs")
|
|
32
|
+
os.makedirs(_log_dir, exist_ok=True)
|
|
33
|
+
|
|
34
|
+
# 清理超过 7 天的日志文件
|
|
35
|
+
_now = datetime.datetime.now()
|
|
36
|
+
for _f in glob.glob(os.path.join(_log_dir, "*.log")):
|
|
37
|
+
try:
|
|
38
|
+
_mtime = datetime.datetime.fromtimestamp(os.path.getmtime(_f))
|
|
39
|
+
if (_now - _mtime).days > 7:
|
|
40
|
+
os.remove(_f)
|
|
41
|
+
except OSError:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
_log_file = os.path.join(_log_dir, _now.strftime("%Y-%m-%d") + ".log")
|
|
45
|
+
logging.basicConfig(
|
|
46
|
+
filename=_log_file,
|
|
47
|
+
level=logging.INFO,
|
|
48
|
+
format="%(asctime)s [%(name)s] %(levelname)s %(message)s",
|
|
49
|
+
)
|
|
50
|
+
logger = logging.getLogger("http")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _build_request(url, method="GET", headers=None, json_body=None):
|
|
54
|
+
"""构造 urllib Request 对象。"""
|
|
55
|
+
data = None
|
|
56
|
+
if json_body is not None:
|
|
57
|
+
data = json.dumps(json_body).encode("utf-8")
|
|
58
|
+
|
|
59
|
+
req = Request(url, data=data)
|
|
60
|
+
req.get_method = lambda: method
|
|
61
|
+
|
|
62
|
+
if headers:
|
|
63
|
+
for k, v in headers.items():
|
|
64
|
+
req.add_header(k, v)
|
|
65
|
+
|
|
66
|
+
if data is not None and not req.has_header("Content-type"):
|
|
67
|
+
req.add_header("Content-Type", "application/json; charset=utf-8")
|
|
68
|
+
|
|
69
|
+
return req
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _create_ssl_context():
|
|
73
|
+
"""创建 SSL 上下文。"""
|
|
74
|
+
ctx = ssl.create_default_context()
|
|
75
|
+
ctx.check_hostname = False
|
|
76
|
+
ctx.verify_mode = ssl.CERT_NONE
|
|
77
|
+
return ctx
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _log_safe_headers(headers):
|
|
81
|
+
"""提取可安全记录的请求头。"""
|
|
82
|
+
safe = {}
|
|
83
|
+
if headers:
|
|
84
|
+
for k in ("SAST-Chat-ID", "SAST-Request-ID", "Comate-User-Id", "Comate-Username"):
|
|
85
|
+
if k in headers:
|
|
86
|
+
safe[k] = headers[k]
|
|
87
|
+
return safe
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _request_curl(url, method="GET", headers=None, json_body=None, timeout=120):
|
|
91
|
+
"""ssl 不可用时,通过 curl 子进程发送请求。"""
|
|
92
|
+
cmd = ["curl", "-s", "-S", "--insecure", "-X", method, "--max-time", str(timeout)]
|
|
93
|
+
|
|
94
|
+
if headers:
|
|
95
|
+
for k, v in headers.items():
|
|
96
|
+
cmd += ["-H", "{}: {}".format(k, v)]
|
|
97
|
+
|
|
98
|
+
if json_body is not None:
|
|
99
|
+
cmd += ["-H", "Content-Type: application/json; charset=utf-8"]
|
|
100
|
+
cmd += ["-d", json.dumps(json_body)]
|
|
101
|
+
|
|
102
|
+
cmd.append(url)
|
|
103
|
+
|
|
104
|
+
logger.info("[curl] %s %s headers=%s", method, url, json.dumps(_log_safe_headers(headers)))
|
|
105
|
+
try:
|
|
106
|
+
proc = subprocess.run(cmd, capture_output=True, timeout=timeout + 10)
|
|
107
|
+
except FileNotFoundError:
|
|
108
|
+
raise RuntimeError(
|
|
109
|
+
"ssl 模块不可用且未找到 curl 命令。"
|
|
110
|
+
"请安装 curl 或修复 Python ssl 依赖(安装 libssl1.1 或重新编译 Python)。"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if proc.returncode != 0:
|
|
114
|
+
stderr_text = proc.stderr.decode("utf-8", errors="replace").strip()
|
|
115
|
+
logger.error("[curl] %s %s -> curl exit %d: %s", method, url, proc.returncode, stderr_text)
|
|
116
|
+
raise RuntimeError("curl 请求失败 (exit {}): {}".format(proc.returncode, stderr_text))
|
|
117
|
+
|
|
118
|
+
body = proc.stdout.decode("utf-8")
|
|
119
|
+
logger.info("[curl] %s %s -> OK (%d bytes)", method, url, len(body))
|
|
120
|
+
return json.loads(body)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def request(url, method="GET", headers=None, json_body=None, timeout=120):
|
|
124
|
+
"""
|
|
125
|
+
发送 HTTP 请求,返回解析后的 JSON dict。
|
|
126
|
+
当 ssl 模块不可用时自动回退到 curl。
|
|
127
|
+
|
|
128
|
+
:param url: 请求 URL
|
|
129
|
+
:param method: HTTP 方法 (GET/POST/PUT)
|
|
130
|
+
:param headers: 请求头 dict
|
|
131
|
+
:param json_body: JSON 请求体 (自动序列化)
|
|
132
|
+
:param timeout: 超时秒数
|
|
133
|
+
:return: 响应 JSON dict
|
|
134
|
+
:raises: HTTPError, URLError, RuntimeError
|
|
135
|
+
"""
|
|
136
|
+
if not _SSL_AVAILABLE:
|
|
137
|
+
return _request_curl(url, method, headers, json_body, timeout)
|
|
138
|
+
|
|
139
|
+
# 记录请求(仅 URL 和关键 header,不记录 body 避免日志过大)
|
|
140
|
+
logger.info("%s %s headers=%s", method, url, json.dumps(_log_safe_headers(headers)))
|
|
141
|
+
|
|
142
|
+
req = _build_request(url, method, headers, json_body)
|
|
143
|
+
ctx = _create_ssl_context()
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
resp = urlopen(req, timeout=timeout, context=ctx)
|
|
147
|
+
status = resp.getcode()
|
|
148
|
+
body = resp.read().decode("utf-8")
|
|
149
|
+
logger.info("%s %s -> %d (%d bytes)", method, url, status, len(body))
|
|
150
|
+
return json.loads(body)
|
|
151
|
+
except HTTPError as e:
|
|
152
|
+
error_body = ""
|
|
153
|
+
try:
|
|
154
|
+
error_body = e.read().decode("utf-8")[:500]
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
logger.error("%s %s -> HTTP %d: %s", method, url, e.code, error_body)
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get(url, headers=None, timeout=120):
|
|
162
|
+
"""发送 GET 请求,返回 JSON。"""
|
|
163
|
+
return request(url, method="GET", headers=headers, timeout=timeout)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def post(url, headers=None, json_body=None, timeout=120):
|
|
167
|
+
"""发送 POST 请求,返回 JSON。"""
|
|
168
|
+
return request(url, method="POST", headers=headers, json_body=json_body, timeout=timeout)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def put(url, headers=None, json_body=None, timeout=120):
|
|
172
|
+
"""发送 PUT 请求,返回 JSON。"""
|
|
173
|
+
return request(url, method="PUT", headers=headers, json_body=json_body, timeout=timeout)
|