Hermes 驱动 CodeArts Agent 发布华为云博客完整流程
Hermes 驱动 CodeArts Agent 发布华为云博客完整流程
本文完整记录 Hermes Agent 通过 Playwright + CodeArts Agent 驱动华为云社区博客发布的全部流程、架构、16个踩坑及修复方案。
一、总体架构
Hermes Agent
│
├── Playwright (浏览器自动化)
│ └── Edge 浏览器
│ ├── 打开 bbs.huaweicloud.cn/blogs/article
│ ├── 用户登录(手动)
│ ├── 填充标题 / 内容 / 摘要 / 标签
│ └── 调用 window.saveBlog(submitData)
│
└── CodeArts Agent CLI (码道命令行)
└── codearts-agent chat --mode ask|agent
核心原则: 单次浏览器会话完成所有操作,不保存 cookies 不重启。
二、环境准备
2.1 项目目录
C:\Users\33808\IDEProjects\hermestest1\
├── package.json
├── publish-once.mjs # 推荐发布脚本
├── blog-codearts-api-publish.md # 博客内容(Markdown源文件)
└── *.mjs # 各种版本的历史脚本
2.2 依赖
npm install playwright
2.3 CodeArts Agent CLI
codearts-agent chat --mode ask "只学习不执行"
codearts-agent chat --mode agent "自主执行任务"
codearts-agent chat --mode edit "编辑代码"
注意:
codearts-agent chat退出码 0 = 消息已送达,但 stdout 为空。输出在 IDE 聊天界面而非终端。
三、发布流程(12步详解)
第1步:清理残留进程
taskkill -f -im msedge.exe 2>/dev/null
taskkill -f -im msedgewebview2.exe 2>/dev/null
反复调试会产生大量 Edge 进程(曾达 28 个),用户会被多个窗口混淆。
第2步:启动浏览器
const br = await chromium.launch({headless:false, channel:'msedge'});
必须 headless:false(用户需手动登录),使用系统 Edge。
第3步:加载页面
await pg.goto(BLOG_URL, {waitUntil:'load', timeout:30000});
必须用 waitUntil:'load',不能用 networkidle。华为云登录页有持续网络活动,networkidle 永远无法完成。
第4步:等待登录(双保险)
for(let i=0;i<300;i++){
const u=pg.url();
const t=await pg.evaluate(()=>!!document.querySelector('#blogTitle'));
if(!u.includes('login')&&!u.includes('auth')&&t) break;
if(i%30===29) await pg.goto(BLOG_URL,{waitUntil:'load',timeout:15000});
await sleep(2000);
}
同时检查 URL 和 #blogTitle 元素存在。每 60 秒刷新一次页面。
第5步:填充标题
const ti = await pg.$('#blogTitle');
if(ti) { await ti.click(); await ti.fill(''); await ti.fill(TITLE); }
第6步:填充内容(关键!)
不要用 tinyMCE.activeEditor.setContent()!华为云 custom eco-tinymce.js 插件在 setContent() 内部抛出:
TypeError: Cannot read properties of undefined (reading 'map')
正确做法: 通过 iframe body 直接写入:
await pg.evaluate(h => {
for(const f of document.querySelectorAll('iframe')) try {
const d = f.contentDocument || f.contentWindow.document;
const b = d.querySelector('body');
if(b && (b.getAttribute('contenteditable')==='true'
|| b.classList.contains('mce-content-body'))) {
b.innerHTML = h;
if(typeof tinymce!=='undefined' && tinymce.editors?.length) {
tinymce.editors[0].fire('change');
tinymce.editors[0].save();
}
return;
}
} catch(e) {}
}, html);
第7步:Markdown → HTML
const lines = md.replace(/\r\n/g, '\n').replace(/\r/g, '').split('\n');
必须优先处理 Windows \r\n 换行符,否则标题正则 $ 无法匹配。支持:标题、代码块、表格、列表、引用、分隔线、加粗、行内代码、链接。
第8步:填充摘要
const intro = raw.replace(/```[\s\S]*?```/g,'')
.replace(/[#*>`|\[\]]/g,'').replace(/\n+/g,' ').trim().substring(0,200);
第9步:填充标签(关键的坑!)
无效标签会导致 HD.92320035 product do not exist。已验证有效的标签:AI Agent、华为云、CodeArts、大模型。博客自动化、华为云码道 不是有效标签。
const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
setter.call(inp, tagText);
inp.dispatchEvent(new Event('input', {bubbles:true}));
inp.dispatchEvent(new KeyboardEvent('keydown', {key:'Enter', bubbles:true}));
inp.dispatchEvent(new KeyboardEvent('keyup', {key:'Enter', bubbles:true}));
第10步:等待 saveBlog 就绪
let sb=false;
for(let i=0;i<30;i++){
sb = await pg.evaluate(()=>typeof window.saveBlog==='function');
if(sb) break;
await sleep(2000);
}
saveBlog 是华为云博客编辑器的原生发布函数,异步加载,不能立即可用。
第11步:调用 saveBlog 发布
await pg.evaluate(() => {
window.saveBlog({
titleData: title,
contentData: contentHtml,
introData: intro,
tagNameData: tags,
isLock: 0,
blogResource: 1, // 1=原创
isReship: 1,
disclaimerFlag: 1,
blogType: 0,
isOriginal: 1,
source: 1,
});
});
必须传参数! 直接 saveBlog() 报 Cannot read properties of undefined (reading 'isLock')。
第12步:检测结果
for(let i=0;i<60;i++){
if(pg.url().includes('processcomplete')) {
console.log('🎉 发布成功!');
break;
}
await sleep(1000);
}
成功标志:页面重定向到 /processcomplete?type=blog。
四、API 参考
POST https://devdata.huaweicloud.cn/rest/developer/fwdu/
rest/developer/user/hdblogservice/v1/blog/blogging
Headers:
Content-Type: application/json
csrf: <从 /api/get-ainfo 或 hd-csrf cookie 获取>
Body:
{
"titleData": "标题",
"contentData": "<p>HTML 内容</p>",
"introData": "摘要(最多200字)",
"tagNameData": "标签1,标签2",
"isLock": 0,
"blogResource": 1,
"isReship": 1,
"disclaimerFlag": 1,
"blogType": 0,
"isOriginal": 1,
"source": 1
}
发布结果码
| 码值 | 含义 |
|---|---|
| HD.92300000 | ✅ 成功 |
| HD.92320035 | ❌ 标签无效(最常见的坑) |
| HD.92311001 | ❌ blogResource 不在 1-3 |
| HD.92320034 | ❌ 发布太频繁 |
| HD.92320038 | ❌ 含敏感词,需二次确认 |
五、CodeArts Agent 集成方式
发送学习指令
codearts-agent chat --mode ask \
--add-file blog.md \
"请学习华为云博客发布的经验..."
发送执行指令
codearts-agent chat --mode agent \
"请运行 node publish-once.mjs 发布博客..."
注意事项
- Agent 的 stdout 始终为空(输出在 IDE 界面),不能通过终端读取执行结果
--reuse-window复用已在运行的 CodeArts 实例- Agent 模式下可用工具:读文件、搜索、git、终端命令
六、踩坑全记录(16个)
| # | 问题 | 现象 | 解决 |
|---|---|---|---|
| 1 | 发表按钮禁用 | 按钮灰色不可点 | 调 saveBlog() 绕过 |
| 2 | TinyMCE 抛错 | setContent() 报 map 错误 | iframe body 直接写入 |
| 3 | saveBlog 不可用 | 调用时不存在 | 轮询等待 |
| 4 | 弹窗删脚本 | saveBlog 丢失 | display:none 而非 remove |
| 5 | API 404 | 请求返回 404 | 用 devdata.huaweicloud.cn |
| 6 | blogResource 越界 | HD.92311001 | 限制在 1/2/3 |
| 7 | 标签无效 | HD.92320035 | 用已验证标签 |
| 8 | 字段名错 | API 不识别 | 用 titleData/contentData |
| 9 | CSRF header 错 | 403 | header 名用 csrf 非 hd-csrf |
| 10 | cookies 过期 | 登录超时 | 单次会话完成 |
| 11 | Markdown 乱 | 标题不渲染 | 先转 \n |
| 12 | Edge 残留 | 多窗口混淆 | 先 taskkill |
| 13 | saveBlog 无参 | isLock 错误 | 必须传参数 |
| 14 | 推广弹窗 | 阻断重定向 | 发布前隐藏 |
| 15 | product 错误 | HD.92320035 | 标签须有效 |
| 16 | 草稿恢复失败 | saveId 未设置 | 手动填充内容 |
七、经验总结
- 能调 API 就别点按钮:前端 UI 自动化有上限,后端 API 直调更可靠。
- 用页面原生函数:
window.saveBlog()比手动fetch()更可靠。 - 标签是隐形的坑:
HD.92320035的错误信息 "product do not exist" 非常误导人——实际是标签与系统 product-item 不匹配。 - 单次会话:不保存 cookies,不跨会话复用,避免过期问题。
- 先清理后启动:
taskkill清理残留 Edge 进程,避免用户困惑。 - 日志驱动调试:
pg.on('response')监听bloggingAPI 响应是最快定位问题的方式。
*本文由 Hermes Agent 自动生成并通过 CodeArts Agent 发布到华为云社区。* *发布时间:2026年7月8日*
- 点赞
- 收藏
- 关注作者
评论(0)