@comate/zulu 1.4.0-beta.4 → 1.4.0-beta.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.
Files changed (53) hide show
  1. package/comate-engine/assets/skills/auto-commit/SKILL.md +2 -0
  2. package/comate-engine/assets/skills/auto-commit-sandbox-comate/SKILL.md +2 -2
  3. package/comate-engine/assets/skills/code-review/SKILL.md +6 -5
  4. package/comate-engine/assets/skills/code-review/agents/custom-reviewer.md +2 -2
  5. package/comate-engine/assets/skills/code-review/agents/meta-reviewer.md +2 -2
  6. package/comate-engine/assets/skills/code-review/agents/style-reviewer.md +72 -10
  7. package/comate-engine/assets/skills/code-review/references/dispatch-template.md +12 -12
  8. package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_STYLE_RULES.md +11 -5
  9. package/comate-engine/assets/skills/code-security/SKILL.md +110 -41
  10. package/comate-engine/assets/skills/code-security/references/credential_hosting.md +190 -28
  11. package/comate-engine/assets/skills/code-security/references/vul_analysis-go_sql_injection.md +149 -0
  12. package/comate-engine/assets/skills/code-security/references/vul_analysis-java_sql_injection.md +185 -0
  13. package/comate-engine/assets/skills/code-security/references/vul_analysis-php_sql_injection.md +147 -0
  14. package/comate-engine/assets/skills/code-security/references/vul_analysis-python_sql_injection.md +143 -0
  15. package/comate-engine/assets/skills/code-security/references/vul_repair-go_sql_injection.md +2 -2
  16. package/comate-engine/assets/skills/code-security/references/vul_repair-sca.md +225 -0
  17. package/comate-engine/assets/skills/code-security/scripts/credential_hosting.py +12 -10
  18. package/comate-engine/assets/skills/code-security/scripts/credential_open_page.py +125 -0
  19. package/comate-engine/assets/skills/code-security/scripts/credential_poll.py +12 -9
  20. package/comate-engine/assets/skills/code-security/scripts/credential_url.py +81 -0
  21. package/comate-engine/assets/skills/code-security/scripts/ducc/get_claude_session_id.sh +33 -0
  22. package/comate-engine/assets/skills/code-security/scripts/ducc/open_browser.py +191 -0
  23. package/comate-engine/assets/skills/code-security/scripts/parse_scan_result.py +99 -16
  24. package/comate-engine/assets/skills/code-security/scripts/repair_vulnerability.py +66 -13
  25. package/comate-engine/assets/skills/code-security/scripts/scan_vulnerability.py +44 -12
  26. package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/SKILL.md +8 -8
  27. package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/long_running_task.md +0 -15
  28. package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/testing_strategy.md +1 -1
  29. package/comate-engine/assets/skills/create-image/SKILL.md +197 -206
  30. package/comate-engine/assets/skills/create-image/scripts/generate-image.ps1 +213 -0
  31. package/comate-engine/assets/skills/create-image/scripts/generate-image.sh +322 -0
  32. package/comate-engine/assets/skills/create-subagent/SKILL.md +23 -5
  33. package/comate-engine/fallbackServer.js +1 -1
  34. package/comate-engine/node_modules/@comate/plugin-shared-internals/dist/index.js +1 -1
  35. package/comate-engine/server.js +89 -66
  36. package/dist/bundle/index.js +3 -3
  37. package/package.json +1 -1
  38. package/scripts/postinstall.js +4 -3
  39. package/comate-engine/assets/skills/code-review/evals/SKILL.md +0 -334
  40. package/comate-engine/assets/skills/code-review/evals/agents/gt-generator.md +0 -76
  41. package/comate-engine/assets/skills/code-review/evals/agents/miner.md +0 -87
  42. package/comate-engine/assets/skills/code-review/evals/agents/score-judge.md +0 -168
  43. package/comate-engine/assets/skills/code-review/evals/references/cli-query-template.md +0 -114
  44. package/comate-engine/assets/skills/code-review/evals/references/gt-schema.md +0 -77
  45. package/comate-engine/assets/skills/code-review/references/custom-rules/RULE_TEMPLATE.md +0 -141
  46. /package/comate-engine/assets/commands/{code-review-comate.md → code-review.md} +0 -0
  47. /package/comate-engine/assets/commands/{debug-comate.md → debug.md} +0 -0
  48. /package/comate-engine/assets/commands/{unit-test-comate.md → unit-test.md} +0 -0
  49. /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/backend_dev.md +0 -0
  50. /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/env_setup.md +0 -0
  51. /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/frontend_dev.md +0 -0
  52. /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/git_operations.md +0 -0
  53. /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/scripts/check_config.py +0 -0
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 打开硬编码风险治理界面 - 构建凭证配置网页 URL 并在 IDE 内嵌浏览器中打开。
4
+
5
+ 用法:
6
+ python3 credential_open_page.py --chat-id <chatID> --username <用户名> \
7
+ [--ide-name <ideType>] [--repo <repo>] [--project-dir <目录>]
8
+
9
+ 输出:
10
+ 1. 可点击的 Markdown 链接(供用户手动打开)
11
+ 2. 通过 comate-kernel socket 在 IDE 内嵌浏览器中自动打开网页
12
+ """
13
+
14
+ import argparse
15
+ import glob
16
+ import json
17
+ import os
18
+ import socket
19
+ import subprocess
20
+ import sys
21
+
22
+ # 导入公共 URL 构建模块
23
+ sys.path.insert(0, os.path.dirname(__file__))
24
+ from credential_url import build_url_from_args, build_markdown_link
25
+
26
+
27
+ def open_in_ide(url: str) -> bool:
28
+ """通过 comate-kernel Unix Socket HTTP API 在 IDE 内嵌浏览器中打开网页。
29
+
30
+ 传入 reuseExisting=True,若相同 URL 已在内嵌浏览器中打开则复用现有页签,不新建。
31
+ """
32
+ body = json.dumps({
33
+ "action": "executeVirtualEditor",
34
+ "method": "openUrlInEditorWebview",
35
+ "payload": {"url": url, "title": "凭证托管助手", "reuseExisting": True},
36
+ })
37
+ req = (
38
+ "POST /editor/command/ HTTP/1.1\r\n"
39
+ "Host: localhost\r\n"
40
+ "Content-Type: application/json\r\n"
41
+ f"Content-Length: {len(body.encode())}\r\n"
42
+ "Connection: close\r\n\r\n"
43
+ + body
44
+ )
45
+
46
+ tmpdir = os.environ.get("TMPDIR", "/tmp").rstrip("/")
47
+ ppid_sock = f"{tmpdir}/comate-kernel-{os.getppid()}.sock"
48
+ others = sorted(
49
+ set(
50
+ glob.glob(f"{tmpdir}/comate-kernel-*.sock")
51
+ + glob.glob("/tmp/comate-kernel-*.sock")
52
+ ),
53
+ key=os.path.getmtime,
54
+ reverse=True,
55
+ )
56
+ socks = ([ppid_sock] if os.path.exists(ppid_sock) else []) + [
57
+ s for s in others if s != ppid_sock
58
+ ]
59
+
60
+ for sock_path in socks:
61
+ try:
62
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
63
+ s.settimeout(2)
64
+ s.connect(sock_path)
65
+ s.send(req.encode())
66
+ resp = b""
67
+ while True:
68
+ chunk = s.recv(4096)
69
+ if not chunk:
70
+ break
71
+ resp += chunk
72
+ s.close()
73
+ if b"ok" in resp:
74
+ return True
75
+ except Exception:
76
+ pass
77
+
78
+ return False
79
+
80
+
81
+ def open_via_cli(url: str) -> bool:
82
+ """降级方案:通过 IDE CLI --open-url 触发 SimpleBrowser。"""
83
+ import urllib.parse
84
+ enc = urllib.parse.quote(json.dumps([url]))
85
+ for cli in ["comate", "code"]:
86
+ try:
87
+ subprocess.run(
88
+ [cli, "--open-url", f"command:simpleBrowser.api.open?{enc}"],
89
+ timeout=3,
90
+ capture_output=True,
91
+ )
92
+ return True
93
+ except Exception:
94
+ pass
95
+ return False
96
+
97
+
98
+ def main():
99
+ """入口函数:解析参数、构建 URL、输出链接并尝试在 IDE 内嵌浏览器中打开。"""
100
+ parser = argparse.ArgumentParser(description="打开硬编码风险治理界面")
101
+ parser.add_argument("--chat-id", required=True, help="会话 ID (_CHAT_ID)")
102
+ parser.add_argument("--username", required=True, help="用户名 (_USERNAME)")
103
+ parser.add_argument("--ide-name", default=os.environ.get("COMATE_IDE_NAME", "comate"), help="IDE 类型")
104
+ parser.add_argument("--repo", default="", help="仓库标识,留空则自动从 git remote 获取")
105
+ parser.add_argument("--project-dir", default=".", help="项目目录,用于自动获取 repo")
106
+ args = parser.parse_args()
107
+
108
+ url = build_url_from_args(args.chat_id, args.username, args.ide_name,
109
+ args.project_dir, args.repo)
110
+
111
+ # 输出可点击链接(在 IDE 内嵌浏览器中打开)
112
+ print("请点击以下链接打开硬编码风险治理网页,配置凭证信息:")
113
+ print()
114
+ print(build_markdown_link(url))
115
+ print()
116
+ print("配置完成后请在网页中点击「生成代码」按钮,我会自动检测到配置完成并继续执行修复。")
117
+ print()
118
+
119
+ # 自动在 IDE 内嵌浏览器中打开
120
+ if not open_in_ide(url):
121
+ open_via_cli(url)
122
+
123
+
124
+ if __name__ == "__main__":
125
+ main()
@@ -14,7 +14,7 @@
14
14
  """
