wechat-channels-video 是做什么的?
Use an Ego Lite session to save a video draft in Channels Assistant. Never click Publish — QR publish is reserved for humans.
Install Ego First
Install Ego Lite first and ensure ego-browser is in PATH. If missing, soft-fail and tell the user to install it — do not directly launch a browser.
Before Each Publish: Login Gate (Mandatory)
The WeChat Channels login session expires quickly. The agent must run the following before uploading or filling in forms:
node scripts/check-login.mjs
# If the window needs to be left for human QR scanning:
node scripts/check-login.mjs --keep
| Exit Code | Meaning | Agent Action |
|---|---|---|
0 | Logged in | Inform the user of login_tip, proceed with the workflow |
2 | Not logged in / no ego-browser | Display how_to_login / how_to_login_zh, stop. Ask the human to scan the QR code in Ego, then run check-login again |
1 | Other error | Report the JSON error, do not pretend the video was published |
See references/login.md for details. Do not create a separate cookie-healthcheck skill.
Input (Generic, No Local Product Paths)
The caller provides the video and text fields — see references/inputs.md. This skill does not hard-code any ~/… business directories.
Minimum required: local video path, description, short_title. Optional: tags, cover, location/originality/activity.
CLI Thin Scripts (Stable Part)
ego-browser nodejs does not forward shell argv; check-login is already wrapped with a temp entry + ego-browser nodejs -e.
# From repo root
node skills/wechat-channels-video/scripts/check-login.mjs
node skills/wechat-channels-video/scripts/check-login.mjs --keep
node skills/wechat-channels-video/scripts/serve-file.mjs --file /absolute/path/to/video.mp4 --port 18765
# Also usable with relative paths from the installed skill directory
node scripts/check-login.mjs
node scripts/serve-file.mjs --file /absolute/path/to/video.mp4 --port 18765
- check-login: The only login hard gate; ends with
finish({ keep: [] })by default (except with--keep). - serve-file: A local CORS file server for the page to
fetch; prints{ url, bytes, port }, ends on SIGINT. Do not base64-encode large videos.
Do not bundle the entire publish UI into a single giant script: wujie, coordinate clicks, and cover waitForFileChooser require the agent to inspect snapshots/screenshots. Scripts handle deterministic login and file transfer; the form is filled by the agent steps below.
ego-browser API Quick Reference
Each ego-browser nodejs is a new process; TaskSpace / Page persists across calls, but JS variables do not.
| API | Description |
|---|---|
taskSpace(name|id) | One space per user goal; record the spaceId for reuse |
task.page("p1") | Reuse the first page via goto; do not open pages unnecessarily |
page.snapshot() | Inspect (can see through open shadows); clicks are often intercepted inside wujie |
page.evaluate(fn, arg) | Accepts one JSON-serializable argument (long text goes here) |
page.mouse.click(x, y) | Commonly used inside wujie popup layers |
page.waitForFileChooser() | The only safe upload path for covers |
task.handOff() | Hand off to human for QR code publish / login |
task.finish({ keep: [] }) | Clean up and close the space; use handOff if the human needs to continue viewing, do not occupy the machine long-term |
Key: snapshot can see wujie shadow; ref/text clicks will report intercepted pointer events. For actions inside wujie, use .click() inside evaluate or coordinate mouse.click.
Agent Workflow (After Login Passes)
0. Navigate to the Publish Page
Do not go directly to /platform/post/create (lazy load white screen). Follow the UI path:
Home → Left sidebar "Content Management" → "Videos" → Right side "Publish Video"
Ready: wujie inner text shows "视频描述" (video description) and "短标题" (short title), and there is a hidden input[type=file][accept*="video"] and a "保存草稿" (save draft) button.
const state = await page.evaluate(() => {
const app = document.querySelector('wujie-app');
if (!app?.shadowRoot) return { wujie: false };
const t = app.shadowRoot.textContent || '';
return {
wujie: true,
hasDesc: t.includes('视频描述'),
hasShort: t.includes('短标题'),
hasUpload: !!app.shadowRoot.querySelector('input[type=file][accept*="video"]'),
hasSave: t.includes('保存草稿'),
};
});
1. Upload Video
First, start serve-file (default port 18765), then fetch the video inside the page and inject it into the hidden file input using DataTransfer (the video component is not isTrusted-protected; the cover is):
const r = await page.evaluate(async (p) => {
const root = document.querySelector('wujie-app').shadowRoot;
const input = root.querySelector('input[type=file][accept*="video"]');
const resp = await fetch(p.url);
const dt = new DataTransfer();
dt.items.add(new File([await resp.blob()], p.name, { type: 'video/mp4' }));
input.files = dt.files;
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
return 'injected ' + dt.files[0].size;
}, { url: 'http://127.0.0.1:18765/video.mp4', name: 'video.mp4' });
Validation: check the DOM for the preview card showing "转发/赞/评论" and a stable progress bar at 100%. The performance resource list will not capture the upload POST. Do not fill any form fields during upload; if wujie becomes temporarily null, wait a moment and try again — do not refresh.
1.5 Checkpoint: Save Draft First
Immediately after upload finishes, click "保存草稿" once, then fill in the description/short title/location, and finally save again. This avoids having to re-upload a large file if something goes wrong midway.
2. Description + Topics
The description uses .input-editor (not contenteditable). Use execCommand; pass the text as the second argument to evaluate:
await page.evaluate((p) => {
const ed = document.querySelector('wujie-app').shadowRoot.querySelector('.input-editor');
ed.focus();
document.execCommand('selectAll', false, null);
document.execCommand('delete', false, null);
document.execCommand('insertText', false, p.desc);
}, { desc });
await page.evaluate((p) => {
const ed = document.querySelector('wujie-app').shadowRoot.querySelector('.input-editor');
ed.focus();
document.execCommand('insertParagraph');
document.execCommand('insertText', false, p.tags);
ed.blur();
}, { tags });
3. Short Title
A real <input>, must use native setter; validate by reading input.value (innerText does not contain input values).
4. Location
A city is usually auto-filled. Safe approach: mouse.click to open → snapshot/screenshot → click "不显示位置" (do not show location) → re-read the field to confirm. Evaluating coordinates might click on a hidden layer.
5. Save Draft (End Point)
Click "保存草稿". Success signal: "已保存" (saved) + the bottom button turns gray. Then goto the drafts folder to verify. Do not navigate away from the editor before the draft is visible in the drafts folder.
Optional: declare originality, search for activities (use native setter for the search term; if not found, use "不参与活动" (do not participate)).
6. Cover (Optional)
The only reliable method: waitForFileChooser() + trusted click on "上传封面" (upload cover) + chooser.setFiles(<absolute path>). The synthetic input.files / OS file dialog approach has been tested and failed. Wait for the confirmation button's spinner to stop and the preview no longer reverts to the default frame, then save the draft.
7. Cleanup
- Stop
serve-file(release the port). - If the human needs to publish: use
handOff()in the drafts folder. - Otherwise,
finish({ keep: [] }), do not occupy the space long-term.
Pitfall Checklist
- wujie shadow: Start queries from
wujie-app.shadowRoot. - Direct create link white screen: Must use the UI path to enter the publish page.
- ref/text clicks inside wujie are intercepted → use evaluate or coordinate click.
- Long text only passes through evaluate parameters.
- Description uses execCommand; short title uses native setter.
- Location uses two-step coordinates + re-read verification.
- Cover can only use waitForFileChooser.
- Upload validation checks the DOM only; save draft right after upload.
- Do not navigate away before the draft is saved.
- "加载失败" (load failed) on the login page is a red herring; handOff lets the human scan the QR code.
- Run check-login before each publish; exit code 2 must stop the workflow.
- Publishing is a human endpoint; this skill only goes up to draft.
Testing
npm test