publishport-opencli 1.0.10 → 1.0.12

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 CHANGED
@@ -47382,6 +47382,48 @@
47382
47382
  "sourceFile": "xianyu/item.js",
47383
47383
  "navigateBefore": false
47384
47384
  },
47385
+ {
47386
+ "site": "xianyu",
47387
+ "name": "item-stats",
47388
+ "description": "读每个在售商品的经营数据(曝光/浏览/询单/支付/退款,逐商品一行)",
47389
+ "access": "read",
47390
+ "domain": "seller.goofish.com",
47391
+ "strategy": "cookie",
47392
+ "browser": true,
47393
+ "args": [
47394
+ {
47395
+ "name": "range",
47396
+ "type": "str",
47397
+ "default": "recent1d",
47398
+ "required": false,
47399
+ "help": "时间范围:recent1d / recent7d / recent30d"
47400
+ }
47401
+ ],
47402
+ "columns": [
47403
+ "itemId",
47404
+ "title",
47405
+ "price",
47406
+ "exposureCount",
47407
+ "exposureUsers",
47408
+ "viewCount",
47409
+ "viewUsers",
47410
+ "inquiryUsers",
47411
+ "payUsers",
47412
+ "payOrders",
47413
+ "payAmount",
47414
+ "viewPayCvr",
47415
+ "refundUsers",
47416
+ "refundOrders",
47417
+ "refundAmount",
47418
+ "refundOkUsers",
47419
+ "refundOkOrders",
47420
+ "refundOkAmount"
47421
+ ],
47422
+ "type": "js",
47423
+ "modulePath": "xianyu/item-stats.js",
47424
+ "sourceFile": "xianyu/item-stats.js",
47425
+ "navigateBefore": false
47426
+ },
47385
47427
  {
47386
47428
  "site": "xianyu",
47387
47429
  "name": "location",
@@ -47715,6 +47757,33 @@
47715
47757
  "sourceFile": "xianyu/manage.js",
47716
47758
  "navigateBefore": false
47717
47759
  },
47760
+ {
47761
+ "site": "xianyu",
47762
+ "name": "promote-stats",
47763
+ "description": "读超级擦亮(付费推广)投放数据:花费/曝光/点击/询单/下单/单次成本 + 账户余额 + 计划状态",
47764
+ "access": "read",
47765
+ "domain": "seller.goofish.com",
47766
+ "strategy": "cookie",
47767
+ "browser": true,
47768
+ "args": [
47769
+ {
47770
+ "name": "range",
47771
+ "type": "str",
47772
+ "default": "today",
47773
+ "required": false,
47774
+ "help": "时间范围:today / yesterday / recent7d / recent30d / recent90d"
47775
+ }
47776
+ ],
47777
+ "columns": [
47778
+ "metric",
47779
+ "value",
47780
+ "compare"
47781
+ ],
47782
+ "type": "js",
47783
+ "modulePath": "xianyu/promote-stats.js",
47784
+ "sourceFile": "xianyu/promote-stats.js",
47785
+ "navigateBefore": false
47786
+ },
47718
47787
  {
47719
47788
  "site": "xianyu",
47720
47789
  "name": "publish",
@@ -47949,6 +48018,62 @@
47949
48018
  "sourceFile": "xianyu/search.js",
47950
48019
  "navigateBefore": false
47951
48020
  },
