Files
mcn-short-video/project/短视频脚本创作/V1.0/mcn-workshop/cdp-verify-oneway2.mjs
T
maogeigei f77735b206 工作台:目录改名 mcn-workshop + 任务进度可见 + 会话归属配置化
1. 目录 mcn-work-shop -> mcn-workshop(空间分组名取会话 cwd 的目录名,只改字符串无效,必须真改名)
2. 全仓替换 mcn-work-shop -> mcn-workshop:37 文件 121 处;历史日志按沿革句规矩保留当时目录名
3. 任务进度可见:/api/run/status 产出 已运行时长/工具调用/最近动作/停滞判定(读会话日志尾部),前端新增右下角常驻面板,四处任务入口接入
4. 会话归属目录配置化:新增 config.sessionCwd(留空=工作台自身目录),现指向 D://AI技能//mcn-workshop,使任务显示为命名分组而非「未分组任务」
5. 修前端轮询静默缺陷:连续 3 次查询失败即提示服务断开(原逻辑静默空转到 15 分钟超时,用户零感知)
2026-10-08 13:09:06 +08:00

124 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 验证「单向联动」v2:全部用 JS 设置 scrollTop 触发 scroll 事件(避免 mouseWheel SIGTERM)
const list = await fetch('http://localhost:9223/json').then((r) => r.json());
const page = list.find((t) => t.type === 'page' && t.url.includes('#/rewrites'));
if (!page) { console.log('ERR: no #/rewrites page'); process.exit(1); }
const ws = new WebSocket(page.webSocketDebuggerUrl);
let id = 0; const pending = new Map();
const send = (method, params = {}) => new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, method, params })); });
ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending.has(m.id)) { const p = pending.get(m.id); pending.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } };
await new Promise((r) => (ws.onopen = r));
const ev = async (expr) => { const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 300) }; return r.result.value; };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await send('Page.enable'); await send('Runtime.enable');
await send('Page.reload', { ignoreCache: true });
await sleep(3000);
await ev(`document.querySelectorAll('.modal-mask').forEach((m) => m.remove()); 'cleaned'`);
await ev(`(() => { const i = document.querySelector('#rwSearch'); if (i) { i.value = '快递'; i.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); } return !!i; })()`);
await sleep(2000);
const clickRes = await ev(`(() => {
const btns = [...document.querySelectorAll('[data-detail]')];
if (!btns.length) return { err: 'no data-detail' };
btns[0].click();
return { clicked: btns[0].dataset.detail };
})()`);
console.log('打开弹窗:', JSON.stringify(clickRes));
await sleep(2500);
const st = async () => ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
if (!secs.length) return { err: 'no rw-stack' };
const ai = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本');
const sb = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词');
if (!ai || !sb) return { err: 'missing col', heads: secs.map((s) => s.querySelector('.rw-sec-head')?.textContent.trim()) };
const aiBody = ai.querySelector('.rw-sec-body'); const sbBody = sb.querySelector('.rw-sec-body');
const vis = [...sbBody.querySelectorAll('.sb-block')].find((b) => b.getBoundingClientRect().top >= sbBody.getBoundingClientRect().top - 4);
return {
aiScrollTop: Math.round(aiBody.scrollTop), sbScrollTop: Math.round(sbBody.scrollTop),
aiHeads: aiBody.querySelectorAll('strong').length, sbBlocks: sbBody.querySelectorAll('.sb-block').length,
sbVis: vis ? (vis.querySelector('.sb-head')?.textContent.trim() || '').slice(0, 30) : null,
};
})()`);
const init = await st();
console.log('初始:', JSON.stringify(init));
// 翻到有分镜的 AI 脚本版本(rewrite 22)
if ((init.sbBlocks || 0) === 0) {
for (let i = 0; i < 5; i++) {
const hasNext = await ev(`(() => { const b = document.querySelector('[data-vnext]'); if (!b || b.disabled) return false; b.click(); return true; })()`);
if (!hasNext) break;
await sleep(900);
const s = await st();
console.log('翻版本后:', JSON.stringify(s));
if ((s.sbBlocks || 0) > 0) break;
}
}
const ready = await st();
console.log('就绪:', JSON.stringify(ready));
if (!(ready.sbBlocks > 0)) { console.log('ERR: 无分镜版本可验证'); process.exit(0); }
// ===== 链路①:AI脚本列滚动 → 分镜列应对齐(设置scrollTop+补发scroll事件模拟滚动) =====
const setAi = async (delta, tag) => {
const r = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
aiBody.scrollTop += ${delta};
aiBody.dispatchEvent(new Event('scroll')); // CDP下scrollTop赋值不触发事件,补发
return Math.round(aiBody.scrollTop);
})()`);
await sleep(350);
const s = await st();
console.log(`${tag}: aiScrollTop=${r} → sbScrollTop=${s.sbScrollTop} sbVis=${s.sbVis}`);
return s;
};
let cur = await setAi(400, '链路① 第1步滚400');
cur = await setAi(400, '链路① 第2步滚400');
cur = await setAi(400, '链路① 第3步滚400');
const link1 = {
aiMoved: Math.abs(cur.aiScrollTop - ready.aiScrollTop) > 50,
sbMoved: Math.abs(cur.sbScrollTop - ready.sbScrollTop) > 50,
};
console.log('链路①判定:', JSON.stringify(link1), link1.aiMoved && link1.sbMoved ? '✅ AI脚本主导联动生效' : '❌ 联动异常');
// ===== 链路②:分镜列滚动 → AI脚本列独立 =====
const beforeSb = await st();
const r2 = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const sbBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词').querySelector('.rw-sec-body');
sbBody.scrollTop += 600;
sbBody.dispatchEvent(new Event('scroll'));
return Math.round(sbBody.scrollTop);
})()`);
await sleep(350);
const afterSb = await st();
console.log(`链路② 分镜列滚600: sbScrollTop=${r2} → aiScrollTop=${afterSb.aiScrollTop} sbVis=${afterSb.sbVis}`);
const link2 = {
sbMoved: Math.abs(afterSb.sbScrollTop - beforeSb.sbScrollTop) > 50,
aiMovedBack: Math.abs(afterSb.aiScrollTop - beforeSb.aiScrollTop) > 5,
};
console.log('链路②判定:', JSON.stringify(link2), link2.sbMoved && !link2.aiMovedBack ? '✅ 分镜列独立滚动,AI脚本列未动' : (link2.sbMoved ? '❌ AI脚本列被反向联动' : '❌ 分镜列未滚动'));
// ===== 链路①b:AI脚本列跳底部 → 分镜列跟随最后场 =====
const jump = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
aiBody.scrollTop = aiBody.scrollHeight;
aiBody.dispatchEvent(new Event('scroll'));
return { set: Math.round(aiBody.scrollTop) };
})()`);
await sleep(400);
const afterJump = await st();
const detail = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
const heads = [...aiBody.querySelectorAll('strong, h1, h2, h3, h4')].filter((el) => /场\\s*\\d/.test(el.textContent));
const sbBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词').querySelector('.rw-sec-body');
const vis = [...sbBody.querySelectorAll('.sb-block')].find((b) => b.getBoundingClientRect().top >= sbBody.getBoundingClientRect().top - 4);
return { lastAiHead: heads.length ? heads[heads.length - 1].textContent.trim().slice(0, 24) : null, sbVis: vis ? (vis.querySelector('.sb-head')?.textContent.trim() || '').slice(0, 30) : null };
})()`);
console.log('链路①b 跳底部:', JSON.stringify({ jump, sbScrollTop: afterJump.sbScrollTop, detail }));
try { ws.close(); } catch (e) {}
process.exit(0);