Files
mcn-short-video/project/短视频脚本创作/V1.0/mcn-workshop/cdp-verify-r29.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

58 lines
3.9 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.
// R29 冒烟:账号详情「视频列表」tab——「时长」前新增「标签」列(固定 40px,超长省略,悬停 title 显示完整)
const list = await fetch('http://localhost:9225/json').then((r) => r.json());
const page = list.find((t) => t.type === 'page');
if (!page) { console.log('ERR: no 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, 200) }; return r.result.value; };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await send('Page.enable'); await send('Runtime.enable');
const waitFor = async (expr, ms = 15000) => { const t0 = Date.now(); while (Date.now() - t0 < ms) { if (await ev(expr)) return true; await sleep(300); } return false; };
const fail = (m) => { console.log('FAIL@' + m); process.exit(1); };
const goto = async (hash) => { await ev(`location.hash = ${JSON.stringify(hash)}`); await sleep(800); };
await send('Page.navigate', { url: 'http://localhost:8900/?v=' + Date.now() });
await new Promise((r) => setTimeout(r, 2000)); // 等所有资源加载(含带版本号的 style.css/data.js)
if (!(await waitFor(`!!document.querySelector('#view')`))) fail('app boot');
await ev(`document.querySelectorAll('.modal-mask').forEach((m) => m.remove())`); // 清 root 设置弹窗
/* ============ 账号 1629 视频列表 tab(persona) ============ */
await goto('#/account/1629/persona');
if (!(await waitFor(`document.querySelector('#view .table tbody tr')`))) fail('video table rows');
const out = await ev(`(() => {
const ths = [...document.querySelectorAll('#view .table thead th')].map((t) => t.textContent.trim());
const thTag = document.querySelector('#view .table thead th.th-tag');
const firstTr = document.querySelector('#view .table tbody tr');
const cells = [...firstTr.children];
const tagTd = firstTr.querySelector('td.td-tag');
return {
ths, // 完整表头文本序列
thTagIdx: ths.indexOf('标签'),
likeIdx: ths.indexOf('点赞'),
durIdx: ths.indexOf('时长'),
thTagW: thTag ? Math.round(thTag.getBoundingClientRect().width) : null,
tagBeforeLike: ths.indexOf('标签') === ths.indexOf('点赞') - 1,
// 数据行
rowCells: cells.map((c) => ({ cls: c.className, txt: (c.textContent || '').trim().slice(0, 24) })),
tagTdText: tagTd ? (tagTd.textContent || '').trim() : null,
tagTdTitle: tagTd ? (tagTd.getAttribute('title') || '') : null,
tagTdClips: tagTd ? tagTd.scrollWidth > tagTd.clientWidth : null, // 是否发生省略截断
tagTdW: tagTd ? Math.round(tagTd.getBoundingClientRect().width) : null,
hasNumClass: tagTd ? tagTd.classList.contains('num') : null,
};
})()`);
console.log(JSON.stringify(out, null, 1));
const ok = out && !out.__err
&& out.ths.length === 9 && out.thTagIdx === 2 && out.likeIdx === 3 && out.durIdx === 7 && out.tagBeforeLike === true
&& out.thTagW === 60
&& out.rowCells.length === 9 && out.rowCells[2].cls.includes('td-tag')
&& !!out.tagTdText && out.tagTdText.length > 0
&& !!out.tagTdTitle && out.tagTdTitle.includes('#') && out.tagTdTitle.length >= out.tagTdText.length
&& out.tagTdClips === true && out.tagTdW === 60 && out.hasNumClass === false;
console.log(ok ? '\n=== PASS ===' : '\n=== CHECK ===');
ws.close();
process.exit(ok ? 0 : 1);