publishport-opencli 1.8.5-pp.31 → 1.8.5-pp.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli-manifest.json +28 -4
- package/clis/imooc/article.js +59 -1
- package/clis/xiaohongshu/publish.js +54 -28
- package/dist/src/cli.js +84 -27
- package/dist/src/daemon.js +1 -1
- package/dist/src/doctor.d.ts +0 -1
- package/dist/src/doctor.js +10 -14
- package/dist/src/main.js +7 -7
- package/package.json +1 -1
- package/skills/opencli-browser/SKILL.md +24 -0
- package/dist/src/update-check.d.ts +0 -66
- package/dist/src/update-check.js +0 -5
- package/dist/src/update-check.test.d.ts +0 -1
package/cli-manifest.json
CHANGED
|
@@ -18617,7 +18617,7 @@
|
|
|
18617
18617
|
{
|
|
18618
18618
|
"site": "imooc",
|
|
18619
18619
|
"name": "article",
|
|
18620
|
-
"description": "发布慕课手记文章(Markdown
|
|
18620
|
+
"description": "发布慕课手记文章(Markdown)。默认走「发表」(半自动:AI 填全部内容/栏目/标签/封面,最后的点选文字验证码需用户在弹出的浏览器窗口点一下);加 --draft 则只存草稿(无验证码)。外链图自动转存到慕课图床。",
|
|
18621
18621
|
"access": "write",
|
|
18622
18622
|
"domain": "www.imooc.com",
|
|
18623
18623
|
"strategy": "cookie",
|
|
@@ -18628,14 +18628,14 @@
|
|
|
18628
18628
|
"type": "str",
|
|
18629
18629
|
"required": true,
|
|
18630
18630
|
"positional": true,
|
|
18631
|
-
"help": "
|
|
18631
|
+
"help": "文章标题(≤80字)"
|
|
18632
18632
|
},
|
|
18633
18633
|
{
|
|
18634
18634
|
"name": "text",
|
|
18635
18635
|
"type": "str",
|
|
18636
18636
|
"required": false,
|
|
18637
18637
|
"positional": true,
|
|
18638
|
-
"help": "文章正文(Markdown
|
|
18638
|
+
"help": "文章正文(Markdown,默认;发表要求正文≥200字)"
|
|
18639
18639
|
},
|
|
18640
18640
|
{
|
|
18641
18641
|
"name": "file",
|
|
@@ -18643,6 +18643,30 @@
|
|
|
18643
18643
|
"required": false,
|
|
18644
18644
|
"help": "正文文件路径(UTF-8,Markdown)"
|
|
18645
18645
|
},
|
|
18646
|
+
{
|
|
18647
|
+
"name": "column",
|
|
18648
|
+
"type": "str",
|
|
18649
|
+
"required": false,
|
|
18650
|
+
"help": "栏目(发表必填):名称或 data-columnid,如「后端开发」/120、「前端开发」/119、「人工智能」/117"
|
|
18651
|
+
},
|
|
18652
|
+
{
|
|
18653
|
+
"name": "tags",
|
|
18654
|
+
"type": "str",
|
|
18655
|
+
"required": false,
|
|
18656
|
+
"help": "标签(发表必填,逗号分隔,至少 1 个):名称或 data-tag,如 MySQL,Java 或 11,3"
|
|
18657
|
+
},
|
|
18658
|
+
{
|
|
18659
|
+
"name": "cover",
|
|
18660
|
+
"type": "str",
|
|
18661
|
+
"required": false,
|
|
18662
|
+
"help": "封面图 URL 或本机路径(发表可选,缺省用慕课随机封面)"
|
|
18663
|
+
},
|
|
18664
|
+
{
|
|
18665
|
+
"name": "original",
|
|
18666
|
+
"type": "str",
|
|
18667
|
+
"required": false,
|
|
18668
|
+
"help": "文章类型(发表可选,默认「原创」):原创 / 首发 / 转载"
|
|
18669
|
+
},
|
|
18646
18670
|
{
|
|
18647
18671
|
"name": "html",
|
|
18648
18672
|
"type": "boolean",
|
|
@@ -18653,7 +18677,7 @@
|
|
|
18653
18677
|
"name": "draft",
|
|
18654
18678
|
"type": "boolean",
|
|
18655
18679
|
"required": false,
|
|
18656
|
-
"help": "
|
|
18680
|
+
"help": "只存草稿(不发表,无需验证码)"
|
|
18657
18681
|
},
|
|
18658
18682
|
{
|
|
18659
18683
|
"name": "execute",
|
package/clis/imooc/article.js
CHANGED
|
@@ -1 +1,59 @@
|
|
|
1
|
-
import{CliError as
|
|
1
|
+
import{CliError as z,CommandExecutionError as T}from"@jackwener/opencli/errors";import{cli as U,Strategy as N}from"@jackwener/opencli/registry";import{publishArticle as P,gotoWritePage as h}from"../_shared/article/publish.js";import{normalizeContent as I}from"../_shared/article/format.js";import{inlineLocalImages as R,isLocalImagePath as x,localImageToDataUri as C}from"../_shared/article/images.js";import{readFile as b,stat as E}from"node:fs/promises";export const imoocProfile={home:"https://www.imooc.com/article",outputFormat:"markdown",checkAuth:async(Q)=>{let G=await(await fetch("https://www.imooc.com/u/card",{credentials:"include"})).text();G=G.replace(/^jsonpcallback\(/,"").replace(/\}\)$/,"}");let V;try{V=JSON.parse(G)}catch(Y){return{isAuthenticated:!1,error:"JSONP 解析失败:"+String(Y&&Y.message||Y)}}if(!V||V.result!==0)return{isAuthenticated:!1,error:V&&V.msg||"未登录"};return{isAuthenticated:!0,userId:String(V.data.uid),username:V.data.nickname,avatar:V.data.img}},image:{uploadFn:async(Q,X)=>{const G=await fetch(Q,{credentials:"omit"});if(!G.ok)throw Error("图片下载失败:HTTP "+G.status);const V=await G.blob(),Y=Date.now()+".jpg",Z=new File([V],Y,{type:V.type||"image/jpeg"}),$=new FormData;$.append("photo",Z,Y);$.append("type",Z.type);$.append("id","WU_FILE_0");$.append("name",Y);$.append("lastModifiedDate",new Date().toString());$.append("size",String(Z.size));const H=await(await fetch("https://www.imooc.com/article/ajaxuploadimg",{method:"POST",credentials:"include",body:$})).json();if(!H||H.result!==0)throw Error(H&&H.msg||"图片上传失败");let K=H.data.imgpath;if(K&&K.indexOf("//")===0)K="https:"+K;return{url:K}},skip:["img.imooc.com","imooc.com"]},publish:async(Q,X)=>{const G=await fetch("https://www.imooc.com/article/savedraft",{method:"POST",credentials:"include",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({editor:"0",draft_id:"0",title:Q.title,content:Q.content})});if(!G.ok){const Z=await G.text();return{ok:!1,stage:"savedraft",status:G.status,message:Z.slice(0,300)}}const V=await G.json();if(!V||!V.data){const Z=JSON.stringify(V).slice(0,300);return{ok:!1,stage:"savedraft",status:G.status,message:"保存草稿失败:"+Z}}const Y=String(V.data);return{ok:!0,id:Y,url:"https://www.imooc.com/article/draft/id/"+Y,draft:!0}}};function k(Q){if(!Q.execute)throw new z("INVALID_INPUT","此命令需要 --execute 才会真正写入。注意:本报错仅表示缺少 --execute,并未校验其余参数,不能当 dry-run 预览用")}async function f(Q){const X=typeof Q.text==="string"?Q.text:void 0,G=typeof Q.file==="string"?Q.file:void 0;if(X&&G)throw new z("INVALID_INPUT","<text> 和 --file 不能同时使用");let V=X??"";if(G){let Y;try{Y=await E(G)}catch{throw new z("INVALID_INPUT","文件不存在:"+G)}if(!Y.isFile())throw new z("INVALID_INPUT","路径必须是可读文本文件:"+G);let Z;try{Z=await b(G)}catch{throw new z("INVALID_INPUT","文件读取失败:"+G)}try{V=new TextDecoder("utf-8",{fatal:!0}).decode(Z)}catch{throw new z("INVALID_INPUT","文件不是有效 UTF-8:"+G)}}if(!V.trim())throw new z("INVALID_INPUT","正文不能为空");return V}function _(Q,X,G,V,Y={}){return[{status:"success",outcome:V,message:Q,target_type:X,target:G,...Y}]}const w="https://www.imooc.com/article/publish",d="https?://www\\.imooc\\.com/article/(\\d+)",n=imoocProfile.image.uploadFn.toString();function c(Q,X){return`(async () => {
|
|
2
|
+
const I = `+JSON.stringify(Q)+`;
|
|
3
|
+
const __upload = (`+X+`);
|
|
4
|
+
const PP = {};
|
|
5
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
6
|
+
var mk = document.querySelector('.js-change-editor[data-type="0"]');
|
|
7
|
+
if (mk && !mk.classList.contains("active")) { mk.click(); await sleep(500); }
|
|
8
|
+
for (var i = 0; i < 60 && !(window.app && typeof window.app.setEditorContent === "function"); i++) { await sleep(200); }
|
|
9
|
+
`+`if (!(window.app && typeof window.app.setEditorContent === "function")) return { ok: false, stage: "editor", message: "Markdown 编辑器未就绪" };
|
|
10
|
+
`+`var skip = I.skip || [];
|
|
11
|
+
function keep(u) { if (!u) return true; if (u.indexOf("data:") === 0) return false; if (/(^|\\/\\/)([^\\/]*\\.)?imooc\\.com/.test(u)) return true; return skip.some(function(s){ return u.indexOf(s) >= 0; }); }
|
|
12
|
+
var md = I.md, uploaded = [], failed = [];
|
|
13
|
+
var urls = new Set(), m;
|
|
14
|
+
var re1 = /!\\[[^\\]]*\\]\\(\\s*<?([^)\\s>]+)>?[^)]*\\)/g;
|
|
15
|
+
while ((m = re1.exec(md))) urls.add(m[1]);
|
|
16
|
+
var re2 = /<img[^>]+src=["']([^"']+)["']/gi;
|
|
17
|
+
while ((m = re2.exec(md))) urls.add(m[1]);
|
|
18
|
+
`+`for (var u of urls) { if (keep(u)) continue; try { var r = await __upload(u, PP); if (r && r.url) { md = md.split(u).join(r.url); uploaded.push({ src: u, url: r.url }); } else { failed.push({ src: u, error: "转存无返回 URL" }); } } catch (e) { failed.push({ src: u, error: String(e && e.message || e) }); } }
|
|
19
|
+
`+`var cover = "";
|
|
20
|
+
`+`if (I.cover) { if (keep(I.cover)) { cover = I.cover; } else { try { var rc = await __upload(I.cover, PP); if (rc && rc.url) cover = rc.url; else return { ok: false, stage: "cover", message: "封面图转存无返回 URL", uploaded: uploaded, failed: failed }; } catch (e) { return { ok: false, stage: "cover", message: "封面图转存失败:" + String(e && e.message || e), uploaded: uploaded, failed: failed }; } } }
|
|
21
|
+
`+`document.getElementById("article_title").value = I.title;
|
|
22
|
+
window.app.setEditorContent(md);
|
|
23
|
+
await sleep(400);
|
|
24
|
+
var plen = (window.parsinCtx && window.parsinCtx.text) ? window.parsinCtx.text.length : 0;
|
|
25
|
+
return { ok: true, uploaded: uploaded, failed: failed, cover: cover, parsinLen: plen };
|
|
26
|
+
})()`}function p(Q){return`(async () => {
|
|
27
|
+
const I = `+JSON.stringify(Q)+`;
|
|
28
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
29
|
+
var pub = document.querySelector(".js-article-publish");
|
|
30
|
+
`+`if (!pub) return { ok: false, message: "找不到「发表」按钮" };
|
|
31
|
+
`+`pub.click();
|
|
32
|
+
await sleep(600);
|
|
33
|
+
var modal = document.getElementById("article_layer");
|
|
34
|
+
if (!modal || getComputedStyle(modal).display === "none") {
|
|
35
|
+
var tip = document.querySelector(".layer_prompt, .prompt-msg, .imooc-prompt, .layer_content");
|
|
36
|
+
`+` return { ok: false, message: (tip && tip.textContent.trim()) || "发表校验未通过(标题需≤80字、正文需≥200字)" };
|
|
37
|
+
`+`}
|
|
38
|
+
if (I.cover) { var pic = document.getElementById("article_pic"); pic.style.backgroundImage = "url(" + I.cover + ")"; pic.setAttribute("data-pic", I.cover); var ic = pic.querySelector("i"); if (ic) ic.style.display = "none"; }
|
|
39
|
+
else { var rnd = document.getElementById("pic_random_btn"); if (rnd) rnd.click(); }
|
|
40
|
+
var cols = Array.from(document.querySelectorAll(".js-column-box li[data-columnid]"));
|
|
41
|
+
var col = cols.find(function(li){ return li.getAttribute("data-columnid") === String(I.column) || li.textContent.trim() === String(I.column); });
|
|
42
|
+
`+`if (!col) return { ok: false, message: "未找到栏目「" + I.column + "」,可选:" + cols.map(function(l){ return l.textContent.trim(); }).join("、") };
|
|
43
|
+
`+`col.click();
|
|
44
|
+
var origs = Array.from(document.querySelectorAll(".js-original-box li"));
|
|
45
|
+
var ot;
|
|
46
|
+
`+`if (I.original === "首发") ot = document.querySelector(".js-original-box li#publish");
|
|
47
|
+
`+`else if (I.original === "转载" || I.original === "其他") ot = origs.find(function(l){ return l.getAttribute("data-original") === "0"; });
|
|
48
|
+
`+`else ot = origs.find(function(l){ return l.getAttribute("data-original") === "1" && l.id !== "publish"; });
|
|
49
|
+
if (ot) ot.click();
|
|
50
|
+
var all = Array.from(document.querySelectorAll(".js-tag-list .tag-item"));
|
|
51
|
+
var chosen = [], notFound = [];
|
|
52
|
+
for (var k of I.tags) { var t = all.find(function(x){ return x.getAttribute("data-tag") === String(k) || x.textContent.trim().toLowerCase() === String(k).toLowerCase(); }); if (t) { t.click(); chosen.push(t.textContent.trim()); } else { notFound.push(k); } }
|
|
53
|
+
var chosenCount = document.querySelectorAll(".js-chosen-tags .tag-item").length;
|
|
54
|
+
`+`if (chosenCount === 0) return { ok: false, message: "标签一个都没匹配到:" + I.tags.join(",") + "。用 data-tag 或标签名,如 MySQL / Java / Python / Go / 前端工具" };
|
|
55
|
+
`+`try { window.addEventListener("beforeunload", function(e){ e.stopImmediatePropagation(); if (e.preventDefault) e.preventDefault(); try { delete e["returnValue"]; } catch (x) {} }, true); window.onbeforeunload = null; } catch (x) {}
|
|
56
|
+
document.getElementById("save_article").click();
|
|
57
|
+
await sleep(400);
|
|
58
|
+
return { ok: true, chosen: chosen, notFound: notFound, column: col.textContent.trim() };
|
|
59
|
+
})()`}async function m(Q,X){const{title:G,body:V,format:Y,column:Z,tags:$,original:B,cover:H}=X,K=I(V,{format:Y}),F=await R(K.markdown);let W=F.content;const A=F.missing||[];let J="";if(H){const M=H.trim();if(/["<>\s]/.test(M))throw new T("封面图路径/URL 含非法字符(引号/尖括号/空白):"+M.slice(0,120));if(x(M))try{J=(await C(M)).dataUri}catch(O){throw new T("封面图读取失败:"+M+"("+String(O&&O.message||O)+")")}else J=M}await h(Q,w,imoocProfile.originRe);const q=await Q.evaluate(c({title:G,md:W,cover:J,skip:imoocProfile.image.skip||[]},n));if(!q||q.ok===!1)throw new T("["+(q?.stage||"setup")+"] "+(q?.message||"内容准备失败"));if(!q.parsinLen||q.parsinLen<200)throw new T("正文进入编辑器后不足 200 字(慕课发表要求正文≥200字),当前约 "+(q.parsinLen||0)+" 字。");const j=await Q.evaluate(p({column:String(Z),tags:$,original:B,cover:q.cover}));if(!j||j.ok===!1)throw new T(j?.message||"填写发表信息失败");const v=new RegExp(d);let L="";for(let M=0;M<75;M++){await Q.wait({time:2});let O="";try{O=await Q.getCurrentUrl()}catch{}const y=O&&O.match(v);if(y){L=y[1];break}}const D=q.uploaded||[],S=(q.failed||[]).concat(A);if(!L)throw new T("内容与发表信息已全部填好并进入发表流程,但未检测到发表成功跳转"+"(约 150 秒内未完成点选文字验证码)。请在弹出的慕课浏览器窗口按提示点选文字完成验证,"+"页面会自动跳到文章页即代表发表成功;或稍后重试本命令。");return{id:L,url:"https://www.imooc.com/article/"+L,images:{uploaded:D,failed:S},column:j.column,tags:j.chosen||[],notFoundTags:j.notFound||[]}}U({site:"imooc",name:"article",access:"write",description:"发布慕课手记文章(Markdown)。默认走「发表」(半自动:AI 填全部内容/栏目/标签/封面,"+"最后的点选文字验证码需用户在弹出的浏览器窗口点一下);加 --draft 则只存草稿(无验证码)。外链图自动转存到慕课图床。",domain:"www.imooc.com",strategy:N.COOKIE,browser:!0,args:[{name:"title",positional:!0,required:!0,help:"文章标题(≤80字)"},{name:"text",positional:!0,help:"文章正文(Markdown,默认;发表要求正文≥200字)"},{name:"file",help:"正文文件路径(UTF-8,Markdown)"},{name:"column",help:"栏目(发表必填):名称或 data-columnid,如「后端开发」/120、「前端开发」/119、「人工智能」/117"},{name:"tags",help:"标签(发表必填,逗号分隔,至少 1 个):名称或 data-tag,如 MySQL,Java 或 11,3"},{name:"cover",help:"封面图 URL 或本机路径(发表可选,缺省用慕课随机封面)"},{name:"original",help:"文章类型(发表可选,默认「原创」):原创 / 首发 / 转载"},{name:"html",type:"boolean",help:"将正文视为原始 HTML 而非 Markdown"},{name:"draft",type:"boolean",help:"只存草稿(不发表,无需验证码)"},{name:"execute",type:"boolean",help:"真正执行写入,不加此标志时拒绝写操作"}],columns:["status","outcome","message","target_type","target","created_target","created_url"],func:async(Q,X)=>{if(!Q)throw new T("慕课手记发布需要浏览器会话");k(X);const G=String(X.title??"").trim();if(!G)throw new z("INVALID_INPUT","文章标题不能为空");if(G.length>80)throw new z("INVALID_INPUT","标题不能超过 80 字,当前 "+G.length+" 字");const V=await f(X);if(X.draft){const W=await P(Q,{title:G,body:V,format:X.html?"html":"markdown",draftOnly:!0,profile:imoocProfile}),A=W.images.uploaded.length|0,J=W.images.failed.length|0;let q="已保存慕课手记草稿";if(A||J)q+=`・图片:${A} 张已转存${J?`,${J} 张失败`:""}`;return _(q,"article","","draft",{created_target:"article:"+W.id,created_url:W.url})}const Y=String(X.column??"").trim();if(!Y)throw new z("INVALID_INPUT","发表必填 --column(栏目)。可选:前端开发/后端开发/人工智能/对话ChatGPT/移动开发/云计算大数据/产品设计/工具资源/职场生活/经验分享/其它。"+"只想存草稿加 --draft。");const Z=String(X.tags??"").split(",").map((W)=>W.trim()).filter(Boolean);if(Z.length===0)throw new z("INVALID_INPUT","发表必填 --tags(标签,逗号分隔,至少 1 个),如 --tags MySQL,Java。只想存草稿加 --draft。");const $=String(X.original??"原创").trim()||"原创";if(!["原创","首发","转载","其他"].includes($))throw new z("INVALID_INPUT","--original 只能是 原创 / 首发 / 转载,当前:"+$);const B=await m(Q,{title:G,body:V,format:X.html?"html":"markdown",column:Y,tags:Z,original:$,cover:typeof X.cover==="string"?X.cover:""}),H=B.images.uploaded.length|0,K=B.images.failed.length|0;let F=`已发表慕课手记・栏目「${B.column}」・标签 ${B.tags.join("/")}`;if(H||K)F+=`・图片:${H} 张已转存${K?`,${K} 张失败`:""}`;if(B.notFoundTags.length)F+=`・未匹配标签:${B.notFoundTags.join(",")}`;return _(F,"article","","published",{created_target:"article:"+B.id,created_url:B.url})}});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import*as y from"node:fs";import*as
|
|
1
|
+
import*as y from"node:fs";import*as B from"node:path";import{CommandExecutionError as H,ArgumentError as X}from"@jackwener/opencli/errors";import{cli as Qz,Strategy as Zz}from"@jackwener/opencli/registry";import{assertLiteralContent as $z}from"../_shared/content-guard.js";const qz="https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image",C=9,T=20,x=3000,u="|||",D="基础",d=[["基础","默认兜底,万能"],["边框","金句/要点卡"],["备忘","提醒/随手记"],["清新","日常/清单贴士"],["涂写","随笔/碎碎念"],["便签","笔记/提醒"],["光影","情绪/文艺"],["涂鸦","趣味/童话"],["简约","干货/观点"],["手写","日记/情感"],["插图","生活方式/轻松话题"],["美漫","活力/趣味/故事感"],["弥散","弥散光氛围"],["柔和","柔和/温柔金句"],["印刷","印刷海报/排版"],["科技","科技/产品"],["贺卡","节日祝福"],["札记","艺术/水彩氛围"],["书摘","书摘/引用"],["手帐","手帐拼贴"],["几何","醒目/有力主张"]],_z=d.map(([z])=>z),L="文字配图",c="再写一张",E="生成图片",n="下一步",M=".tiptap.ProseMirror",jz=["_onPublish","onPublish","_onSubmit","_handlePublish"],Vz=["_onSave","_onSaveDraft","_onDraft"],w=['[contenteditable="true"][placeholder*="标题"]','[contenteditable="true"][placeholder*="赞"]','input[placeholder*="标题"]','input[placeholder*="title" i]','[contenteditable="true"][class*="title"]','input[maxlength="20"]','input[class*="title"]',".title-input input",".note-title input","input[maxlength]"],m=['[contenteditable="true"][class*="content"]','[contenteditable="true"][class*="editor"]','[contenteditable="true"][placeholder*="描述"]','[contenteditable="true"][placeholder*="正文"]','[contenteditable="true"][placeholder*="内容"]','.note-content [contenteditable="true"]','.editor-content [contenteditable="true"]','[contenteditable="true"]:not([placeholder*="标题"]):not([placeholder*="赞"]):not([placeholder*="title" i])'],i={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp"};function Y(z){if(z&&typeof z==="object"&&typeof z.session==="string"&&Object.prototype.hasOwnProperty.call(z,"data"))return z.data;return z}function Hz(z){return z.map((J)=>{const K=B.resolve(J);if(!y.existsSync(K))throw new X(`Image file not found: ${K}`);const Z=B.extname(K).toLowerCase();if(!i[Z])throw new X(`Unsupported image format "${Z}". Supported: jpg, png, gif, webp`);return K})}const l='input[type="file"][accept*="image"],input[type="file"][accept*=".jpg"],input[type="file"][accept*=".jpeg"],input[type="file"][accept*=".png"],input[type="file"][accept*=".gif"],input[type="file"][accept*=".webp"]';async function Gz(z,J=15000){const K=500,Z=Math.max(1,Math.ceil(J/K));for(let Q=0;Q<Z;Q++){if(await z.evaluate(`
|
|
2
2
|
(() => {
|
|
3
|
-
const sels = ${JSON.stringify(
|
|
3
|
+
const sels = ${JSON.stringify(l)};
|
|
4
4
|
return !!document.querySelector(sels);
|
|
5
5
|
})()
|
|
6
|
-
`))return!0;if(Q<Z-1)await z.wait({time:K/1000})}return!1}async function
|
|
6
|
+
`))return!0;if(Q<Z-1)await z.wait({time:K/1000})}return!1}async function o(z,J){if(!await Gz(z))return{ok:!1,count:0,error:"No file input found on page (waited 15s; publish surface did not finish rendering)"};if(z.setFileInput)try{await z.setFileInput(J,l);return{ok:!0,count:J.length}}catch($){const j=$ instanceof Error?$.message:String($);if(j.includes("Unknown action")||j.includes("not supported")||j.includes("Not allowed"));else return{ok:!1,count:0,error:j}}const Z=J.map(($)=>{const j=y.readFileSync($).toString("base64"),W=B.extname($).toLowerCase();return{name:B.basename($),mimeType:i[W],base64:j}}),Q=Z.reduce(($,j)=>$+j.base64.length,0);if(Q>500000)console.warn(`[warn] Total image payload is ${(Q/1024/1024).toFixed(1)}MB (base64). This may fail with the browser bridge. Update the extension to v1.6+ for CDP-based upload, or compress images before publishing.`);const q=JSON.stringify(Z);return z.evaluate(`
|
|
7
7
|
(async () => {
|
|
8
8
|
const images = ${q};
|
|
9
9
|
|
|
@@ -167,7 +167,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
167
167
|
const actual = normalize(el.innerText || el.textContent || '');
|
|
168
168
|
return { ok: actual === normalize(expectedText), actual };
|
|
169
169
|
})(${JSON.stringify(Q.sel)}, ${JSON.stringify(K)})
|
|
170
|
-
`)}catch{$=await q()}}else $=await q();if(!$?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Z}_debug.png`});const j=typeof $?.actual==="string"?$.actual:"";throw Error(`Failed to set ${Z}. Expected "${K}", got "${j}". Debug screenshot: /tmp/xhs_publish_${Z}_debug.png`)}}async function
|
|
170
|
+
`)}catch{$=await q()}}else $=await q();if(!$?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Z}_debug.png`});const j=typeof $?.actual==="string"?$.actual:"";throw Error(`Failed to set ${Z}. Expected "${K}", got "${j}". Debug screenshot: /tmp/xhs_publish_${Z}_debug.png`)}}async function Yz(z,J){return Y(await z.evaluate(`
|
|
171
171
|
(selectors => {
|
|
172
172
|
const el = selectors
|
|
173
173
|
.map(sel => Array.from(document.querySelectorAll(sel)))
|
|
@@ -183,7 +183,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
183
183
|
selection?.addRange(range);
|
|
184
184
|
return true;
|
|
185
185
|
})(${JSON.stringify(J)})
|
|
186
|
-
`))}function
|
|
186
|
+
`))}function Az(z,{click:J=!1}={}){return`
|
|
187
187
|
(topicName => {
|
|
188
188
|
const norm = (value) => (value || '').replace(/^#/, '').replace(/\\s+/g, '').trim();
|
|
189
189
|
const want = norm(topicName);
|
|
@@ -272,14 +272,14 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
272
272
|
}
|
|
273
273
|
return count;
|
|
274
274
|
})(${JSON.stringify(z)}, ${JSON.stringify(J)})
|
|
275
|
-
`}async function
|
|
275
|
+
`}async function Cz(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return Y(await z.evaluate(`
|
|
276
276
|
(text => {
|
|
277
277
|
const ok = document.execCommand('insertText', false, text);
|
|
278
278
|
const active = document.activeElement;
|
|
279
279
|
if (active) active.dispatchEvent(new Event('input', { bubbles: true }));
|
|
280
280
|
return ok;
|
|
281
281
|
})(${JSON.stringify(K)})
|
|
282
|
-
`))}async function
|
|
282
|
+
`))}async function Xz(z,J,K){const Z=[];for(const Q of K){if(!await Yz(z,J))throw new H(`Could not attach topic "${Q}": body editor not found`);const $=Number(Y(await z.evaluate(s(Q,J))))||0;if(typeof z.pressKey==="function")try{await z.pressKey("Enter")}catch{}if(typeof z.insertText!=="function")throw new H(`Could not attach topic "${Q}": page.insertText is unavailable`);try{await z.insertText(`#${Q}`)}catch{throw new H(`Could not attach topic "${Q}": failed to type inline topic query`)}await z.wait({time:1.2});if(typeof z.pressKey!=="function")throw new H(`Could not attach topic "${Q}": page.pressKey is unavailable`);try{await z.pressKey("Enter")}catch(W){throw new H(`Could not attach topic "${Q}": failed to accept suggestion (${W&&W.message||W})`)}await z.wait({time:0.8});if((Number(Y(await z.evaluate(s(Q,J))))||0)<=$)throw new H(`Could not attach topic "${Q}": no real topic entity appeared after selection`);Z.push(Q);await z.wait({time:0.4})}return Z}async function Wz(z){const J=await z.evaluate(`
|
|
283
283
|
() => {
|
|
284
284
|
const isVisible = (el) => {
|
|
285
285
|
if (!el || el.offsetParent === null) return false;
|
|
@@ -328,7 +328,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
328
328
|
}
|
|
329
329
|
return { ok: false, visibleTexts };
|
|
330
330
|
}
|
|
331
|
-
`);if(J?.ok)await z.wait({time:1});return J}async function
|
|
331
|
+
`);if(J?.ok)await z.wait({time:1});return J}async function f(z,J,K=7000){const Z=500,Q=Math.max(1,Math.ceil(K/Z));let q={ok:!1};for(let $=0;$<Q;$++){q=Y(await z.evaluate(`
|
|
332
332
|
((cfg) => {
|
|
333
333
|
const __opencli_xhs_click_label = { wantLabel: ${JSON.stringify(J)} };
|
|
334
334
|
const wantLabel = ${JSON.stringify(J)};
|
|
@@ -363,10 +363,10 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
363
363
|
}
|
|
364
364
|
return { ok: false };
|
|
365
365
|
})()
|
|
366
|
-
`));if(q?.ok)return q;await z.wait({time:Z/1000})}return q}async function
|
|
366
|
+
`));if(q?.ok)return q;await z.wait({time:Z/1000})}return q}async function Fz(z){const J=await z.evaluate(`
|
|
367
367
|
(() => {
|
|
368
368
|
const __opencli_xhs_focus_card = true;
|
|
369
|
-
const sel = ${JSON.stringify(
|
|
369
|
+
const sel = ${JSON.stringify(M)};
|
|
370
370
|
const editors = Array.from(document.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
|
|
371
371
|
// Prefer the editor inside the active swiper slide if present.
|
|
372
372
|
const active = editors.find((el) => el.closest('.swiper-slide-active')) || editors[editors.length - 1];
|
|
@@ -380,34 +380,34 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
380
380
|
selection?.addRange(range);
|
|
381
381
|
return { ok: true };
|
|
382
382
|
})()
|
|
383
|
-
`);return
|
|
383
|
+
`);return Y(J)}async function Oz(z){const J=await z.evaluate(`
|
|
384
384
|
(() => {
|
|
385
385
|
const __opencli_xhs_card_text = true;
|
|
386
|
-
const sel = ${JSON.stringify(
|
|
386
|
+
const sel = ${JSON.stringify(M)};
|
|
387
387
|
const editors = Array.from(document.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
|
|
388
388
|
const active = editors.find((el) => el.closest('.swiper-slide-active')) || editors[editors.length - 1];
|
|
389
389
|
const text = active ? (active.innerText || active.textContent || '').trim() : '';
|
|
390
390
|
return { ok: !!text, text };
|
|
391
391
|
})()
|
|
392
|
-
`);return
|
|
392
|
+
`);return Y(J)}async function p(z){const J=await z.evaluate(`
|
|
393
393
|
(() => {
|
|
394
394
|
const __opencli_xhs_card_count = true;
|
|
395
|
-
const sel = ${JSON.stringify(
|
|
395
|
+
const sel = ${JSON.stringify(M)};
|
|
396
396
|
const editors = Array.from(document.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
|
|
397
397
|
const active = editors.find((el) => el.closest('.swiper-slide-active')) || editors[editors.length - 1];
|
|
398
398
|
const activeText = active ? (active.innerText || active.textContent || '').trim() : '';
|
|
399
399
|
return { ok: true, count: editors.length, activeEmpty: !activeText };
|
|
400
400
|
})()
|
|
401
|
-
`);return
|
|
401
|
+
`);return Y(J)}async function Uz(z,J=8000){const K=300,Z=Math.ceil(J/K);for(let Q=0;Q<Z;Q++){if((await p(z))?.count>=1)return!0;await z.wait({time:K/1000})}return!1}async function Bz(z,J,K=6000){const Z=300,Q=Math.ceil(K/Z);for(let q=0;q<Q;q++){const $=await p(z);if($?.count>=J&&$?.activeEmpty)return!0;await z.wait({time:Z/1000})}return!1}async function vz(z,J,K=4){for(let Z=0;Z<K;Z++){if(!(await f(z,c))?.ok)return!1;if(await Bz(z,J,2500))return!0}return!1}async function Dz(z){const J=await z.evaluate(`
|
|
402
402
|
(() => {
|
|
403
403
|
const __opencli_xhs_preview_ready = true;
|
|
404
404
|
const ready = Array.from(document.querySelectorAll('button'))
|
|
405
405
|
.some((b) => b.offsetParent !== null && (b.innerText || '').replace(/\\s+/g, '') === '下一步');
|
|
406
406
|
return { ok: ready };
|
|
407
407
|
})()
|
|
408
|
-
`);return
|
|
409
|
-
`);if(!(await
|
|
410
|
-
`);for(let $=0;$<q.length;$++){if($>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if(q[$])await z.insertText(q[$])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await
|
|
408
|
+
`);return Y(J)}async function fz(z,J=6){const K=400,Z=Math.ceil(3000/K);for(let Q=0;Q<J;Q++){if(!(await f(z,E))?.ok&&Q===0)return!1;for(let $=0;$<Z;$++){if((await Dz(z))?.ok)return!0;await z.wait({time:K/1000})}}return!1}async function Iz(z,J,K){J=String(J).replace(/\\n/g,`
|
|
409
|
+
`);if(!(await Fz(z))?.ok)throw new H(`文字配图: could not focus card editor #${K+1}`);if(typeof z.insertText==="function"){const q=J.split(`
|
|
410
|
+
`);for(let $=0;$<q.length;$++){if($>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if(q[$])await z.insertText(q[$])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await Oz(z))?.ok)throw new H(`文字配图: card editor #${K+1} is empty after typing`)}async function Nz(z,J){if(!J||J===D)return D;const K=Y(await z.evaluate(`
|
|
411
411
|
(async () => {
|
|
412
412
|
const __opencli_xhs_card_styles = true;
|
|
413
413
|
const want = ${JSON.stringify(J)};
|
|
@@ -436,7 +436,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
436
436
|
}
|
|
437
437
|
return { ok: seen.length > 0, styles: seen, found: false };
|
|
438
438
|
})()
|
|
439
|
-
`));if(!K?.found)throw new H(`文字配图: requested style "${J}" is not available for this content (options: ${(K?.styles||[]).join(" / ")||"none"}). Choose an available style or omit --card-style to use ${
|
|
439
|
+
`));if(!K?.found)throw new H(`文字配图: requested style "${J}" is not available for this content (options: ${(K?.styles||[]).join(" / ")||"none"}). Choose an available style or omit --card-style to use ${D}.`);if(!(await f(z,J))?.ok)throw new H(`文字配图: could not click requested style "${J}".`);await z.wait({time:0.6});return J}async function t(z){const J=await z.evaluate(`
|
|
440
440
|
(() => {
|
|
441
441
|
const __opencli_xhs_composer_media_count = true;
|
|
442
442
|
const visibleBox = (el) => {
|
|
@@ -468,7 +468,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
468
468
|
}
|
|
469
469
|
return { ok: true, count };
|
|
470
470
|
})()
|
|
471
|
-
`);return
|
|
471
|
+
`);return Y(J)}async function a(z,J,K){const Z=await t(z);if(!Z||typeof Z.count!=="number")throw new H(`${K}: could not verify current composer media count`);if(Z.count<J){await z.screenshot({path:"/tmp/xhs_publish_media_debug.png"});throw new H(`${K}: expected at least ${J} visible media item(s), got ${Z.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png`)}}async function hz(z,J,K){if(!(await f(z,L))?.ok){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new H(`文字配图: could not click "${L}" entry. Debug: /tmp/xhs_publish_textimage_debug.png`)}if(!await Uz(z)){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new H(`文字配图: 写文字 card editor did not appear after clicking "${L}". Debug: /tmp/xhs_publish_textimage_debug.png`)}for(let j=0;j<J.length;j++){if(j>0){if(!await vz(z,j+1)){await z.screenshot({path:"/tmp/xhs_publish_addcard_debug.png"});throw new H(`文字配图: new card editor #${j+1} did not render after "${c}". Debug: /tmp/xhs_publish_addcard_debug.png`)}}await Iz(z,J[j],j)}if(!await fz(z)){await z.screenshot({path:"/tmp/xhs_publish_generate_debug.png"});throw new H(`文字配图: "${E}" did not advance to the 预览图片 step. `+"Debug: /tmp/xhs_publish_generate_debug.png")}const q=await Nz(z,K);if(!(await f(z,n))?.ok){await z.screenshot({path:"/tmp/xhs_publish_next_debug.png"});throw new H(`文字配图: could not click "${n}". Debug: /tmp/xhs_publish_next_debug.png`)}await z.wait({time:2});return q}async function e(z){return z.evaluate(`
|
|
472
472
|
() => {
|
|
473
473
|
const text = (document.body?.innerText || '').replace(/s+/g, ' ').trim();
|
|
474
474
|
const hasTitleInput = !!Array.from(document.querySelectorAll('input, textarea')).find((el) => {
|
|
@@ -498,7 +498,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
498
498
|
const state = hasTitleInput ? 'editor_ready' : hasImageInput || !hasVideoSurface ? 'image_surface' : 'video_surface';
|
|
499
499
|
return { state, hasTitleInput, hasImageInput, hasVideoSurface };
|
|
500
500
|
}
|
|
501
|
-
`)}async function kz(z,J=5000){const K=500,Z=Math.max(1,Math.ceil(J/K));let Q=await
|
|
501
|
+
`)}async function kz(z,J=5000){const K=500,Z=Math.max(1,Math.ceil(J/K));let Q=await e(z);for(let q=0;q<Z;q++){if(Q.state!=="video_surface")return Q;if(q<Z-1){await z.wait({time:K/1000});Q=await e(z)}}return Q}async function yz(z,J=1e4){const K=1000,Z=Math.ceil(J/K);for(let Q=0;Q<Z;Q++){if(await z.evaluate(`
|
|
502
502
|
(() => {
|
|
503
503
|
const sels = ${JSON.stringify(w)};
|
|
504
504
|
for (const sel of sels) {
|
|
@@ -506,7 +506,33 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
506
506
|
if (el && el.offsetParent !== null) return true;
|
|
507
507
|
}
|
|
508
508
|
return false;
|
|
509
|
-
})()`))return!0;if(Q<Z-1)await z.wait({time:K/1000})}return!1}
|
|
509
|
+
})()`))return!0;if(Q<Z-1)await z.wait({time:K/1000})}return!1}async function Pz(z){const J=async()=>Y(await z.evaluate(`
|
|
510
|
+
(() => {
|
|
511
|
+
const __opencli_xhs_restore_probe = true;
|
|
512
|
+
const clearBtn = document.querySelector('.title-clear-all');
|
|
513
|
+
const restored = !!clearBtn && clearBtn.offsetParent !== null && !!document.querySelector('.img-container');
|
|
514
|
+
const cleanSurface = (document.body.innerText || '').includes('上传图片,或写文字生成图片');
|
|
515
|
+
return { restored, cleanSurface };
|
|
516
|
+
})()
|
|
517
|
+
`));let K=await J();if(!K?.restored)return!1;for(let Z=0;Z<3;Z++){await z.evaluate(`
|
|
518
|
+
(async () => {
|
|
519
|
+
const __opencli_xhs_restore_clear = true;
|
|
520
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
521
|
+
const fire = (el) => {
|
|
522
|
+
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
|
|
523
|
+
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
|
524
|
+
el.click();
|
|
525
|
+
};
|
|
526
|
+
const clearBtn = document.querySelector('.title-clear-all');
|
|
527
|
+
if (!clearBtn) return false;
|
|
528
|
+
fire(clearBtn);
|
|
529
|
+
await sleep(800);
|
|
530
|
+
const confirm = Array.from(document.querySelectorAll('button, [role="button"]'))
|
|
531
|
+
.find((b) => b.offsetParent !== null && (b.innerText || '').replace(/\\s+/g, '') === '重新上传');
|
|
532
|
+
if (confirm) fire(confirm);
|
|
533
|
+
return !!confirm;
|
|
534
|
+
})()
|
|
535
|
+
`);for(let Q=0;Q<10;Q++){await z.wait({time:0.5});K=await J();if(K?.cleanSurface&&!K?.restored)return!0}}await z.screenshot({path:"/tmp/xhs_publish_restore_debug.png"});throw new H("检测到小红书恢复了上次未发布的编辑内容(含旧图),自动清空失败——继续发布会导致图片重复上传。"+"请到 creator.xiaohongshu.com 发布页手动删除恢复的内容后重试。Debug: /tmp/xhs_publish_restore_debug.png")}Qz({site:"xiaohongshu",name:"publish",access:"write",description:"小红书发布图文笔记 (creator center UI automation)",domain:"creator.xiaohongshu.com",strategy:Zz.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"content",required:!1,positional:!0,help:"笔记正文(字面文本,不会展开 @文件 引用;长正文建议用 --file)"},{name:"file",required:!1,help:"从本机文件读取笔记正文(UTF-8),与位置参数 <content> 二选一"},{name:"title",required:!0,help:"笔记标题 (最多20字)"},{name:"images",required:!1,help:"图片路径,逗号分隔,最多9张 (jpg/png/gif/webp)"},{name:"card-text",required:!1,help:`文字配图卡片文字,多张卡片用 ${u} 分隔,卡内换行用 \\n`},{name:"card-style",required:!1,help:`文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败。省略时使用${D}。可选: ${d.map(([z,J])=>`${z}(${J})`).join(" ")}`},{name:"topics",required:!1,help:"话题标签,逗号分隔,不含 # 号"},{name:"draft",type:"bool",default:!1,help:"保存为草稿,不直接发布"}],columns:["status","detail"],func:async(z,J)=>{if(!z)throw Error("Browser page required");const K=String(J.title??"").trim(),Z=J.file?String(J.file).trim():"";let Q=String(J.content??"").trim();if(Z){if(Q)throw new X("正文位置参数和 --file 只能二选一,不能同时给");const V=B.resolve(Z);if(!y.existsSync(V))throw new X(`--file 指向的文件不存在: ${V}`);Q=y.readFileSync(V,"utf-8").trim();if(!Q)throw new X(`--file 指向的文件内容为空: ${V}`)}const q=J.images?String(J.images).split(",").map((V)=>V.trim()).filter(Boolean):[],$=J.topics?String(J.topics).split(",").map((V)=>V.trim()).filter(Boolean):[],j=Boolean(J.draft),W=J["card-text"]?String(J["card-text"]):"",v=W?W.split(u).map((V)=>V.trim()).filter(Boolean):[],b=J["card-style"]?String(J["card-style"]).trim():"",U=v.length>0;if(!K)throw new X("--title is required");if(K.length>T)throw new X(`Title is ${K.length} chars — must be ≤ ${T}`);if(!Q)throw new X("缺少笔记正文:给位置参数 <content>(字面文本),或用 --file <本机文件路径>");$z(Q,{fileFlag:"--file"});if(!U&&q.length===0)throw new X("Provide --card-text (text-image mode) or --images (upload mode); neither was given.");if(q.length>C)throw new X(`Too many images: ${q.length} (max ${C})`);if(U&&q.some((V)=>B.extname(V).toLowerCase()===".gif"))throw new X("文字配图模式追加的图片不支持 .gif(编辑器图片入口只接受 jpg/jpeg/png/webp)");const F=Hz(q);await z.goto(qz);let I="";for(let V=0;V<30;V++){await z.wait({time:0.5});I=await z.evaluate("() => location.href");if(I.includes("creator.xiaohongshu.com"))break}if(!I.includes("creator.xiaohongshu.com")){await z.screenshot({path:"/tmp/xhs_publish_redirect_debug.png"});throw Error(`Redirected away from creator center (landed on ${I}) — session may have expired. `+"Re-capture browser login via: opencli xiaohongshu creator-profile. Debug screenshot: /tmp/xhs_publish_redirect_debug.png")}const N=await Wz(z);if((await kz(z,N?.ok?5000:2000)).state==="video_surface"){await z.screenshot({path:"/tmp/xhs_publish_tab_debug.png"});const V=N?.ok?`clicked "${N.text}"`:`visible candidates: ${(N?.visibleTexts||[]).join(" | ")||"none"}`;throw Error("Still on the video publish page after trying to select 图文. "+`Details: ${V}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`)}await Pz(z);let h=b;if(U)h=await hz(z,v,b);else{const V=await o(z,F);if(!V.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new H(`Image injection failed: ${V.error??"unknown"}. Debug screenshot: /tmp/xhs_publish_upload_debug.png`)}await z.wait({time:x/1000});await r(z);const O=await t(z);if(O&&typeof O.count==="number"&&O.count>F.length){await z.screenshot({path:"/tmp/xhs_publish_dup_debug.png"});throw new H(`上传后编辑器内有 ${O.count} 张图,超过本次注入的 ${F.length} 张——`+"疑似页面残留了上次未发布的旧图,已中止发布以避免图片重复。Debug: /tmp/xhs_publish_dup_debug.png")}}if(!await yz(z)){await z.screenshot({path:"/tmp/xhs_publish_form_debug.png"});throw new H("Editing form did not appear after image acquisition. The page layout may have changed. Debug screenshot: /tmp/xhs_publish_form_debug.png")}if(U)await a(z,v.length,"文字配图 generated images");if(U&&F.length>0){const V=await o(z,F);if(!V.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new H(`Appending images failed: ${V.error??"unknown"}. Debug screenshot: /tmp/xhs_publish_append_debug.png`)}await z.wait({time:x/1000});await r(z);await a(z,v.length+F.length,"文字配图 appended images")}await g(z,w,K,"title");await z.wait({time:0.5});await g(z,m,Q,"content");await z.wait({time:0.5});let P=[];if($.length)P=await Xz(z,m,$);const R=j?["暂存离开","存草稿"]:["发布","发布笔记"],G=await z.evaluate(`
|
|
510
536
|
(cfg => {
|
|
511
537
|
const { isDraftMode, publishNames, draftNames, labels } = cfg;
|
|
512
538
|
const isVisible = (el) => {
|
|
@@ -545,7 +571,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
545
571
|
}
|
|
546
572
|
}
|
|
547
573
|
return { ok: false, via: 'none', hosts: hosts.length, lastMethodError };
|
|
548
|
-
})(${JSON.stringify({isDraftMode:j,publishNames:
|
|
574
|
+
})(${JSON.stringify({isDraftMode:j,publishNames:jz,draftNames:Vz,labels:R})})
|
|
549
575
|
`);if(!G?.ok){if(j){if(await z.evaluate(`
|
|
550
576
|
(() => {
|
|
551
577
|
const labels = ['返回', '关闭', '取消', '离开'];
|
|
@@ -559,7 +585,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
559
585
|
}
|
|
560
586
|
return false;
|
|
561
587
|
})()
|
|
562
|
-
`)){await z.wait({time:1});const
|
|
588
|
+
`)){await z.wait({time:1});const O=["暂存离开","存草稿","保存草稿"];if(await z.evaluate(`
|
|
563
589
|
(labels => {
|
|
564
590
|
const buttons = document.querySelectorAll('button, [role="button"]');
|
|
565
591
|
for (const btn of buttons) {
|
|
@@ -574,7 +600,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
574
600
|
}
|
|
575
601
|
}
|
|
576
602
|
return false;
|
|
577
|
-
})(${JSON.stringify(
|
|
603
|
+
})(${JSON.stringify(O)})
|
|
578
604
|
`)){G.ok=!0;G.via="leave-save-fallback"}}if(!G?.ok){await z.wait({time:2});if(await z.evaluate(`
|
|
579
605
|
() => {
|
|
580
606
|
const markers = ['草稿箱(', '保存于', '编辑于'];
|
|
@@ -584,7 +610,7 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
584
610
|
}
|
|
585
611
|
return false;
|
|
586
612
|
}
|
|
587
|
-
`)){G.ok=!0;G.via="auto-save"}}}}if(!G?.ok){await z.screenshot({path:"/tmp/xhs_publish_submit_debug.png"});const V=G?.via?` (via=${G.via})`:"",
|
|
613
|
+
`)){G.ok=!0;G.via="auto-save"}}}}if(!G?.ok){await z.screenshot({path:"/tmp/xhs_publish_submit_debug.png"});const V=G?.via?` (via=${G.via})`:"",O=G?.error?`, error=${G.error}`:"",S=G?.lastMethodError?`, lastMethodError=${G.lastMethodError}`:"";throw Error(`Could not trigger "${R[0]}" action${V}${O}${S}. Debug screenshot: /tmp/xhs_publish_submit_debug.png`)}await z.wait({time:4});const k=await z.evaluate("() => location.href"),zz=j?["草稿已保存","暂存成功","保存成功","保存于","图文笔记("]:["发布成功","上传成功"],_=await z.evaluate(`
|
|
588
614
|
(markers => {
|
|
589
615
|
for (const el of document.querySelectorAll('*')) {
|
|
590
616
|
if (el.tagName === 'STYLE' || el.tagName === 'SCRIPT') continue;
|
|
@@ -593,5 +619,5 @@ import*as y from"node:fs";import*as U from"node:path";import{CommandExecutionErr
|
|
|
593
619
|
if (el.children.length === 0 && markers.some(marker => text.includes(marker))) return text;
|
|
594
620
|
}
|
|
595
621
|
return '';
|
|
596
|
-
})(${JSON.stringify(
|
|
597
|
-
`),
|
|
622
|
+
})(${JSON.stringify(zz)})
|
|
623
|
+
`),Jz=!k.includes("/publish/publish"),Kz=_.length>0||Jz,A=j?"暂存成功":"发布成功";if(!Kz)throw new H(`${A} could not be verified: no success marker or post-submit navigation was observed. `+(k?`Current URL: ${k}`:"Current URL was empty."));return[{status:`✅ ${A}`,detail:[`"${K}"`,U?`${v.length}张文字配图${F.length?` + ${F.length}张图片`:""}${h&&h!==D?` (${h})`:""}`:`${F.length}张图片`,P.length?`话题: ${P.join(" ")}`:"",_||k||""].filter(Boolean).join(" · ")}]}});
|