@hupan56/wlkj 2.7.12 → 3.1.2
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 +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
LLM semantic labeling layer for the knowledge graph.
|
|
4
|
-
|
|
5
|
-
After kg_build.py extracts structural data (AST-level), this module:
|
|
6
|
-
1. Finds entities that lack semantic labels (cn == dir, no business_domain)
|
|
7
|
-
2. Outputs a labeling prompt for the AI (QoderWork) to fill in
|
|
8
|
-
3. Provides a write-back function to store labels in DuckDB
|
|
9
|
-
|
|
10
|
-
The AI (QoderWork's LLM) sees the prompt during session-start or when
|
|
11
|
-
kg_build reports new modules, and labels them — zero human trigger.
|
|
12
|
-
|
|
13
|
-
Usage (called by AI automatically):
|
|
14
|
-
python kg_semantic.py --check # check what needs labeling
|
|
15
|
-
python kg_semantic.py --label '{"assets": {"cn": "资产管理", "domain": "固定资产"}}'
|
|
16
|
-
"""
|
|
17
|
-
import json
|
|
18
|
-
import os
|
|
19
|
-
import sys
|
|
20
|
-
|
|
21
|
-
SCRIPTS = os.path.dirname(os.path.abspath(__file__))
|
|
22
|
-
sys.path.insert(0, SCRIPTS)
|
|
23
|
-
from common.paths import DATA_INDEX_DIR
|
|
24
|
-
from kg_duckdb import get_db
|
|
25
|
-
|
|
26
|
-
DB_PATH = str(DATA_INDEX_DIR / 'kg.duckdb')
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def check_unlabeled():
|
|
30
|
-
"""Find modules that need semantic labeling (cn == dir or cn is empty)."""
|
|
31
|
-
con = get_db()
|
|
32
|
-
# Find FEATURE entities where cn == dir (unlabeled) or cn is empty
|
|
33
|
-
rows = con.execute("""
|
|
34
|
-
SELECT id, canonical, cn FROM entities
|
|
35
|
-
WHERE type = 'FEATURE'
|
|
36
|
-
AND (cn = canonical OR cn = '' OR cn IS NULL)
|
|
37
|
-
ORDER BY canonical
|
|
38
|
-
""").fetchall()
|
|
39
|
-
|
|
40
|
-
if not rows:
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
# Build labeling prompt
|
|
44
|
-
modules = []
|
|
45
|
-
for eid, dirname, cn in rows:
|
|
46
|
-
# Gather context: what endpoints/pages does this module have?
|
|
47
|
-
ep_count = con.execute(
|
|
48
|
-
"SELECT COUNT(*) FROM edges WHERE from_id = ? AND edge_type = 'has_endpoint'",
|
|
49
|
-
[eid]
|
|
50
|
-
).fetchone()[0]
|
|
51
|
-
btn_count = con.execute(
|
|
52
|
-
"SELECT COUNT(*) FROM edges WHERE from_id = ? AND edge_type = 'has_button'",
|
|
53
|
-
[eid]
|
|
54
|
-
).fetchone()[0]
|
|
55
|
-
# Sample endpoints for context
|
|
56
|
-
sample_eps = con.execute("""
|
|
57
|
-
SELECT e.to_id FROM edges e
|
|
58
|
-
WHERE e.from_id = ? AND e.edge_type = 'has_endpoint' LIMIT 5
|
|
59
|
-
""", [eid]).fetchall()
|
|
60
|
-
|
|
61
|
-
modules.append({
|
|
62
|
-
'dir': dirname,
|
|
63
|
-
'endpoints': ep_count,
|
|
64
|
-
'buttons': btn_count,
|
|
65
|
-
'sample_apis': [r[0].replace('E:', '') for r in sample_eps],
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
con.close()
|
|
69
|
-
|
|
70
|
-
prompt = {
|
|
71
|
-
'action': 'semantic_labeling_needed',
|
|
72
|
-
'description': '以下模块需要语义标注(中文名+业务领域)。请根据目录名和API端点推断。',
|
|
73
|
-
'modules': modules,
|
|
74
|
-
'instructions': (
|
|
75
|
-
'对每个模块返回: {"目录名": {"cn": "中文名", "domain": "业务领域"}}\n'
|
|
76
|
-
'示例: {"assets": {"cn": "资产管理", "domain": "固定资产管理"}}\n'
|
|
77
|
-
'然后用 kg_semantic.py --label 写回'
|
|
78
|
-
),
|
|
79
|
-
}
|
|
80
|
-
return prompt
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def apply_labels(labels_json):
|
|
84
|
-
"""Write semantic labels back to DuckDB.
|
|
85
|
-
|
|
86
|
-
labels_json: {"assets": {"cn": "资产管理", "domain": "固定资产"}, ...}
|
|
87
|
-
"""
|
|
88
|
-
labels = json.loads(labels_json) if isinstance(labels_json, str) else labels_json
|
|
89
|
-
con = get_db()
|
|
90
|
-
updated = 0
|
|
91
|
-
|
|
92
|
-
for dirname, info in labels.items():
|
|
93
|
-
cn = info.get('cn', dirname)
|
|
94
|
-
domain = info.get('domain', '')
|
|
95
|
-
|
|
96
|
-
# Find the FEATURE entity by canonical name (dir)
|
|
97
|
-
rows = con.execute(
|
|
98
|
-
"SELECT id FROM entities WHERE type='FEATURE' AND canonical = ?",
|
|
99
|
-
[dirname]
|
|
100
|
-
).fetchall()
|
|
101
|
-
|
|
102
|
-
for row in rows:
|
|
103
|
-
eid = row[0]
|
|
104
|
-
# Update cn
|
|
105
|
-
con.execute(
|
|
106
|
-
"UPDATE entities SET cn = ? WHERE id = ?",
|
|
107
|
-
[cn, eid]
|
|
108
|
-
)
|
|
109
|
-
# Update props with domain
|
|
110
|
-
props_row = con.execute(
|
|
111
|
-
"SELECT props FROM entities WHERE id = ?", [eid]
|
|
112
|
-
).fetchone()
|
|
113
|
-
if props_row:
|
|
114
|
-
try:
|
|
115
|
-
props = json.loads(props_row[0]) if props_row[0] else {}
|
|
116
|
-
except:
|
|
117
|
-
props = {}
|
|
118
|
-
props['business_domain'] = domain
|
|
119
|
-
props['labeled'] = True
|
|
120
|
-
con.execute(
|
|
121
|
-
"UPDATE entities SET props = ? WHERE id = ?",
|
|
122
|
-
[json.dumps(props, ensure_ascii=False), eid]
|
|
123
|
-
)
|
|
124
|
-
# Update alias
|
|
125
|
-
con.execute(
|
|
126
|
-
"INSERT OR REPLACE INTO aliases VALUES (?, ?)",
|
|
127
|
-
[cn.lower(), eid]
|
|
128
|
-
)
|
|
129
|
-
updated += 1
|
|
130
|
-
|
|
131
|
-
con.close()
|
|
132
|
-
return updated
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if __name__ == '__main__':
|
|
136
|
-
if len(sys.argv) > 1 and sys.argv[1] == '--check':
|
|
137
|
-
result = check_unlabeled()
|
|
138
|
-
if result:
|
|
139
|
-
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
140
|
-
else:
|
|
141
|
-
print('{"status": "all_labeled", "message": "所有模块已有语义标注"}')
|
|
142
|
-
|
|
143
|
-
elif len(sys.argv) > 2 and sys.argv[1] == '--label':
|
|
144
|
-
count = apply_labels(sys.argv[2])
|
|
145
|
-
print('Updated %d entities with semantic labels' % count)
|
|
146
|
-
|
|
147
|
-
else:
|
|
148
|
-
print('Usage:')
|
|
149
|
-
print(' python kg_semantic.py --check # 查看待标注模块')
|
|
150
|
-
print(' python kg_semantic.py --label \'{"assets":{"cn":"资产管理","domain":"固定资产"}}\'')
|
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
mcp_launcher.py - 统一 MCP 启动器(跨 Win/Mac/Linux,消除写死路径)
|
|
5
|
-
|
|
6
|
-
被谁调用:
|
|
7
|
-
QoderWork 按 ~/.qoderwork/mcp.json 的 command 拉起本脚本。
|
|
8
|
-
mcp.json 的 args = [<本脚本路径>, <MCP名>]
|
|
9
|
-
例: {"command": "py", "args": ["C:/Users/HP/.qoderwork/mcp_launcher.py", "kg"]}
|
|
10
|
-
|
|
11
|
-
为什么需要它:
|
|
12
|
-
之前 mcp.json 写死绝对路径 (E:\\...\\python.exe + 仓库路径 + venv路径),
|
|
13
|
-
换电脑/换系统/别人 clone 全废。本 launcher 运行时自定位仓库根 + 找对的 python,
|
|
14
|
-
mcp.json 只需写 python 命令名 (py/python3) + 本脚本路径 (在 ~/.qoderwork/)。
|
|
15
|
-
|
|
16
|
-
做什么:
|
|
17
|
-
1. 解析 argv[1] = MCP 名 (kg / mysql / lanhu)
|
|
18
|
-
2. find_repo_root(): 环境变量 QODER_REPO > ~/.qoderwork/.repo-root 文件 > 向上找 .qoder/
|
|
19
|
-
3. 按 MCP 名选 python + server 脚本:
|
|
20
|
-
- kg: sys.executable, <repo>/.qoder/scripts/kg_mcp_server.py
|
|
21
|
-
- mysql: sys.executable, <repo>/.qoder/scripts/mysql_mcp_server.py (env 从 .mcp-mysql-env.json 读)
|
|
22
|
-
- lanhu: <repo>/external/lanhu-mcp/.venv python (3.10+), 复用 lanhu_stdio_wrapper 逻辑 (读角色 cookie)
|
|
23
|
-
4. os.execv 启动 (接管进程, QoderWork 管生死)
|
|
24
|
-
|
|
25
|
-
跨平台:
|
|
26
|
-
find_repo_root 用 os.path, venv python 用 _venv_python (win Scripts/mac bin)。
|
|
27
|
-
本脚本被 python3/py/python 跑都行 (它只做定位+execv, 不依赖特定版本)。
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
import json
|
|
31
|
-
import os
|
|
32
|
-
import sys
|
|
33
|
-
from pathlib import Path
|
|
34
|
-
|
|
35
|
-
try:
|
|
36
|
-
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
37
|
-
except (AttributeError, TypeError, OSError):
|
|
38
|
-
pass
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _home():
|
|
42
|
-
"""跨平台 home 目录。"""
|
|
43
|
-
return Path(os.environ.get('USERPROFILE') or os.path.expanduser('~'))
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _log(msg):
|
|
47
|
-
"""调试日志到 stderr (MCP stdio 用 stdout, 不能污染)。"""
|
|
48
|
-
sys.stderr.write('[mcp-launcher] ' + msg + '\n')
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _launch_disabled_mcp(name, reason):
|
|
52
|
-
"""没装/没配时, 启动一个空壳 MCP (只回 initialize + tools/list 空)。
|
|
53
|
-
让 QoderWork 显示绿(已连接)但无工具, 而不是黄(报错)。
|
|
54
|
-
reason 写进 serverInfo 让用户知道为什么没工具。"""
|
|
55
|
-
import json as _j
|
|
56
|
-
_log(f'{name} MCP 以"未启用"模式启动: {reason}')
|
|
57
|
-
# 简单的 stdio 循环, 只响应 initialize 和 tools/list
|
|
58
|
-
while True:
|
|
59
|
-
try:
|
|
60
|
-
line = sys.stdin.buffer.readline()
|
|
61
|
-
except (EOFError, OSError):
|
|
62
|
-
break
|
|
63
|
-
if not line:
|
|
64
|
-
break
|
|
65
|
-
try:
|
|
66
|
-
req = _j.loads(line.decode('utf-8'))
|
|
67
|
-
except Exception:
|
|
68
|
-
continue
|
|
69
|
-
method = req.get('method', '')
|
|
70
|
-
rid = req.get('id')
|
|
71
|
-
if method == 'initialize':
|
|
72
|
-
sys.stdout.write(_j.dumps({
|
|
73
|
-
"jsonrpc": "2.0", "id": rid,
|
|
74
|
-
"result": {
|
|
75
|
-
"protocolVersion": "2024-11-05",
|
|
76
|
-
"capabilities": {"tools": {}},
|
|
77
|
-
"serverInfo": {"name": name, "version": "0.0.0-disabled",
|
|
78
|
-
"reason": reason}
|
|
79
|
-
}
|
|
80
|
-
}) + '\n')
|
|
81
|
-
sys.stdout.flush()
|
|
82
|
-
elif method == 'tools/list':
|
|
83
|
-
sys.stdout.write(_j.dumps({
|
|
84
|
-
"jsonrpc": "2.0", "id": rid,
|
|
85
|
-
"result": {"tools": []}
|
|
86
|
-
}) + '\n')
|
|
87
|
-
sys.stdout.flush()
|
|
88
|
-
elif method == 'notifications/initialized':
|
|
89
|
-
pass
|
|
90
|
-
elif rid is not None:
|
|
91
|
-
sys.stdout.write(_j.dumps({
|
|
92
|
-
"jsonrpc": "2.0", "id": rid,
|
|
93
|
-
"error": {"code": -32601, "message": reason}
|
|
94
|
-
}) + '\n')
|
|
95
|
-
sys.stdout.flush()
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _find_python_with_deps(required_module='duckdb'):
|
|
99
|
-
"""找一个装了指定依赖的 Python。优先级:
|
|
100
|
-
1. sys.executable (当前 Python, 先测它有没有)
|
|
101
|
-
2. 常见 Python 路径 (anaconda/python3等) 逐个测
|
|
102
|
-
返回 python 路径(str) 或 None。
|
|
103
|
-
解决"py -3 选的 Python 没装 duckdb"问题。
|
|
104
|
-
"""
|
|
105
|
-
# 1. 当前 Python 有没有
|
|
106
|
-
try:
|
|
107
|
-
__import__(required_module)
|
|
108
|
-
return sys.executable
|
|
109
|
-
except ImportError:
|
|
110
|
-
pass
|
|
111
|
-
# 2. 找其它 Python
|
|
112
|
-
import shutil
|
|
113
|
-
candidates = []
|
|
114
|
-
for name in ('python', 'python3', 'py'):
|
|
115
|
-
p = shutil.which(name)
|
|
116
|
-
if p and p != sys.executable:
|
|
117
|
-
candidates.append(p)
|
|
118
|
-
# 常见安装路径 (Windows)
|
|
119
|
-
if sys.platform == 'win32':
|
|
120
|
-
for p in [
|
|
121
|
-
os.path.join(os.environ.get('USERPROFILE', ''), 'Anaconda3', 'python.exe'),
|
|
122
|
-
os.path.join(os.environ.get('USERPROFILE', ''), 'anaconda3', 'python.exe'),
|
|
123
|
-
os.path.join(os.environ.get('USERPROFILE', ''), 'miniconda3', 'python.exe'),
|
|
124
|
-
r'C:\ProgramData\Anaconda3\python.exe',
|
|
125
|
-
]:
|
|
126
|
-
if os.path.isfile(p) and p not in candidates:
|
|
127
|
-
candidates.append(p)
|
|
128
|
-
for py in candidates:
|
|
129
|
-
try:
|
|
130
|
-
r = __import__('subprocess').run(
|
|
131
|
-
[py, '-c', 'import %s' % required_module],
|
|
132
|
-
capture_output=True, timeout=5)
|
|
133
|
-
if r.returncode == 0:
|
|
134
|
-
_log(f'找到装了 {required_module} 的 Python: {py}')
|
|
135
|
-
return py
|
|
136
|
-
except Exception:
|
|
137
|
-
continue
|
|
138
|
-
return None
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def find_repo_root():
|
|
142
|
-
"""定位 QODER 仓库根。优先级:
|
|
143
|
-
1. 环境变量 QODER_REPO (用户显式设, 最可靠)
|
|
144
|
-
2. ~/.qoderwork/.repo-root 文件 (install 时写)
|
|
145
|
-
3. 向上找 .qoder/scripts/ (cwd 在仓库内, IDE/CLI 场景)
|
|
146
|
-
4. 本脚本若在 <repo>/.qoder/scripts/ 下 → 上三级 (开发态)
|
|
147
|
-
返回 Path 或 None。
|
|
148
|
-
"""
|
|
149
|
-
# 1. 环境变量
|
|
150
|
-
env_repo = os.environ.get('QODER_REPO')
|
|
151
|
-
if env_repo and (Path(env_repo) / '.qoder' / 'scripts').is_dir():
|
|
152
|
-
return Path(env_repo)
|
|
153
|
-
|
|
154
|
-
# 2. ~/.qoderwork/.repo-root 文件 (install 写的, 主路径)
|
|
155
|
-
anchor = _home() / '.qoderwork' / '.repo-root'
|
|
156
|
-
if anchor.is_file():
|
|
157
|
-
try:
|
|
158
|
-
line = anchor.read_text(encoding='utf-8').strip()
|
|
159
|
-
if line and (Path(line) / '.qoder' / 'scripts').is_dir():
|
|
160
|
-
return Path(line)
|
|
161
|
-
except OSError:
|
|
162
|
-
pass
|
|
163
|
-
|
|
164
|
-
# 3. 向上找 .qoder/scripts/
|
|
165
|
-
here = Path(os.getcwd()).resolve()
|
|
166
|
-
for _ in range(8):
|
|
167
|
-
if (here / '.qoder' / 'scripts').is_dir():
|
|
168
|
-
return here
|
|
169
|
-
parent = here.parent
|
|
170
|
-
if parent == here:
|
|
171
|
-
break
|
|
172
|
-
here = parent
|
|
173
|
-
|
|
174
|
-
# 4. 本脚本位置 (开发态: 在 <repo>/.qoder/scripts/mcp_launcher.py)
|
|
175
|
-
try:
|
|
176
|
-
s = Path(__file__).resolve()
|
|
177
|
-
root = s.parent.parent.parent # scripts/ -> .qoder/ -> repo
|
|
178
|
-
if (root / '.qoder' / 'scripts').is_dir():
|
|
179
|
-
return root
|
|
180
|
-
except Exception:
|
|
181
|
-
pass
|
|
182
|
-
|
|
183
|
-
return None
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def _venv_python(venv):
|
|
187
|
-
"""venv 内 python 路径 (跨平台: win Scripts/python.exe, mac bin/python)。"""
|
|
188
|
-
if sys.platform == 'win32':
|
|
189
|
-
return venv / 'Scripts' / 'python.exe'
|
|
190
|
-
return venv / 'bin' / 'python'
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
def _read_current_developer(repo):
|
|
194
|
-
"""读 .qoder/.developer 当前角色。"""
|
|
195
|
-
try:
|
|
196
|
-
sys.path.insert(0, str(repo / '.qoder' / 'scripts'))
|
|
197
|
-
from common.paths import get_developer
|
|
198
|
-
return get_developer(repo)
|
|
199
|
-
except Exception:
|
|
200
|
-
return None
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
def _read_lanhu_cookie(repo, dev):
|
|
204
|
-
"""读角色的蓝湖 cookie。"""
|
|
205
|
-
try:
|
|
206
|
-
sys.path.insert(0, str(repo / '.qoder' / 'scripts'))
|
|
207
|
-
from setup_lanhu import _read_env_cookie, lanhu_env_file, COOKIE_PLACEHOLDER
|
|
208
|
-
ck = _read_env_cookie(dev)
|
|
209
|
-
return ck if (ck and ck != COOKIE_PLACEHOLDER) else None
|
|
210
|
-
except Exception:
|
|
211
|
-
return None
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def launch_kg(repo):
|
|
215
|
-
"""启动知识图谱 MCP。找装了 duckdb 的 Python (不是盲目用 sys.executable)。"""
|
|
216
|
-
server = repo / '.qoder' / 'scripts' / 'kg_mcp_server.py'
|
|
217
|
-
if not server.is_file():
|
|
218
|
-
_log(f'kg server 不存在: {server}')
|
|
219
|
-
_launch_disabled_mcp('qoder-knowledge-graph',
|
|
220
|
-
'kg_mcp_server.py 缺失。跑 npx @hupan56/wlkj update')
|
|
221
|
-
return
|
|
222
|
-
py = _find_python_with_deps('duckdb')
|
|
223
|
-
if not py:
|
|
224
|
-
_log('⚠️ 找不到装了 duckdb 的 Python! 跑: pip install duckdb')
|
|
225
|
-
_log(' 或: npx @hupan56/wlkj install-env (自动装依赖)')
|
|
226
|
-
_launch_disabled_mcp('qoder-knowledge-graph',
|
|
227
|
-
'duckdb 未装。跑 npx @hupan56/wlkj install-env (自动装依赖)')
|
|
228
|
-
return
|
|
229
|
-
_log(f'启动 kg (python={py})')
|
|
230
|
-
os.execv(py, [py, str(server)])
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
def launch_mysql(repo):
|
|
234
|
-
"""启动 MySQL MCP。连接信息优先从 config.yaml 读 (团队共享, git 里有)。
|
|
235
|
-
回退: ~/.qoderwork/.mcp-mysql-env.json (旧版个人文件)。"""
|
|
236
|
-
server = repo / '.qoder' / 'scripts' / 'mysql_mcp_server.py'
|
|
237
|
-
if not server.is_file():
|
|
238
|
-
_log(f'mysql server 不存在: {server}')
|
|
239
|
-
_launch_disabled_mcp('qoder-mysql',
|
|
240
|
-
'mysql_mcp_server.py 缺失。跑 npx @hupan56/wlkj update')
|
|
241
|
-
return
|
|
242
|
-
# 优先: 从 config.yaml 读 (团队共享, clone 就有)
|
|
243
|
-
cfg_path = repo / '.qoder' / 'config.yaml'
|
|
244
|
-
mysql_cfg = {}
|
|
245
|
-
if cfg_path.is_file():
|
|
246
|
-
try:
|
|
247
|
-
import yaml
|
|
248
|
-
d = yaml.safe_load(cfg_path.read_text(encoding='utf-8')) or {}
|
|
249
|
-
mysql_cfg = d.get('mysql', {}) or {}
|
|
250
|
-
except Exception:
|
|
251
|
-
pass
|
|
252
|
-
if mysql_cfg:
|
|
253
|
-
for k in ('host', 'port', 'user', 'password'):
|
|
254
|
-
v = mysql_cfg.get(k)
|
|
255
|
-
if v:
|
|
256
|
-
os.environ['MYSQL_' + k.upper()] = str(v)
|
|
257
|
-
if mysql_cfg.get('env_name'):
|
|
258
|
-
os.environ['MYSQL_ENV_NAME'] = str(mysql_cfg['env_name'])
|
|
259
|
-
_log(f'mysql env 从 config.yaml 注入 ({len(mysql_cfg)} 项)')
|
|
260
|
-
else:
|
|
261
|
-
# 回退: 旧版个人 env 文件 (~/.qoderwork/.mcp-mysql-env.json)
|
|
262
|
-
env_file = _home() / '.qoderwork' / '.mcp-mysql-env.json'
|
|
263
|
-
if env_file.is_file():
|
|
264
|
-
try:
|
|
265
|
-
cfg = json.loads(env_file.read_text(encoding='utf-8'))
|
|
266
|
-
for k, v in cfg.items():
|
|
267
|
-
if v:
|
|
268
|
-
os.environ[k] = str(v)
|
|
269
|
-
_log(f'mysql env 从个人文件注入 ({len(cfg)} 项)')
|
|
270
|
-
except (json.JSONDecodeError, OSError):
|
|
271
|
-
pass
|
|
272
|
-
else:
|
|
273
|
-
_log('⚠️ mysql 配置缺失: config.yaml 无 mysql 段, 且 ~/.qoderwork/.mcp-mysql-env.json 不存在')
|
|
274
|
-
_log(f'启动 mysql')
|
|
275
|
-
py = _find_python_with_deps('pymysql')
|
|
276
|
-
if not py:
|
|
277
|
-
_log('⚠️ 找不到装了 pymysql 的 Python! 跑: pip install pymysql')
|
|
278
|
-
_launch_disabled_mcp('qoder-mysql',
|
|
279
|
-
'pymysql 未装。跑 pip install pymysql 或 npx @hupan56/wlkj install-env')
|
|
280
|
-
return
|
|
281
|
-
_log(f' python={py}')
|
|
282
|
-
os.execv(py, [py, str(server)])
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
ZENTAO_URL_DEFAULT = 'http://10.89.2.91/zentao'
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
def launch_zentao(repo):
|
|
289
|
-
"""启动禅道 MCP。连接信息从 config.yaml 读(团队共享)。"""
|
|
290
|
-
server = repo / '.qoder' / 'scripts' / 'zentao_mcp_server.py'
|
|
291
|
-
if not server.is_file():
|
|
292
|
-
_log(f'zentao server 不存在: {server}')
|
|
293
|
-
_launch_disabled_mcp('qoder-zentao', 'zentao_mcp_server.py 缺失')
|
|
294
|
-
return
|
|
295
|
-
# 从 config.yaml 读禅道配置
|
|
296
|
-
cfg_path = repo / '.qoder' / 'config.yaml'
|
|
297
|
-
if cfg_path.is_file():
|
|
298
|
-
try:
|
|
299
|
-
import yaml
|
|
300
|
-
d = yaml.safe_load(cfg_path.read_text(encoding='utf-8')) or {}
|
|
301
|
-
zt = d.get('zentao', {}) or {}
|
|
302
|
-
if zt:
|
|
303
|
-
os.environ['ZENTAO_URL'] = str(zt.get('url', ZENTAO_URL_DEFAULT))
|
|
304
|
-
os.environ['ZENTAO_USER'] = str(zt.get('user', ''))
|
|
305
|
-
os.environ['ZENTAO_PASSWORD'] = str(zt.get('password', ''))
|
|
306
|
-
_log(f'zentao env 从 config.yaml 注入')
|
|
307
|
-
except Exception:
|
|
308
|
-
pass
|
|
309
|
-
_log(f'启动 zentao')
|
|
310
|
-
py = _find_python_with_deps('requests')
|
|
311
|
-
if not py:
|
|
312
|
-
_log('⚠️ 找不到装了 requests 的 Python!')
|
|
313
|
-
_launch_disabled_mcp('qoder-zentao', 'requests 未装。pip install requests')
|
|
314
|
-
return
|
|
315
|
-
_log(f' python={py}')
|
|
316
|
-
os.execv(py, [py, str(server)])
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
def launch_lanhu(repo):
|
|
320
|
-
"""启动蓝湖 MCP。用 venv 的 3.10+ python (fastmcp 要求), 读角色 cookie。
|
|
321
|
-
没装 lanhu venv 或没配 cookie 时, 输出友好提示并启动一个空壳 MCP
|
|
322
|
-
(让 QoderWork 显示"未启用"而不是黄/报错)。"""
|
|
323
|
-
venv = repo / 'external' / 'lanhu-mcp' / '.venv'
|
|
324
|
-
server = repo / 'external' / 'lanhu-mcp' / 'lanhu_mcp_server.py'
|
|
325
|
-
|
|
326
|
-
# 检查 venv
|
|
327
|
-
if not venv.is_dir():
|
|
328
|
-
_log('lanhu venv 未安装 — MCP 将以"未启用"模式启动')
|
|
329
|
-
_launch_disabled_mcp('lanhu',
|
|
330
|
-
'蓝湖 MCP 未安装。跑 python .qoder/scripts/setup_lanhu.py 安装')
|
|
331
|
-
return
|
|
332
|
-
py = _venv_python(venv)
|
|
333
|
-
if not py.is_file():
|
|
334
|
-
_log(f'lanhu venv python 不存在: {py}')
|
|
335
|
-
_launch_disabled_mcp('lanhu', '蓝湖 MCP venv 异常, 重装: setup_lanhu.py')
|
|
336
|
-
return
|
|
337
|
-
if not server.is_file():
|
|
338
|
-
_log(f'lanhu server 不存在: {server}')
|
|
339
|
-
_launch_disabled_mcp('lanhu', '蓝湖 MCP server 文件缺失')
|
|
340
|
-
return
|
|
341
|
-
|
|
342
|
-
# 读角色 cookie
|
|
343
|
-
dev = _read_current_developer(repo)
|
|
344
|
-
if not dev:
|
|
345
|
-
_log('未初始化开发者 (.qoder/.developer), 跑 /wl-init')
|
|
346
|
-
_launch_disabled_mcp('lanhu', '未初始化开发者, 跑 /wl-init')
|
|
347
|
-
return
|
|
348
|
-
cookie = _read_lanhu_cookie(repo, dev)
|
|
349
|
-
if not cookie:
|
|
350
|
-
_log(f'角色 {dev} 的蓝湖 cookie 未配置 (占位/缺失)')
|
|
351
|
-
_log(f' 编辑 workspace/members/{dev}/.secrets/lanhu.env')
|
|
352
|
-
_launch_disabled_mcp('lanhu',
|
|
353
|
-
f'蓝湖 cookie 未配置: workspace/members/{dev}/.secrets/lanhu.env')
|
|
354
|
-
return
|
|
355
|
-
|
|
356
|
-
# 注入 env (角色隔离: cookie + 用户名都是当前角色的)
|
|
357
|
-
os.environ['LANHU_COOKIE'] = cookie
|
|
358
|
-
os.environ['MCP_TRANSPORT'] = 'stdio'
|
|
359
|
-
os.environ['LANHU_USER_NAME'] = dev or 'Developer'
|
|
360
|
-
os.environ['LANHU_USER_ROLE'] = 'Developer'
|
|
361
|
-
_log(f'启动 lanhu (角色={dev}, python={py})')
|
|
362
|
-
os.execv(str(py), [str(py), str(server)])
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
LAUNCHERS = {
|
|
366
|
-
'kg': launch_kg,
|
|
367
|
-
'knowledge-graph': launch_kg,
|
|
368
|
-
'mysql': launch_mysql,
|
|
369
|
-
'zentao': launch_zentao,
|
|
370
|
-
'lanhu': launch_lanhu,
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
def main():
|
|
375
|
-
# ⚠️ 关键: 任何失败都走 _launch_disabled_mcp(), 绝不 sys.exit()。
|
|
376
|
-
# sys.exit(1) 会让进程退出 → QoderWork 显示黄色(连接错误), 用户以为坏了。
|
|
377
|
-
# _launch_disabled_mcp() 启动一个干净 stdio 循环响应 initialize →
|
|
378
|
-
# QoderWork 显示绿色(已连接, 0工具), 用户知道是"未启用"而非"坏了"。
|
|
379
|
-
if len(sys.argv) < 2:
|
|
380
|
-
_log('用法: mcp_launcher.py <kg|mysql|zentao|lanhu>')
|
|
381
|
-
_log(f' argv={sys.argv}')
|
|
382
|
-
_launch_disabled_mcp('qoder-mcp',
|
|
383
|
-
'启动参数缺失: mcp_launcher.py <kg|mysql|zentao|lanhu>')
|
|
384
|
-
return
|
|
385
|
-
name = sys.argv[1].lower()
|
|
386
|
-
|
|
387
|
-
repo = find_repo_root()
|
|
388
|
-
if not repo:
|
|
389
|
-
_log('无法定位 QODER 仓库根。')
|
|
390
|
-
_log(' 方式1: 设环境变量 QODER_REPO=<仓库根>')
|
|
391
|
-
_log(' 方式2: 重跑 npx @hupan56/wlkj update (刷新 .repo-root)')
|
|
392
|
-
_log(' 方式3: 在仓库目录内运行')
|
|
393
|
-
_launch_disabled_mcp(name,
|
|
394
|
-
'未定位到 QODER 仓库根。跑 npx @hupan56/wlkj update')
|
|
395
|
-
return
|
|
396
|
-
_log(f'仓库根: {repo}')
|
|
397
|
-
|
|
398
|
-
fn = LAUNCHERS.get(name)
|
|
399
|
-
if not fn:
|
|
400
|
-
_log(f'未知 MCP 名: {name} (支持: {list(LAUNCHERS)})')
|
|
401
|
-
_launch_disabled_mcp(name,
|
|
402
|
-
'未知 MCP 名: %s (支持: %s)' % (name, ', '.join(LAUNCHERS)))
|
|
403
|
-
return
|
|
404
|
-
try:
|
|
405
|
-
fn(repo)
|
|
406
|
-
except Exception as e:
|
|
407
|
-
# 启动函数抛异常也不能让进程死 (死=黄, 捕获=绿)
|
|
408
|
-
_log(f'启动 {name} 时异常: {e}')
|
|
409
|
-
_launch_disabled_mcp(name,
|
|
410
|
-
'%s MCP 启动异常: %s。跑 npx @hupan56/wlkj update' % (name, e))
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if __name__ == '__main__':
|
|
414
|
-
main()
|