publishport-opencli 1.0.7 → 1.0.8

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
@@ -48449,7 +48449,7 @@
48449
48449
  "name": "topics",
48450
48450
  "type": "str",
48451
48451
  "required": false,
48452
- "help": "话题标签,逗号分隔,不含 # "
48452
+ "help": "话题标签,逗号分隔,不含 # 号。平台话题库匹配不上的会自动跳过并在结果中提示,不影响发布"
48453
48453
  },
48454
48454
  {
48455
48455
  "name": "draft",
@@ -48461,7 +48461,8 @@
48461
48461
  ],
48462
48462
  "columns": [
48463
48463
  "status",
48464
- "detail"
48464
+ "detail",
48465
+ "url"
48465
48466
  ],
48466
48467
  "type": "js",
48467
48468
  "modulePath": "xiaohongshu/publish.js",
@@ -1,4 +1,4 @@
1
- import*as L from"node:crypto";import*as Y from"node:fs";import*as C from"node:os";import*as H from"node:path";import{spawnSync as A}from"node:child_process";import{CommandExecutionError as K}from"@jackwener/opencli/errors";import{instagramPrivateApiFetch as S}from"./protocol-capture.js";import{buildReadInstagramRuntimeInfoJs as h}from"./runtime-info.js";export{buildReadInstagramRuntimeInfoJs,extractInstagramRuntimeInfo,resolveInstagramRuntimeInfo}from"./runtime-info.js";const M=0.8,D=1.91,T=0.5625,z=0.75,k="FFFFFF",l="https://www.instagram.com/",u="/api/v1/|/graphql/",v=2,w=2,I=20,d=2000,g=15000,o="19ce5f445dbfd9d29c59dc2a78c616a7fc090a8e018b9267bc4240a30244c53b",n="4",r={manufacturer:"samsung",model:"SM-G930F",android_version:24,android_release:"7.0"};export function derivePrivateApiContextFromCapture(Q){for(let X=Q.length-1;X>=0;X-=1){const Z=Q[X]?.requestHeaders??{},$={asbdId:String(Z["X-ASBD-ID"]||""),csrfToken:String(Z["X-CSRFToken"]||""),igAppId:String(Z["X-IG-App-ID"]||""),igWwwClaim:String(Z["X-IG-WWW-Claim"]||""),instagramAjax:String(Z["X-Instagram-AJAX"]||""),webSessionId:String(Z["X-Web-Session-ID"]||"")};if($.asbdId&&$.csrfToken&&$.igAppId&&$.igWwwClaim&&$.instagramAjax&&$.webSessionId)return $}return null}function p(Q){const X={};for(let Z=Q.length-1;Z>=0;Z-=1){const $=Q[Z]?.requestHeaders??{};if(!X.asbdId&&$["X-ASBD-ID"])X.asbdId=String($["X-ASBD-ID"]);if(!X.csrfToken&&$["X-CSRFToken"])X.csrfToken=String($["X-CSRFToken"]);if(!X.igAppId&&$["X-IG-App-ID"])X.igAppId=String($["X-IG-App-ID"]);if(!X.igWwwClaim&&$["X-IG-WWW-Claim"])X.igWwwClaim=String($["X-IG-WWW-Claim"]);if(!X.instagramAjax&&$["X-Instagram-AJAX"])X.instagramAjax=String($["X-Instagram-AJAX"]);if(!X.webSessionId&&$["X-Web-Session-ID"])X.webSessionId=String($["X-Web-Session-ID"])}return X}export function deriveInstagramJazoest(Q){if(!Q)return"";return`2${Array.from(Q).reduce((Z,$)=>Z+$.charCodeAt(0),0)}`}function i(Q){return new Promise((X)=>setTimeout(X,Q))}function x(Q){const X=Q instanceof Error?Q.message:String(Q);return/fetch failed|network|socket hang up|econnreset|etimedout/i.test(X)}function s(Q,X){return Q.find((Z)=>Z.name===X)?.value||""}export async function resolveInstagramPrivatePublishConfig(Q){let X;for(let Z=0;Z<v;Z+=1)try{if(typeof Q.startNetworkCapture==="function")await Q.startNetworkCapture(u);await Q.goto(`${l}?__opencli_private_probe=${Date.now()}`);await Q.wait({time:2});const[$,q,F]=await Promise.all([Q.getCookies({domain:"instagram.com"}),Q.evaluate(h()),typeof Q.readNetworkCapture==="function"?Q.readNetworkCapture():Promise.resolve([])]),N=Array.isArray(F)?F:[],W=derivePrivateApiContextFromCapture(N)??p(N),O=q?.csrfToken||s($,"csrftoken")||W.csrfToken||"",V=q?.appId||W.igAppId||"",G=q?.instagramAjax||W.instagramAjax||"";if(!O)throw new K("Instagram private route could not derive CSRF token from browser session");if(!V)throw new K("Instagram private route could not derive X-IG-App-ID from instagram runtime");if(!G)throw new K("Instagram private route could not derive X-Instagram-AJAX from instagram runtime");const J=W.asbdId||"",B=W.igWwwClaim||"",R=W.webSessionId||"";return{apiContext:{asbdId:J,csrfToken:O,igAppId:V,igWwwClaim:B,instagramAjax:G,webSessionId:R},jazoest:deriveInstagramJazoest(O)}}catch($){X=$;if(!x($)||Z>=v-1)throw $}throw X instanceof Error?X:Error(String(X))}export function buildConfigureBody(Q){const X=new URLSearchParams;X.set("archive_only","false");X.set("caption",Q.caption);X.set("clips_share_preview_to_feed","1");X.set("disable_comments","0");X.set("disable_oa_reuse","false");X.set("igtv_share_preview_to_feed","1");X.set("is_meta_only_post","0");X.set("is_unified_video","1");X.set("like_and_view_counts_disabled","0");X.set("media_share_flow","creation_flow");X.set("share_to_facebook","");X.set("share_to_fb_destination_type","USER");X.set("source_type","library");X.set("upload_id",Q.uploadId);X.set("video_subtitles_enabled","0");X.set("jazoest",Q.jazoest);return X.toString()}export function buildConfigureSidecarPayload(Q){return{archive_only:!1,caption:Q.caption,children_metadata:Q.uploadIds.map((X)=>({upload_id:X})),client_sidecar_id:Q.clientSidecarId,disable_comments:"0",is_meta_only_post:!1,is_open_to_public_submission:!1,like_and_view_counts_disabled:0,media_share_flow:"creation_flow",share_to_facebook:"",share_to_fb_destination_type:"USER",source_type:"library",jazoest:Q.jazoest}}function a(Q){switch(H.extname(Q).toLowerCase()){case".png":return"image/png";case".webp":return"image/webp";case".jpg":case".jpeg":default:return"image/jpeg"}}function e(Q){if(Q.length<24)return null;if(Q.subarray(0,8).toString("hex").toUpperCase()!=="89504E470D0A1A0A")return null;if(Q.subarray(12,16).toString("ascii")!=="IHDR")return null;return{width:Q.readUInt32BE(16),height:Q.readUInt32BE(20)}}function t(Q){if(Q.length<4||Q[0]!==255||Q[1]!==216)return null;let X=2;while(X+9<Q.length){if(Q[X]!==255){X+=1;continue}const Z=Q[X+1];X+=2;if(Z===216||Z===217)continue;if(X+2>Q.length)break;const $=Q.readUInt16BE(X);if($<2||X+$>Q.length)break;if(Z>=192&&Z<=207&&![196,200,204].includes(Z)&&$>=7)return{height:Q.readUInt16BE(X+3),width:Q.readUInt16BE(X+5)};X+=$}return null}function QQ(Q){if(Q.length<30)return null;if(Q.subarray(0,4).toString("ascii")!=="RIFF"||Q.subarray(8,12).toString("ascii")!=="WEBP")return null;const X=Q.subarray(12,16).toString("ascii");if(X==="VP8X"&&Q.length>=30)return{width:1+Q.readUIntLE(24,3),height:1+Q.readUIntLE(27,3)};if(X==="VP8 "&&Q.length>=30)return{width:Q.readUInt16LE(26)&16383,height:Q.readUInt16LE(28)&16383};if(X==="VP8L"&&Q.length>=25){const Z=Q.readUInt32LE(21);return{width:(Z&16383)+1,height:(Z>>14&16383)+1}}return null}function XQ(Q,X){const Z=H.extname(Q).toLowerCase(),$=Z===".png"?e(X):Z===".webp"?QQ(X):t(X);if(!$)throw new K(`Failed to read image dimensions for ${Q}`);return $}export function readImageAsset(Q){const X=Y.readFileSync(Q),{width:Z,height:$}=XQ(Q,X);return{filePath:Q,fileName:H.basename(Q),mimeType:a(Q),width:Z,height:$,byteLength:X.length,bytes:X}}export function isInstagramFeedAspectRatioAllowed(Q,X){const Z=Q/Math.max(X,1);return Z>=M-0.001&&Z<=D+0.001}export function getInstagramFeedNormalizedDimensions(Q,X){const Z=Q/Math.max(X,1);if(Z<M)return{width:Math.ceil(X*M),height:X};if(Z>D)return{width:Q,height:Math.ceil(Q/D)};return null}export function isInstagramStoryAspectRatioAllowed(Q,X){const Z=Q/Math.max(X,1);return Z>=T-0.001&&Z<=z+0.001}export function getInstagramStoryNormalizedDimensions(Q,X){const Z=Q/Math.max(X,1);if(Z<T)return{width:Math.ceil(X*T),height:X};if(Z>z)return{width:Q,height:Math.ceil(Q/z)};return null}function P(Q){const X=H.parse(Q);return H.join(C.tmpdir(),`opencli-instagram-private-${X.name}-${L.randomUUID()}${X.ext||".png"}`)}export function prepareImageAssetForPrivateUpload(Q){const X=readImageAsset(Q),Z=getInstagramFeedNormalizedDimensions(X.width,X.height);if(!Z)return X;if(process.platform!=="darwin")throw new K(`Instagram private publish does not support auto-normalizing ${X.fileName} on ${process.platform}`,`Use images within ${M.toFixed(2)}-${D.toFixed(2)} aspect ratio, or use the UI route`);const $=P(Q),q=A("sips",["--padToHeightWidth",String(Z.height),String(Z.width),"--padColor",k,Q,"--out",$],{encoding:"utf8"});if(q.error||q.status!==0||!Y.existsSync($)){const F=[q.error?.message,q.stderr,q.stdout].map((N)=>String(N||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private publish failed to normalize ${X.fileName}`,F||"sips padToHeightWidth failed")}return{...readImageAsset($),cleanupPath:$}}export function prepareImageAssetForPrivateStoryUpload(Q){const X=readImageAsset(Q),Z=getInstagramStoryNormalizedDimensions(X.width,X.height);if(!Z)return X;if(process.platform!=="darwin")throw new K(`Instagram private story publish does not support auto-normalizing ${X.fileName} on ${process.platform}`,`Use images within ${T.toFixed(2)}-${z.toFixed(2)} aspect ratio, or use the UI route`);const $=P(Q),q=A("sips",["--padToHeightWidth",String(Z.height),String(Z.width),"--padColor",k,Q,"--out",$],{encoding:"utf8"});if(q.error||q.status!==0||!Y.existsSync($)){const F=[q.error?.message,q.stderr,q.stdout].map((N)=>String(N||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private story publish failed to normalize ${X.fileName}`,F||"sips padToHeightWidth failed")}return{...readImageAsset($),cleanupPath:$}}function E(Q,X,Z){const $=H.join(C.tmpdir(),`opencli-instagram-${L.randomUUID()}.swift`);Y.writeFileSync($,Q);try{const q=A("swift",[$,...X],{encoding:"utf8"});if(q.error||q.status!==0){const F=[q.error?.message,q.stderr,q.stdout].map((N)=>String(N||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private publish failed to ${Z}`,F||"swift helper failed")}return JSON.parse(String(q.stdout||"{}"))}catch(q){if(q instanceof K)throw q;throw new K(`Instagram private publish failed to ${Z}`,q instanceof Error?q.message:String(q))}finally{Y.rmSync($,{force:!0})}}function ZQ(Q){if(process.platform!=="darwin")throw new K(`Instagram private mixed-media publish does not support reading video metadata on ${process.platform}`,"Use macOS for private mixed-media publishing, or rely on the UI fallback");const X=E(`
1
+ import*as B from"node:crypto";import*as U from"node:fs";import*as R from"node:os";import*as V from"node:path";import{spawnSync as z}from"node:child_process";import{CommandExecutionError as K}from"@jackwener/opencli/errors";import{instagramPrivateApiFetch as j}from"./protocol-capture.js";import{buildReadInstagramRuntimeInfoJs as h}from"./runtime-info.js";export{buildReadInstagramRuntimeInfoJs,extractInstagramRuntimeInfo,resolveInstagramRuntimeInfo}from"./runtime-info.js";const C=0.8,M=1.91,D=0.5625,T=0.75,S="FFFFFF",l="https://www.instagram.com/",u="/api/v1/|/graphql/",k=2,v=2,w=20,d=2000,I=15000,n="19ce5f445dbfd9d29c59dc2a78c616a7fc090a8e018b9267bc4240a30244c53b",o="4",p={manufacturer:"samsung",model:"SM-G930F",android_version:24,android_release:"7.0"};export function markPublishOutcomeUncertain(Q){if(Q&&typeof Q==="object")Q.instagramPublishUncertain=!0;return Q}export function isPublishOutcomeUncertain(Q){return Boolean(Q&&typeof Q==="object"&&Q.instagramPublishUncertain)}export function derivePrivateApiContextFromCapture(Q){for(let X=Q.length-1;X>=0;X-=1){const Z=Q[X]?.requestHeaders??{},$={asbdId:String(Z["X-ASBD-ID"]||""),csrfToken:String(Z["X-CSRFToken"]||""),igAppId:String(Z["X-IG-App-ID"]||""),igWwwClaim:String(Z["X-IG-WWW-Claim"]||""),instagramAjax:String(Z["X-Instagram-AJAX"]||""),webSessionId:String(Z["X-Web-Session-ID"]||"")};if($.asbdId&&$.csrfToken&&$.igAppId&&$.igWwwClaim&&$.instagramAjax&&$.webSessionId)return $}return null}function i(Q){const X={};for(let Z=Q.length-1;Z>=0;Z-=1){const $=Q[Z]?.requestHeaders??{};if(!X.asbdId&&$["X-ASBD-ID"])X.asbdId=String($["X-ASBD-ID"]);if(!X.csrfToken&&$["X-CSRFToken"])X.csrfToken=String($["X-CSRFToken"]);if(!X.igAppId&&$["X-IG-App-ID"])X.igAppId=String($["X-IG-App-ID"]);if(!X.igWwwClaim&&$["X-IG-WWW-Claim"])X.igWwwClaim=String($["X-IG-WWW-Claim"]);if(!X.instagramAjax&&$["X-Instagram-AJAX"])X.instagramAjax=String($["X-Instagram-AJAX"]);if(!X.webSessionId&&$["X-Web-Session-ID"])X.webSessionId=String($["X-Web-Session-ID"])}return X}export function deriveInstagramJazoest(Q){if(!Q)return"";return`2${Array.from(Q).reduce((Z,$)=>Z+$.charCodeAt(0),0)}`}function r(Q){return new Promise((X)=>setTimeout(X,Q))}function g(Q){const X=Q instanceof Error?Q.message:String(Q);return/fetch failed|network|socket hang up|econnreset|etimedout/i.test(X)}function s(Q,X){return Q.find((Z)=>Z.name===X)?.value||""}export async function resolveInstagramPrivatePublishConfig(Q){let X;for(let Z=0;Z<k;Z+=1)try{if(typeof Q.startNetworkCapture==="function")await Q.startNetworkCapture(u);await Q.goto(`${l}?__opencli_private_probe=${Date.now()}`);await Q.wait({time:2});const[$,q,F]=await Promise.all([Q.getCookies({domain:"instagram.com"}),Q.evaluate(h()),typeof Q.readNetworkCapture==="function"?Q.readNetworkCapture():Promise.resolve([])]),O=Array.isArray(F)?F:[],G=derivePrivateApiContextFromCapture(O)??i(O),N=q?.csrfToken||s($,"csrftoken")||G.csrfToken||"",J=q?.appId||G.igAppId||"",W=q?.instagramAjax||G.instagramAjax||"";if(!N)throw new K("Instagram private route could not derive CSRF token from browser session");if(!J)throw new K("Instagram private route could not derive X-IG-App-ID from instagram runtime");if(!W)throw new K("Instagram private route could not derive X-Instagram-AJAX from instagram runtime");const H=G.asbdId||"",Y=G.igWwwClaim||"",y=G.webSessionId||"";return{apiContext:{asbdId:H,csrfToken:N,igAppId:J,igWwwClaim:Y,instagramAjax:W,webSessionId:y},jazoest:deriveInstagramJazoest(N)}}catch($){X=$;if(!g($)||Z>=k-1)throw $}throw X instanceof Error?X:Error(String(X))}export function buildConfigureBody(Q){const X=new URLSearchParams;X.set("archive_only","false");X.set("caption",Q.caption);X.set("clips_share_preview_to_feed","1");X.set("disable_comments","0");X.set("disable_oa_reuse","false");X.set("igtv_share_preview_to_feed","1");X.set("is_meta_only_post","0");X.set("is_unified_video","1");X.set("like_and_view_counts_disabled","0");X.set("media_share_flow","creation_flow");X.set("share_to_facebook","");X.set("share_to_fb_destination_type","USER");X.set("source_type","library");X.set("upload_id",Q.uploadId);X.set("video_subtitles_enabled","0");X.set("jazoest",Q.jazoest);return X.toString()}export function buildConfigureSidecarPayload(Q){return{archive_only:!1,caption:Q.caption,children_metadata:Q.uploadIds.map((X)=>({upload_id:X})),client_sidecar_id:Q.clientSidecarId,disable_comments:"0",is_meta_only_post:!1,is_open_to_public_submission:!1,like_and_view_counts_disabled:0,media_share_flow:"creation_flow",share_to_facebook:"",share_to_fb_destination_type:"USER",source_type:"library",jazoest:Q.jazoest}}function a(Q){switch(V.extname(Q).toLowerCase()){case".png":return"image/png";case".webp":return"image/webp";case".jpg":case".jpeg":default:return"image/jpeg"}}function t(Q){if(Q.length<24)return null;if(Q.subarray(0,8).toString("hex").toUpperCase()!=="89504E470D0A1A0A")return null;if(Q.subarray(12,16).toString("ascii")!=="IHDR")return null;return{width:Q.readUInt32BE(16),height:Q.readUInt32BE(20)}}function e(Q){if(Q.length<4||Q[0]!==255||Q[1]!==216)return null;let X=2;while(X+9<Q.length){if(Q[X]!==255){X+=1;continue}const Z=Q[X+1];X+=2;if(Z===216||Z===217)continue;if(X+2>Q.length)break;const $=Q.readUInt16BE(X);if($<2||X+$>Q.length)break;if(Z>=192&&Z<=207&&![196,200,204].includes(Z)&&$>=7)return{height:Q.readUInt16BE(X+3),width:Q.readUInt16BE(X+5)};X+=$}return null}function QQ(Q){if(Q.length<30)return null;if(Q.subarray(0,4).toString("ascii")!=="RIFF"||Q.subarray(8,12).toString("ascii")!=="WEBP")return null;const X=Q.subarray(12,16).toString("ascii");if(X==="VP8X"&&Q.length>=30)return{width:1+Q.readUIntLE(24,3),height:1+Q.readUIntLE(27,3)};if(X==="VP8 "&&Q.length>=30)return{width:Q.readUInt16LE(26)&16383,height:Q.readUInt16LE(28)&16383};if(X==="VP8L"&&Q.length>=25){const Z=Q.readUInt32LE(21);return{width:(Z&16383)+1,height:(Z>>14&16383)+1}}return null}function XQ(Q,X){const Z=V.extname(Q).toLowerCase(),$=Z===".png"?t(X):Z===".webp"?QQ(X):e(X);if(!$)throw new K(`Failed to read image dimensions for ${Q}`);return $}export function readImageAsset(Q){const X=U.readFileSync(Q),{width:Z,height:$}=XQ(Q,X);return{filePath:Q,fileName:V.basename(Q),mimeType:a(Q),width:Z,height:$,byteLength:X.length,bytes:X}}export function isInstagramFeedAspectRatioAllowed(Q,X){const Z=Q/Math.max(X,1);return Z>=C-0.001&&Z<=M+0.001}export function getInstagramFeedNormalizedDimensions(Q,X){const Z=Q/Math.max(X,1);if(Z<C)return{width:Math.ceil(X*C),height:X};if(Z>M)return{width:Q,height:Math.ceil(Q/M)};return null}export function isInstagramStoryAspectRatioAllowed(Q,X){const Z=Q/Math.max(X,1);return Z>=D-0.001&&Z<=T+0.001}export function getInstagramStoryNormalizedDimensions(Q,X){const Z=Q/Math.max(X,1);if(Z<D)return{width:Math.ceil(X*D),height:X};if(Z>T)return{width:Q,height:Math.ceil(Q/T)};return null}function x(Q){const X=V.parse(Q);return V.join(R.tmpdir(),`opencli-instagram-private-${X.name}-${B.randomUUID()}${X.ext||".png"}`)}export function prepareImageAssetForPrivateUpload(Q){const X=readImageAsset(Q),Z=getInstagramFeedNormalizedDimensions(X.width,X.height);if(!Z)return X;if(process.platform!=="darwin")throw new K(`Instagram private publish does not support auto-normalizing ${X.fileName} on ${process.platform}`,`Use images within ${C.toFixed(2)}-${M.toFixed(2)} aspect ratio, or use the UI route`);const $=x(Q),q=z("sips",["--padToHeightWidth",String(Z.height),String(Z.width),"--padColor",S,Q,"--out",$],{encoding:"utf8"});if(q.error||q.status!==0||!U.existsSync($)){const F=[q.error?.message,q.stderr,q.stdout].map((O)=>String(O||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private publish failed to normalize ${X.fileName}`,F||"sips padToHeightWidth failed")}return{...readImageAsset($),cleanupPath:$}}export function prepareImageAssetForPrivateStoryUpload(Q){const X=readImageAsset(Q),Z=getInstagramStoryNormalizedDimensions(X.width,X.height);if(!Z)return X;if(process.platform!=="darwin")throw new K(`Instagram private story publish does not support auto-normalizing ${X.fileName} on ${process.platform}`,`Use images within ${D.toFixed(2)}-${T.toFixed(2)} aspect ratio, or use the UI route`);const $=x(Q),q=z("sips",["--padToHeightWidth",String(Z.height),String(Z.width),"--padColor",S,Q,"--out",$],{encoding:"utf8"});if(q.error||q.status!==0||!U.existsSync($)){const F=[q.error?.message,q.stderr,q.stdout].map((O)=>String(O||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private story publish failed to normalize ${X.fileName}`,F||"sips padToHeightWidth failed")}return{...readImageAsset($),cleanupPath:$}}function A(Q,X,Z){const $=V.join(R.tmpdir(),`opencli-instagram-${B.randomUUID()}.swift`);U.writeFileSync($,Q);try{const q=z("swift",[$,...X],{encoding:"utf8"});if(q.error||q.status!==0){const F=[q.error?.message,q.stderr,q.stdout].map((O)=>String(O||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private publish failed to ${Z}`,F||"swift helper failed")}return JSON.parse(String(q.stdout||"{}"))}catch(q){if(q instanceof K)throw q;throw new K(`Instagram private publish failed to ${Z}`,q instanceof Error?q.message:String(q))}finally{U.rmSync($,{force:!0})}}function ZQ(Q){if(process.platform!=="darwin")throw new K(`Instagram private mixed-media publish does not support reading video metadata on ${process.platform}`,"Use macOS for private mixed-media publishing, or rely on the UI fallback");const X=A(`
2
2
  import AVFoundation
3
3
  import Foundation
4
4
 
@@ -20,7 +20,7 @@ let payload: [String: Int] = [
20
20
  ]
21
21
  let data = try JSONSerialization.data(withJSONObject: payload, options: [])
22
22
  FileHandle.standardOutput.write(data)
23
- `,[Q],"read video metadata");if(!X.width||!X.height||!X.durationMs)throw new K(`Instagram private publish failed to read video metadata for ${Q}`);return{width:X.width,height:X.height,durationMs:X.durationMs}}function $Q(Q){const X=H.parse(Q);return H.join(C.tmpdir(),`opencli-instagram-private-video-cover-${X.name}-${L.randomUUID()}.jpg`)}function qQ(Q){const X=H.parse(Q);return H.join(C.tmpdir(),`opencli-instagram-story-video-${X.name}-${L.randomUUID()}${X.ext||".mp4"}`)}function FQ(Q){if(process.platform!=="darwin")throw new K(`Instagram private mixed-media publish does not support generating video covers on ${process.platform}`,"Use macOS for private mixed-media publishing, or rely on the UI fallback");const X=$Q(Q);E(`
23
+ `,[Q],"read video metadata");if(!X.width||!X.height||!X.durationMs)throw new K(`Instagram private publish failed to read video metadata for ${Q}`);return{width:X.width,height:X.height,durationMs:X.durationMs}}function $Q(Q){const X=V.parse(Q);return V.join(R.tmpdir(),`opencli-instagram-private-video-cover-${X.name}-${B.randomUUID()}.jpg`)}function qQ(Q){const X=V.parse(Q);return V.join(R.tmpdir(),`opencli-instagram-story-video-${X.name}-${B.randomUUID()}${X.ext||".mp4"}`)}function FQ(Q){if(process.platform!=="darwin")throw new K(`Instagram private mixed-media publish does not support generating video covers on ${process.platform}`,"Use macOS for private mixed-media publishing, or rely on the UI fallback");const X=$Q(Q);A(`
24
24
  import AVFoundation
25
25
  import AppKit
26
26
  import Foundation
@@ -40,7 +40,7 @@ try data.write(to: URL(fileURLWithPath: outputPath))
40
40
  let payload = ["ok": true]
41
41
  let json = try JSONSerialization.data(withJSONObject: payload, options: [])
42
42
  FileHandle.standardOutput.write(json)
43
- `,[Q,X],"generate video cover");return{...readImageAsset(X),cleanupPath:X}}export function readVideoAsset(Q){const X=Y.readFileSync(Q),Z=ZQ(Q),$=FQ(Q);return{filePath:Q,fileName:H.basename(Q),mimeType:"video/mp4",width:Z.width,height:Z.height,durationMs:Z.durationMs,byteLength:X.length,bytes:X,coverImage:$,cleanupPaths:$.cleanupPath?[$.cleanupPath]:[]}}function KQ(Q,X){if(process.platform!=="darwin")throw new K(`Instagram private story publish does not support trimming long videos on ${process.platform}`,"Use macOS for private story video publishing, or trim the video to 15 seconds first");const Z=qQ(Q);E(`
43
+ `,[Q,X],"generate video cover");return{...readImageAsset(X),cleanupPath:X}}export function readVideoAsset(Q){const X=U.readFileSync(Q),Z=ZQ(Q),$=FQ(Q);return{filePath:Q,fileName:V.basename(Q),mimeType:"video/mp4",width:Z.width,height:Z.height,durationMs:Z.durationMs,byteLength:X.length,bytes:X,coverImage:$,cleanupPaths:$.cleanupPath?[$.cleanupPath]:[]}}function KQ(Q,X){if(process.platform!=="darwin")throw new K(`Instagram private story publish does not support trimming long videos on ${process.platform}`,"Use macOS for private story video publishing, or trim the video to 15 seconds first");const Z=qQ(Q);A(`
44
44
  import AVFoundation
45
45
  import Foundation
46
46
 
@@ -72,4 +72,4 @@ if exportSession.status != .completed {
72
72
  let payload = ["ok": true]
73
73
  let json = try JSONSerialization.data(withJSONObject: payload, options: [])
74
74
  FileHandle.standardOutput.write(json)
75
- `,[Q,Z,String(X)],"trim story video");return Z}function NQ(Q){const X=readVideoAsset(Q);if(X.durationMs<=g)return X;const Z=KQ(Q,g),$=readVideoAsset(Z);return{...$,cleanupPaths:[...$.cleanupPaths||[],Z]}}function b(Q){const X=Q();return X>10000000000?Math.floor(X/1000):Math.floor(X)}export function buildConfigureToStoryPhotoPayload(Q){const X=Q.now??(()=>Date.now()),Z=b(X);return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,edits:{crop_original_size:[Q.width,Q.height],crop_center:[0,0],crop_zoom:1.3333334},extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}export function buildConfigureToStoryVideoPayload(Q){const X=Q.now??(()=>Date.now()),Z=b(X),$=Number((Q.durationMs/1000).toFixed(3));return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,poster_frame_index:0,length:$,audio_muted:!1,filter_type:"0",video_result:"deprecated",extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}function BQ(Q){const X=new URLSearchParams;for(const[Z,$]of Object.entries(Q)){if($===void 0||$===null)continue;if(typeof $==="object"){X.set(Z,JSON.stringify($));continue}X.set(Z,String($))}return X.toString()}function m(Q){const X=JSON.stringify(Q),Z=L.createHmac("sha256",o).update(X).digest("hex"),$=new URLSearchParams;$.set("ig_sig_key_version",n);$.set("signed_body",`${Z}.${X}`);return $.toString()}function U(Q){return Object.fromEntries(Object.entries({"X-ASBD-ID":Q.asbdId,"X-CSRFToken":Q.csrfToken,"X-IG-App-ID":Q.igAppId,"X-IG-WWW-Claim":Q.igWwwClaim,"X-Instagram-AJAX":Q.instagramAjax,"X-Web-Session-ID":Q.webSessionId}).filter(([,X])=>!!X))}function OQ(Q,X,Z){return{...U(Z),Accept:"*/*","Content-Type":Q.mimeType,Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:1,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}function f(Q){const X=Math.min(Q.width,Q.height),Z=Number((Q.durationMs/1000).toFixed(3));return{crop_height:X,crop_width:X,crop_x1:Math.max(0,Math.floor((Q.width-X)/2)),crop_y1:Math.max(0,Math.floor((Q.height-X)/2)),mute:!1,trim_end:Z,trim_start:0}}function WQ(Q,X,Z){return{...U(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",is_unified_video:"0",is_sidecar:"1",media_type:2,for_album:!1,video_format:"",upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:f(Q)})}}function GQ(Q,X,Z){return{...U(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",media_type:2,upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:f(Q)})}}function HQ(Q,X,Z){return{...U(Z),Accept:"*/*","Content-Type":Q.coverImage.mimeType,Offset:"0","X-Entity-Length":String(Q.coverImage.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.coverImage.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:2,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}async function _(Q,X){const Z=await Q.text();let $;try{$=Z?JSON.parse(Z):{}}catch{throw new K(`Instagram private publish ${X} returned invalid JSON`)}if(!Q.ok){const q=Z?` ${Z.slice(0,500)}`:"";throw new K(`Instagram private publish ${X} failed: ${Q.status}${q}`)}return $}async function j(Q,X,Z){let $;for(let q=0;q<w;q+=1)try{return await Q(X,Z)}catch(F){$=F;if(!x(F)||q>=w-1)throw F}throw $ instanceof Error?$:Error(String($))}async function JQ(Q){if(Q.type==="video")return{type:"video",asset:readVideoAsset(Q.filePath)};return{type:"image",asset:prepareImageAssetForPrivateUpload(Q.filePath)}}function c(Q){for(const X of Q){if(X.type==="image"){if(X.asset.cleanupPath)Y.rmSync(X.asset.cleanupPath,{force:!0});continue}for(const Z of X.asset.cleanupPaths||[])Y.rmSync(Z,{force:!0});if(X.asset.coverImage.cleanupPath)Y.rmSync(X.asset.coverImage.cleanupPath,{force:!0})}}async function y(Q,X,Z,$,q="feed"){if(X.type==="image"){const V=await j(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:OQ(X.asset,Z,$),body:X.asset.bytes}),G=await _(V,"upload");if(String(G?.status||"")!=="ok")throw new K(`Instagram private publish upload failed for ${X.asset.fileName}`);return}const F=await j(Q,`https://i.instagram.com/rupload_igvideo/fb_uploader_${Z}`,{method:"POST",headers:q==="story"?GQ(X.asset,Z,$):WQ(X.asset,Z,$),body:X.asset.bytes}),N=await _(F,"video upload");if(String(N?.status||"")!=="ok")throw new K(`Instagram private publish video upload failed for ${X.asset.fileName}`);const W=await j(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:HQ(X.asset,Z,$),body:X.asset.coverImage.bytes}),O=await _(W,"video cover upload");if(String(O?.status||"")!=="ok")throw new K(`Instagram private publish video cover upload failed for ${X.asset.fileName}`)}async function VQ(Q){const X=Q.waitMs??i,Z={method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/json"},body:JSON.stringify(Q.payload)};for(let $=0;$<I;$+=1){const q=await Q.fetcher("https://www.instagram.com/api/v1/media/configure_sidecar/",Z),F=await q.text();let N={};try{N=F?JSON.parse(F):{}}catch{throw new K("Instagram private publish configure_sidecar returned invalid JSON")}if(!q.ok){const O=F?` ${F.slice(0,500)}`:"";throw new K(`Instagram private publish configure_sidecar failed: ${q.status}${O}`)}const W=String(N?.message||"");if(q.status===202||/transcode not finished yet/i.test(W)){if($>=I-1)throw new K("Instagram private publish configure_sidecar timed out waiting for video transcode",F.slice(0,500));await X(d);continue}if(String(N?.status||"").toLowerCase()==="fail")throw new K("Instagram private publish configure_sidecar failed",W||F.slice(0,500));return{code:N?.media?.code}}throw new K("Instagram private publish configure_sidecar failed")}export async function publishMediaViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.mediaItems.length>1?Q.mediaItems.map((W,O)=>String(X()+O+1)):[String(X())],q=Q.fetcher??((W,O)=>S(Q.page,W,O)),F=Q.prepareMediaAsset??JQ,N=await Promise.all(Q.mediaItems.map((W)=>F(W)));try{for(let O=0;O<N.length;O+=1){const V=N[O],G=$[O];await y(q,V,G,Q.apiContext)}if($.length===1){if(N[0]?.type!=="image")throw new K("Instagram private publish only supports single-video uploads through instagram reel");const O=await q("https://www.instagram.com/api/v1/media/configure/",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:buildConfigureBody({uploadId:$[0],caption:Q.caption,jazoest:Q.jazoest})});return{code:(await _(O,"configure"))?.media?.code,uploadIds:$}}return{code:(await VQ({fetcher:q,payload:buildConfigureSidecarPayload({uploadIds:$,caption:Q.caption,clientSidecarId:Z,jazoest:Q.jazoest}),apiContext:Q.apiContext,waitMs:Q.waitMs})).code,uploadIds:$}}finally{c(N)}}export async function publishImagesViaPrivateApi(Q){return publishMediaViaPrivateApi({page:Q.page,mediaItems:Q.imagePaths.map((X)=>({type:"image",filePath:X})),caption:Q.caption,apiContext:Q.apiContext,jazoest:Q.jazoest,now:Q.now,fetcher:Q.fetcher,waitMs:Q.waitMs,prepareMediaAsset:Q.prepareAsset?async(X)=>({type:"image",asset:await Q.prepareAsset(X.filePath)}):void 0})}export async function publishStoryViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.fetcher??((G,J)=>S(Q.page,G,J)),F=await(Q.prepareMediaAsset??(async(G)=>G.type==="video"?{type:"video",asset:NQ(G.filePath)}:{type:"image",asset:prepareImageAssetForPrivateStoryUpload(G.filePath)}))(Q.mediaItem),N=Q.currentUserId||("getCookies"in Q.page?String((await(Q.page.getCookies?.({domain:"instagram.com"})??Promise.resolve([]))).find((G)=>G.name==="ds_user_id")?.value||""):"");if(!N)throw new K("Instagram story publish could not derive current user id from browser session");const W={_csrftoken:Q.apiContext.csrfToken,_uid:N,_uuid:L.randomUUID(),device:r},O=(G,J)=>m({...buildConfigureToStoryPhotoPayload({uploadId:Z,width:G,height:J,now:X,jazoest:Q.jazoest}),...W}),V=(G,J,B)=>m({...buildConfigureToStoryVideoPayload({uploadId:Z,width:G,height:J,durationMs:B,now:X,jazoest:Q.jazoest}),...W});try{await y($,F,Z,Q.apiContext,"story");if(F.type==="image"){const B=await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:O(F.asset.width,F.asset.height)}),R=await _(B,"configure_to_story");return{mediaPk:String(R?.media?.pk||R?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}await _(await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:O(F.asset.width,F.asset.height)}),"configure_to_story cover");const G=await $("https://i.instagram.com/api/v1/media/configure_to_story/?video=1",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:V(F.asset.width,F.asset.height,F.asset.durationMs)}),J=await _(G,"configure_to_story");return{mediaPk:String(J?.media?.pk||J?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}finally{c([F])}}
75
+ `,[Q,Z,String(X)],"trim story video");return Z}function NQ(Q){const X=readVideoAsset(Q);if(X.durationMs<=I)return X;const Z=KQ(Q,I),$=readVideoAsset(Z);return{...$,cleanupPaths:[...$.cleanupPaths||[],Z]}}function P(Q){const X=Q();return X>10000000000?Math.floor(X/1000):Math.floor(X)}export function buildConfigureToStoryPhotoPayload(Q){const X=Q.now??(()=>Date.now()),Z=P(X);return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,edits:{crop_original_size:[Q.width,Q.height],crop_center:[0,0],crop_zoom:1.3333334},extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}export function buildConfigureToStoryVideoPayload(Q){const X=Q.now??(()=>Date.now()),Z=P(X),$=Number((Q.durationMs/1000).toFixed(3));return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,poster_frame_index:0,length:$,audio_muted:!1,filter_type:"0",video_result:"deprecated",extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}function BQ(Q){const X=new URLSearchParams;for(const[Z,$]of Object.entries(Q)){if($===void 0||$===null)continue;if(typeof $==="object"){X.set(Z,JSON.stringify($));continue}X.set(Z,String($))}return X.toString()}function b(Q){const X=JSON.stringify(Q),Z=B.createHmac("sha256",n).update(X).digest("hex"),$=new URLSearchParams;$.set("ig_sig_key_version",o);$.set("signed_body",`${Z}.${X}`);return $.toString()}function _(Q){return Object.fromEntries(Object.entries({"X-ASBD-ID":Q.asbdId,"X-CSRFToken":Q.csrfToken,"X-IG-App-ID":Q.igAppId,"X-IG-WWW-Claim":Q.igWwwClaim,"X-Instagram-AJAX":Q.instagramAjax,"X-Web-Session-ID":Q.webSessionId}).filter(([,X])=>!!X))}function OQ(Q,X,Z){return{..._(Z),Accept:"*/*","Content-Type":Q.mimeType,Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:1,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}function m(Q){const X=Math.min(Q.width,Q.height),Z=Number((Q.durationMs/1000).toFixed(3));return{crop_height:X,crop_width:X,crop_x1:Math.max(0,Math.floor((Q.width-X)/2)),crop_y1:Math.max(0,Math.floor((Q.height-X)/2)),mute:!1,trim_end:Z,trim_start:0}}function WQ(Q,X,Z){return{..._(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",is_unified_video:"0",is_sidecar:"1",media_type:2,for_album:!1,video_format:"",upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:m(Q)})}}function GQ(Q,X,Z){return{..._(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",media_type:2,upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:m(Q)})}}function HQ(Q,X,Z){return{..._(Z),Accept:"*/*","Content-Type":Q.coverImage.mimeType,Offset:"0","X-Entity-Length":String(Q.coverImage.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.coverImage.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:2,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}async function L(Q,X){const Z=await Q.text();let $;try{$=Z?JSON.parse(Z):{}}catch{throw new K(`Instagram private publish ${X} returned invalid JSON`)}if(!Q.ok){const q=Z?` ${Z.slice(0,500)}`:"";throw new K(`Instagram private publish ${X} failed: ${Q.status}${q}`)}return $}async function E(Q,X,Z){let $;for(let q=0;q<v;q+=1)try{return await Q(X,Z)}catch(F){$=F;if(!g(F)||q>=v-1)throw F}throw $ instanceof Error?$:Error(String($))}async function JQ(Q){if(Q.type==="video")return{type:"video",asset:readVideoAsset(Q.filePath)};return{type:"image",asset:prepareImageAssetForPrivateUpload(Q.filePath)}}function f(Q){for(const X of Q){if(X.type==="image"){if(X.asset.cleanupPath)U.rmSync(X.asset.cleanupPath,{force:!0});continue}for(const Z of X.asset.cleanupPaths||[])U.rmSync(Z,{force:!0});if(X.asset.coverImage.cleanupPath)U.rmSync(X.asset.coverImage.cleanupPath,{force:!0})}}async function c(Q,X,Z,$,q="feed"){if(X.type==="image"){const J=await E(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:OQ(X.asset,Z,$),body:X.asset.bytes}),W=await L(J,"upload");if(String(W?.status||"")!=="ok")throw new K(`Instagram private publish upload failed for ${X.asset.fileName}`);return}const F=await E(Q,`https://i.instagram.com/rupload_igvideo/fb_uploader_${Z}`,{method:"POST",headers:q==="story"?GQ(X.asset,Z,$):WQ(X.asset,Z,$),body:X.asset.bytes}),O=await L(F,"video upload");if(String(O?.status||"")!=="ok")throw new K(`Instagram private publish video upload failed for ${X.asset.fileName}`);const G=await E(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:HQ(X.asset,Z,$),body:X.asset.coverImage.bytes}),N=await L(G,"video cover upload");if(String(N?.status||"")!=="ok")throw new K(`Instagram private publish video cover upload failed for ${X.asset.fileName}`)}async function VQ(Q){const X=Q.waitMs??r,Z={method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/json"},body:JSON.stringify(Q.payload)};for(let $=0;$<w;$+=1){let q,F;try{q=await Q.fetcher("https://www.instagram.com/api/v1/media/configure_sidecar/",Z);F=await q.text()}catch(N){throw markPublishOutcomeUncertain(N instanceof Error?N:new K(`Instagram private publish configure_sidecar request failed: ${String(N)}`))}let O={};try{O=F?JSON.parse(F):{}}catch{throw markPublishOutcomeUncertain(new K("Instagram private publish configure_sidecar returned invalid JSON"))}if(!q.ok){const N=F?` ${F.slice(0,500)}`:"";throw markPublishOutcomeUncertain(new K(`Instagram private publish configure_sidecar failed: ${q.status}${N}`))}const G=String(O?.message||"");if(q.status===202||/transcode not finished yet/i.test(G)){if($>=w-1)throw markPublishOutcomeUncertain(new K("Instagram private publish configure_sidecar timed out waiting for video transcode",F.slice(0,500)));await X(d);continue}if(String(O?.status||"").toLowerCase()==="fail")throw new K("Instagram private publish configure_sidecar failed",G||F.slice(0,500));return{code:O?.media?.code}}throw new K("Instagram private publish configure_sidecar failed")}export async function publishMediaViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.mediaItems.length>1?Q.mediaItems.map((G,N)=>String(X()+N+1)):[String(X())],q=Q.fetcher??((G,N)=>j(Q.page,G,N)),F=Q.prepareMediaAsset??JQ,O=await Promise.all(Q.mediaItems.map((G)=>F(G)));try{for(let N=0;N<O.length;N+=1){const J=O[N],W=$[N];await c(q,J,W,Q.apiContext)}if($.length===1){if(O[0]?.type!=="image")throw new K("Instagram private publish only supports single-video uploads through instagram reel");let N;try{const J=await q("https://www.instagram.com/api/v1/media/configure/",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:buildConfigureBody({uploadId:$[0],caption:Q.caption,jazoest:Q.jazoest})});N=await L(J,"configure")}catch(J){throw markPublishOutcomeUncertain(J instanceof Error?J:new K(`Instagram private publish configure request failed: ${String(J)}`))}return{code:N?.media?.code,uploadIds:$}}return{code:(await VQ({fetcher:q,payload:buildConfigureSidecarPayload({uploadIds:$,caption:Q.caption,clientSidecarId:Z,jazoest:Q.jazoest}),apiContext:Q.apiContext,waitMs:Q.waitMs})).code,uploadIds:$}}finally{f(O)}}export async function publishImagesViaPrivateApi(Q){return publishMediaViaPrivateApi({page:Q.page,mediaItems:Q.imagePaths.map((X)=>({type:"image",filePath:X})),caption:Q.caption,apiContext:Q.apiContext,jazoest:Q.jazoest,now:Q.now,fetcher:Q.fetcher,waitMs:Q.waitMs,prepareMediaAsset:Q.prepareAsset?async(X)=>({type:"image",asset:await Q.prepareAsset(X.filePath)}):void 0})}export async function publishStoryViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.fetcher??((W,H)=>j(Q.page,W,H)),F=await(Q.prepareMediaAsset??(async(W)=>W.type==="video"?{type:"video",asset:NQ(W.filePath)}:{type:"image",asset:prepareImageAssetForPrivateStoryUpload(W.filePath)}))(Q.mediaItem),O=Q.currentUserId||("getCookies"in Q.page?String((await(Q.page.getCookies?.({domain:"instagram.com"})??Promise.resolve([]))).find((W)=>W.name==="ds_user_id")?.value||""):"");if(!O)throw new K("Instagram story publish could not derive current user id from browser session");const G={_csrftoken:Q.apiContext.csrfToken,_uid:O,_uuid:B.randomUUID(),device:p},N=(W,H)=>b({...buildConfigureToStoryPhotoPayload({uploadId:Z,width:W,height:H,now:X,jazoest:Q.jazoest}),...G}),J=(W,H,Y)=>b({...buildConfigureToStoryVideoPayload({uploadId:Z,width:W,height:H,durationMs:Y,now:X,jazoest:Q.jazoest}),...G});try{await c($,F,Z,Q.apiContext,"story");if(F.type==="image"){let H;try{const Y=await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:N(F.asset.width,F.asset.height)});H=await L(Y,"configure_to_story")}catch(Y){throw markPublishOutcomeUncertain(Y instanceof Error?Y:new K(`Instagram private publish configure_to_story request failed: ${String(Y)}`))}return{mediaPk:String(H?.media?.pk||H?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}let W;try{await L(await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:N(F.asset.width,F.asset.height)}),"configure_to_story cover");const H=await $("https://i.instagram.com/api/v1/media/configure_to_story/?video=1",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:J(F.asset.width,F.asset.height,F.asset.durationMs)});W=await L(H,"configure_to_story")}catch(H){throw markPublishOutcomeUncertain(H instanceof Error?H:new K(`Instagram private publish configure_to_story request failed: ${String(H)}`))}return{mediaPk:String(W?.media?.pk||W?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}finally{f([F])}}
@@ -1,4 +1,4 @@
1
- import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy as n}from"@jackwener/opencli/registry";import{ArgumentError as V,AuthRequiredError as R,CommandExecutionError as Y}from"@jackwener/opencli/errors";import{installInstagramProtocolCapture as u,readInstagramProtocolCapture as i}from"./_shared/protocol-capture.js";import{publishMediaViaPrivateApi as r,publishImagesViaPrivateApi as p,resolveInstagramPrivatePublishConfig as a}from"./_shared/private-publish.js";import{resolveInstagramRuntimeInfo as t}from"./_shared/runtime-info.js";import{throwIfFileAccessDenied as e}from"../_shared/video-publish.js";const z="https://www.instagram.com/",jj=new Set([".jpg",".jpeg",".png",".webp"]),Gj=new Set([".mp4"]),C=10,Nj="/tmp/instagram_post_protocol_trace.json";async function b(j,G=!1){if(G){await j.goto(`${z}?__opencli_reset=${Date.now()}`);await j.wait({time:1})}await j.goto(z)}export function buildEnsureComposerOpenJs(){return`
1
+ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy as p}from"@jackwener/opencli/registry";import{ArgumentError as z,AuthRequiredError as R,CommandExecutionError as Y}from"@jackwener/opencli/errors";import{installInstagramProtocolCapture as a,readInstagramProtocolCapture as t}from"./_shared/protocol-capture.js";import{publishMediaViaPrivateApi as e,publishImagesViaPrivateApi as jj,resolveInstagramPrivatePublishConfig as Gj,isPublishOutcomeUncertain as Nj}from"./_shared/private-publish.js";import{resolveInstagramRuntimeInfo as Oj}from"./_shared/runtime-info.js";import{throwIfFileAccessDenied as Qj}from"../_shared/video-publish.js";const K="https://www.instagram.com/",Xj=new Set([".jpg",".jpeg",".png",".webp"]),Yj=new Set([".mp4"]),C=10,Zj="/tmp/instagram_post_protocol_trace.json";async function k(j,G=!1){if(G){await j.goto(`${K}?__opencli_reset=${Date.now()}`);await j.wait({time:1})}await j.goto(K)}export function buildEnsureComposerOpenJs(){return`
2
2
  (() => {
3
3
  const path = window.location?.pathname || '';
4
4
  const onLoginRoute = /\\/accounts\\/login\\/?/.test(path);
@@ -61,7 +61,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
61
61
  const settled = !shared && !composerOpen && !/sharing/.test(visibleText);
62
62
  return { ok: shared, failed, settled, url: /\\/p\\//.test(url) ? url : '' };
63
63
  })()
64
- `}function Oj(j){if(!j)throw new Y("Browser session required for instagram post");return j}function Qj(j){if(!j.length)throw new V('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4");if(j.length>C)throw new V(`Too many media items: ${j.length}`,`Instagram carousel posts support at most ${C} items`);return j.map((N)=>{const O=W.resolve(String(N||"").trim());if(!O)throw new V("Media path cannot be empty");if(!w.existsSync(O))throw new V(`Media file not found: ${O}`);const Q=W.extname(O).toLowerCase();if(jj.has(Q))return{type:"image",filePath:O};if(Gj.has(Q))return{type:"video",filePath:O};throw new V(`Unsupported media format: ${Q}`,"Supported formats: images (.jpg, .jpeg, .png, .webp) and videos (.mp4)")})}function Xj(j){const G=String(j.media??"").trim();return Qj(G.split(",").map((N)=>N.trim()).filter(Boolean))}function Yj(j){if(j.media===void 0)throw new V('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4")}function Zj(j){if(!(j instanceof Y))return!1;return j.message.startsWith("Instagram private publish")||j.message.startsWith("Instagram private route")}function f(j,G){return[{status:"✅ Posted",detail:Vj(j),url:G}]}function _j(j,G){const N=j instanceof Error?j.message:String(j),O=G instanceof Error?G.message:String(G);return`Private route failed first: ${N}. UI fallback then failed: ${O}`}async function $j(j){const G=await a(j.page),N=j.mediaItems.every((Q)=>Q.type==="image")?await p({page:j.page,imagePaths:j.mediaItems.map((Q)=>Q.filePath),caption:j.content,apiContext:G.apiContext,jazoest:G.jazoest}):await r({page:j.page,mediaItems:j.mediaItems,caption:j.content,apiContext:G.apiContext,jazoest:G.jazoest}),O=N.code?new URL(`/p/${N.code}/`,z).toString():await g(j.page,j.existingPostPaths);return f(j.mediaItems,O)}async function qj(j){let G,N=null;for(let O=0;O<j.commandAttemptBudget;O++){let Q=!1;try{await b(j.page,j.forceFreshStart||O>0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await x(j.page);const X=await h(j.page,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds});let Z=!1,_=null;for(const q of X){let J=q;for(let B=0;B<j.uploadAttemptBudget;B++){await Lj(j.page,j.mediaItems,J);const K=await P(j.page,j.previewProbeWindowSeconds);if(K.state==="preview"){Z=!0;break}if(K.state==="failed"){_=F(K.detail);for(let T=0;T<j.inlineUploadRetryBudget;T++){if(!await Rj(j.page))break;await j.page.wait({time:3});const D=await P(j.page,Math.max(3,Math.floor(j.previewProbeWindowSeconds/2)));if(D.state==="preview"){Z=!0;break}if(D.state!=="failed")break}if(Z)break;await Wj(j.page);await A(j.page);if(B<j.uploadAttemptBudget-1){try{await j.drainProtocolCapture();await b(j.page,!0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await x(j.page);J=await E(j.page,J,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds})}catch{throw _}await j.page.wait({time:1.5});continue}break}break}if(Z)break}if(!Z){if(_)throw _;await xj(j.page,j.finalPreviewWaitSeconds)}try{await wj(j.page)}catch(q){await Cj(j.page,q)}if(j.content){await kj(j.page,j.content);await fj(j.page,j.content)}if(j.preShareDelaySeconds>0)await j.page.wait({time:j.preShareDelaySeconds});await v(j.page,["Share","分享"],"caption");Q=!0;let $="";try{$=await c(j.page)}catch(q){if(q instanceof Y&&q.message==="Instagram post share failed"&&await yj(j.page)){await j.page.wait({time:Math.max(2,j.preShareDelaySeconds)});$=await c(j.page)}else throw q}await j.drainProtocolCapture();if(!$)$=await g(j.page,j.existingPostPaths);return f(j.mediaItems,$)}catch(X){G=X;if(X instanceof Y&&X.message!=="Failed to open Instagram post composer")N=X;if(X instanceof R)throw X;if(Q)throw X;if(!(X instanceof Y)||O===j.commandAttemptBudget-1){if(X instanceof Y&&X.message==="Failed to open Instagram post composer"&&N)throw N;throw X}let Z=!1;if(j.mediaItems.length>=10&&j.page.closeWindow)try{await j.drainProtocolCapture();await j.page.closeWindow();Z=!0}catch{}if(!Z){await A(j.page);await j.page.wait({time:1})}}}throw G instanceof Error?G:new Y("Instagram post failed")}async function x(j){const G=await j.evaluate(buildEnsureComposerOpenJs());if(!G?.ok){if(G?.reason==="auth")throw new R("www.instagram.com","Instagram login required before posting");throw new Y("Failed to open Instagram post composer")}}async function A(j){for(let G=0;G<4;G++){if(!(await j.evaluate(`
64
+ `}function _j(j){if(!j)throw new Y("Browser session required for instagram post");return j}function $j(j){if(!j.length)throw new z('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4");if(j.length>C)throw new z(`Too many media items: ${j.length}`,`Instagram carousel posts support at most ${C} items`);return j.map((N)=>{const O=W.resolve(String(N||"").trim());if(!O)throw new z("Media path cannot be empty");if(!b.existsSync(O))throw new z(`Media file not found: ${O}`);const Q=W.extname(O).toLowerCase();if(Xj.has(Q))return{type:"image",filePath:O};if(Yj.has(Q))return{type:"video",filePath:O};throw new z(`Unsupported media format: ${Q}`,"Supported formats: images (.jpg, .jpeg, .png, .webp) and videos (.mp4)")})}function qj(j){const G=String(j.media??"").trim();return $j(G.split(",").map((N)=>N.trim()).filter(Boolean))}function Hj(j){if(j.media===void 0)throw new z('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4")}function Jj(j){if(!(j instanceof Y))return!1;return j.message.startsWith("Instagram private publish")||j.message.startsWith("Instagram private route")}function x(j,G){return[{status:"✅ Posted",detail:Mj(j),url:G}]}function Tj(j,G){const N=j instanceof Error?j.message:String(j),O=G instanceof Error?G.message:String(G);return`Private route failed first: ${N}. UI fallback then failed: ${O}`}async function Vj(j){const G=await Gj(j.page),N=j.mediaItems.every((Q)=>Q.type==="image")?await jj({page:j.page,imagePaths:j.mediaItems.map((Q)=>Q.filePath),caption:j.content,apiContext:G.apiContext,jazoest:G.jazoest}):await e({page:j.page,mediaItems:j.mediaItems,caption:j.content,apiContext:G.apiContext,jazoest:G.jazoest}),O=N.code?new URL(`/p/${N.code}/`,K).toString():j.canDiffProfile?await w(j.page,j.existingPostPaths):"";return x(j.mediaItems,O)}async function zj(j){let G,N=null;for(let O=0;O<j.commandAttemptBudget;O++){let Q=!1;try{await k(j.page,j.forceFreshStart||O>0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await y(j.page);const X=await v(j.page,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds});let _=!1,Z=null;for(const q of X){let T=q;for(let L=0;L<j.uploadAttemptBudget;L++){await Aj(j.page,j.mediaItems,T);const B=await c(j.page,j.previewProbeWindowSeconds);if(B.state==="preview"){_=!0;break}if(B.state==="failed"){Z=D(B.detail);for(let F=0;F<j.inlineUploadRetryBudget;F++){if(!await Cj(j.page))break;await j.page.wait({time:3});const V=await c(j.page,Math.max(3,Math.floor(j.previewProbeWindowSeconds/2)));if(V.state==="preview"){_=!0;break}if(V.state!=="failed")break}if(_)break;await bj(j.page);await A(j.page);if(L<j.uploadAttemptBudget-1){try{await j.drainProtocolCapture();await k(j.page,!0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await y(j.page);T=await I(j.page,T,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds})}catch{throw Z}await j.page.wait({time:1.5});continue}break}break}if(_)break}if(!_){if(Z)throw Z;await kj(j.page,j.finalPreviewWaitSeconds)}try{await hj(j.page)}catch(q){await Ej(j.page,q)}if(j.content){await Sj(j.page,j.content);await vj(j.page,j.content)}if(j.preShareDelaySeconds>0)await j.page.wait({time:j.preShareDelaySeconds});await d(j.page,["Share","分享"],"caption");Q=!0;let $="";try{$=await g(j.page)}catch(q){if(q instanceof Y&&q.message==="Instagram post share failed"&&await Uj(j.page)){await j.page.wait({time:Math.max(2,j.preShareDelaySeconds)});$=await g(j.page)}else throw q}await j.drainProtocolCapture();if(!$&&j.canDiffProfile)$=await w(j.page,j.existingPostPaths);return x(j.mediaItems,$)}catch(X){G=X;if(X instanceof Y&&X.message!=="Failed to open Instagram post composer")N=X;if(X instanceof R)throw X;if(Q){let Z="";if(j.canDiffProfile)try{Z=await w(j.page,j.existingPostPaths)}catch{}if(Z)return x(j.mediaItems,Z);const $=X instanceof Error?X.message:String(X);throw new Y(`Instagram share was clicked but the outcome could not be confirmed: ${$}`,"The post may already be live. Do NOT rerun this command blindly — open the profile (or rerun after checking) and confirm no new post exists first; retrying on a false failure creates a duplicate post.")}if(!(X instanceof Y)||O===j.commandAttemptBudget-1){if(X instanceof Y&&X.message==="Failed to open Instagram post composer"&&N)throw N;throw X}let _=!1;if(j.mediaItems.length>=10&&j.page.closeWindow)try{await j.drainProtocolCapture();await j.page.closeWindow();_=!0}catch{}if(!_){await A(j.page);await j.page.wait({time:1})}}}throw G instanceof Error?G:new Y("Instagram post failed")}async function y(j){const G=await j.evaluate(buildEnsureComposerOpenJs());if(!G?.ok){if(G?.reason==="auth")throw new R("www.instagram.com","Instagram login required before posting");throw new Y("Failed to open Instagram post composer")}}async function A(j){for(let G=0;G<4;G++){if(!(await j.evaluate(`
65
65
  (() => {
66
66
  const isVisible = (el) => {
67
67
  if (!(el instanceof HTMLElement)) return false;
@@ -105,7 +105,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
105
105
 
106
106
  return { ok: false };
107
107
  })()
108
- `))?.ok)return;await j.wait({time:0.5})}}async function k(j,G){const N=G.some((Q)=>Q.type==="video"),O=await j.evaluate(`
108
+ `))?.ok)return;await j.wait({time:0.5})}}async function U(j,G){const N=G.some((Q)=>Q.type==="video"),O=await j.evaluate(`
109
109
  ((includesVideo) => {
110
110
  const isVisible = (el) => {
111
111
  if (!(el instanceof HTMLElement)) return false;
@@ -165,12 +165,12 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
165
165
  });
166
166
  return { ok: true, selectors };
167
167
  })(${JSON.stringify(N)})
168
- `);if(!O?.ok||!O.selectors?.length)throw new Y("Instagram upload input not found","Open the new-post composer in a logged-in browser session and retry");return O.selectors}async function h(j,G){try{return await k(j,G)}catch(N){if(!(N instanceof Y)||!N.message.includes("upload input not found"))throw N;await x(j);await j.wait({time:1.5});try{return await k(j,G)}catch(O){if(!(O instanceof Y)||!O.message.includes("upload input not found"))throw O;await b(j,!0);await j.wait({time:2});await A(j);await x(j);await j.wait({time:2});return k(j,G)}}}function Hj(j){const G=j.match(/data-opencli-ig-upload-index="(\d+)"/);if(!G)return null;const N=Number.parseInt(G[1]||"",10);return Number.isNaN(N)?null:N}async function E(j,G,N){const O=await h(j,N),Q=Hj(G);if(Q!==null&&O[Q])return O[Q];return O[0]||G}async function Jj(j,G,N){const O=G.map(($)=>{const q=W.extname($).toLowerCase(),J=q===".png"?"image/png":q===".webp"?"image/webp":"image/jpeg";return{name:W.basename($),type:J,base64:w.readFileSync($).toString("base64")}}),Q=`__opencliInstagramUpload_${Date.now()}_${Math.random().toString(36).slice(2)}`,X=262144;await j.evaluate(`
168
+ `);if(!O?.ok||!O.selectors?.length)throw new Y("Instagram upload input not found","Open the new-post composer in a logged-in browser session and retry");return O.selectors}async function v(j,G){try{return await U(j,G)}catch(N){if(!(N instanceof Y)||!N.message.includes("upload input not found"))throw N;await y(j);await j.wait({time:1.5});try{return await U(j,G)}catch(O){if(!(O instanceof Y)||!O.message.includes("upload input not found"))throw O;await k(j,!0);await j.wait({time:2});await A(j);await y(j);await j.wait({time:2});return U(j,G)}}}function Kj(j){const G=j.match(/data-opencli-ig-upload-index="(\d+)"/);if(!G)return null;const N=Number.parseInt(G[1]||"",10);return Number.isNaN(N)?null:N}async function I(j,G,N){const O=await v(j,N),Q=Kj(G);if(Q!==null&&O[Q])return O[Q];return O[0]||G}async function Lj(j,G,N){const O=G.map(($)=>{const q=W.extname($).toLowerCase(),T=q===".png"?"image/png":q===".webp"?"image/webp":"image/jpeg";return{name:W.basename($),type:T,base64:b.readFileSync($).toString("base64")}}),Q=`__opencliInstagramUpload_${Date.now()}_${Math.random().toString(36).slice(2)}`,X=262144;await j.evaluate(`
169
169
  (() => {
170
170
  window[${JSON.stringify(Q)}] = [];
171
171
  return { ok: true };
172
172
  })()
173
- `);const Z=JSON.stringify(O);for(let $=0;$<Z.length;$+=X){const q=Z.slice($,$+X);await j.evaluate(`
173
+ `);const _=JSON.stringify(O);for(let $=0;$<_.length;$+=X){const q=_.slice($,$+X);await j.evaluate(`
174
174
  (() => {
175
175
  const key = ${JSON.stringify(Q)};
176
176
  const chunk = ${JSON.stringify(q)};
@@ -179,7 +179,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
179
179
  window[key] = parts;
180
180
  return { ok: true, count: parts.length };
181
181
  })()
182
- `)}const _=await j.evaluate(`
182
+ `)}const Z=await j.evaluate(`
183
183
  (() => {
184
184
  const selector = ${JSON.stringify(N)};
185
185
  const key = ${JSON.stringify(Q)};
@@ -212,7 +212,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
212
212
  return { ok: false, error: String(error) };
213
213
  }
214
214
  })()
215
- `);if(!_?.ok)throw new Y(_?.error||"Instagram fallback file injection failed")}async function Tj(j,G){await j.evaluate(`
215
+ `);if(!Z?.ok)throw new Y(Z?.error||"Instagram fallback file injection failed")}async function Bj(j,G){await j.evaluate(`
216
216
  (() => {
217
217
  const input = document.querySelector(${JSON.stringify(G)});
218
218
  if (!(input instanceof HTMLInputElement)) return { ok: false };
@@ -255,7 +255,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
255
255
  if (failed) return { state: 'failed', detail: dialogText || 'Something went wrong' };
256
256
  return { state: 'pending', detail: dialogText || '' };
257
257
  })()
258
- `}async function M(j){const G=await j.evaluate(buildInspectUploadStageJs());if(G?.state)return G;if(G?.ok===!0)return{state:"preview",detail:G.detail};return{state:"pending",detail:G?.detail}}function F(j){return new Y("Instagram image upload failed",j?`Instagram rejected the upload: ${j}`:"Instagram rejected the upload before the preview stage")}async function Lj(j,G,N){const O=G.map((X)=>X.filePath);if(!j.setFileInput)throw new Y("Instagram posting requires Browser Bridge file upload support","Use Browser Bridge or another browser mode that supports setFileInput");let Q=N;for(let X=0;X<2;X++)try{await j.setFileInput(O,Q);await Tj(j,Q);return}catch(Z){const _=Z instanceof Error?Z.message:String(Z);if((_.includes("No element found matching selector")||_.includes("Could not find node with given id")||_.includes("No node with given id found"))&&X===0){Q=await E(j,Q,G);continue}if(!_.includes("Unknown action")&&!_.includes("set-file-input")&&!_.includes("not supported")&&!_.includes("Not allowed"))throw Z;if(G.some((q)=>q.type==="video")){e(Z);throw new Y("Instagram mixed-media posting requires Browser Bridge file upload support","Use Browser Bridge or another browser mode that supports setFileInput for image/video uploads")}await Jj(j,O,Q);return}}function Vj(j){if(j.every((G)=>G.type==="image"))return j.length===1?"Single image post shared successfully":`${j.length}-image carousel post shared successfully`;return j.length===1?"Single mixed-media post shared successfully":`${j.length}-item mixed-media carousel post shared successfully`}function zj(j){if(j.length>=10)return 6;if(j.length>=5)return 4;return 3}function Bj(j){if(j.length>=10)return 3;if(j.length>=5)return 1.5;return 0}function Kj(j){if(j.length>=10)return 3;if(j.length>=5)return 3;return 2}function Dj(j){if(j.length>=10)return 6;if(j.length>=5)return 6;return 4}function Aj(j){if(j.length>=10)return 12;if(j.length>=5)return 16;return 12}function Mj(j){if(j.length>=10)return 4;if(j.length>=5)return 3;return 0}function Fj(j){if(j.length>=10)return 3;if(j.length>=5)return 2;return 1}async function Wj(j){return!!(await j.evaluate(`
258
+ `}async function M(j){const G=await j.evaluate(buildInspectUploadStageJs());if(G?.state)return G;if(G?.ok===!0)return{state:"preview",detail:G.detail};return{state:"pending",detail:G?.detail}}function D(j){return new Y("Instagram image upload failed",j?`Instagram rejected the upload: ${j}`:"Instagram rejected the upload before the preview stage")}async function Aj(j,G,N){const O=G.map((X)=>X.filePath);if(!j.setFileInput)throw new Y("Instagram posting requires Browser Bridge file upload support","Use Browser Bridge or another browser mode that supports setFileInput");let Q=N;for(let X=0;X<2;X++)try{await j.setFileInput(O,Q);await Bj(j,Q);return}catch(_){const Z=_ instanceof Error?_.message:String(_);if((Z.includes("No element found matching selector")||Z.includes("Could not find node with given id")||Z.includes("No node with given id found"))&&X===0){Q=await I(j,Q,G);continue}if(!Z.includes("Unknown action")&&!Z.includes("set-file-input")&&!Z.includes("not supported")&&!Z.includes("Not allowed"))throw _;if(G.some((q)=>q.type==="video")){Qj(_);throw new Y("Instagram mixed-media posting requires Browser Bridge file upload support","Use Browser Bridge or another browser mode that supports setFileInput for image/video uploads")}await Lj(j,O,Q);return}}function Mj(j){if(j.every((G)=>G.type==="image"))return j.length===1?"Single image post shared successfully":`${j.length}-image carousel post shared successfully`;return j.length===1?"Single mixed-media post shared successfully":`${j.length}-item mixed-media carousel post shared successfully`}function Dj(j){if(j.length>=10)return 6;if(j.length>=5)return 4;return 3}function Fj(j){if(j.length>=10)return 3;if(j.length>=5)return 1.5;return 0}function Wj(j){if(j.length>=10)return 3;if(j.length>=5)return 3;return 2}function Rj(j){if(j.length>=10)return 6;if(j.length>=5)return 6;return 4}function xj(j){if(j.length>=10)return 12;if(j.length>=5)return 16;return 12}function yj(j){if(j.length>=10)return 4;if(j.length>=5)return 3;return 0}function wj(j){if(j.length>=10)return 3;if(j.length>=5)return 2;return 1}async function bj(j){return!!(await j.evaluate(`
259
259
  (() => {
260
260
  const isVisible = (el) => {
261
261
  if (!(el instanceof HTMLElement)) return false;
@@ -278,7 +278,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
278
278
  }
279
279
  return { ok: false };
280
280
  })()
281
- `))?.ok}async function Rj(j){return!!(await j.evaluate(`
281
+ `))?.ok}async function Cj(j){return!!(await j.evaluate(`
282
282
  (() => {
283
283
  const isVisible = (el) => {
284
284
  if (!(el instanceof HTMLElement)) return false;
@@ -312,7 +312,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
312
312
  }
313
313
  return { ok: false };
314
314
  })()
315
- `))?.ok}async function xj(j,G=12){const N=Math.max(1,Math.ceil(G));for(let O=0;O<N;O++){const Q=await M(j);if(Q.state==="preview")return;if(Q.state==="failed"){await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw F("Inspect /tmp/instagram_post_preview_debug.png. "+(Q.detail||""))}if(O<N-1)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw new Y("Instagram image preview did not appear after upload","The selected file input may not match the active composer; inspect /tmp/instagram_post_preview_debug.png")}async function P(j,G=4){const N=Math.max(1,Math.ceil(G*2));for(let O=0;O<N;O++){const Q=await M(j);if(Q.state!=="pending")return Q;if(O<N-1)await j.wait({time:0.5})}return{state:"pending"}}export function buildClickActionJs(j,G="any"){return`
315
+ `))?.ok}async function kj(j,G=12){const N=Math.max(1,Math.ceil(G));for(let O=0;O<N;O++){const Q=await M(j);if(Q.state==="preview")return;if(Q.state==="failed"){await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw D("Inspect /tmp/instagram_post_preview_debug.png. "+(Q.detail||""))}if(O<N-1)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw new Y("Instagram image preview did not appear after upload","The selected file input may not match the active composer; inspect /tmp/instagram_post_preview_debug.png")}async function c(j,G=4){const N=Math.max(1,Math.ceil(G*2));for(let O=0;O<N;O++){const Q=await M(j);if(Q.state!=="pending")return Q;if(O<N-1)await j.wait({time:0.5})}return{state:"pending"}}export function buildClickActionJs(j,G="any"){return`
316
316
  ((labels, scope) => {
317
317
  const isVisible = (el) => {
318
318
  if (!(el instanceof HTMLElement)) return false;
@@ -367,7 +367,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
367
367
  }
368
368
  return { ok: false };
369
369
  })(${JSON.stringify(j)}, ${JSON.stringify(G)})
370
- `}async function v(j,G,N="any"){const O=await j.evaluate(buildClickActionJs(G,N));if(!O?.ok)throw new Y(`Instagram action button not found: ${G.join(" / ")}`);return O.label||G[0]}async function yj(j){return!!(await j.evaluate(`
370
+ `}async function d(j,G,N="any"){const O=await j.evaluate(buildClickActionJs(G,N));if(!O?.ok)throw new Y(`Instagram action button not found: ${G.join(" / ")}`);return O.label||G[0]}async function Uj(j){return!!(await j.evaluate(`
371
371
  (() => {
372
372
  const isVisible = (el) => {
373
373
  if (!(el instanceof HTMLElement)) return false;
@@ -405,12 +405,12 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
405
405
 
406
406
  return { ok: false };
407
407
  })()
408
- `))?.ok}async function S(j){return!!(await j.evaluate(`
408
+ `))?.ok}async function l(j){return!!(await j.evaluate(`
409
409
  (() => {
410
410
  const editable = document.querySelector('textarea, [contenteditable="true"]');
411
411
  return { ok: !!editable };
412
412
  })()
413
- `))?.ok}async function I(j){return!!(await j.evaluate(`
413
+ `))?.ok}async function m(j){return!!(await j.evaluate(`
414
414
  (() => {
415
415
  const editable = document.querySelector('textarea, [contenteditable="true"]');
416
416
  const dialogText = Array.from(document.querySelectorAll('[role="dialog"]'))
@@ -424,7 +424,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
424
424
  || dialogText.includes('advanced settings'),
425
425
  };
426
426
  })()
427
- `))?.ok}async function wj(j){for(let G=0;G<3;G++){if(await I(j))return;try{await v(j,["Next","下一步"],"media")}catch(O){if(O instanceof Y){await j.wait({time:1.5});if(await I(j))return;const Q=await M(j);if(Q.state==="failed")throw F(Q.detail);if(G<2)continue}throw O}await j.wait({time:1.5});if(await S(j))return;const N=await M(j);if(N.state==="failed")throw F(N.detail)}await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new Y("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}async function dj(j){if(!await S(j)){await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new Y("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}}async function Cj(j,G){const N=await M(j);if(N.state==="failed")throw F(N.detail);throw G}async function bj(j){return!!(await j.evaluate(`
427
+ `))?.ok}async function hj(j){for(let G=0;G<3;G++){if(await m(j))return;try{await d(j,["Next","下一步"],"media")}catch(O){if(O instanceof Y){await j.wait({time:1.5});if(await m(j))return;const Q=await M(j);if(Q.state==="failed")throw D(Q.detail);if(G<2)continue}throw O}await j.wait({time:1.5});if(await l(j))return;const N=await M(j);if(N.state==="failed")throw D(N.detail)}await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new Y("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}async function sj(j){if(!await l(j)){await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new Y("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}}async function Ej(j,G){const N=await M(j);if(N.state==="failed")throw D(N.detail);throw G}async function fj(j){return!!(await j.evaluate(`
428
428
  (() => {
429
429
  const textarea = document.querySelector('[aria-label="Write a caption..."], textarea');
430
430
  if (textarea instanceof HTMLTextAreaElement) {
@@ -484,7 +484,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
484
484
 
485
485
  return { ok: true, kind: 'contenteditable' };
486
486
  })()
487
- `))?.ok}async function kj(j,G){if(j.insertText&&await bj(j))try{await j.insertText(G);await j.wait({time:0.3});await j.evaluate(`
487
+ `))?.ok}async function Sj(j,G){if(j.insertText&&await fj(j))try{await j.insertText(G);await j.wait({time:0.3});await j.evaluate(`
488
488
  (() => {
489
489
  const textarea = document.querySelector('[aria-label="Write a caption..."], textarea');
490
490
  if (textarea instanceof HTMLTextAreaElement) {
@@ -596,7 +596,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
596
596
 
597
597
  return { ok: false };
598
598
  })(${JSON.stringify(G)})
599
- `))?.ok)throw new Y("Failed to fill Instagram caption")}async function Uj(j,G){return!!(await j.evaluate(`
599
+ `))?.ok)throw new Y("Failed to fill Instagram caption")}async function Pj(j,G){return!!(await j.evaluate(`
600
600
  ((content) => {
601
601
  const normalized = content.trim();
602
602
  const readLexicalText = (node) => {
@@ -650,7 +650,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
650
650
 
651
651
  return { ok: false };
652
652
  })(${JSON.stringify(G)})
653
- `))?.ok}async function fj(j,G){for(let N=0;N<6;N++){if(await Uj(j,G))return;if(N<5)await j.wait({time:0.5})}await j.screenshot({path:"/tmp/instagram_post_caption_fill_debug.png"});throw new Y("Instagram caption did not stick before sharing","Inspect /tmp/instagram_post_caption_fill_debug.png for the caption editor state")}async function c(j){let G=0;for(let N=0;N<90;N++){const O=await j.evaluate(buildPublishStatusProbeJs());if(O?.failed){await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new Y("Instagram post share failed","Inspect /tmp/instagram_post_share_debug.png for the share failure state")}if(O?.ok)return O.url||"";if(O?.settled){G+=1;if(G>=3)return""}else G=0;if(N<89)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new Y("Instagram post share confirmation did not appear","Inspect /tmp/instagram_post_share_debug.png for the final publish state")}async function d(j){return(await j.getCookies({domain:"instagram.com"})).find((N)=>N.name==="ds_user_id")?.value||""}async function l(j,G=""){if(G){const O=await t(j),Q=await j.evaluate(`
653
+ `))?.ok}async function vj(j,G){for(let N=0;N<6;N++){if(await Pj(j,G))return;if(N<5)await j.wait({time:0.5})}await j.screenshot({path:"/tmp/instagram_post_caption_fill_debug.png"});throw new Y("Instagram caption did not stick before sharing","Inspect /tmp/instagram_post_caption_fill_debug.png for the caption editor state")}async function g(j){let G=0;for(let N=0;N<90;N++){const O=await j.evaluate(buildPublishStatusProbeJs());if(O?.failed){await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new Y("Instagram post share failed","Inspect /tmp/instagram_post_share_debug.png for the share failure state")}if(O?.ok)return O.url||"";if(O?.settled){G+=1;if(G>=3)return""}else G=0;if(N<89)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new Y("Instagram post share confirmation did not appear","Inspect /tmp/instagram_post_share_debug.png for the final publish state")}async function o(j){return(await j.getCookies({domain:"instagram.com"})).find((N)=>N.name==="ds_user_id")?.value||""}async function s(j,G=""){if(G){const O=await Oj(j),Q=await j.evaluate(`
654
654
  (async () => {
655
655
  const userId = ${JSON.stringify(G)};
656
656
  const appId = ${JSON.stringify(O.appId||"")};
@@ -670,7 +670,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
670
670
  return { ok: false };
671
671
  }
672
672
  })()
673
- `);if(Q?.ok&&Q.username)return new URL(`/${Q.username}/`,z).toString()}const N=await j.evaluate(`
673
+ `);if(Q?.ok&&Q.username)return new URL(`/${Q.username}/`,K).toString()}const N=await j.evaluate(`
674
674
  (() => {
675
675
  const isVisible = (el) => {
676
676
  if (!(el instanceof HTMLElement)) return false;
@@ -695,7 +695,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
695
695
  const path = explicitProfile;
696
696
  return { ok: !!path, path };
697
697
  })()
698
- `);if(!N?.ok||!N.path)return"";return new URL(N.path,z).toString()}async function m(j){const G=await j.evaluate(`
698
+ `);if(!N?.ok||!N.path)return"";return new URL(N.path,K).toString()}async function n(j){const G=await j.evaluate(`
699
699
  (() => {
700
700
  const isVisible = (el) => {
701
701
  if (!(el instanceof HTMLElement)) return false;
@@ -715,4 +715,4 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
715
715
 
716
716
  return { ok: hrefs.length > 0, hrefs };
717
717
  })()
718
- `);return Array.isArray(G?.hrefs)?G.hrefs.filter(Boolean):[]}async function hj(j){const G=await d(j);if(!G)return new Set;const N=await l(j,G);if(!N)return new Set;try{await j.goto(N);await j.wait({time:3});return new Set(await m(j))}catch{return new Set}}async function g(j,G){const N=await j.getCurrentUrl?.();if(N&&/\/p\//.test(N))return N;const O=await d(j),Q=await l(j,O);if(!Q)return"";await j.goto(Q);await j.wait({time:4});for(let X=0;X<8;X++){const _=(await m(j)).find(($)=>!G.has($))||"";if(_)return new URL(_,z).toString();if(X<7)await j.wait({time:1})}return""}s({site:"instagram",name:"post",access:"write",description:"Post an Instagram feed image or mixed-media carousel",domain:"www.instagram.com",strategy:n.UI,browser:!0,args:[{name:"media",required:!1,valueRequired:!0,help:`Comma-separated media paths (images/videos, up to ${C})`},{name:"content",positional:!0,required:!1,help:"Caption text"},{name:"timeout",type:"int",required:!1,default:300,help:"Max seconds for the overall command (default: 300)"}],columns:["status","detail","url"],validateArgs:Yj,func:async(j,G)=>{const N=Oj(j),O=Xj(G),Q=String(G.content??"").trim(),X=await hj(N),Z=zj(O),_=Bj(O),$=Kj(O),q=Dj(O),J=Aj(O),B=Mj(O),K=Fj(O),T=process.env.OPENCLI_INSTAGRAM_CAPTURE==="1",y=[],D=[],o=async()=>{if(!T)return;await u(N)},U=async()=>{if(!T)return;const H=await i(N);if(H.data.length)y.push(...H.data);if(H.errors.length)D.push(...H.errors)};try{try{return await $j({page:N,mediaItems:O,content:Q,existingPostPaths:X})}catch(H){if(H instanceof R||!Zj(H))throw H;try{return await qj({page:N,mediaItems:O,content:Q,existingPostPaths:X,commandAttemptBudget:Z,preUploadDelaySeconds:_,uploadAttemptBudget:$,previewProbeWindowSeconds:q,finalPreviewWaitSeconds:J,preShareDelaySeconds:B,inlineUploadRetryBudget:K,installProtocolCapture:o,drainProtocolCapture:U,forceFreshStart:!0})}catch(L){if(L instanceof R)throw L;if(L instanceof Y)throw new Y(L.message,_j(H,L));throw L}}}finally{if(T){try{await U()}catch{}w.writeFileSync(Nj,JSON.stringify({data:y,errors:D},null,2))}}}});
718
+ `);return Array.isArray(G?.hrefs)?G.hrefs.filter(Boolean):[]}async function Ij(j){const G=await o(j);if(!G)return null;const N=await s(j,G);if(!N)return null;try{await j.goto(N);await j.wait({time:3});return new Set(await n(j))}catch{return null}}async function w(j,G){const N=await j.getCurrentUrl?.();if(N&&/\/p\//.test(N))return N;const O=await o(j),Q=await s(j,O);if(!Q)return"";await j.goto(Q);await j.wait({time:4});for(let X=0;X<8;X++){const Z=(await n(j)).find(($)=>!G.has($))||"";if(Z)return new URL(Z,K).toString();if(X<7)await j.wait({time:1})}return""}r({site:"instagram",name:"post",access:"write",description:"Post an Instagram feed image or mixed-media carousel",domain:"www.instagram.com",strategy:p.UI,browser:!0,args:[{name:"media",required:!1,valueRequired:!0,help:`Comma-separated media paths (images/videos, up to ${C})`},{name:"content",positional:!0,required:!1,help:"Caption text"},{name:"timeout",type:"int",required:!1,default:300,help:"Max seconds for the overall command (default: 300)"}],columns:["status","detail","url"],validateArgs:Hj,func:async(j,G)=>{const N=_j(j),O=qj(G),Q=String(G.content??"").trim(),X=await Ij(N),_=X!==null,Z=X??new Set,$=Dj(O),q=Fj(O),T=Wj(O),L=Rj(O),B=xj(O),F=yj(O),h=wj(O),V=process.env.OPENCLI_INSTAGRAM_CAPTURE==="1",E=[],f=[],u=async()=>{if(!V)return;await a(N)},S=async()=>{if(!V)return;const H=await t(N);if(H.data.length)E.push(...H.data);if(H.errors.length)f.push(...H.errors)};try{try{return await Vj({page:N,mediaItems:O,content:Q,existingPostPaths:Z,canDiffProfile:_})}catch(H){if(H instanceof R)throw H;if(Nj(H)){let J="",P=!1;if(_)try{J=await w(N,Z);P=!0}catch{}if(J)return x(O,J);if(!P){const i=H instanceof Error?H.message:String(H);throw new Y(`Instagram publish outcome uncertain: ${i}`,"The post may already be live. Do NOT rerun this command blindly — check the profile for a new post first; retrying on a false failure creates a duplicate post.")}}if(!Jj(H))throw H;try{return await zj({page:N,mediaItems:O,content:Q,existingPostPaths:Z,canDiffProfile:_,commandAttemptBudget:$,preUploadDelaySeconds:q,uploadAttemptBudget:T,previewProbeWindowSeconds:L,finalPreviewWaitSeconds:B,preShareDelaySeconds:F,inlineUploadRetryBudget:h,installProtocolCapture:u,drainProtocolCapture:S,forceFreshStart:!0})}catch(J){if(J instanceof R)throw J;if(J instanceof Y)throw new Y(J.message,Tj(H,J));throw J}}}finally{if(V){try{await S()}catch{}b.writeFileSync(Zj,JSON.stringify({data:E,errors:f},null,2))}}}});
@@ -1,9 +1,9 @@
1
- import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionError as V,ArgumentError as F}from"@jackwener/opencli/errors";import{cli as Yz,Strategy as Xz}from"@jackwener/opencli/registry";import{assertLiteralContent as Wz}from"../_shared/content-guard.js";import{humanType as A,humanWait as N,humanMoveTo as Fz,elementViewportCenter as Oz}from"./human.js";const Uz="https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image",u=9,d=20,n=3000,m="|||",k="基础",i=[["基础","默认兜底,万能"],["边框","金句/要点卡"],["备忘","提醒/随手记"],["清新","日常/清单贴士"],["涂写","随笔/碎碎念"],["便签","笔记/提醒"],["光影","情绪/文艺"],["涂鸦","趣味/童话"],["简约","干货/观点"],["手写","日记/情感"],["插图","生活方式/轻松话题"],["美漫","活力/趣味/故事感"],["弥散","弥散光氛围"],["柔和","柔和/温柔金句"],["印刷","印刷海报/排版"],["科技","科技/产品"],["贺卡","节日祝福"],["札记","艺术/水彩氛围"],["书摘","书摘/引用"],["手帐","手帐拼贴"],["几何","醒目/有力主张"]],iz=i.map(([z])=>z),T="文字配图",l="再写一张",o="生成图片",s="下一步",S=".tiptap.ProseMirror",vz=["_onPublish","onPublish","_onSubmit","_handlePublish"],Bz=["_onSave","_onSaveDraft","_onDraft"],C=['[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]"],r=['[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])'],g={".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 Dz(z){return z.map((J)=>{const K=D.resolve(J);if(!_.existsSync(K))throw new F(`Image file not found: ${K}`);const Z=D.extname(K).toLowerCase();if(!g[Z])throw new F(`Unsupported image format "${Z}". Supported: jpg, png, gif, webp`);return K})}const p='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 fz(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(`
1
+ import*as w from"node:fs";import*as I from"node:path";import{CommandExecutionError as H,ArgumentError as U}from"@jackwener/opencli/errors";import{cli as Fz,Strategy as Oz}from"@jackwener/opencli/registry";import{assertLiteralContent as Uz}from"../_shared/content-guard.js";import{humanType as x,humanWait as P,humanMoveTo as Bz,elementViewportCenter as vz}from"./human.js";const Dz="https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image",n=9,l=20,i=3000,o="|||",M="基础",s=[["基础","默认兜底,万能"],["边框","金句/要点卡"],["备忘","提醒/随手记"],["清新","日常/清单贴士"],["涂写","随笔/碎碎念"],["便签","笔记/提醒"],["光影","情绪/文艺"],["涂鸦","趣味/童话"],["简约","干货/观点"],["手写","日记/情感"],["插图","生活方式/轻松话题"],["美漫","活力/趣味/故事感"],["弥散","弥散光氛围"],["柔和","柔和/温柔金句"],["印刷","印刷海报/排版"],["科技","科技/产品"],["贺卡","节日祝福"],["札记","艺术/水彩氛围"],["书摘","书摘/引用"],["手帐","手帐拼贴"],["几何","醒目/有力主张"]],rz=s.map(([z])=>z),E="文字配图",g="再写一张",r="生成图片",p="下一步",u=".tiptap.ProseMirror",kz=["_onPublish","onPublish","_onSubmit","_handlePublish"],Nz=["_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])'],a={".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=I.resolve(J);if(!w.existsSync(K))throw new U(`Image file not found: ${K}`);const Q=I.extname(K).toLowerCase();if(!a[Q])throw new U(`Unsupported image format "${Q}". Supported: jpg, png, gif, webp`);return K})}const e='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 Pz(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
- const sels = ${JSON.stringify(p)};
3
+ const sels = ${JSON.stringify(e)};
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 t(z,J){if(!await fz(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,p);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=_.readFileSync($).toString("base64"),Y=D.extname($).toLowerCase();return{name:D.basename($),mimeType:g[Y],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(`
6
+ `))return!0;if(Z<Q-1)await z.wait({time:K/1000})}return!1}async function zz(z,J){if(!await Pz(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,e);return{ok:!0,count:J.length}}catch($){const V=$ instanceof Error?$.message:String($);if(V.includes("Unknown action")||V.includes("not supported")||V.includes("Not allowed"));else return{ok:!1,count:0,error:V}}const Q=J.map(($)=>{const V=w.readFileSync($).toString("base64"),Y=I.extname($).toLowerCase();return{name:I.basename($),mimeType:a[Y],base64:V}}),Z=Q.reduce(($,V)=>$+V.base64.length,0);if(Z>500000)console.warn(`[warn] Total image payload is ${(Z/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(Q);return z.evaluate(`
7
7
  (async () => {
8
8
  const images = ${q};
9
9
 
@@ -43,11 +43,11 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
43
43
 
44
44
  return { ok: true, count: dt.files.length };
45
45
  })()
46
- `)}async function a(z,J=30000){const K=2000,Z=Math.ceil(J/K);for(let Q=0;Q<Z;Q++){if(!await z.evaluate(`
46
+ `)}async function Jz(z,J=30000){const K=2000,Q=Math.ceil(J/K);for(let Z=0;Z<Q;Z++){if(!await z.evaluate(`
47
47
  () => !!document.querySelector(
48
48
  '[class*="upload"][class*="progress"], [class*="uploading"], [class*="loading"][class*="image"]'
49
49
  )
50
- `))return;await z.wait({time:K/1000})}}async function e(z,J,K,Z){const Q=await z.evaluate(`
50
+ `))return;await z.wait({time:K/1000})}}async function Kz(z,J,K,Q){const Z=await z.evaluate(`
51
51
  (function(selectors) {
52
52
  const __opencli_xhs_fill_phase = "locate";
53
53
  for (const sel of selectors) {
@@ -62,7 +62,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
62
62
  }
63
63
  return { ok: false };
64
64
  })(${JSON.stringify(J)})
65
- `);if(!Q.ok){await z.screenshot({path:`/tmp/xhs_publish_${Z}_debug.png`});throw Error(`Could not find ${Z} input. Debug screenshot: /tmp/xhs_publish_${Z}_debug.png`)}const q=()=>z.evaluate(`
65
+ `);if(!Z.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});throw Error(`Could not find ${Q} input. Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}const q=()=>z.evaluate(`
66
66
  ((selector, expectedText) => {
67
67
  const __opencli_xhs_fill_phase = "apply";
68
68
  const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
@@ -118,8 +118,8 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
118
118
  el.blur();
119
119
  const actual = normalize(el.innerText || el.textContent || '');
120
120
  return { ok: actual === normalize(expectedText), actual };
121
- })(${JSON.stringify(Q.sel)}, ${JSON.stringify(K)})
122
- `);let $;if(Q.kind==="contenteditable"&&z.insertText){if(!(await z.evaluate(`
121
+ })(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
122
+ `);let $;if(Z.kind==="contenteditable"&&z.insertText){if(!(await z.evaluate(`
123
123
  ((selector, nextText) => {
124
124
  const __opencli_xhs_fill_phase = "prepare";
125
125
  const fireBeforeInput = (el, value) => {
@@ -145,8 +145,8 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
145
145
  selection?.addRange(range);
146
146
  fireBeforeInput(el, nextText);
147
147
  return { ok: true };
148
- })(${JSON.stringify(Q.sel)}, ${JSON.stringify(K)})
149
- `))?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Z}_debug.png`});throw Error(`Could not prepare ${Z} input. Debug screenshot: /tmp/xhs_publish_${Z}_debug.png`)}try{await A(z,K);$=await z.evaluate(`
148
+ })(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
149
+ `))?.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);$=await z.evaluate(`
150
150
  ((selector, expectedText) => {
151
151
  const __opencli_xhs_fill_phase = "verify";
152
152
  const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
@@ -168,8 +168,8 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
168
168
  el.blur();
169
169
  const actual = normalize(el.innerText || el.textContent || '');
170
170
  return { ok: actual === normalize(expectedText), actual };
171
- })(${JSON.stringify(Q.sel)}, ${JSON.stringify(K)})
172
- `)}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 Nz(z,J){return W(await z.evaluate(`
171
+ })(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
172
+ `)}catch{$=await q()}}else $=await q();if(!$?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});const V=typeof $?.actual==="string"?$.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(`
173
173
  (selectors => {
174
174
  const el = selectors
175
175
  .map(sel => Array.from(document.querySelectorAll(sel)))
@@ -185,7 +185,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
185
185
  selection?.addRange(range);
186
186
  return true;
187
187
  })(${JSON.stringify(J)})
188
- `))}function lz(z,{click:J=!1}={}){return`
188
+ `))}function pz(z,{click:J=!1}={}){return`
189
189
  (topicName => {
190
190
  const norm = (value) => (value || '').replace(/^#/, '').replace(/\\s+/g, '').trim();
191
191
  const want = norm(topicName);
@@ -225,7 +225,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
225
225
  text: (target.innerText || target.textContent || '').trim().slice(0, 40),
226
226
  };
227
227
  })(${JSON.stringify(z)})
228
- `}function oz(z,J){return`
228
+ `}function tz(z,J){return`
229
229
  ((topicName, selectors) => {
230
230
  const norm = (value) => (value || '').replace(/^#/, '').replace(/\\s+/g, '').trim();
231
231
  const want = norm(topicName);
@@ -256,7 +256,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
256
256
  }
257
257
  return count;
258
258
  })(${JSON.stringify(z)}, ${JSON.stringify(J)})
259
- `}function zz(z,J){return`
259
+ `}function Qz(z,J){return`
260
260
  ((topicName, selectors) => {
261
261
  const __opencli_xhs_topic_marker_count = true;
262
262
  const marker = '#' + topicName + '[话题]';
@@ -274,14 +274,24 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
274
274
  }
275
275
  return count;
276
276
  })(${JSON.stringify(z)}, ${JSON.stringify(J)})
277
- `}async function sz(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return W(await z.evaluate(`
277
+ `}async function az(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return W(await z.evaluate(`
278
278
  (text => {
279
279
  const ok = document.execCommand('insertText', false, text);
280
280
  const active = document.activeElement;
281
281
  if (active) active.dispatchEvent(new Event('input', { bubbles: true }));
282
282
  return ok;
283
283
  })(${JSON.stringify(K)})
284
- `))}async function kz(z,J,K){const Z=[];for(const Q of K){if(!await Nz(z,J))throw new V(`Could not attach topic "${Q}": body editor not found`);const $=Number(W(await z.evaluate(zz(Q,J))))||0;if(typeof z.pressKey==="function")try{await z.pressKey("Enter")}catch{}if(typeof z.insertText!=="function")throw new V(`Could not attach topic "${Q}": page.insertText is unavailable`);try{await A(z,`#${Q}`)}catch{throw new V(`Could not attach topic "${Q}": failed to type inline topic query`)}await N(z,1.2);if(typeof z.pressKey!=="function")throw new V(`Could not attach topic "${Q}": page.pressKey is unavailable`);try{await z.pressKey("Enter")}catch(Y){throw new V(`Could not attach topic "${Q}": failed to accept suggestion (${Y&&Y.message||Y})`)}await z.wait({time:0.8});if((Number(W(await z.evaluate(zz(Q,J))))||0)<=$)throw new V(`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 Lz(z){const J=await z.evaluate(`
284
+ `))}function Zz(z){return`
285
+ (selectors => {
286
+ let editor = null;
287
+ for (const sel of selectors) {
288
+ editor = document.querySelector(sel);
289
+ if (editor) break;
290
+ }
291
+ if (!editor) return -1;
292
+ return (editor.innerText || editor.textContent || '').length;
293
+ })(${JSON.stringify(z)})
294
+ `}async function Lz(z,J,K){if(!await Mz(z,J))throw new H(`Could not attach topic "${K}": body editor not found`);const Z=Number(W(await z.evaluate(Qz(K,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 "${K}": page.insertText is unavailable`);try{await x(z,`#${K}`)}catch{throw new H(`Could not attach topic "${K}": failed to type inline topic query`)}await P(z,1.2);if(typeof z.pressKey!=="function")throw new H(`Could not attach topic "${K}": page.pressKey is unavailable`);try{await z.pressKey("Enter")}catch($){throw new H(`Could not attach topic "${K}": failed to accept suggestion (${$&&$.message||$})`)}await z.wait({time:0.8});if((Number(W(await z.evaluate(Qz(K,J))))||0)<=Z)throw new H(`Could not attach topic "${K}": no real topic entity appeared after selection`)}async function _z(z,J,K){const Q=[],Z=[];for(const q of K){let $=-1;try{const V=Number(W(await z.evaluate(Zz(J))));$=Number.isFinite(V)?V:-1}catch{}try{await Lz(z,J,q);Q.push(q);await z.wait({time:0.4})}catch(V){Z.push({topic:q,reason:V&&V.message||String(V)});if($>=0&&typeof z.pressKey==="function")try{const Y=q.length+6;for(let G=0;G<Y;G++){const O=Number(W(await z.evaluate(Zz(J))));if(!Number.isFinite(O)||O<0||O<=$)break;await z.pressKey("Backspace");await z.wait({time:0.15})}}catch{}}}return{added:Q,failed:Z}}async function hz(z){const J=await z.evaluate(`
285
295
  () => {
286
296
  const isVisible = (el) => {
287
297
  if (!el || el.offsetParent === null) return false;
@@ -330,7 +340,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
330
340
  }
331
341
  return { ok: false, visibleTexts };
332
342
  }
333
- `);if(J?.ok)await z.wait({time:1});return J}async function L(z,J,K=7000){const Z=500,Q=Math.max(1,Math.ceil(K/Z));let q={ok:!1};for(let $=0;$<Q;$++){q=W(await z.evaluate(`
343
+ `);if(J?.ok)await z.wait({time:1});return J}async function L(z,J,K=7000){const Q=500,Z=Math.max(1,Math.ceil(K/Q));let q={ok:!1};for(let $=0;$<Z;$++){q=W(await z.evaluate(`
334
344
  ((cfg) => {
335
345
  const __opencli_xhs_click_label = { wantLabel: ${JSON.stringify(J)} };
336
346
  const wantLabel = ${JSON.stringify(J)};
@@ -365,10 +375,10 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
365
375
  }
366
376
  return { ok: false };
367
377
  })()
368
- `));if(q?.ok)return q;await z.wait({time:Z/1000})}return q}async function Pz(z){const J=await z.evaluate(`
378
+ `));if(q?.ok)return q;await z.wait({time:Q/1000})}return q}async function fz(z){const J=await z.evaluate(`
369
379
  (() => {
370
380
  const __opencli_xhs_focus_card = true;
371
- const sel = ${JSON.stringify(S)};
381
+ const sel = ${JSON.stringify(u)};
372
382
  const editors = Array.from(document.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
373
383
  // Prefer the editor inside the active swiper slide if present.
374
384
  const active = editors.find((el) => el.closest('.swiper-slide-active')) || editors[editors.length - 1];
@@ -382,34 +392,34 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
382
392
  selection?.addRange(range);
383
393
  return { ok: true };
384
394
  })()
385
- `);return W(J)}async function Iz(z){const J=await z.evaluate(`
395
+ `);return W(J)}async function Az(z){const J=await z.evaluate(`
386
396
  (() => {
387
397
  const __opencli_xhs_card_text = true;
388
- const sel = ${JSON.stringify(S)};
398
+ const sel = ${JSON.stringify(u)};
389
399
  const editors = Array.from(document.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
390
400
  const active = editors.find((el) => el.closest('.swiper-slide-active')) || editors[editors.length - 1];
391
401
  const text = active ? (active.innerText || active.textContent || '').trim() : '';
392
402
  return { ok: !!text, text };
393
403
  })()
394
- `);return W(J)}async function Jz(z){const J=await z.evaluate(`
404
+ `);return W(J)}async function $z(z){const J=await z.evaluate(`
395
405
  (() => {
396
406
  const __opencli_xhs_card_count = true;
397
- const sel = ${JSON.stringify(S)};
407
+ const sel = ${JSON.stringify(u)};
398
408
  const editors = Array.from(document.querySelectorAll(sel)).filter((el) => el.offsetParent !== null);
399
409
  const active = editors.find((el) => el.closest('.swiper-slide-active')) || editors[editors.length - 1];
400
410
  const activeText = active ? (active.innerText || active.textContent || '').trim() : '';
401
411
  return { ok: true, count: editors.length, activeEmpty: !activeText };
402
412
  })()
403
- `);return W(J)}async function hz(z,J=8000){const K=300,Z=Math.ceil(J/K);for(let Q=0;Q<Z;Q++){if((await Jz(z))?.count>=1)return!0;await z.wait({time:K/1000})}return!1}async function Mz(z,J,K=6000){const Z=300,Q=Math.ceil(K/Z);for(let q=0;q<Q;q++){const $=await Jz(z);if($?.count>=J&&$?.activeEmpty)return!0;await z.wait({time:Z/1000})}return!1}async function wz(z,J,K=4){for(let Z=0;Z<K;Z++){if(!(await L(z,l))?.ok)return!1;if(await Mz(z,J,2500))return!0}return!1}async function Rz(z){const J=await z.evaluate(`
413
+ `);return W(J)}async function Rz(z,J=8000){const K=300,Q=Math.ceil(J/K);for(let Z=0;Z<Q;Z++){if((await $z(z))?.count>=1)return!0;await z.wait({time:K/1000})}return!1}async function wz(z,J,K=6000){const Q=300,Z=Math.ceil(K/Q);for(let q=0;q<Z;q++){const $=await $z(z);if($?.count>=J&&$?.activeEmpty)return!0;await z.wait({time:Q/1000})}return!1}async function yz(z,J,K=4){for(let Q=0;Q<K;Q++){if(!(await L(z,g))?.ok)return!1;if(await wz(z,J,2500))return!0}return!1}async function bz(z){const J=await z.evaluate(`
404
414
  (() => {
405
415
  const __opencli_xhs_preview_ready = true;
406
416
  const ready = Array.from(document.querySelectorAll('button'))
407
417
  .some((b) => b.offsetParent !== null && (b.innerText || '').replace(/\\s+/g, '') === '下一步');
408
418
  return { ok: ready };
409
419
  })()
410
- `);return W(J)}async function _z(z,J=6){const K=400,Z=Math.ceil(3000/K);for(let Q=0;Q<J;Q++){if(!(await L(z,o))?.ok&&Q===0)return!1;for(let $=0;$<Z;$++){if((await Rz(z))?.ok)return!0;await z.wait({time:K/1000})}}return!1}async function bz(z,J,K){J=String(J).replace(/\\n/g,`
411
- `);if(!(await Pz(z))?.ok)throw new V(`文字配图: could not focus card editor #${K+1}`);if(typeof z.insertText==="function"){const q=J.split(`
412
- `);for(let $=0;$<q.length;$++){if($>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if(q[$])await A(z,q[$])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await Iz(z))?.ok)throw new V(`文字配图: card editor #${K+1} is empty after typing`)}async function yz(z,J){if(!J||J===k)return k;const K=W(await z.evaluate(`
420
+ `);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 L(z,r))?.ok&&Z===0)return!1;for(let $=0;$<Q;$++){if((await bz(z))?.ok)return!0;await z.wait({time:K/1000})}}return!1}async function Tz(z,J,K){J=String(J).replace(/\\n/g,`
421
+ `);if(!(await fz(z))?.ok)throw new H(`文字配图: could not focus card editor #${K+1}`);if(typeof z.insertText==="function"){const q=J.split(`
422
+ `);for(let $=0;$<q.length;$++){if($>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 Az(z))?.ok)throw new H(`文字配图: card editor #${K+1} is empty after typing`)}async function Sz(z,J){if(!J||J===M)return M;const K=W(await z.evaluate(`
413
423
  (async () => {
414
424
  const __opencli_xhs_card_styles = true;
415
425
  const want = ${JSON.stringify(J)};
@@ -438,7 +448,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
438
448
  }
439
449
  return { ok: seen.length > 0, styles: seen, found: false };
440
450
  })()
441
- `));if(!K?.found)throw new V(`文字配图: requested style "${J}" is not available for this content (options: ${(K?.styles||[]).join(" / ")||"none"}). Choose an available style or omit --card-style to use ${k}.`);if(!(await L(z,J))?.ok)throw new V(`文字配图: could not click requested style "${J}".`);await z.wait({time:0.6});return J}async function Kz(z){const J=await z.evaluate(`
451
+ `));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 ${M}.`);if(!(await L(z,J))?.ok)throw new H(`文字配图: could not click requested style "${J}".`);await z.wait({time:0.6});return J}async function qz(z){const J=await z.evaluate(`
442
452
  (() => {
443
453
  const __opencli_xhs_composer_media_count = true;
444
454
  const visibleBox = (el) => {
@@ -451,7 +461,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
451
461
  const r = el.getBoundingClientRect();
452
462
  return r.width >= 48 && r.height >= 48;
453
463
  };
454
- const titleSelectors = ${JSON.stringify(C)};
464
+ const titleSelectors = ${JSON.stringify(m)};
455
465
  const titleEl = titleSelectors
456
466
  .map((sel) => Array.from(document.querySelectorAll(sel)))
457
467
  .flat()
@@ -470,7 +480,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
470
480
  }
471
481
  return { ok: true, count };
472
482
  })()
473
- `);return W(J)}async function Qz(z,J,K){const Z=await Kz(z);if(!Z||typeof Z.count!=="number")throw new V(`${K}: could not verify current composer media count`);if(Z.count<J){await z.screenshot({path:"/tmp/xhs_publish_media_debug.png"});throw new V(`${K}: expected at least ${J} visible media item(s), got ${Z.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png`)}}async function Az(z,J,K){if(!(await L(z,T))?.ok){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new V(`文字配图: could not click "${T}" entry. Debug: /tmp/xhs_publish_textimage_debug.png`)}if(!await hz(z)){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new V(`文字配图: 写文字 card editor did not appear after clicking "${T}". Debug: /tmp/xhs_publish_textimage_debug.png`)}for(let j=0;j<J.length;j++){if(j>0){if(!await wz(z,j+1)){await z.screenshot({path:"/tmp/xhs_publish_addcard_debug.png"});throw new V(`文字配图: new card editor #${j+1} did not render after "${l}". Debug: /tmp/xhs_publish_addcard_debug.png`)}}await bz(z,J[j],j)}if(!await _z(z)){await z.screenshot({path:"/tmp/xhs_publish_generate_debug.png"});throw new V(`文字配图: "${o}" did not advance to the 预览图片 step. `+"Debug: /tmp/xhs_publish_generate_debug.png")}const q=await yz(z,K);if(!(await L(z,s))?.ok){await z.screenshot({path:"/tmp/xhs_publish_next_debug.png"});throw new V(`文字配图: could not click "${s}". Debug: /tmp/xhs_publish_next_debug.png`)}await z.wait({time:2});return q}async function Zz(z){return z.evaluate(`
483
+ `);return W(J)}async function Vz(z,J,K){const Q=await qz(z);if(!Q||typeof Q.count!=="number")throw new H(`${K}: could not verify current composer media count`);if(Q.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 ${Q.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png`)}}async function xz(z,J,K){if(!(await L(z,E))?.ok){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new H(`文字配图: could not click "${E}" entry. Debug: /tmp/xhs_publish_textimage_debug.png`)}if(!await Rz(z)){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new H(`文字配图: 写文字 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 yz(z,V+1)){await z.screenshot({path:"/tmp/xhs_publish_addcard_debug.png"});throw new H(`文字配图: new card editor #${V+1} did not render after "${g}". Debug: /tmp/xhs_publish_addcard_debug.png`)}}await Tz(z,J[V],V)}if(!await Cz(z)){await z.screenshot({path:"/tmp/xhs_publish_generate_debug.png"});throw new H(`文字配图: "${r}" did not advance to the 预览图片 step. `+"Debug: /tmp/xhs_publish_generate_debug.png")}const q=await Sz(z,K);if(!(await L(z,p))?.ok){await z.screenshot({path:"/tmp/xhs_publish_next_debug.png"});throw new H(`文字配图: could not click "${p}". Debug: /tmp/xhs_publish_next_debug.png`)}await z.wait({time:2});return q}async function jz(z){return z.evaluate(`
474
484
  () => {
475
485
  const text = (document.body?.innerText || '').replace(/s+/g, ' ').trim();
476
486
  const hasTitleInput = !!Array.from(document.querySelectorAll('input, textarea')).find((el) => {
@@ -500,22 +510,22 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
500
510
  const state = hasTitleInput ? 'editor_ready' : hasImageInput || !hasVideoSurface ? 'image_surface' : 'video_surface';
501
511
  return { state, hasTitleInput, hasImageInput, hasVideoSurface };
502
512
  }
503
- `)}async function Tz(z,J=5000){const K=500,Z=Math.max(1,Math.ceil(J/K));let Q=await Zz(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 Zz(z)}}return Q}async function Sz(z,J=1e4){const K=1000,Z=Math.ceil(J/K);for(let Q=0;Q<Z;Q++){if(await z.evaluate(`
513
+ `)}async function Ez(z,J=5000){const K=500,Q=Math.max(1,Math.ceil(J/K));let Z=await jz(z);for(let q=0;q<Q;q++){if(Z.state!=="video_surface")return Z;if(q<Q-1){await z.wait({time:K/1000});Z=await jz(z)}}return Z}async function uz(z,J=1e4){const K=1000,Q=Math.ceil(J/K);for(let Z=0;Z<Q;Z++){if(await z.evaluate(`
504
514
  (() => {
505
- const sels = ${JSON.stringify(C)};
515
+ const sels = ${JSON.stringify(m)};
506
516
  for (const sel of sels) {
507
517
  const el = document.querySelector(sel);
508
518
  if (el && el.offsetParent !== null) return true;
509
519
  }
510
520
  return false;
511
- })()`))return!0;if(Q<Z-1)await z.wait({time:K/1000})}return!1}async function Cz(z){const J=W(await z.evaluate(`
521
+ })()`))return!0;if(Z<Q-1)await z.wait({time:K/1000})}return!1}async function mz(z){const J=W(await z.evaluate(`
512
522
  (() => {
513
523
  const __opencli_xhs_restore_srcs = true;
514
524
  return Array.from(document.querySelectorAll('.img-container img'))
515
525
  .map((img) => img.currentSrc || img.src || '')
516
526
  .filter(Boolean);
517
527
  })()
518
- `));return Array.isArray(J)?J:[]}async function $z(z,J){if(!Array.isArray(J)||J.length===0)return 0;const K=W(await z.evaluate(`
528
+ `));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(`
519
529
  (async (wanted) => {
520
530
  const __opencli_xhs_delete_by_src = true;
521
531
  const want = new Set(wanted);
@@ -546,7 +556,16 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
546
556
  }
547
557
  return removed;
548
558
  })(${JSON.stringify(J)})
549
- `));return Number(K)||0}function xz(z){const J=String(z&&(z.url||z.key)||""),K=String(z&&z.method||"").toUpperCase(),Z=Number(z&&z.status||0);if(K!=="PUT")return null;if(!/ros-upload[^/]*\/spectrum\//.test(J))return null;if(Z<200||Z>=300)return null;const Q=J.match(/\/spectrum\/([^?#]+)/);return Q?Q[1]:J}function qz(z){return Math.min(45,6+z*4)}async function jz(z,J,K,Z=90000){if(J<=0)return{ok:!0,completed:0};if(!K||typeof z.readNetworkCapture!=="function"){await z.wait({time:qz(J)});return{ok:!0,completed:null,viaFallback:!0}}const Q=1500,$=Math.ceil(9000/Q),j=Math.ceil(Z/Q),Y=new Set;for(let U=0;U<j;U++){const f=await z.readNetworkCapture().catch(()=>[]);for(const v of Array.isArray(f)?f:[]){const G=xz(v);if(G)Y.add(G)}if(Y.size>=J)return{ok:!0,completed:Y.size};if(U>=$&&Y.size===0){await z.wait({time:qz(J)});return{ok:!0,completed:null,viaFallback:!0}}await z.wait({time:Q/1000})}return{ok:!1,completed:Y.size}}Yz({site:"xiaohongshu",name:"publish",access:"write",description:"小红书发布图文笔记 (creator center UI automation)",domain:"creator.xiaohongshu.com",strategy:Xz.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:`文字配图卡片文字,多张卡片用 ${m} 分隔,卡内换行用 \\n`},{name:"card-style",required:!1,help:`文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败。省略时使用${k}。可选: ${i.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 F("正文位置参数和 --file 只能二选一,不能同时给");const H=D.resolve(Z);if(!_.existsSync(H))throw new F(`--file 指向的文件不存在: ${H}`);Q=_.readFileSync(H,"utf-8").trim();if(!Q)throw new F(`--file 指向的文件内容为空: ${H}`)}const q=J.images?String(J.images).split(",").map((H)=>H.trim()).filter(Boolean):[],$=J.topics?String(J.topics).split(",").map((H)=>H.trim()).filter(Boolean):[],j=Boolean(J.draft),Y=J["card-text"]?String(J["card-text"]):"",U=Y?Y.split(m).map((H)=>H.trim()).filter(Boolean):[],f=J["card-style"]?String(J["card-style"]).trim():"",v=U.length>0;if(!K)throw new F("--title is required");if(K.length>d)throw new F(`Title is ${K.length} chars — must be ≤ ${d}`);if(!Q)throw new F("缺少笔记正文:给位置参数 <content>(字面文本),或用 --file <本机文件路径>");Wz(Q,{fileFlag:"--file"});if(!v&&q.length===0)throw new F("Provide --card-text (text-image mode) or --images (upload mode); neither was given.");if(q.length>u)throw new F(`Too many images: ${q.length} (max ${u})`);if(v&&q.some((H)=>D.extname(H).toLowerCase()===".gif"))throw new F("文字配图模式追加的图片不支持 .gif(编辑器图片入口只接受 jpg/jpeg/png/webp)");const G=Dz(q);await z.goto(Uz);await z.wait({time:1});await z.evaluate("() => { try { window.onbeforeunload = null; } catch (e) {} location.reload(); }").catch(()=>{return});let P="";for(let H=0;H<30;H++){await z.wait({time:0.5});P=await z.evaluate("() => location.href");if(P.includes("creator.xiaohongshu.com"))break}if(!P.includes("creator.xiaohongshu.com")){await z.screenshot({path:"/tmp/xhs_publish_redirect_debug.png"});throw Error(`Redirected away from creator center (landed on ${P}) — session may have expired. `+"Re-capture browser login via: ppcli xiaohongshu creator-profile. Debug screenshot: /tmp/xhs_publish_redirect_debug.png")}const I=await Lz(z);if((await Tz(z,I?.ok?5000:2000)).state==="video_surface"){await z.screenshot({path:"/tmp/xhs_publish_tab_debug.png"});const H=I?.ok?`clicked "${I.text}"`:`visible candidates: ${(I?.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 h=await Cz(z);let M=f;if(v)M=await Az(z,U,f);else{const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,O=await t(z,G);if(!O.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new V(`图片注入失败:${O.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_upload_debug.png")}await z.wait({time:n/1000});const B=await jz(z,G.length,H);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new V(`图片上传未全部完成:只确认 ${B.completed}/${G.length} 张传到小红书 CDN,`+"继续发布会丢图(通常只剩封面)。直接重新运行本 publish 命令即可(会自动重置发布页重传);"+"请勿手动修改当前页面。Debug: /tmp/xhs_publish_upload_debug.png")}await a(z);if(h.length){await $z(z,h);await z.wait({time:0.5})}const R=await Kz(z);if(R&&typeof R.count==="number"&&R.count>G.length){await z.screenshot({path:"/tmp/xhs_publish_dup_debug.png"});throw new V(`清掉恢复态旧图后编辑器内仍有 ${R.count} 张图,超过本次注入的 ${G.length} 张。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。Debug: /tmp/xhs_publish_dup_debug.png")}}if(!await Sz(z)){await z.screenshot({path:"/tmp/xhs_publish_form_debug.png"});throw new V("Editing form did not appear after image acquisition. The page layout may have changed. Debug screenshot: /tmp/xhs_publish_form_debug.png")}if(v)await Qz(z,U.length,"文字配图 generated images");if(v&&G.length>0){const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,O=await t(z,G);if(!O.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new V(`追加图片失败:${O.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await z.wait({time:n/1000});const B=await jz(z,G.length,H);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new V(`追加图片上传未全部完成:只确认 ${B.completed}/${G.length} 张传到小红书 CDN。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await a(z);await Qz(z,U.length+G.length,"文字配图 appended images")}if(v&&h.length){await $z(z,h);await z.wait({time:0.5})}await e(z,C,K,"title");await N(z,0.6);await e(z,r,Q,"content");await N(z,0.6);let b=[];if($.length)b=await kz(z,r,$);await N(z,1,0.6);const y=await Oz(z,["xhs-publish-btn"]);if(y){await Fz(z,y.x,y.y);await N(z,0.4)}const x=j?["暂存离开","存草稿"]:["发布","发布笔记"],X=await z.evaluate(`
559
+ `));return Number(K)||0}function cz(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 Gz(z){return Math.min(45,6+z*4)}async function Yz(z,J,K,Q=90000){if(J<=0)return{ok:!0,completed:0};if(!K||typeof z.readNetworkCapture!=="function"){await z.wait({time:Gz(J)});return{ok:!0,completed:null,viaFallback:!0}}const Z=1500,$=Math.ceil(9000/Z),V=Math.ceil(Q/Z),Y=new Set;for(let G=0;G<V;G++){const O=await z.readNetworkCapture().catch(()=>[]);for(const v of Array.isArray(O)?O:[]){const X=cz(v);if(X)Y.add(X)}if(Y.size>=J)return{ok:!0,completed:Y.size};if(G>=$&&Y.size===0){await z.wait({time:Gz(J)});return{ok:!0,completed:null,viaFallback:!0}}await z.wait({time:Z/1000})}return{ok:!1,completed:Y.size}}async function dz(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 q;try{q=W(await z.evaluate(`(() => {
560
+ const noteIdRe = /"noteId":"([0-9a-f]{24})"/;
561
+ return Array.from(document.querySelectorAll('div.note[data-impression], div.note')).slice(0, 3).map((card) => {
562
+ const impression = card.getAttribute('data-impression') || '';
563
+ const id = impression.match(noteIdRe)?.[1] || '';
564
+ const title = (card.querySelector('.title, .raw')?.innerText || '').trim();
565
+ const dateText = (card.querySelector('.time_status, .time')?.innerText || '').trim();
566
+ return { id, title, dateText };
567
+ }).filter((entry) => entry.title);
568
+ })()`))}catch{return null}if(Array.isArray(q)){const $=q.find((V)=>{const Y=String(V.title||"").trim();if(!(Y===Q||Y.startsWith(Q)||Q.startsWith(Y)))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($)return $}}return null}Fz({site:"xiaohongshu",name:"publish",access:"write",description:"小红书发布图文笔记 (creator center UI automation)",domain:"creator.xiaohongshu.com",strategy:Oz.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:`文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败。省略时使用${M}。可选: ${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 j=I.resolve(Q);if(!w.existsSync(j))throw new U(`--file 指向的文件不存在: ${j}`);Z=w.readFileSync(j,"utf-8").trim();if(!Z)throw new U(`--file 指向的文件内容为空: ${j}`)}const q=J.images?String(J.images).split(",").map((j)=>j.trim()).filter(Boolean):[],$=J.topics?String(J.topics).split(",").map((j)=>j.trim()).filter(Boolean):[],V=Boolean(J.draft),Y=J["card-text"]?String(J["card-text"]):"",G=Y?Y.split(o).map((j)=>j.trim()).filter(Boolean):[],O=J["card-style"]?String(J["card-style"]).trim():"",v=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 <本机文件路径>");Uz(Z,{fileFlag:"--file"});if(!v&&q.length===0)throw new U("Provide --card-text (text-image mode) or --images (upload mode); neither was given.");if(q.length>n)throw new U(`Too many images: ${q.length} (max ${n})`);if(v&&q.some((j)=>I.extname(j).toLowerCase()===".gif"))throw new U("文字配图模式追加的图片不支持 .gif(编辑器图片入口只接受 jpg/jpeg/png/webp)");const X=Iz(q);await z.goto(Dz);await z.wait({time:1});await z.evaluate("() => { try { window.onbeforeunload = null; } catch (e) {} location.reload(); }").catch(()=>{return});let _="";for(let j=0;j<30;j++){await z.wait({time:0.5});_=await z.evaluate("() => location.href");if(_.includes("creator.xiaohongshu.com"))break}if(!_.includes("creator.xiaohongshu.com")){await z.screenshot({path:"/tmp/xhs_publish_redirect_debug.png"});throw Error(`Redirected away from creator center (landed on ${_}) — session may have expired. `+"Re-capture browser login via: ppcli xiaohongshu creator-profile. Debug screenshot: /tmp/xhs_publish_redirect_debug.png")}const h=await hz(z);if((await Ez(z,h?.ok?5000:2000)).state==="video_surface"){await z.screenshot({path:"/tmp/xhs_publish_tab_debug.png"});const j=h?.ok?`clicked "${h.text}"`:`visible candidates: ${(h?.visibleTexts||[]).join(" | ")||"none"}`;throw Error("Still on the video publish page after trying to select 图文. "+`Details: ${j}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`)}const f=await mz(z);let A=O;if(v)A=await xz(z,G,O);else{const j=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await zz(z,X);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new H(`图片注入失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_upload_debug.png")}await z.wait({time:i/1000});const D=await Yz(z,X.length,j);if(!D.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new H(`图片上传未全部完成:只确认 ${D.completed}/${X.length} 张传到小红书 CDN,`+"继续发布会丢图(通常只剩封面)。直接重新运行本 publish 命令即可(会自动重置发布页重传);"+"请勿手动修改当前页面。Debug: /tmp/xhs_publish_upload_debug.png")}await Jz(z);if(f.length){await Hz(z,f);await z.wait({time:0.5})}const R=await qz(z);if(R&&typeof R.count==="number"&&R.count>X.length){await z.screenshot({path:"/tmp/xhs_publish_dup_debug.png"});throw new H(`清掉恢复态旧图后编辑器内仍有 ${R.count} 张图,超过本次注入的 ${X.length} 张。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。Debug: /tmp/xhs_publish_dup_debug.png")}}if(!await uz(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(v)await Vz(z,G.length,"文字配图 generated images");if(v&&X.length>0){const j=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await zz(z,X);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new H(`追加图片失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await z.wait({time:i/1000});const D=await Yz(z,X.length,j);if(!D.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new H(`追加图片上传未全部完成:只确认 ${D.completed}/${X.length} 张传到小红书 CDN。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await Jz(z);await Vz(z,G.length+X.length,"文字配图 appended images")}if(v&&f.length){await Hz(z,f);await z.wait({time:0.5})}await Kz(z,m,K,"title");await P(z,0.6);await Kz(z,t,Z,"content");await P(z,0.6);let y=[],b=[];if($.length){const j=await _z(z,t,$);y=j.added;b=j.failed}await P(z,1,0.6);const C=await vz(z,["xhs-publish-btn"]);if(C){await Bz(z,C.x,C.y);await P(z,0.4)}const c=V?["暂存离开","存草稿"]:["发布","发布笔记"],F=await z.evaluate(`
550
569
  (cfg => {
551
570
  const { isDraftMode, publishNames, draftNames, labels } = cfg;
552
571
  const isVisible = (el) => {
@@ -585,8 +604,8 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
585
604
  }
586
605
  }
587
606
  return { ok: false, via: 'none', hosts: hosts.length, lastMethodError };
588
- })(${JSON.stringify({isDraftMode:j,publishNames:vz,draftNames:Bz,labels:x})})
589
- `);if(!X?.ok){if(j){if(await z.evaluate(`
607
+ })(${JSON.stringify({isDraftMode:V,publishNames:kz,draftNames:Nz,labels:c})})
608
+ `);if(!F?.ok){if(V){if(await z.evaluate(`
590
609
  (() => {
591
610
  const labels = ['返回', '关闭', '取消', '离开'];
592
611
  const buttons = document.querySelectorAll('button, [role="button"], div, span');
@@ -599,7 +618,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
599
618
  }
600
619
  return false;
601
620
  })()
602
- `)){await z.wait({time:1});const O=["暂存离开","存草稿","保存草稿"];if(await z.evaluate(`
621
+ `)){await z.wait({time:1});const B=["暂存离开","存草稿","保存草稿"];if(await z.evaluate(`
603
622
  (labels => {
604
623
  const buttons = document.querySelectorAll('button, [role="button"]');
605
624
  for (const btn of buttons) {
@@ -614,8 +633,8 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
614
633
  }
615
634
  }
616
635
  return false;
617
- })(${JSON.stringify(O)})
618
- `)){X.ok=!0;X.via="leave-save-fallback"}}if(!X?.ok){await z.wait({time:2});if(await z.evaluate(`
636
+ })(${JSON.stringify(B)})
637
+ `)){F.ok=!0;F.via="leave-save-fallback"}}if(!F?.ok){await z.wait({time:2});if(await z.evaluate(`
619
638
  () => {
620
639
  const markers = ['草稿箱(', '保存于', '编辑于'];
621
640
  for (const el of document.querySelectorAll('*')) {
@@ -624,7 +643,7 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
624
643
  }
625
644
  return false;
626
645
  }
627
- `)){X.ok=!0;X.via="auto-save"}}}}if(!X?.ok){await z.screenshot({path:"/tmp/xhs_publish_submit_debug.png"});const H=X?.via?` (via=${X.via})`:"",O=X?.error?`, error=${X.error}`:"",B=X?.lastMethodError?`, lastMethodError=${X.lastMethodError}`:"";throw Error(`未能触发「${x[0]}」按钮${H}${O}${B}。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_submit_debug.png")}await z.wait({time:4});const w=await z.evaluate("() => location.href"),Hz=j?["草稿已保存","暂存成功","保存成功","保存于","图文笔记("]:["发布成功","上传成功"],E=await z.evaluate(`
646
+ `)){F.ok=!0;F.via="auto-save"}}}}if(!F?.ok){await z.screenshot({path:"/tmp/xhs_publish_submit_debug.png"});const j=F?.via?` (via=${F.via})`:"",B=F?.error?`, error=${F.error}`:"",D=F?.lastMethodError?`, lastMethodError=${F.lastMethodError}`:"";throw Error(`未能触发「${c[0]}」按钮${j}${B}${D}。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_submit_debug.png")}await z.wait({time:4});const N=await z.evaluate("() => location.href"),Xz=V?["草稿已保存","暂存成功","保存成功","保存于","图文笔记("]:["发布成功","上传成功"],d=await z.evaluate(`
628
647
  (markers => {
629
648
  for (const el of document.querySelectorAll('*')) {
630
649
  if (el.tagName === 'STYLE' || el.tagName === 'SCRIPT') continue;
@@ -633,5 +652,5 @@ import*as _ from"node:fs";import*as D from"node:path";import{CommandExecutionErr
633
652
  if (el.children.length === 0 && markers.some(marker => text.includes(marker))) return text;
634
653
  }
635
654
  return '';
636
- })(${JSON.stringify(Hz)})
637
- `),Vz=!w.includes("/publish/publish"),Gz=E.length>0||Vz,c=j?"暂存成功":"发布成功";if(!Gz)throw new V(`${c}无法确认:未观察到成功提示或提交后跳转。`+(w?`当前 URL: ${w}。`:"当前 URL 为空。")+"若确属失败,直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。");return[{status:`✅ ${c}`,detail:[`"${K}"`,v?`${U.length}张文字配图${G.length?` + ${G.length}张图片`:""}${M&&M!==k?` (${M})`:""}`:`${G.length}张图片`,b.length?`话题: ${b.join(" ")}`:"",E||w||""].filter(Boolean).join(" · ")}]}});
655
+ })(${JSON.stringify(Xz)})
656
+ `),Wz=!N.includes("/publish/publish");let T=d.length>0||Wz;const S=V?"暂存成功":"发布成功";let k=null;if(!V){k=await dz(z,K,T?4:8);if(k)T=!0}if(!T){if(V)throw new H(`${S}无法确认:未观察到成功提示或提交后跳转。`+(N?`当前 URL: ${N}。`:"当前 URL 为空。")+"先用 `xiaohongshu drafts` 查草稿箱确认是否已存上;确认没有再重新运行本命令,避免堆出重复草稿。");throw new H(`${S}无法确认:未观察到成功提示或提交后跳转,创作者后台也暂未查到同标题笔记。`+(N?`当前 URL: ${N}。`:"")+"注意:发布按钮已被触发,笔记仍可能延迟入库。禁止立即重发——请等 1-2 分钟后用 `xiaohongshu creator-notes` 回查本篇标题是否已出现;确认不存在后才重新运行本 publish 命令,否则会重复发布。")}return[{status:`✅ ${S}`,detail:[`"${K}"`,v?`${G.length}张文字配图${X.length?` + ${X.length}张图片`:""}${A&&A!==M?` (${A})`:""}`:`${X.length}张图片`,y.length?`话题: ${y.join(" ")}`:"",b.length?`⚠️ 话题未附上(已自动跳过,不影响发布): ${b.map((j)=>j.topic).join(" ")}`:"",k?.id?`笔记ID: ${k.id}`:"",d||(k?"已在创作者后台确认":"")||N||""].filter(Boolean).join(" · "),url:k?.id?`https://www.xiaohongshu.com/explore/${k.id}`:""}]}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "publishport-opencli",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "provenance": false