@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.
- package/comate-engine/assets/skills/auto-commit/SKILL.md +2 -0
- package/comate-engine/assets/skills/auto-commit-sandbox-comate/SKILL.md +2 -2
- package/comate-engine/assets/skills/code-review/SKILL.md +6 -5
- package/comate-engine/assets/skills/code-review/agents/custom-reviewer.md +2 -2
- package/comate-engine/assets/skills/code-review/agents/meta-reviewer.md +2 -2
- package/comate-engine/assets/skills/code-review/agents/style-reviewer.md +72 -10
- package/comate-engine/assets/skills/code-review/references/dispatch-template.md +12 -12
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_STYLE_RULES.md +11 -5
- package/comate-engine/assets/skills/code-security/SKILL.md +110 -41
- package/comate-engine/assets/skills/code-security/references/credential_hosting.md +190 -28
- package/comate-engine/assets/skills/code-security/references/vul_analysis-go_sql_injection.md +149 -0
- package/comate-engine/assets/skills/code-security/references/vul_analysis-java_sql_injection.md +185 -0
- package/comate-engine/assets/skills/code-security/references/vul_analysis-php_sql_injection.md +147 -0
- package/comate-engine/assets/skills/code-security/references/vul_analysis-python_sql_injection.md +143 -0
- package/comate-engine/assets/skills/code-security/references/vul_repair-go_sql_injection.md +2 -2
- package/comate-engine/assets/skills/code-security/references/vul_repair-sca.md +225 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_hosting.py +12 -10
- package/comate-engine/assets/skills/code-security/scripts/credential_open_page.py +125 -0
- package/comate-engine/assets/skills/code-security/scripts/credential_poll.py +12 -9
- package/comate-engine/assets/skills/code-security/scripts/credential_url.py +81 -0
- package/comate-engine/assets/skills/code-security/scripts/ducc/get_claude_session_id.sh +33 -0
- package/comate-engine/assets/skills/code-security/scripts/ducc/open_browser.py +191 -0
- package/comate-engine/assets/skills/code-security/scripts/parse_scan_result.py +99 -16
- package/comate-engine/assets/skills/code-security/scripts/repair_vulnerability.py +66 -13
- package/comate-engine/assets/skills/code-security/scripts/scan_vulnerability.py +44 -12
- package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/SKILL.md +8 -8
- package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/long_running_task.md +0 -15
- package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/testing_strategy.md +1 -1
- package/comate-engine/assets/skills/create-image/SKILL.md +197 -206
- package/comate-engine/assets/skills/create-image/scripts/generate-image.ps1 +213 -0
- package/comate-engine/assets/skills/create-image/scripts/generate-image.sh +322 -0
- package/comate-engine/assets/skills/create-subagent/SKILL.md +23 -5
- package/comate-engine/fallbackServer.js +1 -1
- package/comate-engine/node_modules/@comate/plugin-shared-internals/dist/index.js +1 -1
- package/comate-engine/server.js +89 -66
- package/dist/bundle/index.js +3 -3
- package/package.json +1 -1
- package/scripts/postinstall.js +4 -3
- package/comate-engine/assets/skills/code-review/evals/SKILL.md +0 -334
- package/comate-engine/assets/skills/code-review/evals/agents/gt-generator.md +0 -76
- package/comate-engine/assets/skills/code-review/evals/agents/miner.md +0 -87
- package/comate-engine/assets/skills/code-review/evals/agents/score-judge.md +0 -168
- package/comate-engine/assets/skills/code-review/evals/references/cli-query-template.md +0 -114
- package/comate-engine/assets/skills/code-review/evals/references/gt-schema.md +0 -77
- package/comate-engine/assets/skills/code-review/references/custom-rules/RULE_TEMPLATE.md +0 -141
- /package/comate-engine/assets/commands/{code-review-comate.md → code-review.md} +0 -0
- /package/comate-engine/assets/commands/{debug-comate.md → debug.md} +0 -0
- /package/comate-engine/assets/commands/{unit-test-comate.md → unit-test.md} +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/backend_dev.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/env_setup.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/frontend_dev.md +0 -0
- /package/comate-engine/assets/skills/{create-automation-tasks-comate → create-automation}/references/git_operations.md +0 -0
- /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
|
|
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
|
|
226
|
+
def poll_credential_config(chat_id):
|
|
225
227
|
"""通过 WebSocket 监听凭证配置,返回配置数据 dict 或 None。"""
|
|
226
|
-
url = "{}/api/v1/ws/credential/events".format(
|
|
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":
|
|
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
|
-
|
|
330
|
+
USER_ID = make_user_id(args.username)
|
|
328
331
|
|
|
329
|
-
result = poll_credential_config(args.chat_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()
|