@hupan56/wlkj 3.1.10 → 3.1.12

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 CHANGED
@@ -1219,6 +1219,7 @@ function doHelp() {
1219
1219
  console.log(" (提交/同步 /wl-task 和 /wl-commit 会自动做, 不用单独 npx sync)");
1220
1220
  console.log("");
1221
1221
  console.log("=== 进阶 (管理员/特殊情况才用, 不常用) ===");
1222
+ console.log(" npx @hupan56/wlkj selftest 全套件自检 (跨环境+QoderWork工具协议+smoke+体检+MCP)");
1222
1223
  console.log(" npx @hupan56/wlkj doctor 体检 (或在 Qoder 里 /wl-init 无参数)");
1223
1224
  console.log(" npx @hupan56/wlkj migrate 老团队一次性升级到 Trellis 架构");
1224
1225
  console.log(" npx @hupan56/wlkj run <脚本> 万能跑脚本 (AI 内部用, 用户很少需要)");
@@ -1432,5 +1433,43 @@ switch (cmd) {
1432
1433
  case "提交": case "拉取最新": case "同步":
1433
1434
  case "查看状态": case "提交PRD": case "提交Spec": case "提交任务":
1434
1435
  gitOp(cmd); break;
1436
+
1437
+ // ── 自检: 一条命令跑完所有验证套件 (替代 7 步手动清单的"环境验证"部分) ──
1438
+ // 用法: npx @hupan56/wlkj selftest
1439
+ // 跑: cross-env 模拟 + QoderWork 工具协议 + smoke + doctor + mcp_doctor
1440
+ case "selftest": case "自检": {
1441
+ const { spawnSync: _ss } = require("child_process");
1442
+ const cwd = process.cwd();
1443
+ const pyCmd = detectPyCmd();
1444
+ const env = { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1", PYTHONPATH: path.join(cwd, ".qoder", "scripts") };
1445
+ console.log("\n" + "=".repeat(56));
1446
+ console.log(" wlkj selftest — 全套件自检 (验证跨环境 + 第一宿主)");
1447
+ console.log("=".repeat(56));
1448
+ let totalFail = 0;
1449
+ const suites = [
1450
+ ["1. 跨环境模拟验证", ["packages/wlkj/tests/test_cross_env.py"]],
1451
+ ["2. QoderWork 工具协议 (真实 MCP JSON-RPC)", ["packages/wlkj/tests/test_qoderwork_tools.py"]],
1452
+ ["3. 命令底层脚本 smoke", ["-c", "import sys; sys.path.insert(0, r'" + path.join(cwd, ".qoder", "scripts") + "'); exec(open(r'" + path.join(cwd, ".qoder", "scripts", "validation", "test", "smoke_all_commands.py") + "').read())"]],
1453
+ ["4. 环境体检 (doctor)", [path.join(cwd, ".qoder", "scripts", "orchestration", "wlkj.py"), "doctor"]],
1454
+ ["5. MCP 实测 (mcp_doctor)", [path.join(cwd, ".qoder", "scripts", "protocol", "mcp", "mcp_doctor.py")]],
1455
+ ];
1456
+ for (const [label, args] of suites) {
1457
+ console.log("\n--- " + label + " ---");
1458
+ if (!pyCmd) { console.log(" [SKIP] Python 未装"); continue; }
1459
+ // suites 1-2 在 packages/wlkj/tests/ 下 (相对 cwd), 3-5 用绝对路径
1460
+ const isTestFile = args[0] && args[0].endsWith(".py") && !args[0].startsWith(path.sep) && !args[0].includes(":");
1461
+ const fullArgs = isTestFile ? [path.join(cwd, args[0])] : args;
1462
+ try {
1463
+ const r = _ss(pyCmd, fullArgs, { cwd, stdio: "inherit", timeout: 120000, env });
1464
+ if (r.status !== 0) { console.log(" [FAIL] exit " + r.status); totalFail++; }
1465
+ } catch (e) { console.log(" [FAIL] " + (e.message || "").slice(0, 80)); totalFail++; }
1466
+ }
1467
+ console.log("\n" + "=".repeat(56));
1468
+ console.log(totalFail === 0 ? " ✓ 全部自检通过" : " ⚠ " + totalFail + " 个套件失败, 见上方");
1469
+ console.log("=".repeat(56) + "\n");
1470
+ process.exit(totalFail === 0 ? 0 : 1);
1471
+ break;
1472
+ }
1473
+
1435
1474
  default: doHelp();
1436
1475
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hupan56/wlkj",
3
- "version": "3.1.10",
3
+ "version": "3.1.12",
4
4
  "description": "AI Product R&D Workflow - PRD/Prototype/Search/Task/Report",
5
5
  "bin": {
6
6
  "wlkj": "bin/cli.js"
@@ -198,7 +198,7 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
198
198
  }, "required": ["module"]})
199
199
  def _t_get_workflow(self, args):
200
200
  from domain.kg.kg_capabilities import workflow_query
201
- from domain.kg.storage.kg_duckdb import get_db
201
+ from domain.kg.storage.kg_duckdb import get_db_readonly
202
202
  q = (args.get("module") or '').strip()
203
203
  if not q:
204
204
  return "请提供模块名。可选: assets/cost/hr/maintenance/operation/qualityAndSafety/veh/settings/dashboard/flow/systems/monitor/quasafe"
@@ -207,7 +207,9 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
207
207
  _hint = _require_kgdb()
208
208
  if _hint:
209
209
  return _hint
210
- con = get_db()
210
+ con = get_db_readonly()
211
+ if con is None:
212
+ return "知识图谱暂不可用 (kg.duckdb 连接失败), 稍后重试或联系管理员。"
211
213
  try:
212
214
  return workflow_query(con, q)
213
215
  finally:
@@ -222,7 +224,7 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
222
224
  }, "required": ["symbol"]})
