publishport-opencli 1.8.5-pp.39 → 1.8.5-pp.40

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.
@@ -1,4 +1,4 @@
1
- import{readFile as S}from"node:fs/promises";import{fileURLToPath as j}from"node:url";const N=/<img\b[^>]*?\ssrc=("|')(.*?)\1[^>]*>/gi,O=/!\[[^\]]*\]\(\s*<?([^)\s>]+)>?[^)]*\)/g;export function extractImageRefs(q){const v=[];if(typeof q!=="string"||!q)return v;let w;N.lastIndex=0;while((w=N.exec(q))!==null)v.push({full:w[0],src:w[2]});O.lastIndex=0;while((w=O.exec(q))!==null)v.push({full:w[0],src:w[1]});return v}export function pickPath(q,v){if(q==null)return;const w=String(v||"").split(".").filter(Boolean);let z=q;for(const C of w){if(z==null)return;z=z[C]}return z}const k=`
1
+ import{readFile as S}from"node:fs/promises";import{resolve as j}from"node:path";import{fileURLToPath as k}from"node:url";const N=/<img\b[^>]*?\ssrc=("|')(.*?)\1[^>]*>/gi,O=/!\[[^\]]*\]\(\s*<?([^)\s>]+)>?[^)]*\)/g;export function extractImageRefs(q){const w=[];if(typeof q!=="string"||!q)return w;let z;N.lastIndex=0;while((z=N.exec(q))!==null)w.push({full:z[0],src:z[2]});O.lastIndex=0;while((z=O.exec(q))!==null)w.push({full:z[0],src:z[1]});return w}export function pickPath(q,w){if(q==null)return;const z=String(w||"").split(".").filter(Boolean);let C=q;for(const K of z){if(C==null)return;C=C[K]}return C}const F=`
2
2
  const htmlRe = /<img\\b[^>]*?\\ssrc=("|')(.*?)\\1[^>]*>/gi;
3
3
  const mdRe = /!\\[[^\\]]*\\]\\(\\s*<?([^)\\s>]+)>?[^)]*\\)/g;
4
4
  const refs = [];
@@ -87,9 +87,9 @@ import{readFile as S}from"node:fs/promises";import{fileURLToPath as j}from"node:
87
87
  }
88
88
 
89
89
  return { content: out, uploaded: uploaded, failed: failed };
