@hupan56/wlkj 2.5.0 → 2.7.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/bin/cli.js +289 -12
- package/package.json +1 -1
- package/templates/qoder/agents/insight-planning.md +67 -0
- package/templates/qoder/agents/insight-research.md +61 -0
- package/templates/qoder/agents/prd-quick.md +1 -0
- package/templates/qoder/agents/prd-reference.md +10 -2
- package/templates/qoder/commands/optional/wl-insight.md +275 -0
- package/templates/qoder/commands/{wl-report.md → optional/wl-report.md} +13 -5
- package/templates/qoder/commands/{wl-spec.md → optional/wl-spec.md} +1 -1
- package/templates/qoder/commands/{wl-status.md → optional/wl-status.md} +28 -2
- package/templates/qoder/commands/wl-code.md +10 -2
- package/templates/qoder/commands/wl-commit.md +1 -1
- package/templates/qoder/commands/wl-design-draw.md +78 -0
- package/templates/qoder/commands/wl-design-scan.md +108 -0
- package/templates/qoder/commands/wl-design-spec.md +154 -0
- package/templates/qoder/commands/wl-design.md +32 -0
- package/templates/qoder/commands/wl-init.md +24 -3
- package/templates/qoder/commands/wl-prd-full.md +226 -0
- package/templates/qoder/commands/wl-prd-quick.md +134 -0
- package/templates/qoder/commands/wl-prd-review.md +104 -0
- package/templates/qoder/commands/wl-prd.md +17 -288
- package/templates/qoder/commands/wl-search.md +66 -30
- package/templates/qoder/commands/wl-task.md +290 -59
- package/templates/qoder/commands/wl-test.md +92 -24
- package/templates/qoder/config.yaml +59 -15
- package/templates/qoder/hooks/inject-workflow-state.py +35 -9
- package/templates/qoder/hooks/session-start.py +144 -62
- package/templates/qoder/rules/wl-pipeline.md +216 -105
- package/templates/qoder/scripts/__pycache__/search_index.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/archive_prd.py +377 -0
- package/templates/qoder/scripts/autotest.py +1715 -0
- package/templates/qoder/scripts/autotest_batch.py +224 -0
- package/templates/qoder/scripts/autotest_run.py +297 -0
- package/templates/qoder/scripts/benchmark.py +210 -209
- package/templates/qoder/scripts/build_style_index.py +444 -4
- package/templates/qoder/scripts/check_carriers.py +238 -0
- package/templates/qoder/scripts/check_mcp.py +298 -0
- package/templates/qoder/scripts/check_qoderwork_consistency.py +166 -0
- package/templates/qoder/scripts/common/developer.py +26 -19
- package/templates/qoder/scripts/common/events.py +46 -0
- package/templates/qoder/scripts/common/extract.py +419 -0
- package/templates/qoder/scripts/common/graph_traverse.py +533 -0
- package/templates/qoder/scripts/common/identity.py +6 -1
- package/templates/qoder/scripts/common/paths.py +89 -0
- package/templates/qoder/scripts/common/pip_install.py +144 -0
- package/templates/qoder/scripts/common/platform_guard.py +61 -0
- package/templates/qoder/scripts/common/search_engine.py +205 -205
- package/templates/qoder/scripts/common/terms.py +57 -0
- package/templates/qoder/scripts/common/ts_extract.py +536 -0
- package/templates/qoder/scripts/context_pack.py +73 -13
- package/templates/qoder/scripts/enrich_prompt.py +226 -0
- package/templates/qoder/scripts/eval_prd.py +318 -225
- package/templates/qoder/scripts/export.py +487 -487
- package/templates/qoder/scripts/extract_api_params.py +246 -0
- package/templates/qoder/scripts/extract_routes.py +54 -0
- package/templates/qoder/scripts/extract_routes_tree.py +78 -0
- package/templates/qoder/scripts/fill_prototype.py +707 -0
- package/templates/qoder/scripts/gen_design_doc.py +394 -0
- package/templates/qoder/scripts/git_sync.py +27 -15
- package/templates/qoder/scripts/init_doctor.py +306 -41
- package/templates/qoder/scripts/install_qoderwork.py +366 -9
- package/templates/qoder/scripts/kg.py +708 -0
- package/templates/qoder/scripts/kg_auto_login.py +196 -0
- package/templates/qoder/scripts/kg_build.py +612 -0
- package/templates/qoder/scripts/kg_build_db.py +327 -0
- package/templates/qoder/scripts/kg_duckdb.py +549 -0
- package/templates/qoder/scripts/kg_incremental.py +393 -0
- package/templates/qoder/scripts/kg_link_db.py +224 -0
- package/templates/qoder/scripts/kg_mcp_server.py +801 -0
- package/templates/qoder/scripts/kg_semantic.py +150 -0
- package/templates/qoder/scripts/kg_test_runner.py +241 -0
- package/templates/qoder/scripts/lanhu_stdio_wrapper.py +119 -0
- package/templates/qoder/scripts/learn.py +118 -39
- package/templates/qoder/scripts/learn_aggregate.py +201 -0
- package/templates/qoder/scripts/mcp_launcher.py +359 -0
- package/templates/qoder/scripts/mysql_mcp_server.py +396 -0
- package/templates/qoder/scripts/repo_root.py +106 -0
- package/templates/qoder/scripts/role.py +12 -0
- package/templates/qoder/scripts/run_weekly_update.bat +5 -0
- package/templates/qoder/scripts/run_weekly_update.sh +5 -0
- package/templates/qoder/scripts/search_index.py +307 -60
- package/templates/qoder/scripts/secure-ls.js +5640 -0
- package/templates/qoder/scripts/setup.py +706 -453
- package/templates/qoder/scripts/setup_lanhu.py +963 -0
- package/templates/qoder/scripts/status.py +250 -11
- package/templates/qoder/scripts/sync_carriers.py +259 -0
- package/templates/qoder/scripts/syncgate.py +5 -4
- package/templates/qoder/scripts/task.py +75 -0
- package/templates/qoder/scripts/team_sync.py +60 -4
- package/templates/qoder/scripts/workspace_init.py +1 -1
- package/templates/qoder/skills/design-import/SKILL.md +226 -0
- package/templates/qoder/skills/design-import/figma-workflow.md +81 -0
- package/templates/qoder/skills/design-review/SKILL.md +82 -25
- package/templates/qoder/skills/prd-generator/SKILL.md +185 -58
- package/templates/qoder/skills/prd-review/SKILL.md +18 -1
- package/templates/qoder/skills/prompt-enrich/SKILL.md +90 -0
- package/templates/qoder/skills/prototype-generator/SKILL.md +256 -141
- package/templates/qoder/skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp +0 -0
- package/templates/qoder/skills/spec-coder/SKILL.md +18 -1
- package/templates/qoder/skills/spec-generator/SKILL.md +18 -1
- package/templates/qoder/skills/test-generator/SKILL.md +15 -2
- package/templates/qoder/skills/wl-code/SKILL.md +55 -36
- package/templates/qoder/skills/wl-commit/SKILL.md +89 -76
- package/templates/qoder/skills/wl-design/SKILL.md +55 -0
- package/templates/qoder/skills/wl-init/SKILL.md +76 -67
- package/templates/qoder/skills/wl-insight/SKILL.md +201 -81
- package/templates/qoder/skills/wl-prd-full/SKILL.md +69 -0
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +49 -0
- package/templates/qoder/skills/wl-prd-review/SKILL.md +34 -0
- package/templates/qoder/skills/wl-report/SKILL.md +131 -107
- package/templates/qoder/skills/wl-search/SKILL.md +141 -75
- package/templates/qoder/skills/wl-spec/SKILL.md +49 -39
- package/templates/qoder/skills/wl-status/SKILL.md +83 -61
- package/templates/qoder/skills/wl-task/SKILL.md +132 -58
- package/templates/qoder/skills/wl-test/SKILL.md +406 -40
- package/templates/qoder/templates/prd-full-template.md +2 -0
- package/templates/qoder/templates/prd-quick-template.md +1 -0
- package/templates/qoder/templates/prototype-app.html +13 -8
- package/templates/qoder/templates/prototype-web.html +376 -93
- package/templates/root/AGENTS.md +89 -34
- package/templates/root/requirements.txt +21 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +259 -259
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +186 -186
- package/templates/qoder/agents/prd-planning.md +0 -56
- package/templates/qoder/agents/prd-research.md +0 -33
- package/templates/qoder/commands/wl-insight.md +0 -51
- package/templates/qoder/skills/wl-prd/SKILL.md +0 -89
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
全自动登录: 获取验证码 → ddddocr识别 → 页面JS加密提交 → 保存token
|
|
4
|
+
登录态保存到 auth-state.json, 后续测试复用(无需再次识别验证码)。
|
|
5
|
+
"""
|
|
6
|
+
import base64
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import requests
|
|
11
|
+
import ddddocr
|
|
12
|
+
|
|
13
|
+
BASE_URL = 'https://icsqas.inforecloud.com'
|
|
14
|
+
API_PREFIX = '/test-api'
|
|
15
|
+
USERNAME = '15888888887'
|
|
16
|
+
PASSWORD = 'admin123'
|
|
17
|
+
CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
|
18
|
+
|
|
19
|
+
# RSA公钥 (从 .env.production)
|
|
20
|
+
RSA_PUBLIC_KEY_B64 = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN/EkuOg1gq/t2/PUI8GpcJCsCFQgHkSHUEdk7ZPrEQYd8IM1hFkHmDJrCOQ+qUPqKHG9a5BAI44yE6FuPI9Vvqu2ZrCww/zryR7A+2n5jaKIG2rJG9KHDzT5mAVOJrlhBf8L2i7wZc8eWrNg0md0BlwEowQyVEEj9oIBlw/nqTwIDAQAB'
|
|
21
|
+
|
|
22
|
+
# 保存路径
|
|
23
|
+
SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _read_developer():
|
|
27
|
+
"""读 .qoder/.developer 拿当前开发者名, 用于身份隔离。
|
|
28
|
+
多人共用 workspace 时, auth-state 必须按开发者分目录, 否则会串登录态。
|
|
29
|
+
没有身份 → 抛异常 (绝不 fallback 到 hupan/default, 防串号)。"""
|
|
30
|
+
dev_file = os.path.join(SCRIPTS_DIR, '..', '..', '.qoder', '.developer')
|
|
31
|
+
if os.path.isfile(dev_file):
|
|
32
|
+
for ln in open(dev_file, encoding='utf-8'):
|
|
33
|
+
if ln.strip().startswith('name='):
|
|
34
|
+
return ln.strip().split('=', 1)[1].strip()
|
|
35
|
+
raise RuntimeError(
|
|
36
|
+
'未检测到开发者身份(.qoder/.developer)。请先运行 /wl-init 注册身份。'
|
|
37
|
+
' (auth-state 按开发者分目录, 没身份无法隔离 → 拒绝执行防串号)'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
STATE_FILE = os.path.join(SCRIPTS_DIR, '..', '..', 'workspace', 'members',
|
|
42
|
+
_read_developer(), 'auth-state.json')
|
|
43
|
+
|
|
44
|
+
out = []
|
|
45
|
+
def log(s=''):
|
|
46
|
+
out.append(s)
|
|
47
|
+
print(s)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_captcha():
|
|
51
|
+
"""获取验证码图片 + uuid"""
|
|
52
|
+
resp = requests.get(BASE_URL + API_PREFIX + '/auth/code', timeout=10)
|
|
53
|
+
data = resp.json().get('data', {})
|
|
54
|
+
return data.get('uuid'), data.get('img'), data.get('captchaEnabled', False)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def recognize_captcha(img_b64):
|
|
58
|
+
"""用 ddddocr 识别验证码图片, 并解析数学题(ICS 是 a op b = ? 形式).
|
|
59
|
+
返回算出的答案字符串; 解析失败返回 None."""
|
|
60
|
+
import re as _re
|
|
61
|
+
ocr = ddddocr.DdddOcr(show_ad=False)
|
|
62
|
+
img_bytes = base64.b64decode(img_b64)
|
|
63
|
+
raw = ocr.classification(img_bytes)
|
|
64
|
+
# 规范化运算符 (x/X/× → *, ÷ → /), 去掉 = ? 尾部
|
|
65
|
+
expr = raw.replace('x', '*').replace('X', '*').replace('×', '*').replace('÷', '/')
|
|
66
|
+
expr = expr.rstrip('=??').strip()
|
|
67
|
+
m = _re.match(r'^\s*(\d+)\s*([+\-*/])\s*(\d+)\s*$', expr)
|
|
68
|
+
if not m:
|
|
69
|
+
return None
|
|
70
|
+
a, op, b = int(m.group(1)), m.group(2), int(m.group(3))
|
|
71
|
+
if op == '+':
|
|
72
|
+
ans = a + b
|
|
73
|
+
elif op == '-':
|
|
74
|
+
ans = a - b
|
|
75
|
+
elif op == '*':
|
|
76
|
+
ans = a * b
|
|
77
|
+
elif op == '/':
|
|
78
|
+
ans = a // b if b else None
|
|
79
|
+
else:
|
|
80
|
+
return None
|
|
81
|
+
return str(ans) if ans is not None else None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def encrypt_login(username, password, code, uuid):
|
|
85
|
+
"""用 RSA+AES 加密登录请求 (复现前端加密逻辑)。
|
|
86
|
+
使用 cryptography 库 (替代 pycryptodome)。"""
|
|
87
|
+
import os as _os
|
|
88
|
+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
|
89
|
+
from cryptography.hazmat.primitives.padding import PKCS7
|
|
90
|
+
from cryptography.hazmat.primitives.asymmetric import padding as asym_padding
|
|
91
|
+
from cryptography.hazmat.primitives.serialization import load_der_public_key
|
|
92
|
+
|
|
93
|
+
# 1. 生成随机 AES 密钥 (前端用 randomUUID = 32 hex chars)
|
|
94
|
+
aes_key = _os.urandom(16).hex().encode('utf-8') # 32 bytes
|
|
95
|
+
|
|
96
|
+
# 2. AES-ECB-PKCS7 加密请求体
|
|
97
|
+
login_body = json.dumps({
|
|
98
|
+
'username': username,
|
|
99
|
+
'password': password,
|
|
100
|
+
'grantType': 'password',
|
|
101
|
+
'tenantId': '000000',
|
|
102
|
+
'clientId': CLIENT_ID,
|
|
103
|
+
'code': code,
|
|
104
|
+
'uuid': uuid,
|
|
105
|
+
}, ensure_ascii=False).encode('utf-8')
|
|
106
|
+
|
|
107
|
+
padder = PKCS7(128).padder()
|
|
108
|
+
padded = padder.update(login_body) + padder.finalize()
|
|
109
|
+
cipher = Cipher(algorithms.AES(aes_key), modes.ECB())
|
|
110
|
+
encryptor = cipher.encryptor()
|
|
111
|
+
encrypted_body = base64.b64encode(encryptor.update(padded) + encryptor.finalize()).decode()
|
|
112
|
+
|
|
113
|
+
# 3. RSA 加密 AES 密钥 (前端: encrypt(encryptBase64(aesKey)))
|
|
114
|
+
aes_key_b64 = base64.b64encode(aes_key).decode()
|
|
115
|
+
pub_key = load_der_public_key(base64.b64decode(RSA_PUBLIC_KEY_B64))
|
|
116
|
+
encrypted_key_bytes = pub_key.encrypt(aes_key_b64.encode('utf-8'), asym_padding.PKCS1v15())
|
|
117
|
+
encrypted_key = base64.b64encode(encrypted_key_bytes).decode()
|
|
118
|
+
|
|
119
|
+
return encrypted_body, encrypted_key
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def login():
|
|
123
|
+
"""全自动登录流程"""
|
|
124
|
+
log('=== 全自动登录 ===')
|
|
125
|
+
|
|
126
|
+
# 1. 获取验证码
|
|
127
|
+
uuid, img_b64, captcha_enabled = get_captcha()
|
|
128
|
+
log('验证码: enabled=%s, uuid=%s' % (captcha_enabled, uuid[:8] if uuid else 'N/A'))
|
|
129
|
+
|
|
130
|
+
if not captcha_enabled:
|
|
131
|
+
log('验证码未开启, 直接登录')
|
|
132
|
+
code = ''
|
|
133
|
+
uuid = ''
|
|
134
|
+
else:
|
|
135
|
+
# 2. OCR 识别 + 数学题解析
|
|
136
|
+
code = recognize_captcha(img_b64)
|
|
137
|
+
log('OCR识别验证码: %s' % code)
|
|
138
|
+
if code is None:
|
|
139
|
+
# ddddocr 没读对(数学题识别不稳定), 本次放弃, 让外层重试
|
|
140
|
+
log('验证码识别失败(数学题未解析), 重试')
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
# 3. 加密 + 登录
|
|
144
|
+
try:
|
|
145
|
+
encrypted_body, encrypted_key = encrypt_login(USERNAME, PASSWORD, code, uuid)
|
|
146
|
+
log('加密完成, 发送登录请求...')
|
|
147
|
+
|
|
148
|
+
resp = requests.post(
|
|
149
|
+
BASE_URL + API_PREFIX + '/auth/login',
|
|
150
|
+
data=encrypted_body,
|
|
151
|
+
headers={
|
|
152
|
+
'Content-Type': 'application/json',
|
|
153
|
+
'encrypt-key': encrypted_key,
|
|
154
|
+
},
|
|
155
|
+
timeout=10,
|
|
156
|
+
)
|
|
157
|
+
result = resp.json()
|
|
158
|
+
log('登录响应: code=%s, msg=%s' % (result.get('code'), result.get('msg')))
|
|
159
|
+
|
|
160
|
+
if result.get('code') == 200 and result.get('data', {}).get('access_token'):
|
|
161
|
+
token = result['data']['access_token']
|
|
162
|
+
log('✓ 登录成功! token=%s...' % token[:20])
|
|
163
|
+
|
|
164
|
+
# 保存登录态
|
|
165
|
+
os.makedirs(os.path.dirname(STATE_FILE), exist_ok=True)
|
|
166
|
+
with open(STATE_FILE, 'w', encoding='utf-8') as f:
|
|
167
|
+
json.dump({
|
|
168
|
+
'access_token': token,
|
|
169
|
+
'base_url': BASE_URL,
|
|
170
|
+
'api_prefix': API_PREFIX,
|
|
171
|
+
'saved_at': __import__('datetime').datetime.now().isoformat(),
|
|
172
|
+
}, f, indent=2)
|
|
173
|
+
log('登录态已保存: %s' % STATE_FILE)
|
|
174
|
+
return token
|
|
175
|
+
else:
|
|
176
|
+
log('✗ 登录失败: %s' % json.dumps(result, ensure_ascii=False)[:200])
|
|
177
|
+
log(' 可能验证码识别错误, 重试...')
|
|
178
|
+
return None
|
|
179
|
+
except Exception as e:
|
|
180
|
+
log('登录异常: %s' % str(e))
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == '__main__':
|
|
185
|
+
# 数学题验证码识别不稳定, 多重试几次 (实测 ~30% 一次读对, 20 次基本必中)
|
|
186
|
+
import time as _time
|
|
187
|
+
for attempt in range(20):
|
|
188
|
+
log('--- 尝试 %d ---' % (attempt + 1))
|
|
189
|
+
token = login()
|
|
190
|
+
if token:
|
|
191
|
+
break
|
|
192
|
+
_time.sleep(0.3)
|
|
193
|
+
else:
|
|
194
|
+
log('20次尝试均失败, 请检查账号/密码/验证码/网络')
|
|
195
|
+
|
|
196
|
+
open(os.path.join(SCRIPTS_DIR, '_login_log.txt'), 'w', encoding='utf-8').write('\n'.join(out))
|