223
225
  def _t_multi_hop(self, args):
224
226
  from domain.kg.kg_capabilities import hop_query
225
- from domain.kg.storage.kg_duckdb import get_db, multi_hop
227
+ from domain.kg.storage.kg_duckdb import get_db_readonly, multi_hop
226
228
  sym = (args.get("symbol") or '').strip()
227
229
  if not sym:
228
230
  return "请提供起点符号 (entity_id / 端点 / 函数名 / 中文别名)"
@@ -234,7 +236,9 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
234
236
  _hint = _require_kgdb()
235
237
  if _hint:
236
238
  return _hint
237
- con = get_db()
239
+ con = get_db_readonly()
240
+ if con is None:
241
+ return "知识图谱暂不可用 (kg.duckdb 连接失败), 稍后重试或联系管理员。"
238
242
  try:
239
243
  return hop_query(con, sym, depth, multi_hop_fn=multi_hop)
240
244
  finally:
@@ -259,12 +263,14 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
259
263
  "field": {"type": "string", "description": "字段名(英文field或中文标题)"},
260
264
  }, "required": ["field"]})
261
265
  def _t_search_field(self, args):
262
- from domain.kg.storage.kg_duckdb import get_db
266
+ from domain.kg.storage.kg_duckdb import get_db_readonly
263
267
  field = (args.get("field") or '').strip()
264
268
  _hint = _require_kgdb()
265
269
  if _hint:
266
270
  return _hint
267
- con = get_db()
271
+ con = get_db_readonly()
272
+ if con is None:
273
+ return "知识图谱暂不可用 (kg.duckdb 连接失败), 稍后重试或联系管理员。"
268
274
  try:
269
275
  rows = con.execute(
270
276
  "SELECT field, title, count, sample_files FROM fields "
@@ -286,12 +292,14 @@ class KnowledgeGraphMCPServer(BaseMCPServer):
286
292
  "platform": {"type": "string", "enum": ["web", "app"], "description": "平台过滤。可选"},
287
293
  }})
288
294
  def _t_list_modules(self, args):
289
- from domain.kg.storage.kg_duckdb import get_db
295
+ from domain.kg.storage.kg_duckdb import get_db_readonly
290
296
  platform = args.get("platform", "")
291
297
  _hint = _require_kgdb()
292
298
  if _hint:
293
299
  return _hint
294
- con = get_db()
300
+ con = get_db_readonly()
301
+ if con is None:
302
+ return "知识图谱暂不可用 (kg.duckdb 连接失败), 稍后重试或联系管理员。"
295
303
  try:
296
304
  if platform == 'web':
297
305
  rows = con.execute("SELECT dir, cn, project FROM modules WHERE project LIKE '%fywl%' ORDER BY dir").fetchall()