@hupan56/wlkj 3.3.4 → 3.3.5
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/package.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hupan56/wlkj",
|
|
3
|
-
"version": "3.3.
|
|
4
|
-
"description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
|
|
5
|
-
"bin": {
|
|
6
|
-
"wlkj": "bin/cli.js"
|
|
7
|
-
},
|
|
8
|
-
"files": [
|
|
9
|
-
"bin/",
|
|
10
|
-
"templates/",
|
|
11
|
-
"MAC-VERIFY.md"
|
|
12
|
-
],
|
|
13
|
-
"keywords": [
|
|
14
|
-
"workflow",
|
|
15
|
-
"ai",
|
|
16
|
-
"prd",
|
|
17
|
-
"pipeline",
|
|
18
|
-
"qoder",
|
|
19
|
-
"product",
|
|
20
|
-
"team"
|
|
21
|
-
],
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"publishConfig": {
|
|
24
|
-
"access": "public"
|
|
25
|
-
},
|
|
26
|
-
"engines": {
|
|
27
|
-
"node": ">=16"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@hupan56/wlkj",
|
|
3
|
+
"version": "3.3.5",
|
|
4
|
+
"description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
|
|
5
|
+
"bin": {
|
|
6
|
+
"wlkj": "bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"templates/",
|
|
11
|
+
"MAC-VERIFY.md"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"workflow",
|
|
15
|
+
"ai",
|
|
16
|
+
"prd",
|
|
17
|
+
"pipeline",
|
|
18
|
+
"qoder",
|
|
19
|
+
"product",
|
|
20
|
+
"team"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=16"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -56,6 +56,57 @@ def _zentao_creds():
|
|
|
56
56
|
return ZentaoClient.from_config()
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def _cloud_my_panel():
|
|
60
|
+
"""云优先:调平台 get_my_zentao_panel(凭据走平台 per-user 配置,不出平台)。
|
|
61
|
+
|
|
62
|
+
直连云 /mcp JSON-RPC(读 .qoder/mcp_config.json 的 token+url)。
|
|
63
|
+
Returns: {ok:True, tasks, bugs, stories, account} 或 None(云不可达/工具失败)。
|
|
64
|
+
云返 {ok:False,error}(未配置/登录失败)→ 返该 dict(调用方据此提示+回退本地)。
|
|
65
|
+
"""
|
|
66
|
+
import json as _json
|
|
67
|
+
import urllib.request as _u
|
|
68
|
+
import urllib.error as _ue
|
|
69
|
+
# 定位 mcp_config.json(switch_project 写在 .qoder/)
|
|
70
|
+
cfg_candidates = []
|
|
71
|
+
try:
|
|
72
|
+
from foundation.core.paths import get_repo_root
|
|
73
|
+
cfg_candidates.append(os.path.join(str(get_repo_root()), '.qoder', 'mcp_config.json'))
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
cfg_candidates.append(os.path.join(_SCRIPTS, '..', 'mcp_config.json'))
|
|
77
|
+
token, url = '', ''
|
|
78
|
+
for cp in cfg_candidates:
|
|
79
|
+
try:
|
|
80
|
+
with open(cp, encoding='utf-8') as f:
|
|
81
|
+
cfg = _json.load(f)
|
|
82
|
+
srv = (cfg.get('mcpServers') or {}).get('wlinkj-knowledge') or {}
|
|
83
|
+
token = srv.get('token', '')
|
|
84
|
+
url = (srv.get('url') or '').rstrip('/')
|
|
85
|
+
if token and url:
|
|
86
|
+
break
|
|
87
|
+
except Exception:
|
|
88
|
+
continue
|
|
89
|
+
if not token or not url:
|
|
90
|
+
return None # 云未绑定(switch_project 没跑)→ 回退本地
|
|
91
|
+
endpoint = url if url.endswith('/mcp') else url + '/mcp'
|
|
92
|
+
body = _json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
|
93
|
+
"params": {"name": "get_my_zentao_panel", "arguments": {}}}).encode('utf-8')
|
|
94
|
+
req = _u.Request(endpoint, data=body, method='POST')
|
|
95
|
+
req.add_header('Content-Type', 'application/json')
|
|
96
|
+
req.add_header('X-MCP-Token', token)
|
|
97
|
+
try:
|
|
98
|
+
with _u.urlopen(req, timeout=30) as resp:
|
|
99
|
+
r = _json.loads(resp.read().decode('utf-8', errors='replace'))
|
|
100
|
+
except Exception:
|
|
101
|
+
return None # 网络不通 → 回退本地
|
|
102
|
+
try:
|
|
103
|
+
text = r.get('result', {}).get('content', [{}])[0].get('text', '')
|
|
104
|
+
data = _json.loads(text) if text else {}
|
|
105
|
+
except Exception:
|
|
106
|
+
return None
|
|
107
|
+
return data # {ok:True/False, ...}
|
|
108
|
+
|
|
109
|
+
|
|
59
110
|
def _resolve_my_account(developer):
|
|
60
111
|
"""解析当前 developer → 禅道 account。返回 (account, 错误原因)。"""
|
|
61
112
|
mem = get_member(developer)
|
|
@@ -359,12 +410,55 @@ def fetch_workbench(include_closed=False, kind=None):
|
|
|
359
410
|
"""
|
|
360
411
|
developer = get_developer()
|
|
361
412
|
|
|
362
|
-
#
|
|
413
|
+
# ⓪ 云优先:调平台 get_my_zentao_panel(凭据走平台 per-user,不出平台)。
|
|
414
|
+
# 成功 → 直接用云数据,跳过本地凭据/连通/归属/收集(① ~ ④)。
|
|
415
|
+
# 未配置/登录失败 → 返回 {ok:False} 提示用户去平台配置,并回退本地(本地配了仍可用)。
|
|
416
|
+
# 云不可达(返回 None) → 静默回退本地全流程。
|
|
417
|
+
cloud = _cloud_my_panel()
|
|
418
|
+
if isinstance(cloud, dict):
|
|
419
|
+
# 云可达 → 云的判断权威(平台是凭据源)。
|
|
420
|
+
if cloud.get('ok'):
|
|
421
|
+
tasks = cloud.get('tasks') or []
|
|
422
|
+
bugs = cloud.get('bugs') or []
|
|
423
|
+
stories = cloud.get('stories') or []
|
|
424
|
+
# 按 kind 裁剪(与本地一致)
|
|
425
|
+
if kind == 'tasks':
|
|
426
|
+
bugs, stories = [], []
|
|
427
|
+
elif kind == 'bugs':
|
|
428
|
+
tasks, stories = [], []
|
|
429
|
+
elif kind == 'stories':
|
|
430
|
+
tasks, bugs = [], []
|
|
431
|
+
tasks, bugs, stories = _filter_live(tasks, bugs, stories, include_closed)
|
|
432
|
+
# 云路径:数据已平台聚合,归一化即可(进度检测需本地 client,云模式跳过给默认值)
|
|
433
|
+
norm_tasks = [_norm_task(t) for t in tasks]
|
|
434
|
+
norm_bugs = [_norm_bug(b) for b in bugs]
|
|
435
|
+
norm_stories = [_norm_story(s) for s in stories]
|
|
436
|
+
for it in norm_tasks + norm_bugs + norm_stories:
|
|
437
|
+
it['progress'] = {'prd': False, 'spec': 'none', 'code': False}
|
|
438
|
+
data = {'account': cloud.get('account', ''), 'developer': developer,
|
|
439
|
+
'current_task': None, 'tasks': norm_tasks,
|
|
440
|
+
'bugs': norm_bugs, 'stories': norm_stories}
|
|
441
|
+
meta = {'fetched_at': datetime.now().isoformat(timespec='seconds'),
|
|
442
|
+
'source': 'cloud',
|
|
443
|
+
'counts': {'tasks': len(norm_tasks), 'bugs': len(norm_bugs),
|
|
444
|
+
'stories': len(norm_stories)},
|
|
445
|
+
'include_closed': include_closed}
|
|
446
|
+
return {'ok': True, 'data': data, 'meta': meta}, 0
|
|
447
|
+
# 云可达但失败(未配置/登录失败/防火墙)→ 记下云诊断,回退本地。
|
|
448
|
+
# 回退价值:云到禅道常被防火墙挡,但本机(demo 机器,内网)可能直连可达。
|
|
449
|
+
# 本地也失败时,把云诊断一起带出(平台有凭据但云连不上 + 本地无凭据)。
|
|
450
|
+
cloud_error = cloud.get('error', '禅道不可用')
|
|
451
|
+
else:
|
|
452
|
+
cloud_error = None # 云不可达(switch_project 没跑/平台宕机)→ 静默回退本地
|
|
453
|
+
|
|
454
|
+
# ① 凭据(本地回退)
|
|
363
455
|
client = _zentao_creds()
|
|
364
456
|
if client is None:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
457
|
+
base_msg = ('禅道凭据未配置。推荐在【平台设置 → 禅道】配置(panel 自动走云);'
|
|
458
|
+
'或本地 .qoder/config.yaml 的 zentao.url + secrets/zentao.env 账号密码。')
|
|
459
|
+
if cloud_error:
|
|
460
|
+
base_msg += '(注:平台已配凭据但云端连不上禅道:%s;本机若在内网可配本地凭据直连。)' % cloud_error
|
|
461
|
+
return {'ok': False, 'error': base_msg}, 3
|
|
368
462
|
|
|
369
463
|
ok, msg = client.credentials_ok()
|
|
370
464
|
if not ok:
|