Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Post articles and image-text content to WeChat Official Account via API or Chrome CDP, with markdown-to-WeChat HTML conversion.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/wechat-browser.ts
1import fs from 'node:fs';2import { readdir } from 'node:fs/promises';3import path from 'node:path';4import process from 'node:process';56import {7CdpConnection,8findChromeExecutable,9getDefaultProfileDir,10getAccountProfileDir,11launchChrome,12sleep,13} from './cdp.ts';14import { loadWechatExtendConfig, resolveAccount } from './wechat-extend-config.ts';1516const WECHAT_URL = 'https://mp.weixin.qq.com/';1718interface MarkdownMeta {19title: string;20author: string;21content: string;22}2324function parseMarkdownFile(filePath: string): MarkdownMeta {25const text = fs.readFileSync(filePath, 'utf-8');26let title = '';27let author = '';28let content = '';2930const fmMatch = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);31if (fmMatch) {32const fm = fmMatch[1]!;33const titleMatch = fm.match(/^title:\s*(.+)$/m);34if (titleMatch) title = titleMatch[1]!.trim().replace(/^["']|["']$/g, '');35const authorMatch = fm.match(/^author:\s*(.+)$/m);36if (authorMatch) author = authorMatch[1]!.trim().replace(/^["']|["']$/g, '');37}3839const bodyText = fmMatch ? text.slice(fmMatch[0].length) : text;4041if (!title) {42const h1Match = bodyText.match(/^#\s+(.+)$/m);43if (h1Match) title = h1Match[1]!.trim();44}4546const lines = bodyText.split('\n');47const paragraphs: string[] = [];48for (const line of lines) {49const trimmed = line.trim();50if (!trimmed) continue;51if (trimmed.startsWith('#')) continue;52if (trimmed.startsWith('![')) continue;53if (trimmed.startsWith('---')) continue;54paragraphs.push(trimmed);55if (paragraphs.join('\n').length > 1200) break;56}57content = paragraphs.join('\n');5859return { title, author, content };60}6162function compressTitle(title: string, maxLen = 20): string {63if (title.length <= maxLen) return title;6465const prefixes = ['如何', '为什么', '什么是', '怎样', '怎么', '关于'];66let t = title;67for (const p of prefixes) {68if (t.startsWith(p) && t.length > maxLen) {69t = t.slice(p.length);70if (t.length <= maxLen) return t;71}72}7374const fillers = ['的', '了', '在', '是', '和', '与', '以及', '或者', '或', '还是', '而且', '并且', '但是', '但', '因为', '所以', '如果', '那么', '虽然', '不过', '然而', '——', '…'];75for (const f of fillers) {76if (t.length <= maxLen) break;77t = t.replace(new RegExp(f, 'g'), '');78}7980if (t.length > maxLen) t = t.slice(0, maxLen);8182return t;83}8485function compressContent(content: string, maxLen = 1000): string {86if (content.length <= maxLen) return content;8788const lines = content.split('\n');89const result: string[] = [];90let len = 0;9192for (const line of lines) {93if (len + line.length + 1 > maxLen) {94const remaining = maxLen - len - 1;95if (remaining > 20) result.push(line.slice(0, remaining - 3) + '...');96break;97}98result.push(line);99len += line.length + 1;100}101102return result.join('\n');103}104105async function loadImagesFromDir(dir: string): Promise<string[]> {106const entries = await readdir(dir);107const images = entries108.filter(f => /\.(png|jpg|jpeg|gif|webp)$/i.test(f))109.sort()110.map(f => path.join(dir, f));111return images;112}113114interface WeChatBrowserOptions {115title?: string;116content?: string;117images?: string[];118imagesDir?: string;119markdownFile?: string;120submit?: boolean;121timeoutMs?: number;122profileDir?: string;123chromePath?: string;124}125126export async function postToWeChat(options: WeChatBrowserOptions): Promise<void> {127const { submit = false, timeoutMs = 120_000, profileDir = getDefaultProfileDir() } = options;128129let title = options.title || '';130let content = options.content || '';131let images = options.images || [];132133if (options.markdownFile) {134const absPath = path.isAbsolute(options.markdownFile) ? options.markdownFile : path.resolve(process.cwd(), options.markdownFile);135if (!fs.existsSync(absPath)) throw new Error(`Markdown file not found: ${absPath}`);136const meta = parseMarkdownFile(absPath);137if (!title) title = meta.title;138if (!content) content = meta.content;139console.log(`[wechat-browser] Parsed markdown: title="${meta.title}", content=${meta.content.length} chars`);140}141142if (options.imagesDir) {143const absDir = path.isAbsolute(options.imagesDir) ? options.imagesDir : path.resolve(process.cwd(), options.imagesDir);144if (!fs.existsSync(absDir)) throw new Error(`Images directory not found: ${absDir}`);145images = await loadImagesFromDir(absDir);146console.log(`[wechat-browser] Found ${images.length} images in ${absDir}`);147}148149if (title.length > 20) {150const original = title;151title = compressTitle(title, 20);152console.log(`[wechat-browser] Title compressed: "${original}" → "${title}"`);153}154155if (content.length > 1000) {156const original = content.length;157content = compressContent(content, 1000);158console.log(`[wechat-browser] Content compressed: ${original} → ${content.length} chars`);159}160161if (!title) throw new Error('Title is required (use --title or --markdown)');162if (!content) throw new Error('Content is required (use --content or --markdown)');163if (images.length === 0) throw new Error('At least one image is required (use --image or --images)');164165for (const img of images) {166if (!fs.existsSync(img)) throw new Error(`Image not found: ${img}`);167}168169const chromePath = findChromeExecutable(options.chromePath);170if (!chromePath) throw new Error('Chrome not found. Set WECHAT_BROWSER_CHROME_PATH env var.');171172console.log(`[wechat-browser] Launching Chrome (profile: ${profileDir})`);173174const launched = await launchChrome(WECHAT_URL, profileDir, chromePath);175const chrome = launched.chrome;176177let cdp: CdpConnection | null = null;178179try {180cdp = launched.cdp;181182const targets = await cdp.send<{ targetInfos: Array<{ targetId: string; url: string; type: string }> }>('Target.getTargets');183let pageTarget = targets.targetInfos.find((t) => t.type === 'page' && t.url.includes('mp.weixin.qq.com'));184185if (!pageTarget) {186const { targetId } = await cdp.send<{ targetId: string }>('Target.createTarget', { url: WECHAT_URL });187pageTarget = { targetId, url: WECHAT_URL, type: 'page' };188}189190let { sessionId } = await cdp.send<{ sessionId: string }>('Target.attachToTarget', { targetId: pageTarget.targetId, flatten: true });191192await cdp.send('Page.enable', {}, { sessionId });193await cdp.send('Runtime.enable', {}, { sessionId });194await cdp.send('DOM.enable', {}, { sessionId });195196console.log('[wechat-browser] Waiting for page load...');197await sleep(3000);198199const checkLoginStatus = async (): Promise<boolean> => {200const result = await cdp!.send<{ result: { value: string } }>('Runtime.evaluate', {201expression: `window.location.href`,202returnByValue: true,203}, { sessionId });204return result.result.value.includes('/cgi-bin/home');205};206207const waitForLogin = async (): Promise<boolean> => {208const start = Date.now();209while (Date.now() - start < timeoutMs) {210if (await checkLoginStatus()) return true;211await sleep(2000);212}213return false;214};215216let isLoggedIn = await checkLoginStatus();217if (!isLoggedIn) {218console.log('[wechat-browser] Not logged in. Please scan QR code to log in...');219isLoggedIn = await waitForLogin();220if (!isLoggedIn) throw new Error('Timed out waiting for login. Please log in first.');221}222console.log('[wechat-browser] Logged in.');223224await sleep(2000);225226console.log('[wechat-browser] Looking for "贴图" menu...');227const menuResult = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {228expression: `229const menuItems = document.querySelectorAll('.new-creation__menu .new-creation__menu-item');230const count = menuItems.length;231const texts = Array.from(menuItems).map(m => m.querySelector('.new-creation__menu-title')?.textContent?.trim() || m.textContent?.trim() || '');232JSON.stringify({ count, texts });233`,234returnByValue: true,235}, { sessionId });236console.log(`[wechat-browser] Menu items: ${menuResult.result.value}`);237238const getTargets = async () => {239return await cdp!.send<{ targetInfos: Array<{ targetId: string; url: string; type: string }> }>('Target.getTargets');240};241242const initialTargets = await getTargets();243const initialIds = new Set(initialTargets.targetInfos.map(t => t.targetId));244console.log(`[wechat-browser] Initial targets count: ${initialTargets.targetInfos.length}`);245246console.log('[wechat-browser] Finding "贴图" menu position...');247const menuPos = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {248expression: `249(function() {250const menuItems = document.querySelectorAll('.new-creation__menu .new-creation__menu-item');251console.log('Found menu items:', menuItems.length);252for (const item of menuItems) {253const title = item.querySelector('.new-creation__menu-title');254const text = title?.textContent?.trim() || '';255console.log('Menu item text:', text);256if (text === '图文' || text === '贴图') {257item.scrollIntoView({ block: 'center' });258const rect = item.getBoundingClientRect();259console.log('Found 贴图,rect:', JSON.stringify(rect));260return JSON.stringify({ x: rect.x + rect.width / 2, y: rect.y + rect.height / 2, width: rect.width, height: rect.height });261}262}263return 'null';264})()265`,266returnByValue: true,267}, { sessionId });268console.log(`[wechat-browser] Menu position: ${menuPos.result.value}`);269270const pos = menuPos.result.value !== 'null' ? JSON.parse(menuPos.result.value) : null;271if (!pos) throw new Error('贴图 menu not found or not visible');272273console.log('[wechat-browser] Clicking "贴图" menu with mouse events...');274await cdp.send('Input.dispatchMouseEvent', {275type: 'mousePressed',276x: pos.x,277y: pos.y,278button: 'left',279clickCount: 1,280}, { sessionId });281await sleep(100);282await cdp.send('Input.dispatchMouseEvent', {283type: 'mouseReleased',284x: pos.x,285y: pos.y,286button: 'left',287clickCount: 1,288}, { sessionId });289290console.log('[wechat-browser] Waiting for editor...');291await sleep(3000);292293const waitForEditor = async (): Promise<{ targetId: string; isNewTab: boolean } | null> => {294const start = Date.now();295296while (Date.now() - start < 30_000) {297const targets = await getTargets();298const pageTargets = targets.targetInfos.filter(t => t.type === 'page');299300for (const t of pageTargets) {301console.log(`[wechat-browser] Target: ${t.url}`);302}303304const newTab = pageTargets.find(t => !initialIds.has(t.targetId) && t.url.includes('mp.weixin.qq.com'));305if (newTab) {306console.log(`[wechat-browser] Found new tab: ${newTab.url}`);307return { targetId: newTab.targetId, isNewTab: true };308}309310const editorTab = pageTargets.find(t => t.url.includes('appmsg'));311if (editorTab) {312console.log(`[wechat-browser] Found editor tab: ${editorTab.url}`);313return { targetId: editorTab.targetId, isNewTab: !initialIds.has(editorTab.targetId) };314}315316const currentUrl = await cdp!.send<{ result: { value: string } }>('Runtime.evaluate', {317expression: `window.location.href`,318returnByValue: true,319}, { sessionId });320console.log(`[wechat-browser] Current page URL: ${currentUrl.result.value}`);321322if (currentUrl.result.value.includes('appmsg')) {323console.log(`[wechat-browser] Current page navigated to editor`);324return { targetId: pageTarget!.targetId, isNewTab: false };325}326327await sleep(1000);328}329return null;330};331332const editorInfo = await waitForEditor();333if (!editorInfo) {334const finalTargets = await getTargets();335console.log(`[wechat-browser] Final targets: ${finalTargets.targetInfos.filter(t => t.type === 'page').map(t => t.url).join(', ')}`);336throw new Error('Editor not found.');337}338339if (editorInfo.isNewTab) {340console.log('[wechat-browser] Switching to editor tab...');341const editorSession = await cdp.send<{ sessionId: string }>('Target.attachToTarget', { targetId: editorInfo.targetId, flatten: true });342sessionId = editorSession.sessionId;343344await cdp.send('Page.enable', {}, { sessionId });345await cdp.send('Runtime.enable', {}, { sessionId });346await cdp.send('DOM.enable', {}, { sessionId });347} else {348console.log('[wechat-browser] Editor opened in current page');349}350351await cdp.send('Page.enable', {}, { sessionId });352await cdp.send('Runtime.enable', {}, { sessionId });353await cdp.send('DOM.enable', {}, { sessionId });354355await sleep(2000);356357console.log('[wechat-browser] Uploading all images at once...');358const absolutePaths = images.map(p => path.isAbsolute(p) ? p : path.resolve(process.cwd(), p));359console.log(`[wechat-browser] Images: ${absolutePaths.join(', ')}`);360361// --- PRIMARY approach: intercept file chooser dialog ---362let uploadSuccess = false;363try {364console.log('[wechat-browser] [primary] Enabling file chooser interception...');365await cdp.send('Page.setInterceptFileChooserDialog', { enabled: true }, { sessionId });366367// Set up listener for file chooser opened event BEFORE clicking368const fileChooserPromise = new Promise<{ backendNodeId: number; mode: string }>((resolve, reject) => {369const timeout = setTimeout(() => reject(new Error('File chooser dialog not opened within 10s')), 10_000);370cdp!.on('Page.fileChooserOpened', (params: unknown) => {371clearTimeout(timeout);372const p = params as { backendNodeId: number; mode: string };373console.log(`[wechat-browser] [primary] File chooser opened: backendNodeId=${p.backendNodeId}, mode=${p.mode}`);374resolve(p);375});376});377378// Trigger file chooser by calling .click() on the file input with userGesture379const fileInputSelectors = [380'.js_upload_btn_container input[type=file]',381'input[type=file][multiple][accept*="image"]',382'input[type=file][accept*="image"]',383'input[type=file][multiple]',384'input[type=file]',385];386387console.log('[wechat-browser] [primary] Clicking file input via JS .click() with userGesture...');388const clickResult = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {389expression: `390(function() {391const selectors = ${JSON.stringify(fileInputSelectors)};392for (const sel of selectors) {393const el = document.querySelector(sel);394if (el) {395el.click();396return JSON.stringify({ clicked: sel });397}398}399const debug = [];400document.querySelectorAll('input[type=file]').forEach((inp, i) => {401debug.push({ i, accept: inp.accept, multiple: inp.multiple, parentClass: inp.parentElement?.className?.slice(0, 60) });402});403return JSON.stringify({ error: 'no file input found', fileInputs: debug });404})()405`,406returnByValue: true,407userGesture: true,408}, { sessionId });409console.log(`[wechat-browser] [primary] Click result: ${clickResult.result.value}`);410411const clickStatus = JSON.parse(clickResult.result.value);412if (clickStatus.error) {413throw new Error(`File input not found: ${clickStatus.error}`);414}415416// Wait for the file chooser event417console.log('[wechat-browser] [primary] Waiting for file chooser dialog...');418const chooser = await fileChooserPromise;419420console.log(`[wechat-browser] [primary] Setting files via backendNodeId=${chooser.backendNodeId}...`);421await cdp.send('DOM.setFileInputFiles', {422files: absolutePaths,423backendNodeId: chooser.backendNodeId,424}, { sessionId });425console.log('[wechat-browser] [primary] Files set successfully via file chooser interception');426uploadSuccess = true;427} catch (primaryErr) {428console.log(`[wechat-browser] [primary] File chooser approach failed: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}`);429// Disable interception before falling back430try { await cdp.send('Page.setInterceptFileChooserDialog', { enabled: false }, { sessionId }); } catch {}431}432433// --- FALLBACK approach: direct DOM.setFileInputFiles on nodeId ---434if (!uploadSuccess) {435console.log('[wechat-browser] [fallback] Trying direct DOM.setFileInputFiles...');436const { root } = await cdp.send<{ root: { nodeId: number } }>('DOM.getDocument', {}, { sessionId });437438const fileInputSelectors = [439'.js_upload_btn_container input[type=file]',440'input[type=file][multiple][accept*="image"]',441'input[type=file][accept*="image"]',442'input[type=file][multiple]',443'input[type=file]',444];445446let nodeId = 0;447for (const sel of fileInputSelectors) {448const result = await cdp.send<{ nodeId: number }>('DOM.querySelector', { nodeId: root.nodeId, selector: sel }, { sessionId });449if (result.nodeId) {450console.log(`[wechat-browser] [fallback] Found file input with selector: ${sel}`);451nodeId = result.nodeId;452break;453}454}455456if (!nodeId) throw new Error('File input not found with any selector');457458await cdp.send('DOM.setFileInputFiles', { nodeId, files: absolutePaths }, { sessionId });459console.log('[wechat-browser] [fallback] Files set via nodeId');460461// Dispatch change event462await cdp.send('Runtime.evaluate', {463expression: `464(function() {465const selectors = ${JSON.stringify(fileInputSelectors)};466for (const sel of selectors) {467const el = document.querySelector(sel);468if (el) {469el.dispatchEvent(new Event('change', { bubbles: true }));470el.dispatchEvent(new Event('input', { bubbles: true }));471return 'dispatched on ' + sel;472}473}474return 'no input found for event dispatch';475})()476`,477returnByValue: true,478}, { sessionId });479console.log('[wechat-browser] [fallback] Change event dispatched');480}481482// Wait for images to upload483console.log('[wechat-browser] Waiting for images to upload...');484const targetCount = absolutePaths.length;485for (let i = 0; i < 30; i++) {486await sleep(2000);487const uploadCheck = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {488expression: `489JSON.stringify({490uploaded: document.querySelectorAll('.weui-desktop-upload__thumb, .pic_item, [class*=upload_thumb], [class*="pic_item"], [class*="upload__thumb"]').length,491loading: document.querySelectorAll('[class*="upload_loading"], [class*="uploading"], .weui-desktop-upload__loading').length492})493`,494returnByValue: true,495}, { sessionId });496const status = JSON.parse(uploadCheck.result.value);497console.log(`[wechat-browser] Upload progress: ${status.uploaded}/${targetCount} (loading: ${status.loading})`);498if (status.uploaded >= targetCount) break;499}500501console.log('[wechat-browser] Filling title...');502await cdp.send('Runtime.evaluate', {503expression: `504const titleInput = document.querySelector('#title');505if (titleInput) {506titleInput.value = ${JSON.stringify(title)};507titleInput.dispatchEvent(new Event('input', { bubbles: true }));508} else {509throw new Error('Title input not found');510}511`,512}, { sessionId });513await sleep(500);514515console.log('[wechat-browser] Filling content...');516// Try ProseMirror editor first (new WeChat UI), then fallback to old editor517const contentResult = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {518expression: `519(function() {520const contentHtml = ${JSON.stringify('<p>' + content.split('\n').filter(l => l.trim()).join('</p><p>') + '</p>')};521522// New UI: ProseMirror contenteditable523const pm = document.querySelector('.ProseMirror[contenteditable=true]');524if (pm) {525pm.innerHTML = contentHtml;526pm.dispatchEvent(new Event('input', { bubbles: true }));527return 'ProseMirror: content set, length=' + pm.textContent.length;528}529530// Old UI: .js_pmEditorArea531const oldEditor = document.querySelector('.js_pmEditorArea');532if (oldEditor) {533return JSON.stringify({ type: 'old', x: oldEditor.getBoundingClientRect().x + 50, y: oldEditor.getBoundingClientRect().y + 20 });534}535536return 'editor_not_found';537})()538`,539returnByValue: true,540}, { sessionId });541542const contentStatus = contentResult.result.value;543console.log(`[wechat-browser] Content result: ${contentStatus}`);544545if (contentStatus === 'editor_not_found') {546throw new Error('Content editor not found');547}548549// Fallback: old editor uses keyboard simulation550if (contentStatus.startsWith('{')) {551const editorClickPos = JSON.parse(contentStatus);552if (editorClickPos.type === 'old') {553console.log('[wechat-browser] Using old editor with keyboard simulation...');554await cdp.send('Input.dispatchMouseEvent', {555type: 'mousePressed',556x: editorClickPos.x,557y: editorClickPos.y,558button: 'left',559clickCount: 1,560}, { sessionId });561await sleep(50);562await cdp.send('Input.dispatchMouseEvent', {563type: 'mouseReleased',564x: editorClickPos.x,565y: editorClickPos.y,566button: 'left',567clickCount: 1,568}, { sessionId });569await sleep(300);570571const lines = content.split('\n');572for (let i = 0; i < lines.length; i++) {573const line = lines[i];574if (line!.length > 0) {575await cdp.send('Input.insertText', { text: line }, { sessionId });576}577if (i < lines.length - 1) {578await cdp.send('Input.dispatchKeyEvent', {579type: 'keyDown',580key: 'Enter',581code: 'Enter',582windowsVirtualKeyCode: 13,583}, { sessionId });584await cdp.send('Input.dispatchKeyEvent', {585type: 'keyUp',586key: 'Enter',587code: 'Enter',588windowsVirtualKeyCode: 13,589}, { sessionId });590}591await sleep(50);592}593console.log('[wechat-browser] Content typed via keyboard.');594}595}596await sleep(500);597598if (submit) {599console.log('[wechat-browser] Saving as draft...');600const submitResult = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {601expression: `602(function() {603// Try new UI: find button by text604const allBtns = document.querySelectorAll('button');605for (const btn of allBtns) {606const text = btn.textContent?.trim();607if (text === '保存为草稿') {608btn.click();609return 'clicked:保存为草稿';610}611}612// Fallback: old UI selector613const oldBtn = document.querySelector('#js_submit');614if (oldBtn) {615oldBtn.click();616return 'clicked:#js_submit';617}618// List available buttons for debugging619const btnTexts = [];620allBtns.forEach(b => {621const t = b.textContent?.trim();622if (t && t.length < 20) btnTexts.push(t);623});624return 'not_found:' + btnTexts.join(',');625})()626`,627returnByValue: true,628}, { sessionId });629console.log(`[wechat-browser] Submit result: ${submitResult.result.value}`);630await sleep(3000);631632// Verify save success by checking for toast633const toastCheck = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {634expression: `635const toasts = document.querySelectorAll('.weui-desktop-toast, [class*=toast]');636const msgs = [];637toasts.forEach(t => { const text = t.textContent?.trim(); if (text) msgs.push(text); });638JSON.stringify(msgs);639`,640returnByValue: true,641}, { sessionId });642console.log(`[wechat-browser] Toast messages: ${toastCheck.result.value}`);643console.log('[wechat-browser] Draft saved!');644} else {645console.log('[wechat-browser] Article composed (preview mode). Add --submit to save as draft.');646}647} finally {648if (cdp) {649cdp.close();650}651console.log('[wechat-browser] Done. Browser window left open.');652}653}654655function printUsage(): never {656console.log(`Post image-text (贴图) to WeChat Official Account657658Usage:659npx -y bun wechat-browser.ts [options]660661Options:662--markdown <path> Markdown file for title/content extraction663--images <dir> Directory containing images (PNG/JPG)664--title <text> Article title (max 20 chars, auto-compressed)665--content <text> Article content (max 1000 chars, auto-compressed)666--image <path> Add image (can be repeated)667--submit Save as draft (default: preview only)668--profile <dir> Chrome profile directory669--account <alias> Select account by alias (for multi-account setups)670--help Show this help671672Examples:673npx -y bun wechat-browser.ts --markdown article.md --images ./photos/674npx -y bun wechat-browser.ts --title "测试" --content "内容" --image ./photo.png675npx -y bun wechat-browser.ts --markdown article.md --images ./photos/ --submit676`);677process.exit(0);678}679680async function main(): Promise<void> {681const args = process.argv.slice(2);682if (args.includes('--help') || args.includes('-h')) printUsage();683684const images: string[] = [];685let submit = false;686let profileDir: string | undefined;687let title: string | undefined;688let content: string | undefined;689let markdownFile: string | undefined;690let imagesDir: string | undefined;691let accountAlias: string | undefined;692693for (let i = 0; i < args.length; i++) {694const arg = args[i]!;695if (arg === '--image' && args[i + 1]) {696images.push(args[++i]!);697} else if (arg === '--images' && args[i + 1]) {698imagesDir = args[++i];699} else if (arg === '--title' && args[i + 1]) {700title = args[++i];701} else if (arg === '--content' && args[i + 1]) {702content = args[++i];703} else if (arg === '--markdown' && args[i + 1]) {704markdownFile = args[++i];705} else if (arg === '--submit') {706submit = true;707} else if (arg === '--profile' && args[i + 1]) {708profileDir = args[++i];709} else if (arg === '--account' && args[i + 1]) {710accountAlias = args[++i];711}712}713714const extConfig = loadWechatExtendConfig();715const resolved = resolveAccount(extConfig, accountAlias);716if (resolved.name) console.log(`[wechat-browser] Account: ${resolved.name} (${resolved.alias})`);717718if (!profileDir && resolved.alias) {719profileDir = resolved.chrome_profile_path || getAccountProfileDir(resolved.alias);720}721722if (!markdownFile && !title) {723console.error('Error: --title or --markdown is required');724process.exit(1);725}726if (!markdownFile && !content) {727console.error('Error: --content or --markdown is required');728process.exit(1);729}730if (images.length === 0 && !imagesDir) {731console.error('Error: --image or --images is required');732process.exit(1);733}734735await postToWeChat({ title, content, images: images.length > 0 ? images : undefined, imagesDir, markdownFile, submit, profileDir });736}737738await main().catch((err) => {739console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);740process.exit(1);741});742