@hupan56/wlkj 3.3.2 → 3.3.4
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 +16 -1
- package/package.json +1 -1
- package/templates/qoder/scripts/capability/adapters/mcp.py +8 -1
- package/templates/qoder/scripts/capability/registry_mcp.py +10 -4
- package/templates/qoder/scripts/deployment/setup/init_doctor.py +6 -2
- package/templates/qoder/scripts/deployment/setup/setup.py +5 -2
- package/templates/qoder/scripts/domain/integration/return_to_platform.py +3 -2
- package/templates/qoder/scripts/domain/integration/spec_upload.py +1 -1
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +41 -5
- package/templates/qoder/scripts/domain/kg/switch_project.py +2 -2
- package/templates/qoder/scripts/engine/poller.py +1 -1
- package/templates/qoder/scripts/capability/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/__pycache__/present_html.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/__pycache__/registry.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/__pycache__/registry_mcp.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/adapters/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/adapters/__pycache__/cli.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/adapters/__pycache__/mcp.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/adapters/__pycache__/qw.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/context.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/cron.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/identity.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/memory.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/notify.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/present.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/repo.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/capability/caps/__pycache__/sandbox.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/domain/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/domain/integration/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/domain/integration/__pycache__/return_to_platform.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/domain/task/__pycache__/wlkj_panel.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/core/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/foundation/core/__pycache__/paths.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/protocol/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/protocol/transports/__pycache__/__init__.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/protocol/transports/__pycache__/base.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/protocol/transports/__pycache__/cli.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/protocol/transports/__pycache__/http.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/protocol/transports/__pycache__/stdio.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/validation/metrics/__pycache__/present_board.cpython-39.pyc +0 -0
package/bin/cli.js
CHANGED
|
@@ -383,7 +383,7 @@ async function doInit(name, roleArg) {
|
|
|
383
383
|
console.log(`\n--- 自动初始化 ---`);
|
|
384
384
|
const setupPath = path.join(cwd, ".qoder", "scripts", "deployment", "setup", "setup.py");
|
|
385
385
|
if (fs.existsSync(setupPath)) {
|
|
386
|
-
const setupArgs = [name, role, "--skip-cron", "--skip-qoderwork"].filter(Boolean);
|
|
386
|
+
const setupArgs = [name, role, "--skip-cron", "--skip-qoderwork", "--skip-code"].filter(Boolean);
|
|
387
387
|
try {
|
|
388
388
|
const cmd = `python "${setupPath}" ${setupArgs.map(a => `"${a}"`).join(" ")}`;
|
|
389
389
|
console.log(` 运行: setup.py ${setupArgs.join(" ")}`);
|
|
@@ -443,6 +443,21 @@ async function doInit(name, roleArg) {
|
|
|
443
443
|
}
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
+
// === 6.6. 连云平台 bind 项目(云模式:KG 在云平台,引擎调云 MCP)===
|
|
447
|
+
if (pyCmd) {
|
|
448
|
+
const switchScript = path.join(cwd, ".qoder", "scripts", "domain", "kg", "switch_project.py");
|
|
449
|
+
if (fs.existsSync(switchScript)) {
|
|
450
|
+
console.log(`\n--- 连云平台 bind 项目 ---`);
|
|
451
|
+
console.log(` 问平台邮箱/密码 → 拉项目列表 → 选项目 → 写 mcp_config(连云)`);
|
|
452
|
+
try {
|
|
453
|
+
const env = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" };
|
|
454
|
+
execSync(`${pyCmd} "${switchScript}"`, { cwd, stdio: "inherit", timeout: 180000, env });
|
|
455
|
+
} catch (e) {
|
|
456
|
+
console.log(` [WARN] 云 bind 未完成,可手动: python .qoder/scripts/domain/kg/switch_project.py`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
446
461
|
// === 7. 写版本戳 ===
|
|
447
462
|
writeEngineVersion(cwd);
|
|
448
463
|
|
package/package.json
CHANGED
|
@@ -132,6 +132,11 @@ class McpCap:
|
|
|
132
132
|
|
|
133
133
|
def _cli_fallback(self, tool_name: str, args: dict) -> Optional[str]:
|
|
134
134
|
"""MCP 工具名 → wlkj.py 执行。无映射返回 None。"""
|
|
135
|
+
# ★ 递归守卫:search_code→kg search→search_index→call_remote→call→_cli_fallback 会无限套娃。
|
|
136
|
+
# 已在降级子进程内(_run_wlkj 设了 _WLKJ_IN_CLI_FALLBACK=1)则直接放弃降级,返回原始错误。
|
|
137
|
+
import os as _os
|
|
138
|
+
if _os.environ.get("_WLKJ_IN_CLI_FALLBACK"):
|
|
139
|
+
return None
|
|
135
140
|
entry = self._TOOL_TO_WLKJ.get(tool_name)
|
|
136
141
|
if entry is None:
|
|
137
142
|
return None
|
|
@@ -183,10 +188,12 @@ class McpCap:
|
|
|
183
188
|
repo = os.getcwd()
|
|
184
189
|
wlkj = os.path.join(repo, ".qoder", "scripts", "orchestration", "wlkj.py")
|
|
185
190
|
try:
|
|
191
|
+
# 递归守卫:子进程内 _WLKJ_IN_CLI_FALLBACK=1,其 McpCap 不再降级(否则套娃)
|
|
192
|
+
env = dict(os.environ, _WLKJ_IN_CLI_FALLBACK="1")
|
|
186
193
|
r = subprocess.run(
|
|
187
194
|
[sys.executable, wlkj, cmd] + args,
|
|
188
195
|
capture_output=True, text=True, timeout=60,
|
|
189
|
-
encoding="utf-8", errors="replace",
|
|
196
|
+
encoding="utf-8", errors="replace", env=env,
|
|
190
197
|
)
|
|
191
198
|
if r.returncode == 0:
|
|
192
199
|
return r.stdout.strip()
|
|
@@ -51,7 +51,7 @@ _KNOWLEDGE_SERVER_ALIASES = ["wlinkj-knowledge", "qoder-knowledge-graph"]
|
|
|
51
51
|
SERVERS: dict[str, dict] = {
|
|
52
52
|
"qoder-knowledge-graph": {
|
|
53
53
|
"kind": "http",
|
|
54
|
-
"url": "http://
|
|
54
|
+
"url": os.environ.get("WLKJ_BASE_URL", "http://10.89.7.120") + "/mcp",
|
|
55
55
|
"note": "知识图谱。已改为 http 模式,指向资产平台 MCP 服务(POST /mcp JSON-RPC)",
|
|
56
56
|
},
|
|
57
57
|
"qoder-zentao": {
|
|
@@ -73,16 +73,22 @@ SERVERS: dict[str, dict] = {
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def _mcp_config_path() -> Path:
|
|
76
|
-
"""定位 mcp_config.json(在仓库根/宿主下)。
|
|
76
|
+
"""定位 mcp_config.json(在仓库根/宿主下)。
|
|
77
|
+
|
|
78
|
+
★ switch_project.py 实际写入位置是 {repo}/.qoder/mcp_config.json(云模式连云 bind 后),
|
|
79
|
+
必须优先查这里——否则读不到 token,transport 用空 token 调云 MCP 鉴权失败(历史 bug:
|
|
80
|
+
引擎 search 全返回"MCP返回非JSON"+递归降级套娃)。老形态 repo/mcp_config.json 兼容。
|
|
81
|
+
"""
|
|
77
82
|
repo = _get_repo_root()
|
|
78
83
|
# registry_mcp.py 在 .../scripts/capability/, mcp_config.json 在宿主根(.qoder)。
|
|
79
84
|
for cand in [
|
|
80
|
-
repo / "mcp_config.json",
|
|
85
|
+
repo / ".qoder" / "mcp_config.json", # ★ 云模式实际写入位置(switch_project)
|
|
86
|
+
repo / "mcp_config.json", # 老形态:仓库根
|
|
81
87
|
repo / "registry_mcp_config.json",
|
|
82
88
|
]:
|
|
83
89
|
if cand.is_file():
|
|
84
90
|
return cand
|
|
85
|
-
return repo / "mcp_config.json"
|
|
91
|
+
return repo / ".qoder" / "mcp_config.json"
|
|
86
92
|
|
|
87
93
|
|
|
88
94
|
def _load_servers() -> dict[str, dict]:
|
|
@@ -834,6 +834,7 @@ def check_lanhu():
|
|
|
834
834
|
def main():
|
|
835
835
|
args = [a for a in sys.argv[1:]]
|
|
836
836
|
fix = '--fix' in args
|
|
837
|
+
skip_code = '--skip-code' in args or '--cloud' in args
|
|
837
838
|
pos = [a for a in args if not a.startswith('--')]
|
|
838
839
|
name = pos[0] if pos else None
|
|
839
840
|
role = pos[1] if len(pos) > 1 else None
|
|
@@ -848,8 +849,11 @@ def main():
|
|
|
848
849
|
check_dependencies(fix)
|
|
849
850
|
check_developer(fix, name, role)
|
|
850
851
|
check_team_sync(fix)
|
|
851
|
-
|
|
852
|
-
|
|
852
|
+
if skip_code:
|
|
853
|
+
print('\n--- 4. 源码/索引: 云模式跳过(KG 在云平台,引擎调云 MCP)---')
|
|
854
|
+
else:
|
|
855
|
+
check_source_repos(fix, config)
|
|
856
|
+
check_index(fix)
|
|
853
857
|
check_style(fix)
|
|
854
858
|
check_prd_templates()
|
|
855
859
|
check_weekly(fix)
|
|
@@ -242,9 +242,11 @@ def detect_name(explicit=None):
|
|
|
242
242
|
# Step 2: 调 init_doctor --fix (主体)
|
|
243
243
|
# ============================================================
|
|
244
244
|
|
|
245
|
-
def run_doctor(name, role):
|
|
245
|
+
def run_doctor(name, role, skip_code=False):
|
|
246
246
|
print('\n--- 初始化 (init_doctor) ---')
|
|
247
247
|
cmd = [sys.executable, str(THIS_DIR / 'init_doctor.py'), '--fix', name, role]
|
|
248
|
+
if skip_code:
|
|
249
|
+
cmd.append('--skip-code')
|
|
248
250
|
# 透传 stdio (用户能看到实时输出) + 强制 UTF-8 环境 (解决 cmd GBK 乱码)
|
|
249
251
|
env = dict(os.environ)
|
|
250
252
|
env['PYTHONIOENCODING'] = 'utf-8'
|
|
@@ -911,6 +913,7 @@ def main():
|
|
|
911
913
|
parser.add_argument('--skip-cron', action='store_true', help='跳过 cron 注册')
|
|
912
914
|
parser.add_argument('--skip-qoderwork', action='store_true', help='跳过 QoderWork 安装')
|
|
913
915
|
parser.add_argument('--skip-lanhu', action='store_true', help='跳过蓝湖 MCP 引导')
|
|
916
|
+
parser.add_argument('--skip-code', '--cloud', action='store_true', help='云模式:跳过本地源码 clone/索引(KG 在云平台)')
|
|
914
917
|
args = parser.parse_args()
|
|
915
918
|
|
|
916
919
|
print('=' * 56)
|
|
@@ -931,7 +934,7 @@ def main():
|
|
|
931
934
|
configure_git_identity(name)
|
|
932
935
|
|
|
933
936
|
# Step 2
|
|
934
|
-
if not run_doctor(name, args.role):
|
|
937
|
+
if not run_doctor(name, args.role, args.skip_code):
|
|
935
938
|
warn('init_doctor 未完全成功, 继续 setup 后续步骤...')
|
|
936
939
|
|
|
937
940
|
# Step 3
|
|
@@ -20,6 +20,7 @@ integration.return_to_platform —— 引擎产出回流平台的统一通道。
|
|
|
20
20
|
"""
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
|
+
import os
|
|
23
24
|
from typing import Optional
|
|
24
25
|
|
|
25
26
|
|
|
@@ -121,7 +122,7 @@ def _via_http(payload: dict) -> tuple[bool, str]:
|
|
|
121
122
|
|
|
122
123
|
|
|
123
124
|
def _platform_base() -> str:
|
|
124
|
-
"""平台 base url
|
|
125
|
+
"""平台 base url(默认 http://10.89.7.120 云),从 mcp_config server url 推导。"""
|
|
125
126
|
from urllib.parse import urlparse
|
|
126
127
|
cfg = _load_mcp_config()
|
|
127
128
|
for srv in (cfg.get("mcpServers") or {}).values():
|
|
@@ -133,7 +134,7 @@ def _platform_base() -> str:
|
|
|
133
134
|
if ep:
|
|
134
135
|
p = urlparse(ep)
|
|
135
136
|
return "%s://%s" % (p.scheme, p.netloc)
|
|
136
|
-
return "http://
|
|
137
|
+
return os.environ.get("WLKJ_BASE_URL", "http://10.89.7.120")
|
|
137
138
|
|
|
138
139
|
|
|
139
140
|
def _engine_token() -> Optional[str]:
|
|
@@ -48,7 +48,7 @@ def _resolve_endpoint() -> Tuple[str, str, str]:
|
|
|
48
48
|
u = srv.get("url", "")
|
|
49
49
|
if "/mcp" in u:
|
|
50
50
|
base = u.split("/mcp")[0]; break
|
|
51
|
-
base = (base or "http://
|
|
51
|
+
base = (base or os.environ.get("WLKJ_BASE_URL", "http://10.89.7.120")).rstrip("/")
|
|
52
52
|
token = os.environ.get("WLKJ_MCP_TOKEN", "")
|
|
53
53
|
if not token:
|
|
54
54
|
for srv in (cfg.get("mcpServers") or {}).values():
|
|
@@ -278,6 +278,31 @@ def build_embeddings(batch_size=64):
|
|
|
278
278
|
con.close()
|
|
279
279
|
|
|
280
280
|
|
|
281
|
+
def _cloud_rag_search(query, top_k=10):
|
|
282
|
+
"""云优先语义搜索:调工作台 MCP rag_search(本地无 KG/embedding,全迁云)。
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
[(entity_id, text, score), ...] 或 None(云不可用时返回 None,让调用方退本地)。
|
|
286
|
+
"""
|
|
287
|
+
try:
|
|
288
|
+
from domain.kg.search._remote import call_remote
|
|
289
|
+
data = call_remote("rag_search", {"query": query, "top_k": top_k})
|
|
290
|
+
except Exception:
|
|
291
|
+
return None
|
|
292
|
+
items = data.get("items") or []
|
|
293
|
+
out = []
|
|
294
|
+
for it in items[:top_k]:
|
|
295
|
+
eid = it.get("entity_id") or it.get("id") or ""
|
|
296
|
+
text = it.get("text") or it.get("name") or ""
|
|
297
|
+
score = it.get("score")
|
|
298
|
+
try:
|
|
299
|
+
score = float(score) if score is not None else 0.0
|
|
300
|
+
except Exception:
|
|
301
|
+
score = 0.0
|
|
302
|
+
out.append((eid, text, score))
|
|
303
|
+
return out
|
|
304
|
+
|
|
305
|
+
|
|
281
306
|
def semantic_search(query, top_k=10):
|
|
282
307
|
"""语义搜索: query→向量→余弦相似度→top_k 实体。
|
|
283
308
|
|
|
@@ -656,13 +681,24 @@ if __name__ == '__main__':
|
|
|
656
681
|
n = build_embeddings()
|
|
657
682
|
print('Built %d embeddings' % n)
|
|
658
683
|
|
|
659
|
-
elif len(sys.argv) > 2 and sys.argv[1] == 'search'
|
|
660
|
-
|
|
684
|
+
elif (len(sys.argv) > 2 and sys.argv[1] == 'search') or \
|
|
685
|
+
(len(sys.argv) > 1 and sys.argv[1] not in ('--check', '--label', 'build-embeddings', '--status', '--backfill-cn')):
|
|
686
|
+
# 兼容两种姿势: `search <词>` 和裸词 `<词>`(wlkj.py semantic 保险异常 透传过来)。
|
|
687
|
+
# ★ 云优先:本地无 KG/embedding(全迁云),先调云 rag_search;云不可用才退本地 semantic_search。
|
|
688
|
+
query = sys.argv[2] if sys.argv[1] == 'search' else sys.argv[1]
|
|
689
|
+
results = _cloud_rag_search(query)
|
|
690
|
+
if results is None:
|
|
691
|
+
# 云不可用 → 退本地(开发期有本地 embedding 时仍可用)
|
|
692
|
+
results = semantic_search(query)
|
|
661
693
|
if results:
|
|
662
|
-
for
|
|
663
|
-
|
|
694
|
+
for row in results:
|
|
695
|
+
if len(row) == 3:
|
|
696
|
+
eid, text, score = row
|
|
697
|
+
print(' %.3f %s' % (score, text[:70]))
|
|
698
|
+
else:
|
|
699
|
+
print(' %s' % row)
|
|
664
700
|
else:
|
|
665
|
-
print('(
|
|
701
|
+
print('(无结果或云 rag_search 不可用 + 本地 embedding 未构建)')
|
|
666
702
|
|
|
667
703
|
elif len(sys.argv) > 1 and sys.argv[1] == '--status':
|
|
668
704
|
st = embedding_status()
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
python switch_project.py --email a@b.c --pw xxx # 非交互:直接指定
|
|
7
7
|
python switch_project.py --project-uuid <UUID> # 非交互:直接指定项目
|
|
8
8
|
|
|
9
|
-
需工作台后端在跑(默认 http://
|
|
9
|
+
需工作台后端在跑(默认 http://10.89.7.120 云)。
|
|
10
10
|
"""
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
@@ -33,7 +33,7 @@ def _find_config_path():
|
|
|
33
33
|
CONFIG_PATH = _find_config_path()
|
|
34
34
|
|
|
35
35
|
# 平台地址(默认 10010,可 env 覆盖)
|
|
36
|
-
BASE_URL = os.environ.get("WLKJ_BASE_URL", "http://
|
|
36
|
+
BASE_URL = os.environ.get("WLKJ_BASE_URL", "http://10.89.7.120")
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def bind(email, password, base_url=BASE_URL):
|
|
@@ -51,7 +51,7 @@ def _get_base(cfg: dict) -> str:
|
|
|
51
51
|
u = srv.get("url", "")
|
|
52
52
|
if "/mcp" in u:
|
|
53
53
|
return u.split("/mcp")[0]
|
|
54
|
-
return "http://
|
|
54
|
+
return os.environ.get("WLKJ_BASE_URL", "http://10.89.7.120")
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def _api(base, method, path, token, body=None):
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/templates/qoder/scripts/domain/integration/__pycache__/return_to_platform.cpython-39.pyc
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|