48021
+ {
48022
+ "site": "xianyu",
48023
+ "name": "seller-data",
48024
+ "description": "读卖家工作台经营数据(曝光/访问/浏览/转化/复购/售后等指标卡)",
48025
+ "access": "read",
48026
+ "domain": "seller.goofish.com",
48027
+ "strategy": "cookie",
48028
+ "browser": true,
48029
+ "args": [
48030
+ {
48031
+ "name": "page",
48032
+ "type": "str",
48033
+ "default": "overview",
48034
+ "required": false,
48035
+ "help": "数据页:overview / commodity / fans / service"
48036
+ },
48037
+ {
48038
+ "name": "range",
48039
+ "type": "str",
48040
+ "default": "recent1d",
48041
+ "required": false,
48042
+ "help": "时间范围:recent1d / recent7d / recent30d"
48043
+ }
48044
+ ],
48045
+ "columns": [
48046
+ "metric",
48047
+ "value",
48048
+ "compare"
48049
+ ],
48050
+ "type": "js",
48051
+ "modulePath": "xianyu/seller-data.js",
48052
+ "sourceFile": "xianyu/seller-data.js",
48053
+ "navigateBefore": false
48054
+ },
48055
+ {
48056
+ "site": "xianyu",
48057
+ "name": "seller-list",
48058
+ "description": "读卖家工作台列表页(refund / review / complaint / income / expense / shipping-template)—— 表头即列,动态返回",
48059
+ "access": "read",
48060
+ "domain": "seller.goofish.com",
48061
+ "strategy": "cookie",
48062
+ "browser": true,
48063
+ "args": [
48064
+ {
48065
+ "name": "page",
48066
+ "type": "str",
48067
+ "required": true,
48068
+ "positional": true,
48069
+ "help": "列表页:refund / review / complaint / income / expense / shipping-template"
48070
+ }
48071
+ ],
48072
+ "type": "js",
48073
+ "modulePath": "xianyu/seller-list.js",
48074
+ "sourceFile": "xianyu/seller-list.js",
48075
+ "navigateBefore": false
48076
+ },
47952
48077
  {
47953
48078
  "site": "xianyu",
47954
48079
  "name": "user",
@@ -0,0 +1,2 @@
1
+ import{CommandExecutionError as Y,ArgumentError as O}from"@jackwener/opencli/errors";import{cli as P,Strategy as f}from"@jackwener/opencli/registry";const $={recent1d:"近1天",recent7d:"近7天",recent30d:"近30天"},C="https://seller.goofish.com/?site=COMMONPRO#/seller-data/commodity",b={商品曝光次数:"exposureCount",商品曝光人数:"exposureUsers",商品浏览次数:"viewCount",商品浏览人数:"viewUsers",询单人数:"inquiryUsers",支付人数:"payUsers",支付订单数:"payOrders",支付金额:"payAmount",浏览支付转化率:"viewPayCvr",发起退款人数:"refundUsers",发起退款订单数:"refundOrders",发起退款金额:"refundAmount",成功退款人数:"refundOkUsers",成功退款订单数:"refundOkOrders",成功退款金额:"refundOkAmount"};function j(q){const W=Array.from(document.querySelectorAll("thead th")).map((z)=>(z.innerText||"").trim());if(!W.length)return{error:"no-table"};const F={};W.forEach((z,v)=>{if(q[z]!=null)F[q[z]]=v});const J=[];document.querySelectorAll("tbody tr").forEach((z)=>{const v=Array.from(z.querySelectorAll("td"));if(!v.length)return;if(v.every((V)=>!(V.innerText||"").trim()))return;const Q=(v[0].innerText||"").replace(/\r/g,""),B=Q.match(/(\d{10,})/),L=Q.match(/¥\s*([\d.]+)/),H=Q.split(`
2
+ `).map((V)=>V.trim()).filter(Boolean)[0]||"";if(!B)return;const S={itemId:B[1],title:H,price:L?L[1]:""};for(const V of Object.keys(q)){const U=q[V],Z=F[U];S[U]=Z!=null&&v[Z]?(v[Z].innerText||"").trim():""}J.push(S)});return{rows:J}}P({site:"xianyu",name:"item-stats",access:"read",description:"读每个在售商品的经营数据(曝光/浏览/询单/支付/退款,逐商品一行)",domain:"seller.goofish.com",strategy:f.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"range",default:"recent1d",help:"时间范围:recent1d / recent7d / recent30d"}],columns:["itemId","title","price","exposureCount","exposureUsers","viewCount","viewUsers","inquiryUsers","payUsers","payOrders","payAmount","viewPayCvr","refundUsers","refundOrders","refundAmount","refundOkUsers","refundOkOrders","refundOkAmount"],func:async(q,W)=>{const F=String(W.range||"recent1d").trim(),J=$[F];if(!J)throw new O(`--range 只能是 ${Object.keys($).join(" / ")},收到 "${F}"`);await q.goto(C);await q.wait(3);if(F!=="recent1d"){await q.evaluate((Q)=>{Array.from(document.querySelectorAll("label,span,div,button")).filter((B)=>B.children.length===0&&(B.innerText||"").trim()===Q).forEach((B)=>B.click())},J);await q.wait(2)}const z=await q.evaluate(j,b);if(z.error==="no-table")throw new Y("商品数据表未渲染 —— 可能未登录卖家中心 / 页面改版 / 无权限");const v=z.rows||[];if(v.length===0)throw new Y("商品数据表为空 —— 该账号可能无在售商品或当前范围无数据");return v}});export const __test__={readItemTable:j,HEADER_MAP:b,RANGE_TEXT:$};
@@ -0,0 +1,2 @@
1
+ import{CommandExecutionError as C,ArgumentError as H}from"@jackwener/opencli/errors";import{cli as M,Strategy as O}from"@jackwener/opencli/registry";const Z={today:"今日",yesterday:"昨日",recent7d:"近7日",recent30d:"近30日",recent90d:"近90日"},S="https://seller.goofish.com/?site=COMMONPRO#/seller-ad/home";function $(){const B=[],I=(z,f,q)=>{if(z&&f!==""&&f!=null)B.push({metric:z,value:f,compare:q||""})};document.querySelectorAll('[class*="metricCard--"]').forEach((z)=>{const f=(z.innerText||"").split(`
2
+ `).map((U)=>U.trim()).filter(Boolean);if(f.length<2)return;const q=f[0],L=f[1],Y=f.slice(2).join(" ");I(q,L,Y)});const P=(z)=>{const f=Array.from(document.querySelectorAll("div,span")).filter((q)=>q.children.length===0&&(q.innerText||"").trim().replace(/(.*)|\(.*\)/g,"")===z);for(const q of f){let L=q.parentElement;for(let Y=0;Y<3&&L;Y++){const U=(L.innerText||"").match(/-?[\d,]+(\.\d+)?%?/);if(U&&U[0]!==z)return U[0];L=L.parentElement}}return null};[["擦亮中计划","擦亮中计划"],["已暂停擦亮计划","已暂停擦亮计划"],["即将消耗完成计划","即将消耗完成计划"],["已完成擦亮计划","已完成擦亮计划"]].forEach(([z,f])=>{const q=P(f);if(q!=null)I(z,q,"")});const Q=document.body?document.body.innerText.replace(/\s+/g," "):"",J=/[^\d-]*(-?[\d,]+(?:\s*\.\s*\d+)?)/,W=Q.match(new RegExp("可用余额[((]元[))]"+J.source));if(W)I("可用余额(元)",W[1].replace(/\s+/g,""),"");const F=Q.match(new RegExp("今日花费[((]元[))]"+J.source));if(F)I("今日花费(元)",F[1].replace(/\s+/g,""),"");return B}M({site:"xianyu",name:"promote-stats",access:"read",description:"读超级擦亮(付费推广)投放数据:花费/曝光/点击/询单/下单/单次成本 + 账户余额 + 计划状态",domain:"seller.goofish.com",strategy:O.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"range",default:"today",help:"时间范围:today / yesterday / recent7d / recent30d / recent90d"}],columns:["metric","value","compare"],func:async(B,I)=>{const P=String(I.range||"today").trim(),Q=Z[P];if(!Q)throw new H(`--range 只能是 ${Object.keys(Z).join(" / ")},收到 "${P}"`);await B.goto(S);await B.wait(4);if(P!=="today"){await B.evaluate((W)=>{Array.from(document.querySelectorAll("label,span,div,button")).filter((F)=>F.children.length===0&&(F.innerText||"").trim()===W).forEach((F)=>F.click())},Q);await B.wait(2)}const J=await B.evaluate($);if(!Array.isArray(J)||J.length===0)throw new C("未读到擦亮数据 —— 可能未开通超级擦亮 / 未登录 / 页面改版");return J}});export const __test__={readAdDashboard:$,RANGE_TEXT:Z};
@@ -0,0 +1 @@
1
+ import{ArgumentError as $,CommandExecutionError as M}from"@jackwener/opencli/errors";import{cli as j,Strategy as L}from"@jackwener/opencli/registry";const V={overview:{hash:"#/seller-data/data",label:"数据总览"},commodity:{hash:"#/seller-data/commodity",label:"商品数据"},fans:{hash:"#/seller-data/fanData",label:"粉丝数据"},service:{hash:"#/seller-data/customerService",label:"客服数据"}},W={recent1d:"近1天",recent7d:"近7天",recent30d:"近30天"},x="https://seller.goofish.com/?site=COMMONPRO";function H(){const f=[],I=new Set;document.querySelectorAll('[class*="statValue--"]').forEach((B)=>{let b=B,q=null,D=null;for(let Z=0;Z<5&&b;Z++){b=b.parentElement;if(!b)break;q=b.querySelector('[class*="statHeader--"]');if(q){D=b;break}}if(!q||!D)return;const z=(q.innerText||"").trim(),Q=(B.innerText||"").trim();if(!z)return;const J=D.querySelector('[class*="statPrevious--"]'),F=J?(J.innerText||"").replace(/\s+/g," ").trim():"",Y=z+"|"+Q;if(I.has(Y))return;I.add(Y);f.push({metric:z,value:Q,compare:F})});return f}j({site:"xianyu",name:"seller-data",access:"read",description:"读卖家工作台经营数据(曝光/访问/浏览/转化/复购/售后等指标卡)",domain:"seller.goofish.com",strategy:L.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"page",default:"overview",help:`数据页:${Object.keys(V).join(" / ")}`},{name:"range",default:"recent1d",help:"时间范围:recent1d / recent7d / recent30d"}],columns:["metric","value","compare"],func:async(f,I)=>{const B=String(I.page||"overview").trim(),b=V[B];if(!b)throw new $(`--page 只能是 ${Object.keys(V).join(" / ")},收到 "${B}"`);const q=String(I.range||"recent1d").trim(),D=W[q];if(!D)throw new $(`--range 只能是 ${Object.keys(W).join(" / ")},收到 "${q}"`);await f.goto(`${x}${b.hash}`);await f.wait(3);if(q!=="recent1d"){await f.evaluate((Q)=>{const J=Array.from(document.querySelectorAll("label,span,div,button")).filter((F)=>F.children.length===0&&(F.innerText||"").trim()===Q);J.forEach((F)=>F.click());return J.length},D);await f.wait(2)}const z=await f.evaluate(H);if(!Array.isArray(z)||z.length===0)throw new M(`未读到任何指标卡(page=${B})—— 可能未登录卖家中心、页面结构改版或该账号无权限`);return z}});export const __test__={readCards:H,PAGE_ROUTES:V,RANGE_TEXT:W};
@@ -0,0 +1 @@
1
+ import{CommandExecutionError as M,ArgumentError as V}from"@jackwener/opencli/errors";import{cli as W,Strategy as X}from"@jackwener/opencli/registry";const D={refund:{hash:"#/seller-trade/refund-manage",label:"退款管理"},review:{hash:"#/seller-trade/evaluation-manage",label:"评价管理"},complaint:{hash:"#/seller-trade/complaint-manage",label:"投诉管理"},income:{hash:"#/seller-finance/income-bill",label:"收入账单"},expense:{hash:"#/seller-finance/expense-bill",label:"支出账单"},"shipping-template":{hash:"#/seller-item/post-temple",label:"运费模版"}};function N(){const q=Array.from(document.querySelectorAll("thead th")).map((z)=>(z.innerText||"").trim()).filter(Boolean);if(!q.length)return{error:"no-table"};const F=[];let B=!1;document.querySelectorAll("tbody tr").forEach((z)=>{const f=Array.from(z.querySelectorAll("td"));if(!f.length)return;if(f.every((I)=>!(I.innerText||"").trim()))return;if(f.length===1)return;if(f.length!==q.length){B=!0;return}const H={};f.forEach((I,L)=>{const Q=q[L]||`col${L+1}`;H[Q]=(I.innerText||"").replace(/\s+/g," ").trim()});const J=Object.values(H).filter(Boolean);if(J.length<=1&&/^(暂无数据|没有数据|暂无记录|空空如也)$/.test(J[0]||""))return;F.push(H)});return{heads:q,rows:F,misaligned:B}}W({site:"xianyu",name:"seller-list",access:"read",description:`读卖家工作台列表页(${Object.keys(D).join(" / ")})—— 表头即列,动态返回`,domain:"seller.goofish.com",strategy:X.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"page",required:!0,positional:!0,help:`列表页:${Object.keys(D).join(" / ")}`}],func:async(q,F)=>{const B=String(F.page||"").trim(),z=D[B];if(!z)throw new V(`page 只能是 ${Object.keys(D).join(" / ")},收到 "${B}"`);await q.goto(`https://seller.goofish.com/?site=COMMONPRO${z.hash}`);await q.wait(4);const f=await q.evaluate(N);if(f.error==="no-table")throw new M(`${z.label} 未渲染出表格 —— 可能未登录 / 页面改版 / 无权限`);if(f.misaligned&&(f.rows||[]).length===0)throw new M(`${z.label} 表格单元格数与表头(${(f.heads||[]).length}列)不一致,疑似带选择/展开列,需为该页单独适配以免列错位`);return f.rows||[]}});export const __test__={readTable:N,PAGE_ROUTES:D};
@@ -1,4 +1,4 @@
1
- import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionError as j,ArgumentError as U}from"@jackwener/opencli/errors";import{cli as Wz,Strategy as Fz}from"@jackwener/opencli/registry";import{assertLiteralContent as Oz}from"../_shared/content-guard.js";import{setInputFiles as Uz}from"../_shared/file-inject.js";import{humanType as x,humanWait as N,humanMoveTo as Bz,elementViewportCenter as Dz}from"./human.js";const vz="https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image",n=9,l=20,i=3000,o="|||",I="基础",s=[["基础","默认兜底,万能"],["边框","金句/要点卡"],["备忘","提醒/随手记"],["清新","日常/清单贴士"],["涂写","随笔/碎碎念"],["便签","笔记/提醒"],["光影","情绪/文艺"],["涂鸦","趣味/童话"],["简约","干货/观点"],["手写","日记/情感"],["插图","生活方式/轻松话题"],["美漫","活力/趣味/故事感"],["弥散","弥散光氛围"],["柔和","柔和/温柔金句"],["印刷","印刷海报/排版"],["科技","科技/产品"],["贺卡","节日祝福"],["札记","艺术/水彩氛围"],["书摘","书摘/引用"],["手帐","手帐拼贴"],["几何","醒目/有力主张"]],tz=s.map(([z])=>z),E="文字配图",g="再写一张",r="生成图片",p="下一步",u=".tiptap.ProseMirror",Pz=["_onPublish","onPublish","_onSubmit","_handlePublish"],kz=["_onSave","_onSaveDraft","_onDraft"],m=['[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]"],t=['[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])'],Nz={".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 Iz(z){return z.map((J)=>{const K=R.resolve(J);if(!S.existsSync(K))throw new U(`Image file not found: ${K}`);const Q=R.extname(K).toLowerCase();if(!Nz[Q])throw new U(`Unsupported image format "${Q}". Supported: jpg, png, gif, webp`);return K})}const a='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 hz(z,J=15000){const K=500,Q=Math.max(1,Math.ceil(J/K));for(let Z=0;Z<Q;Z++){if(await z.evaluate(`
1
+ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionError as G,ArgumentError as U}from"@jackwener/opencli/errors";import{cli as Wz,Strategy as Fz}from"@jackwener/opencli/registry";import{assertLiteralContent as Oz}from"../_shared/content-guard.js";import{setInputFiles as Uz}from"../_shared/file-inject.js";import{humanType as x,humanWait as N,humanMoveTo as Bz,elementViewportCenter as Dz}from"./human.js";const vz="https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image",n=9,l=20,i=3000,o="|||",I="基础",s=[["基础","默认兜底,万能"],["边框","金句/要点卡"],["备忘","提醒/随手记"],["清新","日常/清单贴士"],["涂写","随笔/碎碎念"],["便签","笔记/提醒"],["光影","情绪/文艺"],["涂鸦","趣味/童话"],["简约","干货/观点"],["手写","日记/情感"],["插图","生活方式/轻松话题"],["美漫","活力/趣味/故事感"],["弥散","弥散光氛围"],["柔和","柔和/温柔金句"],["印刷","印刷海报/排版"],["科技","科技/产品"],["贺卡","节日祝福"],["札记","艺术/水彩氛围"],["书摘","书摘/引用"],["手帐","手帐拼贴"],["几何","醒目/有力主张"]],tz=s.map(([z])=>z),E="文字配图",g="再写一张",r="生成图片",p="下一步",u=".tiptap.ProseMirror",Pz=["_onPublish","onPublish","_onSubmit","_handlePublish"],kz=["_onSave","_onSaveDraft","_onDraft"],m=['[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]"],t=['[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])'],Nz={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp"};function W(z){if(z&&typeof z==="object"&&typeof z.session==="string"&&Object.prototype.hasOwnProperty.call(z,"data"))return z.data;return z}function Iz(z){return z.map((J)=>{const K=R.resolve(J);if(!S.existsSync(K))throw new U(`Image file not found: ${K}`);const Q=R.extname(K).toLowerCase();if(!Nz[Q])throw new U(`Unsupported image format "${Q}". Supported: jpg, png, gif, webp`);return K})}const a='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 hz(z,J=15000){const K=500,Q=Math.max(1,Math.ceil(J/K));for(let Z=0;Z<Q;Z++){if(await z.evaluate(`
2
2
  (() => {
3
3
  const sels = ${JSON.stringify(a)};
4
4
  return !!document.querySelector(sels);
@@ -80,33 +80,29 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
80
80
  return { ok: actual === normalize(expectedText), actual };
81
81
  })(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
82
82
  `);let q;if(Z.kind==="contenteditable"&&z.insertText){if(!(await z.evaluate(`
83
- ((selector, nextText) => {
83
+ ((selector) => {
84
84
  const __opencli_xhs_fill_phase = "prepare";
85
- const fireBeforeInput = (el, value) => {
86
- try {
87
- el.dispatchEvent(new InputEvent('beforeinput', {
88
- bubbles: true,
89
- data: value,
90
- inputType: 'insertText',
91
- }));
92
- } catch {
93
- el.dispatchEvent(new Event('beforeinput', { bubbles: true }));
94
- }
95
- };
96
85
  const el = Array.from(document.querySelectorAll(selector)).find(node => node && node.offsetParent !== null);
97
86
  if (!el) return { ok: false };
98
87
  el.focus();
99
- el.textContent = '';
100
88
  const selection = window.getSelection();
101
89
  const range = document.createRange();
102
90
  range.selectNodeContents(el);
91
+ selection?.removeAllRanges();
92
+ selection?.addRange(range);
93
+ if ((el.innerText || el.textContent || '').trim()) {
94
+ const deleted = document.execCommand('delete', false);
95
+ if (!deleted) el.textContent = '';
96
+ }
97
+ range.selectNodeContents(el);
103
98
  range.collapse(false);
104
99
  selection?.removeAllRanges();
105
100
  selection?.addRange(range);
106
- fireBeforeInput(el, nextText);
107
101
  return { ok: true };
108
- })(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
109
- `))?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});throw Error(`Could not prepare ${Q} input. Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}try{await x(z,K);q=await z.evaluate(`
102
+ })(${JSON.stringify(Z.sel)})
103
+ `))?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});throw Error(`Could not prepare ${Q} input. Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}await z.wait({time:0.35});try{const X=String(K).split(`
104
+ `);for(let j=0;j<X.length;j++){if(j>0)if(typeof z.pressKey==="function"){await z.pressKey("Enter");await z.wait({time:0.15})}else await z.insertText(`
105
+ `);if(X[j])await x(z,X[j])}q=await z.evaluate(`
110
106
  ((selector, expectedText) => {
111
107
  const __opencli_xhs_fill_phase = "verify";
112
108
  const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
@@ -129,7 +125,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
129
125
  const actual = normalize(el.innerText || el.textContent || '');
130
126
  return { ok: actual === normalize(expectedText), actual };
131
127
  })(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
132
- `)}catch{q=await $()}}else q=await $();if(!q?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});const V=typeof q?.actual==="string"?q.actual:"";throw Error(`Failed to set ${Q}. Expected "${K}", got "${V}". Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}}async function Mz(z,J){return Y(await z.evaluate(`
128
+ `)}catch{q=await $()}}else q=await $();if(!q?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});const V=typeof q?.actual==="string"?q.actual:"";throw Error(`Failed to set ${Q}. Expected "${K}", got "${V}". Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}}async function Mz(z,J){return W(await z.evaluate(`
133
129
  (selectors => {
134
130
  const el = selectors
135
131
  .map(sel => Array.from(document.querySelectorAll(sel)))
@@ -234,7 +230,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
234
230
  }
235
231
  return count;
236
232
  })(${JSON.stringify(z)}, ${JSON.stringify(J)})
237
- `}async function zJ(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return Y(await z.evaluate(`
233
+ `}async function zJ(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return W(await z.evaluate(`
238
234
  (text => {
239
235
  const ok = document.execCommand('insertText', false, text);
240
236
  const active = document.activeElement;
@@ -251,7 +247,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
251
247
  if (!editor) return -1;
252
248
  return (editor.innerText || editor.textContent || '').length;
253
249
  })(${JSON.stringify(z)})
254
- `}async function Lz(z,J,K){if(!await Mz(z,J))throw new j(`Could not attach topic "${K}": body editor not found`);const Z=Number(Y(await z.evaluate(Kz(K,J))))||0;if(typeof z.pressKey==="function")try{await z.pressKey("Enter")}catch{}if(typeof z.insertText!=="function")throw new j(`Could not attach topic "${K}": page.insertText is unavailable`);try{await x(z,`#${K}`)}catch{throw new j(`Could not attach topic "${K}": failed to type inline topic query`)}await N(z,1.2);if(typeof z.pressKey!=="function")throw new j(`Could not attach topic "${K}": page.pressKey is unavailable`);try{await z.pressKey("Enter")}catch(q){throw new j(`Could not attach topic "${K}": failed to accept suggestion (${q&&q.message||q})`)}await z.wait({time:0.8});if((Number(Y(await z.evaluate(Kz(K,J))))||0)<=Z)throw new j(`Could not attach topic "${K}": no real topic entity appeared after selection`)}async function _z(z,J,K){const Q=[],Z=[];for(const $ of K){let q=-1;try{const V=Number(Y(await z.evaluate(Qz(J))));q=Number.isFinite(V)?V:-1}catch{}try{await Lz(z,J,$);Q.push($);await z.wait({time:0.4})}catch(V){Z.push({topic:$,reason:V&&V.message||String(V)});if(q>=0&&typeof z.pressKey==="function")try{const W=$.length+6;for(let G=0;G<W;G++){const O=Number(Y(await z.evaluate(Qz(J))));if(!Number.isFinite(O)||O<0||O<=q)break;await z.pressKey("Backspace");await z.wait({time:0.15})}}catch{}}}return{added:Q,failed:Z}}async function fz(z){const J=await z.evaluate(`
250
+ `}async function Lz(z,J,K){if(!await Mz(z,J))throw new G(`Could not attach topic "${K}": body editor not found`);const Z=Number(W(await z.evaluate(Kz(K,J))))||0;if(typeof z.pressKey==="function")try{await z.pressKey("Enter")}catch{}if(typeof z.insertText!=="function")throw new G(`Could not attach topic "${K}": page.insertText is unavailable`);try{await x(z,`#${K}`)}catch{throw new G(`Could not attach topic "${K}": failed to type inline topic query`)}await N(z,1.2);if(typeof z.pressKey!=="function")throw new G(`Could not attach topic "${K}": page.pressKey is unavailable`);try{await z.pressKey("Enter")}catch(q){throw new G(`Could not attach topic "${K}": failed to accept suggestion (${q&&q.message||q})`)}await z.wait({time:0.8});if((Number(W(await z.evaluate(Kz(K,J))))||0)<=Z)throw new G(`Could not attach topic "${K}": no real topic entity appeared after selection`)}async function _z(z,J,K){const Q=[],Z=[];for(const $ of K){let q=-1;try{const V=Number(W(await z.evaluate(Qz(J))));q=Number.isFinite(V)?V:-1}catch{}try{await Lz(z,J,$);Q.push($);await z.wait({time:0.4})}catch(V){Z.push({topic:$,reason:V&&V.message||String(V)});if(q>=0&&typeof z.pressKey==="function")try{const X=$.length+6;for(let j=0;j<X;j++){const O=Number(W(await z.evaluate(Qz(J))));if(!Number.isFinite(O)||O<0||O<=q)break;await z.pressKey("Backspace");await z.wait({time:0.15})}}catch{}}}return{added:Q,failed:Z}}async function fz(z){const J=await z.evaluate(`
255
251
  () => {
256
252
  const isVisible = (el) => {
257
253
  if (!el || el.offsetParent === null) return false;
@@ -300,7 +296,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
300
296
  }
301
297
  return { ok: false, visibleTexts };
302
298
  }
303
- `);if(J?.ok)await z.wait({time:1});return J}async function h(z,J,K=7000){const Q=500,Z=Math.max(1,Math.ceil(K/Q));let $={ok:!1};for(let q=0;q<Z;q++){$=Y(await z.evaluate(`
299
+ `);if(J?.ok)await z.wait({time:1});return J}async function h(z,J,K=7000){const Q=500,Z=Math.max(1,Math.ceil(K/Q));let $={ok:!1};for(let q=0;q<Z;q++){$=W(await z.evaluate(`
304
300
  ((cfg) => {
305
301
  const __opencli_xhs_click_label = { wantLabel: ${JSON.stringify(J)} };
306
302
  const wantLabel = ${JSON.stringify(J)};
@@ -352,7 +348,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
352
348
  selection?.addRange(range);
353
349
  return { ok: true };
354
350
  })()
355
- `);return Y(J)}async function Rz(z){const J=await z.evaluate(`
351
+ `);return W(J)}async function Rz(z){const J=await z.evaluate(`
356
352
  (() => {
357
353
  const __opencli_xhs_card_text = true;
358
354
  const sel = ${JSON.stringify(u)};
@@ -361,7 +357,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
361
357
  const text = active ? (active.innerText || active.textContent || '').trim() : '';
362
358
  return { ok: !!text, text };
363
359
  })()
364
- `);return Y(J)}async function Zz(z){const J=await z.evaluate(`
360
+ `);return W(J)}async function Zz(z){const J=await z.evaluate(`
365
361
  (() => {
366
362
  const __opencli_xhs_card_count = true;
367
363
  const sel = ${JSON.stringify(u)};
@@ -370,16 +366,16 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
370
366
  const activeText = active ? (active.innerText || active.textContent || '').trim() : '';
371
367
  return { ok: true, count: editors.length, activeEmpty: !activeText };
372
368
  })()
373
- `);return Y(J)}async function yz(z,J=8000){const K=300,Q=Math.ceil(J/K);for(let Z=0;Z<Q;Z++){if((await Zz(z))?.count>=1)return!0;await z.wait({time:K/1000})}return!1}async function bz(z,J,K=6000){const Q=300,Z=Math.ceil(K/Q);for(let $=0;$<Z;$++){const q=await Zz(z);if(q?.count>=J&&q?.activeEmpty)return!0;await z.wait({time:Q/1000})}return!1}async function wz(z,J,K=4){for(let Q=0;Q<K;Q++){if(!(await h(z,g))?.ok)return!1;if(await bz(z,J,2500))return!0}return!1}async function Tz(z){const J=await z.evaluate(`
369
+ `);return W(J)}async function yz(z,J=8000){const K=300,Q=Math.ceil(J/K);for(let Z=0;Z<Q;Z++){if((await Zz(z))?.count>=1)return!0;await z.wait({time:K/1000})}return!1}async function bz(z,J,K=6000){const Q=300,Z=Math.ceil(K/Q);for(let $=0;$<Z;$++){const q=await Zz(z);if(q?.count>=J&&q?.activeEmpty)return!0;await z.wait({time:Q/1000})}return!1}async function wz(z,J,K=4){for(let Q=0;Q<K;Q++){if(!(await h(z,g))?.ok)return!1;if(await bz(z,J,2500))return!0}return!1}async function Tz(z){const J=await z.evaluate(`
374
370
  (() => {
375
371
  const __opencli_xhs_preview_ready = true;
376
372
  const ready = Array.from(document.querySelectorAll('button'))
377
373
  .some((b) => b.offsetParent !== null && (b.innerText || '').replace(/\\s+/g, '') === '下一步');
378
374
  return { ok: ready };
379
375
  })()
380
- `);return Y(J)}async function Cz(z,J=6){const K=400,Q=Math.ceil(3000/K);for(let Z=0;Z<J;Z++){if(!(await h(z,r))?.ok&&Z===0)return!1;for(let q=0;q<Q;q++){if((await Tz(z))?.ok)return!0;await z.wait({time:K/1000})}}return!1}async function Sz(z,J,K){J=String(J).replace(/\\n/g,`
381
- `);if(!(await Az(z))?.ok)throw new j(`文字配图: could not focus card editor #${K+1}`);if(typeof z.insertText==="function"){const $=J.split(`
382
- `);for(let q=0;q<$.length;q++){if(q>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if($[q])await x(z,$[q])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await Rz(z))?.ok)throw new j(`文字配图: card editor #${K+1} is empty after typing`)}async function xz(z,J){if(!J||J===I)return I;const K=Y(await z.evaluate(`
376
+ `);return W(J)}async function Cz(z,J=6){const K=400,Q=Math.ceil(3000/K);for(let Z=0;Z<J;Z++){if(!(await h(z,r))?.ok&&Z===0)return!1;for(let q=0;q<Q;q++){if((await Tz(z))?.ok)return!0;await z.wait({time:K/1000})}}return!1}async function Sz(z,J,K){J=String(J).replace(/\\n/g,`
377
+ `);if(!(await Az(z))?.ok)throw new G(`文字配图: could not focus card editor #${K+1}`);if(typeof z.insertText==="function"){const $=J.split(`
378
+ `);for(let q=0;q<$.length;q++){if(q>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if($[q])await x(z,$[q])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await Rz(z))?.ok)throw new G(`文字配图: card editor #${K+1} is empty after typing`)}async function xz(z,J){if(!J||J===I)return I;const K=W(await z.evaluate(`
383
379
  (async () => {
384
380
  const __opencli_xhs_card_styles = true;
385
381
  const want = ${JSON.stringify(J)};
@@ -408,7 +404,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
408
404
  }
409
405
  return { ok: seen.length > 0, styles: seen, found: false };
410
406
  })()
411
- `));if(!K?.found)throw new j(`文字配图: requested style "${J}" is not available for this content (options: ${(K?.styles||[]).join(" / ")||"none"}). Choose an available style or omit --card-style to use ${I}.`);if(!(await h(z,J))?.ok)throw new j(`文字配图: could not click requested style "${J}".`);await z.wait({time:0.6});return J}async function $z(z){const J=await z.evaluate(`
407
+ `));if(!K?.found)throw new G(`文字配图: requested style "${J}" is not available for this content (options: ${(K?.styles||[]).join(" / ")||"none"}). Choose an available style or omit --card-style to use ${I}.`);if(!(await h(z,J))?.ok)throw new G(`文字配图: could not click requested style "${J}".`);await z.wait({time:0.6});return J}async function $z(z){const J=await z.evaluate(`
412
408
  (() => {
413
409
  const __opencli_xhs_composer_media_count = true;
414
410
  const visibleBox = (el) => {
@@ -440,7 +436,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
440
436
  }
441
437
  return { ok: true, count };
442
438
  })()
443
- `);return Y(J)}async function qz(z,J,K){const Q=await $z(z);if(!Q||typeof Q.count!=="number")throw new j(`${K}: could not verify current composer media count`);if(Q.count<J){await z.screenshot({path:"/tmp/xhs_publish_media_debug.png"});throw new j(`${K}: expected at least ${J} visible media item(s), got ${Q.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png`)}}async function Ez(z,J,K){if(!(await h(z,E))?.ok){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new j(`文字配图: could not click "${E}" entry. Debug: /tmp/xhs_publish_textimage_debug.png`)}if(!await yz(z)){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new j(`文字配图: 写文字 card editor did not appear after clicking "${E}". Debug: /tmp/xhs_publish_textimage_debug.png`)}for(let V=0;V<J.length;V++){if(V>0){if(!await wz(z,V+1)){await z.screenshot({path:"/tmp/xhs_publish_addcard_debug.png"});throw new j(`文字配图: new card editor #${V+1} did not render after "${g}". Debug: /tmp/xhs_publish_addcard_debug.png`)}}await Sz(z,J[V],V)}if(!await Cz(z)){await z.screenshot({path:"/tmp/xhs_publish_generate_debug.png"});throw new j(`文字配图: "${r}" did not advance to the 预览图片 step. `+"Debug: /tmp/xhs_publish_generate_debug.png")}const $=await xz(z,K);if(!(await h(z,p))?.ok){await z.screenshot({path:"/tmp/xhs_publish_next_debug.png"});throw new j(`文字配图: could not click "${p}". Debug: /tmp/xhs_publish_next_debug.png`)}await z.wait({time:2});return $}async function Vz(z){return z.evaluate(`
439
+ `);return W(J)}async function qz(z,J,K){const Q=await $z(z);if(!Q||typeof Q.count!=="number")throw new G(`${K}: could not verify current composer media count`);if(Q.count<J){await z.screenshot({path:"/tmp/xhs_publish_media_debug.png"});throw new G(`${K}: expected at least ${J} visible media item(s), got ${Q.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png`)}}async function Ez(z,J,K){if(!(await h(z,E))?.ok){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new G(`文字配图: could not click "${E}" entry. Debug: /tmp/xhs_publish_textimage_debug.png`)}if(!await yz(z)){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new G(`文字配图: 写文字 card editor did not appear after clicking "${E}". Debug: /tmp/xhs_publish_textimage_debug.png`)}for(let V=0;V<J.length;V++){if(V>0){if(!await wz(z,V+1)){await z.screenshot({path:"/tmp/xhs_publish_addcard_debug.png"});throw new G(`文字配图: new card editor #${V+1} did not render after "${g}". Debug: /tmp/xhs_publish_addcard_debug.png`)}}await Sz(z,J[V],V)}if(!await Cz(z)){await z.screenshot({path:"/tmp/xhs_publish_generate_debug.png"});throw new G(`文字配图: "${r}" did not advance to the 预览图片 step. `+"Debug: /tmp/xhs_publish_generate_debug.png")}const $=await xz(z,K);if(!(await h(z,p))?.ok){await z.screenshot({path:"/tmp/xhs_publish_next_debug.png"});throw new G(`文字配图: could not click "${p}". Debug: /tmp/xhs_publish_next_debug.png`)}await z.wait({time:2});return $}async function Vz(z){return z.evaluate(`
444
440
  () => {
445
441
  const text = (document.body?.innerText || '').replace(/s+/g, ' ').trim();
446
442
  const hasTitleInput = !!Array.from(document.querySelectorAll('input, textarea')).find((el) => {
@@ -478,14 +474,14 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
478
474
  if (el && el.offsetParent !== null) return true;
479
475
  }
480
476
  return false;
481
- })()`))return!0;if(Z<Q-1)await z.wait({time:K/1000})}return!1}async function cz(z){const J=Y(await z.evaluate(`
477
+ })()`))return!0;if(Z<Q-1)await z.wait({time:K/1000})}return!1}async function cz(z){const J=W(await z.evaluate(`
482
478
  (() => {
483
479
  const __opencli_xhs_restore_srcs = true;
484
480
  return Array.from(document.querySelectorAll('.img-container img'))
485
481
  .map((img) => img.currentSrc || img.src || '')
486
482
  .filter(Boolean);
487
483
  })()
488
- `));return Array.isArray(J)?J:[]}async function Hz(z,J){if(!Array.isArray(J)||J.length===0)return 0;const K=Y(await z.evaluate(`
484
+ `));return Array.isArray(J)?J:[]}async function Hz(z,J){if(!Array.isArray(J)||J.length===0)return 0;const K=W(await z.evaluate(`
489
485
  (async (wanted) => {
490
486
  const __opencli_xhs_delete_by_src = true;
491
487
  const want = new Set(wanted);
@@ -516,7 +512,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
516
512
  }
517
513
  return removed;
518
514
  })(${JSON.stringify(J)})
519
- `));return Number(K)||0}function dz(z){const J=String(z&&(z.url||z.key)||""),K=String(z&&z.method||"").toUpperCase(),Q=Number(z&&z.status||0);if(K!=="PUT")return null;if(!/ros-upload[^/]*\/spectrum\//.test(J))return null;if(Q<200||Q>=300)return null;const Z=J.match(/\/spectrum\/([^?#]+)/);return Z?Z[1]:J}function jz(z){return Math.min(45,6+z*4)}async function Gz(z,J,K,Q=90000){if(J<=0)return{ok:!0,completed:0};if(!K||typeof z.readNetworkCapture!=="function"){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}const Z=1500,q=Math.ceil(9000/Z),V=Math.ceil(Q/Z),W=new Set;for(let G=0;G<V;G++){const O=await z.readNetworkCapture().catch(()=>[]);for(const D of Array.isArray(O)?O:[]){const X=dz(D);if(X)W.add(X)}if(W.size>=J)return{ok:!0,completed:W.size};if(G>=q&&W.size===0){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}await z.wait({time:Z/1000})}return{ok:!1,completed:W.size}}async function nz(z,J,K){try{await z.goto("https://creator.xiaohongshu.com/new/note-manager")}catch{return null}const Q=J.trim();for(let Z=0;Z<K;Z++){await z.wait({time:1.5});let $;try{$=Y(await z.evaluate(`(() => {
515
+ `));return Number(K)||0}function dz(z){const J=String(z&&(z.url||z.key)||""),K=String(z&&z.method||"").toUpperCase(),Q=Number(z&&z.status||0);if(K!=="PUT")return null;if(!/ros-upload[^/]*\/spectrum\//.test(J))return null;if(Q<200||Q>=300)return null;const Z=J.match(/\/spectrum\/([^?#]+)/);return Z?Z[1]:J}function jz(z){return Math.min(45,6+z*4)}async function Gz(z,J,K,Q=90000){if(J<=0)return{ok:!0,completed:0};if(!K||typeof z.readNetworkCapture!=="function"){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}const Z=1500,q=Math.ceil(9000/Z),V=Math.ceil(Q/Z),X=new Set;for(let j=0;j<V;j++){const O=await z.readNetworkCapture().catch(()=>[]);for(const D of Array.isArray(O)?O:[]){const Y=dz(D);if(Y)X.add(Y)}if(X.size>=J)return{ok:!0,completed:X.size};if(j>=q&&X.size===0){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}await z.wait({time:Z/1000})}return{ok:!1,completed:X.size}}async function nz(z,J,K){try{await z.goto("https://creator.xiaohongshu.com/new/note-manager")}catch{return null}const Q=J.trim();for(let Z=0;Z<K;Z++){await z.wait({time:1.5});let $;try{$=W(await z.evaluate(`(() => {
520
516
  const noteIdRe = /"noteId":"([0-9a-f]{24})"/;
521
517
  return Array.from(document.querySelectorAll('div.note[data-impression], div.note')).slice(0, 3).map((card) => {
522
518
  const impression = card.getAttribute('data-impression') || '';
@@ -525,7 +521,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
525
521
  const dateText = (card.querySelector('.time_status, .time')?.innerText || '').trim();
526
522
  return { id, title, dateText };
527
523
  }).filter((entry) => entry.title);
528
- })()`))}catch{return null}if(Array.isArray($)){const q=$.find((V)=>{const W=String(V.title||"").trim();if(!(W===Q||W.startsWith(Q)||Q.startsWith(W)))return!1;const G=String(V.dateText||"").match(/(\d{4})年(\d{2})月(\d{2})日 (\d{2}):(\d{2})/);if(!G)return!0;const O=Date.UTC(Number(G[1]),Number(G[2])-1,Number(G[3]),Number(G[4]),Number(G[5]))-28800000;return Math.abs(Date.now()-O)<=93600000});if(q)return q}}return null}Wz({site:"xiaohongshu",name:"publish",access:"write",description:"小红书发布图文笔记 (creator center UI automation)",domain:"creator.xiaohongshu.com",strategy:Fz.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:`文字配图卡片文字,多张卡片用 ${o} 分隔,卡内换行用 \\n`},{name:"card-style",required:!1,help:`文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败。省略时使用${I}。可选: ${s.map(([z,J])=>`${z}(${J})`).join(" ")}`},{name:"topics",required:!1,help:"话题标签,逗号分隔,不含 # 号。平台话题库匹配不上的会自动跳过并在结果中提示,不影响发布"},{name:"draft",type:"bool",default:!1,help:"保存为草稿,不直接发布"}],columns:["status","detail","url"],func:async(z,J)=>{if(!z)throw Error("Browser page required");const K=String(J.title??"").trim(),Q=J.file?String(J.file).trim():"";let Z=String(J.content??"").trim();if(Q){if(Z)throw new U("正文位置参数和 --file 只能二选一,不能同时给");const H=R.resolve(Q);if(!S.existsSync(H))throw new U(`--file 指向的文件不存在: ${H}`);Z=S.readFileSync(H,"utf-8").trim();if(!Z)throw new U(`--file 指向的文件内容为空: ${H}`)}const $=J.images?String(J.images).split(",").map((H)=>H.trim()).filter(Boolean):[],q=J.topics?String(J.topics).split(",").map((H)=>H.trim()).filter(Boolean):[],V=Boolean(J.draft),W=J["card-text"]?String(J["card-text"]):"",G=W?W.split(o).map((H)=>H.trim()).filter(Boolean):[],O=J["card-style"]?String(J["card-style"]).trim():"",D=G.length>0;if(!K)throw new U("--title is required");if(K.length>l)throw new U(`Title is ${K.length} chars — must be ≤ ${l}`);if(!Z)throw new U("缺少笔记正文:给位置参数 <content>(字面文本),或用 --file <本机文件路径>");Oz(Z,{fileFlag:"--file"});if(!D&&$.length===0)throw new U("Provide --card-text (text-image mode) or --images (upload mode); neither was given.");if($.length>n)throw new U(`Too many images: ${$.length} (max ${n})`);if(D&&$.some((H)=>R.extname(H).toLowerCase()===".gif"))throw new U("文字配图模式追加的图片不支持 .gif(编辑器图片入口只接受 jpg/jpeg/png/webp)");const X=Iz($);await z.goto(vz);await z.wait({time:1});await z.evaluate("() => { try { window.onbeforeunload = null; } catch (e) {} location.reload(); }").catch(()=>{return});let M="";for(let H=0;H<30;H++){await z.wait({time:0.5});M=await z.evaluate("() => location.href");if(M.includes("creator.xiaohongshu.com"))break}if(!M.includes("creator.xiaohongshu.com")){await z.screenshot({path:"/tmp/xhs_publish_redirect_debug.png"});throw Error(`Redirected away from creator center (landed on ${M}) — session may have expired. `+"Re-capture browser login via: ppcli xiaohongshu creator-profile. Debug screenshot: /tmp/xhs_publish_redirect_debug.png")}const L=await fz(z);if((await uz(z,L?.ok?5000:2000)).state==="video_surface"){await z.screenshot({path:"/tmp/xhs_publish_tab_debug.png"});const H=L?.ok?`clicked "${L.text}"`:`visible candidates: ${(L?.visibleTexts||[]).join(" | ")||"none"}`;throw Error("Still on the video publish page after trying to select 图文. "+`Details: ${H}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`)}const _=await cz(z);let f=O;if(D)f=await Ez(z,G,O);else{const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await e(z,X);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new j(`图片注入失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_upload_debug.png")}await z.wait({time:i/1000});const v=await Gz(z,X.length,H);if(!v.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new j(`图片上传未全部完成:只确认 ${v.completed}/${X.length} 张传到小红书 CDN,`+"继续发布会丢图(通常只剩封面)。直接重新运行本 publish 命令即可(会自动重置发布页重传);"+"请勿手动修改当前页面。Debug: /tmp/xhs_publish_upload_debug.png")}await zz(z);if(_.length){await Hz(z,_);await z.wait({time:0.5})}const A=await $z(z);if(A&&typeof A.count==="number"&&A.count>X.length){await z.screenshot({path:"/tmp/xhs_publish_dup_debug.png"});throw new j(`清掉恢复态旧图后编辑器内仍有 ${A.count} 张图,超过本次注入的 ${X.length} 张。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。Debug: /tmp/xhs_publish_dup_debug.png")}}if(!await mz(z)){await z.screenshot({path:"/tmp/xhs_publish_form_debug.png"});throw new j("Editing form did not appear after image acquisition. The page layout may have changed. Debug screenshot: /tmp/xhs_publish_form_debug.png")}if(D)await qz(z,G.length,"文字配图 generated images");if(D&&X.length>0){const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await e(z,X);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new j(`追加图片失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await z.wait({time:i/1000});const v=await Gz(z,X.length,H);if(!v.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new j(`追加图片上传未全部完成:只确认 ${v.completed}/${X.length} 张传到小红书 CDN。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await zz(z);await qz(z,G.length+X.length,"文字配图 appended images")}if(D&&_.length){await Hz(z,_);await z.wait({time:0.5})}await Jz(z,m,K,"title");await N(z,0.6);await Jz(z,t,Z,"content");await N(z,0.6);let y=[],b=[];if(q.length){const H=await _z(z,t,q);y=H.added;b=H.failed}await N(z,1,0.6);const w=await Dz(z,["xhs-publish-btn"]);if(w){await Bz(z,w.x,w.y);await N(z,0.4)}const c=V?["暂存离开","存草稿"]:["发布","发布笔记"],F=await z.evaluate(`
524
+ })()`))}catch{return null}if(Array.isArray($)){const q=$.find((V)=>{const X=String(V.title||"").trim();if(!(X===Q||X.startsWith(Q)||Q.startsWith(X)))return!1;const j=String(V.dateText||"").match(/(\d{4})年(\d{2})月(\d{2})日 (\d{2}):(\d{2})/);if(!j)return!0;const O=Date.UTC(Number(j[1]),Number(j[2])-1,Number(j[3]),Number(j[4]),Number(j[5]))-28800000;return Math.abs(Date.now()-O)<=93600000});if(q)return q}}return null}Wz({site:"xiaohongshu",name:"publish",access:"write",description:"小红书发布图文笔记 (creator center UI automation)",domain:"creator.xiaohongshu.com",strategy:Fz.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:`文字配图卡片文字,多张卡片用 ${o} 分隔,卡内换行用 \\n`},{name:"card-style",required:!1,help:`文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败。省略时使用${I}。可选: ${s.map(([z,J])=>`${z}(${J})`).join(" ")}`},{name:"topics",required:!1,help:"话题标签,逗号分隔,不含 # 号。平台话题库匹配不上的会自动跳过并在结果中提示,不影响发布"},{name:"draft",type:"bool",default:!1,help:"保存为草稿,不直接发布"}],columns:["status","detail","url"],func:async(z,J)=>{if(!z)throw Error("Browser page required");const K=String(J.title??"").trim(),Q=J.file?String(J.file).trim():"";let Z=String(J.content??"").trim();if(Q){if(Z)throw new U("正文位置参数和 --file 只能二选一,不能同时给");const H=R.resolve(Q);if(!S.existsSync(H))throw new U(`--file 指向的文件不存在: ${H}`);Z=S.readFileSync(H,"utf-8").trim();if(!Z)throw new U(`--file 指向的文件内容为空: ${H}`)}const $=J.images?String(J.images).split(",").map((H)=>H.trim()).filter(Boolean):[],q=J.topics?String(J.topics).split(",").map((H)=>H.trim()).filter(Boolean):[],V=Boolean(J.draft),X=J["card-text"]?String(J["card-text"]):"",j=X?X.split(o).map((H)=>H.trim()).filter(Boolean):[],O=J["card-style"]?String(J["card-style"]).trim():"",D=j.length>0;if(!K)throw new U("--title is required");if(K.length>l)throw new U(`Title is ${K.length} chars — must be ≤ ${l}`);if(!Z)throw new U("缺少笔记正文:给位置参数 <content>(字面文本),或用 --file <本机文件路径>");Oz(Z,{fileFlag:"--file"});if(!D&&$.length===0)throw new U("Provide --card-text (text-image mode) or --images (upload mode); neither was given.");if($.length>n)throw new U(`Too many images: ${$.length} (max ${n})`);if(D&&$.some((H)=>R.extname(H).toLowerCase()===".gif"))throw new U("文字配图模式追加的图片不支持 .gif(编辑器图片入口只接受 jpg/jpeg/png/webp)");const Y=Iz($);await z.goto(vz);await z.wait({time:1});await z.evaluate("() => { try { window.onbeforeunload = null; } catch (e) {} location.reload(); }").catch(()=>{return});let M="";for(let H=0;H<30;H++){await z.wait({time:0.5});M=await z.evaluate("() => location.href");if(M.includes("creator.xiaohongshu.com"))break}if(!M.includes("creator.xiaohongshu.com")){await z.screenshot({path:"/tmp/xhs_publish_redirect_debug.png"});throw Error(`Redirected away from creator center (landed on ${M}) — session may have expired. `+"Re-capture browser login via: ppcli xiaohongshu creator-profile. Debug screenshot: /tmp/xhs_publish_redirect_debug.png")}const L=await fz(z);if((await uz(z,L?.ok?5000:2000)).state==="video_surface"){await z.screenshot({path:"/tmp/xhs_publish_tab_debug.png"});const H=L?.ok?`clicked "${L.text}"`:`visible candidates: ${(L?.visibleTexts||[]).join(" | ")||"none"}`;throw Error("Still on the video publish page after trying to select 图文. "+`Details: ${H}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`)}const _=await cz(z);let f=O;if(D)f=await Ez(z,j,O);else{const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await e(z,Y);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new G(`图片注入失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_upload_debug.png")}await z.wait({time:i/1000});const v=await Gz(z,Y.length,H);if(!v.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new G(`图片上传未全部完成:只确认 ${v.completed}/${Y.length} 张传到小红书 CDN,`+"继续发布会丢图(通常只剩封面)。直接重新运行本 publish 命令即可(会自动重置发布页重传);"+"请勿手动修改当前页面。Debug: /tmp/xhs_publish_upload_debug.png")}await zz(z);if(_.length){await Hz(z,_);await z.wait({time:0.5})}const A=await $z(z);if(A&&typeof A.count==="number"&&A.count>Y.length){await z.screenshot({path:"/tmp/xhs_publish_dup_debug.png"});throw new G(`清掉恢复态旧图后编辑器内仍有 ${A.count} 张图,超过本次注入的 ${Y.length} 张。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。Debug: /tmp/xhs_publish_dup_debug.png")}}if(!await mz(z)){await z.screenshot({path:"/tmp/xhs_publish_form_debug.png"});throw new G("Editing form did not appear after image acquisition. The page layout may have changed. Debug screenshot: /tmp/xhs_publish_form_debug.png")}if(D)await qz(z,j.length,"文字配图 generated images");if(D&&Y.length>0){const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await e(z,Y);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new G(`追加图片失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await z.wait({time:i/1000});const v=await Gz(z,Y.length,H);if(!v.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new G(`追加图片上传未全部完成:只确认 ${v.completed}/${Y.length} 张传到小红书 CDN。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await zz(z);await qz(z,j.length+Y.length,"文字配图 appended images")}if(D&&_.length){await Hz(z,_);await z.wait({time:0.5})}await Jz(z,m,K,"title");await N(z,0.6);await Jz(z,t,Z,"content");await N(z,0.6);let y=[],b=[];if(q.length){const H=await _z(z,t,q);y=H.added;b=H.failed}await N(z,1,0.6);const w=await Dz(z,["xhs-publish-btn"]);if(w){await Bz(z,w.x,w.y);await N(z,0.4)}const c=V?["暂存离开","存草稿"]:["发布","发布笔记"],F=await z.evaluate(`
529
525
  (cfg => {
530
526
  const { isDraftMode, publishNames, draftNames, labels } = cfg;
531
527
  const isVisible = (el) => {
@@ -613,4 +609,4 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
613
609
  }
614
610
  return '';
615
611
  })(${JSON.stringify(Xz)})
616
- `),Yz=!k.includes("/publish/publish");let T=d.length>0||Yz;const C=V?"暂存成功":"发布成功";let P=null;if(!V){P=await nz(z,K,T?4:8);if(P)T=!0}if(!T){if(V)throw new j(`${C}无法确认:未观察到成功提示或提交后跳转。`+(k?`当前 URL: ${k}。`:"当前 URL 为空。")+"先用 `xiaohongshu drafts` 查草稿箱确认是否已存上;确认没有再重新运行本命令,避免堆出重复草稿。");throw new j(`${C}无法确认:未观察到成功提示或提交后跳转,创作者后台也暂未查到同标题笔记。`+(k?`当前 URL: ${k}。`:"")+"注意:发布按钮已被触发,笔记仍可能延迟入库。禁止立即重发——请等 1-2 分钟后用 `xiaohongshu creator-notes` 回查本篇标题是否已出现;确认不存在后才重新运行本 publish 命令,否则会重复发布。")}return[{status:`✅ ${C}`,detail:[`"${K}"`,D?`${G.length}张文字配图${X.length?` + ${X.length}张图片`:""}${f&&f!==I?` (${f})`:""}`:`${X.length}张图片`,y.length?`话题: ${y.join(" ")}`:"",b.length?`⚠️ 话题未附上(已自动跳过,不影响发布): ${b.map((H)=>H.topic).join(" ")}`:"",P?.id?`笔记ID: ${P.id}`:"",d||(P?"已在创作者后台确认":"")||k||""].filter(Boolean).join(" · "),url:P?.id?`https://www.xiaohongshu.com/explore/${P.id}`:""}]}});
612
+ `),Yz=!k.includes("/publish/publish");let T=d.length>0||Yz;const C=V?"暂存成功":"发布成功";let P=null;if(!V){P=await nz(z,K,T?4:8);if(P)T=!0}if(!T){if(V)throw new G(`${C}无法确认:未观察到成功提示或提交后跳转。`+(k?`当前 URL: ${k}。`:"当前 URL 为空。")+"先用 `xiaohongshu drafts` 查草稿箱确认是否已存上;确认没有再重新运行本命令,避免堆出重复草稿。");throw new G(`${C}无法确认:未观察到成功提示或提交后跳转,创作者后台也暂未查到同标题笔记。`+(k?`当前 URL: ${k}。`:"")+"注意:发布按钮已被触发,笔记仍可能延迟入库。禁止立即重发——请等 1-2 分钟后用 `xiaohongshu creator-notes` 回查本篇标题是否已出现;确认不存在后才重新运行本 publish 命令,否则会重复发布。")}return[{status:`✅ ${C}`,detail:[`"${K}"`,D?`${j.length}张文字配图${Y.length?` + ${Y.length}张图片`:""}${f&&f!==I?` (${f})`:""}`:`${Y.length}张图片`,y.length?`话题: ${y.join(" ")}`:"",b.length?`⚠️ 话题未附上(已自动跳过,不影响发布): ${b.map((H)=>H.topic).join(" ")}`:"",P?.id?`笔记ID: ${P.id}`:"",d||(P?"已在创作者后台确认":"")||k||""].filter(Boolean).join(" · "),url:P?.id?`https://www.xiaohongshu.com/explore/${P.id}`:""}]}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "publishport-opencli",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": false