15
15
 
16
16
  import argparse
17
- import base64
17
+ from utils import make_user_id, WS_HOST
18
18
  import json
19
19
  import logging
20
20
  import os
@@ -23,15 +23,16 @@ import struct
23
23
  import sys
24
24
  import time
25
25
 
26
+ # 触发共享日志配置
27
+ import http_client # noqa: F401
28
+
26
29
  try:
27
30
  import ssl
28
31
  _SSL_AVAILABLE = True
29
32
  except ImportError:
30
33
  _SSL_AVAILABLE = False
31
34
 
32
- # 触发共享日志配置
33
- import http_client # noqa: F401
34
- import utils
35
+ USER_ID = ""
35
36
 
36
37
  logger = logging.getLogger("credential_poll")
37
38
 
@@ -49,6 +50,7 @@ OP_PONG = 0xA
49
50
 
50
51
  def _generate_ws_key():
51
52
  """生成随机的 Sec-WebSocket-Key。"""
53
+ import base64
52
54
  return base64.b64encode(os.urandom(16)).decode("ascii")
53
55
 
54
56
 
@@ -221,15 +223,15 @@ def _parse_ws_url(url):
221
223
  return host, port, path, use_ssl
222
224
 
223
225
 
224
- def poll_credential_config(chat_id, user_id):
226
+ def poll_credential_config(chat_id):
225
227
  """通过 WebSocket 监听凭证配置,返回配置数据 dict 或 None。"""
