dispatch_251_pytest.cjs 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. // R150 251 langgraph 派发器:kick(分离启动)+ read(单次读日志),不在等待期新建 shell(B-125)
  2. const BASE = process.env.BASE || 'http://192.168.16.251:4096';
  3. const DIR = process.env.DIR || '/sc-data/cfc/cfc-langgraph';
  4. const LOG = process.env.LOG || '/tmp/r150_pytest.log';
  5. const AUTH = 'Basic ' + Buffer.from('opencode:' + (process.env.OPENCODE_SERVER_PASSWORD || 'IwinTrue@123')).toString('base64');
  6. const H = { Authorization: AUTH, 'Content-Type': 'application/json' };
  7. const LOC = '?location%5Bdirectory%5D=' + encodeURIComponent(DIR);
  8. async function j(url, opt) {
  9. try {
  10. const r = await fetch(url, Object.assign({ headers: H }, opt || {}));
  11. const t = await r.text();
  12. try { return { status: r.status, json: JSON.parse(t) }; } catch { return { status: r.status, text: t }; }
  13. } catch (e) { return { status: 0, err: e.message }; }
  14. }
  15. async function sh(cmd, capMs) {
  16. const cr = await j(BASE + '/api/shell' + LOC, { method: 'POST', body: JSON.stringify({ command: cmd, cwd: DIR }) });
  17. const id = cr.json && cr.json.data && cr.json.data.id;
  18. if (!id) return { err: 'create ' + cr.status + ' ' + JSON.stringify(cr).slice(0, 200) };
  19. const t0 = Date.now();
  20. while (Date.now() - t0 < (capMs || 60000)) {
  21. await new Promise(r => setTimeout(r, 2500));
  22. const s = await j(BASE + '/api/shell/' + id + LOC);
  23. const d = (s.json && s.json.data) || null;
  24. if (!d) continue;
  25. if (d.status && d.status !== 'running') {
  26. const o = await j(BASE + '/api/shell/' + id + '/output' + LOC + '&limit=200000');
  27. return { code: d.status, out: (o.json && o.json.data && o.json.data.output) || '' };
  28. }
  29. }
  30. return { err: 'timeout' };
  31. }
  32. (async () => {
  33. const mode = process.argv[2] || 'kick';
  34. if (mode === 'kick') {
  35. // R156 修:251 系统 /usr/bin/python 无 pytest,必须用节点 venv(.venv/bin/python → python3.11);
  36. // 退出码另存变量后再写盘(B-126:$? 直接内联在 kick 串里可能落盘失败)。
  37. const inner = "cd " + DIR + " && .venv/bin/python -m pytest -q --ignore=tests/qna --ignore=tests/test_chat.py > " + LOG + " 2>&1; rc=$?; echo EXIT=$rc >> " + LOG;
  38. const cmd = "rm -f " + LOG + "; setsid nohup bash -c '" + inner + "' >/dev/null 2>&1 & echo KICKED";
  39. const r = await sh(cmd, 40000);
  40. console.log('KICK=' + JSON.stringify(r).slice(0, 400));
  41. const h = await sh('cd ' + DIR + ' && git rev-parse --short HEAD; date', 30000);
  42. console.log('NODE_HEAD=' + ((h.out || h.err || '').trim()));
  43. } else if (mode === 'read') {
  44. const r = await sh('tail -c 4000 ' + LOG, 45000);
  45. console.log(r.err ? 'READ_ERR=' + r.err : r.out);
  46. } else if (mode === 'run') {
  47. const r = await sh(process.argv[3], 120000);
  48. console.log(r.err ? 'ERR=' + r.err : r.out);
  49. }
  50. })();