| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 |
- // R150 251 langgraph 派发器:kick(分离启动)+ read(单次读日志),不在等待期新建 shell(B-125)
- const BASE = process.env.BASE || 'http://192.168.16.251:4096';
- const DIR = process.env.DIR || '/sc-data/cfc/cfc-langgraph';
- const LOG = process.env.LOG || '/tmp/r150_pytest.log';
- const AUTH = 'Basic ' + Buffer.from('opencode:' + (process.env.OPENCODE_SERVER_PASSWORD || 'IwinTrue@123')).toString('base64');
- const H = { Authorization: AUTH, 'Content-Type': 'application/json' };
- const LOC = '?location%5Bdirectory%5D=' + encodeURIComponent(DIR);
- async function j(url, opt) {
- try {
- const r = await fetch(url, Object.assign({ headers: H }, opt || {}));
- const t = await r.text();
- try { return { status: r.status, json: JSON.parse(t) }; } catch { return { status: r.status, text: t }; }
- } catch (e) { return { status: 0, err: e.message }; }
- }
- async function sh(cmd, capMs) {
- const cr = await j(BASE + '/api/shell' + LOC, { method: 'POST', body: JSON.stringify({ command: cmd, cwd: DIR }) });
- const id = cr.json && cr.json.data && cr.json.data.id;
- if (!id) return { err: 'create ' + cr.status + ' ' + JSON.stringify(cr).slice(0, 200) };
- const t0 = Date.now();
- while (Date.now() - t0 < (capMs || 60000)) {
- await new Promise(r => setTimeout(r, 2500));
- const s = await j(BASE + '/api/shell/' + id + LOC);
- const d = (s.json && s.json.data) || null;
- if (!d) continue;
- if (d.status && d.status !== 'running') {
- const o = await j(BASE + '/api/shell/' + id + '/output' + LOC + '&limit=200000');
- return { code: d.status, out: (o.json && o.json.data && o.json.data.output) || '' };
- }
- }
- return { err: 'timeout' };
- }
- (async () => {
- const mode = process.argv[2] || 'kick';
- // R173 增:LOG 若从本机 env 注入,Git Bash(MSYS) 会把 /tmp/x 改写成 C:/Users/.../x,
- // 远端 `tail` 恒报 No such file,且 kick 的重定向也会失败 → 看起来像「日志没生成」。
- // 这里直接挡住 Windows 风格路径,强制调用方使用远端 POSIX 路径。
- if (LOG.includes(':') || LOG.includes('\\')) {
- console.error('BAD_LOG_PATH=' + LOG + ' 请使用远端 POSIX 路径(如 /tmp/rX_pytest.log),不要经由本地 env 注入');
- process.exit(2);
- }
- if (mode === 'kick') {
- // R156 修:251 系统 /usr/bin/python 无 pytest,必须用节点 venv(.venv/bin/python → python3.11);
- // 退出码另存变量后再写盘(B-126:$? 直接内联在 kick 串里可能落盘失败)。
- // R173 复现(B-126 第 6 轮):手工 kick 用 `echo EXIT=$?` 时 186 行日志零 EXIT 行;
- // 已在 /tmp/r173_probe.log 验证「先存 rc 再落盘」可写入 → 保留 rc 写法为唯一正确姿势。
- 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;
- const cmd = "rm -f " + LOG + "; setsid nohup bash -c '" + inner + "' >/dev/null 2>&1 & echo KICKED";
- const r = await sh(cmd, 40000);
- console.log('KICK=' + JSON.stringify(r).slice(0, 400));
- const h = await sh('cd ' + DIR + ' && git rev-parse --short HEAD; date', 30000);
- console.log('NODE_HEAD=' + ((h.out || h.err || '').trim()));
- } else if (mode === 'read') {
- const r = await sh('tail -c 4000 ' + LOG, 45000);
- console.log(r.err ? 'READ_ERR=' + r.err : r.out);
- } else if (mode === 'run') {
- const r = await sh(process.argv[3], 120000);
- console.log(r.err ? 'ERR=' + r.err : r.out);
- }
- })();
|