226
- url = "{}/api/v1/ws/credential/events".format(utils.WS_HOST)
228
+ url = "{}/api/v1/ws/credential/events".format(WS_HOST)
227
229
  host, port, path, use_ssl = _parse_ws_url(url)
228
230
  logger.info("credential_poll start: chat_id=%s", chat_id)
229
231
 
230
232
  ws_headers = {
231
233
  "SAST-Chat-ID": chat_id,
232
- "Comate-User-Id": user_id,
234
+ "Comate-User-Id": USER_ID,
233
235
  }
234
236
 
235
237
  reconnect_attempts = 0
@@ -318,15 +320,16 @@ def poll_credential_config(chat_id, user_id):
318
320
 
319
321
 
320
322
  def main():
323
+ global USER_ID
321
324
  parser = argparse.ArgumentParser(description="凭证配置轮询工具")
322
325
  parser.add_argument("--chat-id", required=True, help="scanChatID,用于关联扫描会话")
323
326
  parser.add_argument("--username", required=True, help="Comate 用户名")
324
327
  parser.add_argument("--output", default=None, help="将结果保存到指定文件路径(同时仍输出到标准输出)")
325
328
  args = parser.parse_args()
326
329
 
327
- user_id = utils.make_user_id(args.username)
330
+ USER_ID = make_user_id(args.username)
328
331
 
329
- result = poll_credential_config(args.chat_id, user_id)
332
+ result = poll_credential_config(args.chat_id)
330
333
  if result:
331
334
  output_json = json.dumps(result, ensure_ascii=False, indent=2)