90
- `;export function buildTransferImagesJs(q,v,w){return`(async () => {
90
+ `;export function buildTransferImagesJs(q,w,z){return`(async () => {
91
91
  const content = `+JSON.stringify(q)+`;
92
- const spec = `+JSON.stringify(v||{})+`;
93
- const skip = `+JSON.stringify(w||[])+`;
94
- `+k+`
95
- })()`}const U={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",bmp:"image/bmp",ico:"image/x-icon",avif:"image/avif",tif:"image/tiff",tiff:"image/tiff"};export function isLocalImagePath(q){if(typeof q!=="string"||!q)return!1;if(q.startsWith("file://"))return!0;if(/^[a-zA-Z]:[\\/]/.test(q))return!0;if(q.startsWith("//"))return!1;if(/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(q))return!1;if(q.startsWith("/"))return!0;return!1}function X(q){if(q.startsWith("file://"))try{return j(q)}catch{return q.slice(7)}return q}export async function localImageToDataUri(q,v={}){const w=v.readFile||S,z=X(q),C=await w(z),Z=C.byteLength!=null?C.byteLength:C.length,Q=(z.split(/[\\/]/).pop().split(".").pop()||"").toLowerCase().split("?")[0],V=U[Q]||"image/png";return{dataUri:`data:${V};base64,${Buffer.from(C).toString("base64")}`,bytes:Z,mime:V}}export async function inlineLocalImages(q,v={}){const w=v.readFile||S,z=[],C=[];if(typeof q!=="string"||!q)return{content:q||"",inlined:z,missing:C};const Z=extractImageRefs(q),Q={};let V=q;for(const A of Z){const K=A.src;if(!isLocalImagePath(K))continue;if(!(K in Q))try{const W=X(K),$=await w(W),B=$.byteLength!=null?$.byteLength:$.length,H=(W.split(/[\\/]/).pop().split(".").pop()||"").toLowerCase().split("?")[0],J=U[H]||"image/png",Y=Buffer.from($).toString("base64");Q[K]=`data:${J};base64,${Y}`;z.push({src:K,bytes:B,mime:J})}catch(W){Q[K]=null;C.push({src:K,error:String(W&&W.message||W)})}const y=Q[K];if(y)V=V.split(A.full).join(A.full.replace(K,y))}return{content:V,inlined:z,missing:C}}export async function transferImages(q,v,w={}){const{spec:z,skip:C=[]}=w;if(!z||!z.url)return{content:v,uploaded:[],failed:[]};if(extractImageRefs(v).length===0)return{content:v,uploaded:[],failed:[]};return await q.evaluate(buildTransferImagesJs(v,z,C))||{content:v,uploaded:[],failed:[]}}export const __test__={extractImageRefs,pickPath,buildTransferImagesJs,isLocalImagePath,inlineLocalImages,localImageToDataUri,HTML_IMG_RE:N,MD_IMG_RE:O};
92
+ const spec = `+JSON.stringify(w||{})+`;
93
+ const skip = `+JSON.stringify(z||[])+`;
94
+ `+F+`
95
+ })()`}const U={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",bmp:"image/bmp",ico:"image/x-icon",avif:"image/avif",tif:"image/tiff",tiff:"image/tiff"};export function isLocalImagePath(q){if(typeof q!=="string"||!q)return!1;if(q.startsWith("file://"))return!0;if(/^[a-zA-Z]:[\\/]/.test(q))return!0;if(q.startsWith("//"))return!1;if(/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(q))return!1;return!0}function X(q){if(q.startsWith("file://"))try{return k(q)}catch{return q.slice(7)}return j(q)}export async function localImageToDataUri(q,w={}){const z=w.readFile||S,C=X(q),K=await z(C),$=K.byteLength!=null?K.byteLength:K.length,V=(C.split(/[\\/]/).pop().split(".").pop()||"").toLowerCase().split("?")[0],W=U[V]||"image/png";return{dataUri:`data:${W};base64,${Buffer.from(K).toString("base64")}`,bytes:$,mime:W}}export async function inlineLocalImages(q,w={}){const z=w.readFile||S,C=[],K=[];if(typeof q!=="string"||!q)return{content:q||"",inlined:C,missing:K};const $=extractImageRefs(q),V={};let W=q;for(const A of $){const Q=A.src;if(!isLocalImagePath(Q))continue;if(!(Q in V))try{const Z=X(Q),v=await z(Z),B=v.byteLength!=null?v.byteLength:v.length,H=(Z.split(/[\\/]/).pop().split(".").pop()||"").toLowerCase().split("?")[0],J=U[H]||"image/png",Y=Buffer.from(v).toString("base64");V[Q]=`data:${J};base64,${Y}`;C.push({src:Q,bytes:B,mime:J})}catch(Z){V[Q]=null;K.push({src:Q,error:String(Z&&Z.message||Z)})}const y=V[Q];if(y)W=W.split(A.full).join(A.full.replace(Q,y))}return{content:W,inlined:C,missing:K}}export async function transferImages(q,w,z={}){const{spec:C,skip:K=[]}=z;if(!C||!C.url)return{content:w,uploaded:[],failed:[]};if(extractImageRefs(w).length===0)return{content:w,uploaded:[],failed:[]};return await q.evaluate(buildTransferImagesJs(w,C,K))||{content:w,uploaded:[],failed:[]}}export const __test__={extractImageRefs,pickPath,buildTransferImagesJs,isLocalImagePath,inlineLocalImages,localImageToDataUri,HTML_IMG_RE:N,MD_IMG_RE:O};
@@ -1 +1 @@
1
- import{CliError as V,CommandExecutionError as y}from"@jackwener/opencli/errors";import{cli as h,Strategy as T}from"@jackwener/opencli/registry";import{publishArticle as b}from"../_shared/article/publish.js";import{readFile as C,stat as E}from"node:fs/promises";function I(J){if(!J.execute)throw new V("INVALID_INPUT","此头条号写操作需要 --execute 参数才能真正发布。注意:本报错仅表示缺少 --execute,并未校验其余参数,不能当 dry-run 预览用")}async function v(J){const G=typeof J.text==="string"?J.text:void 0,z=typeof J.file==="string"?J.file:void 0;if(G&&z)throw new V("INVALID_INPUT","正文和 --file 只能选一个");let D=G??"";if(z){let Q;try{Q=await E(z)}catch{throw new V("INVALID_INPUT",`找不到文件: ${z}`)}if(!Q.isFile())throw new V("INVALID_INPUT",`--file 必须是可读文本文件: ${z}`);let X;try{X=await C(z)}catch{throw new V("INVALID_INPUT",`文件无法读取: ${z}`)}try{D=new TextDecoder("utf-8",{fatal:!0}).decode(X)}catch{throw new V("INVALID_INPUT",`文件必须是 UTF-8 编码: ${z}`)}}if(!D.trim())throw new V("INVALID_INPUT","正文不能为空");return D}function j(J,G,z,D,Q={}){return[{status:"success",outcome:D,message:J,target_type:G,target:z,...Q}]}export const toutiaoProfile={home:"https://mp.toutiao.com/profile_v3/graphic/publish",outputFormat:"html",preprocessConfig:{},publish:async(J,G)=>{const z=J.params||{},D=[],Q=[],X=["toutiao.com","pstatp.com","byteimg.com","bytecdn","ibytedtos","toutiaoimg","tos-cn"];async function W(B){const L=await fetch(B,{credentials:"omit"});if(!L.ok)throw Error("图片下载失败 HTTP "+L.status+":"+B.slice(0,80));const M=await L.blob(),x=((M.type||"").split("/")[1]||"jpg").split("+")[0].split(";")[0]||"jpg",R=new FormData;R.append("upfile",M,"image."+x);const U=await(await fetch("https://mp.toutiao.com/mp/agw/article_material/photo/upload_picture?type=ueditor&pgc_watermark=1&action=uploadimage&encode=utf-8",{method:"POST",credentials:"include",body:R})).json();if(U.code!==0||U.state!=="SUCCESS"||!U.url)throw Error(U.message||"图片上传失败");return{url:U.url,web_uri:U.origin_web_uri||U.web_uri,width:U.width,height:U.height}}const Y=document.createElement("div");Y.innerHTML=J.content;const Z=Array.prototype.slice.call(Y.querySelectorAll("img"));for(const B of Z){const L=B.getAttribute("src")||"";if(!L){B.remove();continue}if(X.some((M)=>L.indexOf(M)!==-1))continue;try{const M=await W(L);B.setAttribute("src",M.url);B.setAttribute("web_uri",M.web_uri);D.push({src:L.slice(0,120),url:M.url})}catch(M){Q.push({src:L.slice(0,120),error:String(M&&M.message||M)})}}const $=Y.innerHTML,A=[];if(z.cover&&String(z.cover).trim()){let B;try{B=await W(String(z.cover))}catch(L){return{ok:!1,stage:"cover",message:"封面图转存失败:"+String(L&&L.message||L),uploaded:D,failed:Q}}A.push({id:0,url:B.url,uri:B.web_uri,origin_uri:B.web_uri,ic_uri:"",thumb_width:B.width,thumb_height:B.height})}await fetch("https://mp.toutiao.com/profile_v4/graphic/publish",{credentials:"include"});function N(){const B=document.cookie.match(/passport_csrf_token=([^;]+)/);return B?B[1]:""}const F=N(),S=new URLSearchParams({title:J.title,article_ad_type:"2",article_type:"0",from_diagnosis:"0",origin_debut_check_pgc_normal:"0",tree_plan_article:"0",save:"1",pgc_id:"0",content:$,pgc_feed_covers:JSON.stringify(A)}),H={"Content-Type":"application/x-www-form-urlencoded"};if(F)H["X-CSRFToken"]=F;const O=await fetch("https://mp.toutiao.com/mp/agw/article/publish?source=mp&type=article&aid=1231",{method:"POST",credentials:"include",headers:H,body:S});let K=null;try{K=await O.json()}catch(B){}const P=K&&(K.code===0||K.err_no===0),q=K&&K.data&&K.data.pgc_id;if(!P||!q||String(q)==="0"){let B=K&&(K.message||K.reason)||"头条号发布失败";if(K&&(K.code===7050||K.err_no===7050))B+="(code 7050「保存失败」:内容可能被风控拦截或账号被平台限制发文,"+"请登录 mp.toutiao.com 查看站内通知)";return{ok:!1,stage:"publish",status:O.status,message:B,detail:K,uploaded:D,failed:Q}}const _="https://mp.toutiao.com/profile_v4/graphic/publish?pgc_id="+String(q);return{ok:!0,id:String(q),url:_,draft:!1,uploaded:D,failed:Q}},checkAuth:async(J)=>{try{const z=await(await fetch("https://mp.toutiao.com/mp/agw/media/get_media_info",{credentials:"include"})).json(),D=z&&z.data&&z.data.user;if(z&&z.code===0&&D&&D.id)return{isAuthenticated:!0,userId:String(D.id),username:D.screen_name||D.name||"",avatar:D.https_avatar_url||D.avatar_url||""};return{isAuthenticated:!1}}catch(G){return{isAuthenticated:!1,error:String(G&&G.message||G)}}}};h({site:"toutiao",name:"article",access:"write",description:"正式发布头条号图文文章(会立即公开,头条无可靠的存草稿接口,故不支持草稿)。正文默认 Markdown,图片自动转存到头条图床。标题需 2~30 字;封面可选(--cover,不传则头条自动从正文图选封面)。",domain:"mp.toutiao.com",strategy:T.COOKIE,browser:!0,args:[{name:"title",positional:!0,required:!0,help:"文章标题(2~30 字)"},{name:"text",positional:!0,help:"文章正文(默认 Markdown;传 --html 则视为原始 HTML)"},{name:"file",help:"正文文件路径(UTF-8,默认 Markdown)"},{name:"html",type:"boolean",help:"将正文视为原始 HTML 而非 Markdown"},{name:"cover",help:"封面图 URL 或本机路径(可选;会先转存到头条图床)"},{name:"execute",type:"boolean",help:"真正执行写操作。不加此参数时命令拒绝写入。"}],columns:["status","outcome","message","target_type","target","created_target","created_url"],func:async(J,G)=>{if(!J)throw new y("头条号文章发布需要浏览器会话");I(G);const z=String(G.title??"").trim();if(!z)throw new V("INVALID_INPUT","文章标题不能为空");if(z.length<2||z.length>30)throw new V("INVALID_INPUT",`头条号标题需 2~30 字,当前 ${z.length} 字`);const D=await v(G),X={cover:typeof G.cover==="string"?G.cover.trim():""},W=await b(J,{title:z,body:D,format:G.html?"html":"markdown",profile:toutiaoProfile,publishParams:X}),Y=W.images.uploaded.length|0,Z=W.images.failed.length|0;let $="已正式发布头条号文章";if(Y||Z)$+=`(图片:${Y} 张已转存${Z?`,${Z} 张失败`:""})`;return j($,"article","","created",{created_target:"article:"+W.id,created_url:W.url})}});
1
+ import{CliError as V,CommandExecutionError as y}from"@jackwener/opencli/errors";import{cli as h,Strategy as T}from"@jackwener/opencli/registry";import{publishArticle as b}from"../_shared/article/publish.js";import{readFile as C,stat as E}from"node:fs/promises";function I(J){if(!J.execute)throw new V("INVALID_INPUT","此头条号写操作需要 --execute 参数才能真正发布。注意:本报错仅表示缺少 --execute,并未校验其余参数,不能当 dry-run 预览用")}async function v(J){const G=typeof J.text==="string"?J.text:void 0,z=typeof J.file==="string"?J.file:void 0;if(G&&z)throw new V("INVALID_INPUT","正文和 --file 只能选一个");let D=G??"";if(z){let U;try{U=await E(z)}catch{throw new V("INVALID_INPUT",`找不到文件: ${z}`)}if(!U.isFile())throw new V("INVALID_INPUT",`--file 必须是可读文本文件: ${z}`);let X;try{X=await C(z)}catch{throw new V("INVALID_INPUT",`文件无法读取: ${z}`)}try{D=new TextDecoder("utf-8",{fatal:!0}).decode(X)}catch{throw new V("INVALID_INPUT",`文件必须是 UTF-8 编码: ${z}`)}}if(!D.trim())throw new V("INVALID_INPUT","正文不能为空");return D}function n(J,G,z,D,U={}){return[{status:"success",outcome:D,message:J,target_type:G,target:z,...U}]}export const toutiaoProfile={home:"https://mp.toutiao.com/profile_v3/graphic/publish",outputFormat:"html",preprocessConfig:{},publish:async(J,G)=>{const z=J.params||{},D=[],U=[],X=["toutiao.com","pstatp.com","byteimg.com","bytecdn","ibytedtos","toutiaoimg","tos-cn"];async function W(B){const L=await fetch(B,{credentials:"omit"});if(!L.ok)throw Error("图片下载失败 HTTP "+L.status+":"+B.slice(0,80));const M=await L.blob(),x=((M.type||"").split("/")[1]||"jpg").split("+")[0].split(";")[0]||"jpg",R=new FormData;R.append("upfile",M,"image."+x);const Q=await(await fetch("https://mp.toutiao.com/mp/agw/article_material/photo/upload_picture?type=ueditor&pgc_watermark=1&action=uploadimage&encode=utf-8",{method:"POST",credentials:"include",body:R})).json();if(Q.code!==0||Q.state!=="SUCCESS"||!Q.url)throw Error((Q.message||"图片上传失败")+(Q.code!=null?"(接口 code "+Q.code+")":""));return{url:Q.url,web_uri:Q.origin_web_uri||Q.web_uri,width:Q.width,height:Q.height}}const Y=document.createElement("div");Y.innerHTML=J.content;const Z=Array.prototype.slice.call(Y.querySelectorAll("img"));for(const B of Z){const L=B.getAttribute("src")||"";if(!L){B.remove();continue}if(X.some((M)=>L.indexOf(M)!==-1))continue;try{const M=await W(L);B.setAttribute("src",M.url);B.setAttribute("web_uri",M.web_uri);D.push({src:L.slice(0,120),url:M.url})}catch(M){U.push({src:L.slice(0,120),error:String(M&&M.message||M)})}}const $=Y.innerHTML,A=[];if(z.cover&&String(z.cover).trim()){let B;try{B=await W(String(z.cover))}catch(L){return{ok:!1,stage:"cover",message:"封面图转存失败:"+String(L&&L.message||L),uploaded:D,failed:U}}A.push({id:0,url:B.url,uri:B.web_uri,origin_uri:B.web_uri,ic_uri:"",thumb_width:B.width,thumb_height:B.height})}await fetch("https://mp.toutiao.com/profile_v4/graphic/publish",{credentials:"include"});function N(){const B=document.cookie.match(/passport_csrf_token=([^;]+)/);return B?B[1]:""}const F=N(),S=new URLSearchParams({title:J.title,article_ad_type:"2",article_type:"0",from_diagnosis:"0",origin_debut_check_pgc_normal:"0",tree_plan_article:"0",save:"1",pgc_id:"0",content:$,pgc_feed_covers:JSON.stringify(A)}),H={"Content-Type":"application/x-www-form-urlencoded"};if(F)H["X-CSRFToken"]=F;const O=await fetch("https://mp.toutiao.com/mp/agw/article/publish?source=mp&type=article&aid=1231",{method:"POST",credentials:"include",headers:H,body:S});let K=null;try{K=await O.json()}catch(B){}const P=K&&(K.code===0||K.err_no===0),q=K&&K.data&&K.data.pgc_id;if(!P||!q||String(q)==="0"){let B=K&&(K.message||K.reason)||"头条号发布失败";if(K&&(K.code===7050||K.err_no===7050))B+="(code 7050「保存失败」:内容可能被风控拦截或账号被平台限制发文,"+"请登录 mp.toutiao.com 查看站内通知)";return{ok:!1,stage:"publish",status:O.status,message:B,detail:K,uploaded:D,failed:U}}const _="https://mp.toutiao.com/profile_v4/graphic/publish?pgc_id="+String(q);return{ok:!0,id:String(q),url:_,draft:!1,uploaded:D,failed:U}},checkAuth:async(J)=>{try{const z=await(await fetch("https://mp.toutiao.com/mp/agw/media/get_media_info",{credentials:"include"})).json(),D=z&&z.data&&z.data.user;if(z&&z.code===0&&D&&D.id)return{isAuthenticated:!0,userId:String(D.id),username:D.screen_name||D.name||"",avatar:D.https_avatar_url||D.avatar_url||""};return{isAuthenticated:!1}}catch(G){return{isAuthenticated:!1,error:String(G&&G.message||G)}}}};h({site:"toutiao",name:"article",access:"write",description:"正式发布头条号图文文章(会立即公开,头条无可靠的存草稿接口,故不支持草稿)。正文默认 Markdown,图片自动转存到头条图床。标题需 2~30 字;封面可选(--cover,不传则头条自动从正文图选封面)。",domain:"mp.toutiao.com",strategy:T.COOKIE,browser:!0,args:[{name:"title",positional:!0,required:!0,help:"文章标题(2~30 字)"},{name:"text",positional:!0,help:"文章正文(默认 Markdown;传 --html 则视为原始 HTML)"},{name:"file",help:"正文文件路径(UTF-8,默认 Markdown)"},{name:"html",type:"boolean",help:"将正文视为原始 HTML 而非 Markdown"},{name:"cover",help:"封面图 URL 或本机路径(可选;会先转存到头条图床)"},{name:"execute",type:"boolean",help:"真正执行写操作。不加此参数时命令拒绝写入。"}],columns:["status","outcome","message","target_type","target","created_target","created_url"],func:async(J,G)=>{if(!J)throw new y("头条号文章发布需要浏览器会话");I(G);const z=String(G.title??"").trim();if(!z)throw new V("INVALID_INPUT","文章标题不能为空");if(z.length<2||z.length>30)throw new V("INVALID_INPUT",`头条号标题需 2~30 字,当前 ${z.length} 字`);const D=await v(G),X={cover:typeof G.cover==="string"?G.cover.trim():""},W=await b(J,{title:z,body:D,format:G.html?"html":"markdown",profile:toutiaoProfile,publishParams:X}),Y=W.images.uploaded.length|0,Z=W.images.failed.length|0;let $="已正式发布头条号文章";if(Y||Z)$+=`(图片:${Y} 张已转存${Z?`,${Z} 张失败`:""})`;return n($,"article","","created",{created_target:"article:"+W.id,created_url:W.url})}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "publishport-opencli",
3
- "version": "1.8.5-pp.39",
3
+ "version": "1.8.5-pp.40",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": false