332
335
  print(output_json)
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 凭证配置网页 URL 构建工具(公共模块)
4
+
5
+ 被 credential_open_page.py 和 ducc/open_browser.py 共同使用。
6
+ """
7
+
8
+ import hashlib
9
+ import os
10
+ import subprocess
11
+ import urllib.parse
12
+ import json
13
+
14
+ from utils import HOST
15
+
16
+ BASE_URL = f"{HOST}/app/credential"
17
+ VERSION = "2.9.1"
18
+
19
+
20
+ def compute_uid(username: str) -> str:
21
+ """计算用户 UID:username 的 MD5 前 12 位。"""
22
+ return hashlib.md5(username.encode()).hexdigest()[:12]
23
+
24
+
25
+ def get_repo(project_dir: str) -> str:
26
+ """从 git remote 获取仓库三级路径标识,失败则回退到目录名。
27
+
28
+ SSH 格式(git@icode.baidu.com:baidu/scan/cnap-test.git):取 : 后的部分,去掉 .git 后缀。
29
+ HTTP 格式(https://icode.baidu.com/baidu/scan/cnap-test):取域名后的路径,去掉 .git 后缀。
30
+ """
31
+ try:
32
+ result = subprocess.run(
33
+ ["git", "remote", "get-url", "origin"],
34
+ cwd=project_dir,
35
+ capture_output=True,
36
+ text=True,
37
+ timeout=5,
38
+ )
39
+ remote = result.stdout.strip()
40
+ if remote:
41
+ if ":" in remote and not remote.startswith("http"):
42
+ # SSH 格式
43
+ remote = remote.split(":", 1)[1]
44
+ else:
45
+ # HTTP 格式
46
+ from urllib.parse import urlparse
47
+ remote = urlparse(remote).path.lstrip("/")
48
+ return remote.removesuffix(".git")
49
+ except Exception:
50
+ pass
51
+ return os.path.basename(os.path.abspath(project_dir))
52
+
53
+
54
+ def build_url(chat_id: str, uid: str, ide_name: str, repo: str) -> str:
55
+ """构建完整的凭证配置网页 URL(参数中的特殊字符均已 URL 编码)。"""
56
+ params = urllib.parse.urlencode({
57
+ "chatID": chat_id,
58
+ "comateUID": uid,
59
+ "version": VERSION,
60
+ "repo": repo,
61
+ "ideType": ide_name,
62
+ })
63
+ return f"{BASE_URL}?{params}"
64
+
65
+
66
+ def build_markdown_link(url: str) -> str:
67
+ """构建可点击的 Markdown 链接,点击后在 IDE SimpleBrowser 中打开。
68
+
69
+ 对 URL 做完整特殊字符转码(safe=''),确保 &、=、/、()、# 等字符
70
+ 不破坏 Markdown 链接语法或 command 参数解析。
71
+ """
72
+ enc = urllib.parse.quote(json.dumps([url]), safe="")
73
+ return f"[打开硬编码风险治理界面](command:simpleBrowser.api.open?{enc})"
74
+
75
+
76
+ def build_url_from_args(chat_id: str, username: str, ide_name: str,
77
+ project_dir: str, repo: str = "") -> str:
78
+ """从原始参数一步构建 URL(便捷方法)。"""
79
+ uid = compute_uid(username)
80
+ repo = repo or get_repo(project_dir)
81
+ return build_url(chat_id, uid, ide_name, repo)
@@ -0,0 +1,33 @@
1
+ #!/bin/bash
2
+ # 获取当前 Claude Code 会话 ID
3
+
4
+ # 获取当前工作目录的绝对路径
5
+ CURRENT_DIR=$(pwd)
6
+
7
+ # 将路径中的斜杠替换为连字符,构建项目目录名
8
+ PROJECT_DIR_NAME=$(echo "$CURRENT_DIR" | sed 's/\//-/g')
9
+
10
+ # Claude projects 目录路径
11
+ PROJECTS_DIR="$HOME/.claude/projects"
12
+ PROJECT_PATH="$PROJECTS_DIR/$PROJECT_DIR_NAME"
13
+
14
+ # 检查项目目录是否存在
15
+ if [ ! -d "$PROJECT_PATH" ]; then
16
+ echo "Error: Project directory not found: $PROJECT_PATH"
17
+ exit 1
18
+ fi
19
+
20
+ # 找到最新的 .jsonl 文件(按修改时间排序)
21
+ LATEST_JSONL=$(ls -t "$PROJECT_PATH"/*.jsonl 2>/dev/null | head -1)
22
+
23
+ # 检查是否找到文件
24
+ if [ -z "$LATEST_JSONL" ]; then
25
+ echo "Error: No session JSONL file found in $PROJECT_PATH"
26
+ exit 1
27
+ fi
28
+
29
+ # 从文件名中提取会话 ID(去掉路径和 .jsonl 后缀)
30
+ SESSION_ID=$(basename "$LATEST_JSONL" .jsonl)
31
+
32
+ # 输出会话 ID
33
+ echo "$SESSION_ID"
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 在 DUCC 内嵌浏览器中打开硬编码风险治理网页。
4
+
5
+ 用法(凭证参数模式,与 credential_open_page.py 参数一致):
6
+ python3 open_browser.py --chat-id <chatID> --username <用户名> \
7
+ [--ide-name <ideType>] [--repo <repo>] [--project-dir <目录>]
8
+
9
+ 用法(直接指定 URL 模式):
10
+ python3 open_browser.py --url <url> [--title <标题>] [--pid <kernel_pid>]
11
+ """
12
+
13
+ import argparse
14
+ import glob
15
+ import json
16
+ import os
17
+ import subprocess
18
+ import socket
19
+ import sys
20
+
21
+ # 导入公共 URL 构建模块
22
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
23
+ from credential_url import build_url_from_args
24
+
25
+
26
+ def get_parent_pid(pid: int) -> int | None:
27
+ """获取进程的父进程 PID"""
28
+ try:
29
+ result = subprocess.run(
30
+ ["ps", "-p", str(pid), "-o", "ppid="],
31
+ capture_output=True, text=True, timeout=5
32
+ )
33
+ val = result.stdout.strip()
34
+ return int(val) if val else None
35
+ except Exception:
36
+ return None
37
+
38
+
39
+ def find_kernel_socket() -> str | None:
40
+ """沿进程树向上追溯,找到有对应 comate-kernel socket 文件的祖先进程。
41
+
42
+ 适用于 DUCC/Comate 两种环境:无论脚本由哪个父进程调用,
43
+ 都能找到当前 VSCode 窗口对应的 kernel socket。
44
+ """
45
+ tmpdir = os.environ.get("TMPDIR", "/tmp").rstrip("/")
46
+ pid = os.getpid()
47
+ for _ in range(20): # 最多向上追溯 20 层
48
+ pid = get_parent_pid(pid)
49
+ if not pid or pid == 1:
50
+ break
51
+ for sock_dir in [tmpdir, "/tmp"]:
52
+ sock_path = f"{sock_dir}/comate-kernel-{pid}.sock"
53
+ if os.path.exists(sock_path):
54
+ return sock_path
55
+ return None
56
+
57
+
58
+ def open_url(url: str, pid: str = None, title: str = "网页") -> bool:
59
+ """在当前 DUCC 聊天框所在 VSCode 窗口的内嵌浏览器中打开 URL"""
60
+ tmpdir = os.environ.get("TMPDIR", "/tmp").rstrip("/")
61
+
62
+ body = json.dumps({
63
+ "action": "executeVirtualEditor",
64
+ "method": "openUrlInEditorWebview",
65
+ "payload": {"url": url, "title": title, "reuseExisting": True},
66
+ })
67
+ req = (
68
+ "POST /editor/command/ HTTP/1.1\r\n"
69
+ "Host: localhost\r\n"
70
+ "Content-Type: application/json\r\n"
71
+ f"Content-Length: {len(body.encode())}\r\n"
72
+ "Connection: close\r\n\r\n"
73
+ + body
74
+ )
75
+
76
+ def send_request(sock_path: str) -> bool:
77
+ try:
78
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
79
+ s.settimeout(3)
80
+ s.connect(sock_path)
81
+ s.send(req.encode())
82
+ resp = b""
83
+ while True:
84
+ chunk = s.recv(4096)
85
+ if not chunk:
86
+ break
87
+ resp += chunk
88
+ s.close()
89
+ return b"ok" in resp
90
+ except Exception:
91
+ return False
92
+
93
+ # 如果指定了 PID,直接使用
94
+ if pid:
95
+ for sock_dir in [tmpdir, "/tmp"]:
96
+ sock_path = f"{sock_dir}/comate-kernel-{pid}.sock"
97
+ if os.path.exists(sock_path) and send_request(sock_path):
98
+ print(f"✅ 已在内嵌浏览器中打开 (PID: {pid})")
99
+ return True
100
+ print(f"❌ 连接失败 (PID: {pid})")
101
+ return False
102
+
103
+ # 优先使用 init_env.sh 在 shell 层检测到的 kernel PID
104
+ env_kernel_pid = os.environ.get("_KERNEL_PID", "").strip()
105
+ if env_kernel_pid:
106
+ for sock_dir in [tmpdir, "/tmp"]:
107
+ sock_path = f"{sock_dir}/comate-kernel-{env_kernel_pid}.sock"
108
+ if os.path.exists(sock_path) and send_request(sock_path):
109
+ print(f"✅ 已在内嵌浏览器中打开 (当前窗口 PID: {env_kernel_pid})")
110
+ return True
111
+
112
+ # 次选:Python 进程树向上查找
113
+ kernel_sock = find_kernel_socket()
114
+ if kernel_sock and send_request(kernel_sock):
115
+ k_pid = kernel_sock.rsplit("-", 1)[-1].replace(".sock", "")
116
+ print(f"✅ 已在内嵌浏览器中打开 (当前窗口 PID: {k_pid})")
117
+ return True
118
+
119
+ # 降级:用最新的活跃 socket(只考虑进程仍存在的)
120
+ def is_process_alive(pid_str: str) -> bool:
121
+ try:
122
+ subprocess.run(["ps", "-p", pid_str], capture_output=True, timeout=2, check=True)
123
+ return True
124
+ except Exception:
125
+ return False
126
+
127
+ socket_candidates = set(glob.glob(f"{tmpdir}/comate-kernel-*.sock") +
128
+ glob.glob("/tmp/comate-kernel-*.sock"))
129
+
130
+ active_sockets = []
131
+ for sock_path in socket_candidates:
132
+ k_pid = sock_path.rsplit("-", 1)[-1].replace(".sock", "")
133
+ if is_process_alive(k_pid):
134
+ active_sockets.append((sock_path, k_pid))
135
+
136
+ # 先按访问时间排序尝试,再按修改时间排序
137
+ tried_pids = set()
138
+
139
+ for sort_key in [os.path.getatime, os.path.getmtime]:
140
+ sorted_sockets = sorted(active_sockets, key=lambda x: sort_key(x[0]), reverse=True)
141
+ for sock_path, k_pid in sorted_sockets:
142
+ if k_pid in tried_pids:
143
+ continue
144
+ if send_request(sock_path):
145
+ mode = "修改时间" if sort_key == os.path.getmtime else "访问时间"
146
+ print(f"✅ 已在内嵌浏览器中打开 (PID: {k_pid}, 降级模式 - 按{mode})")
147
+ return True
148
+ tried_pids.add(k_pid)
149
+
150
+ print("❌ 无法打开")
151
+ return False
152
+
153
+
154
+ """
155
+ 入口函数
156
+ python3 open_browser.py --chat-id <chatID> --username <用户名> \
157
+ [--ide-name <ideType>] [--repo <repo>] [--project-dir <目录>]
158
+ ducc打开硬编码风险治理页面。
159
+ """
160
+ def main():
161
+ """入口函数:解析参数并在 DUCC 内嵌浏览器中打开硬编码风险治理网页。"""
162
+ parser = argparse.ArgumentParser(description="在 DUCC 内嵌浏览器中打开硬编码风险治理网页")
163
+
164
+ # 凭证参数模式(与 credential_open_page.py 一致)
165
+ parser.add_argument("--chat-id", help="会话 ID (_CHAT_ID)")
166
+ parser.add_argument("--username", help="用户名 (_USERNAME)")
167
+ parser.add_argument("--ide-name", default="ducc", help="IDE 类型")
168
+ parser.add_argument("--repo", default="", help="仓库标识,留空则自动从 git remote 获取")
169
+ parser.add_argument("--project-dir", default=".", help="项目目录,用于自动获取 repo")
170
+
171
+ # 直接指定 URL 模式
172
+ parser.add_argument("--url", help="直接指定要打开的 URL(与凭证参数模式二选一)")
173
+ parser.add_argument("--title", default="硬编码风险治理", help="页面标题")
174
+ parser.add_argument("--pid", help="指定 comate-kernel 进程 PID")
175
+
176
+ args = parser.parse_args()
177
+
178
+ if args.url:
179
+ url = args.url
180
+ elif args.chat_id and args.username:
181
+ url = build_url_from_args(args.chat_id, args.username, args.ide_name,
182
+ args.project_dir, args.repo)
183
+ else:
184
+ parser.error("必须提供 --url 或同时提供 --chat-id 和 --username")
185
+
186
+ success = open_url(url, args.pid, args.title)
187
+ sys.exit(0 if success else 1)
188
+
189
+
190
+ if __name__ == "__main__":
191
+ main()