Easel
An open-source content workbench for social media creators: one agent runs the whole loop — aggregate the hot lists, plan a topic, generate the copy, the cards, the voice and the video, publish the finished file to an account that is already logged in on seven Chinese platforms, then read the numbers back into the account profile that shaped the next round.

What it is
Easel is an open-source workbench that runs a social-media account end to end, built on the OpenClaw agent runtime and shipped as a Python command line plus a React console on port 7860. Its capabilities are organised as five consecutive stages — discover, plan, produce, publish, attribute — and they are directories rather than menu entries: each skill is a SKILL.md of under two hundred lines, with reference documents loaded only when needed and runnable scripts beside them, so cards, voice-overs, subtitles, edits, short dramas and paper explainers come out as files written into an outputs/ project folder. An account profile of six dimensions — positioning, style, audience, platforms, preferences and red lines, long-term memory — is prefixed onto every turn, which is what makes the output drift toward a real account instead of a generic model voice. Publishing covers seven platforms, each with a guard before the real send, and the account data comes back through the same skill system, so the next round starts from what the last one measured.
Who built itZJU-REAL is a laboratory account rather than a person: its README carries Zhejiang University, Peking University, REAL Lab and OpenDCAI Lab marks, and frames the project as research moved into real social-media work. Its 210 commits are spread over thirteen author names. A hundred and forty-three of them arrive from an address at xiaohongshu.com and are not linked to a GitHub account; the account lidingm, whose commits are signed Dingming Li, has nineteen more; mengyuyuan has twenty-three. Sixty-five of the 210 carry a linked account, and twelve carry a co-author trailer, seven of which name a Claude model — six say only “Claude”, one names Opus 4.8 with a million-token context. GitHub lists ten contributors, and the repository sits at 2,633 stars and 402 forks.
How it is put together
The parts · 6A Python program that owns the local machine and delegates judgement to an agent runtime. The front door is a command line; the main surface is a FastAPI server with a React console managing conversations, media, accounts, profiles, the output library and publishing state; behind both sits the agent runtime, which reads a skill directory, calls the scripts named there and writes results to disk. That split explains the shape of the repository: the intelligence lives in prose and reference documents, the work lives in Python scripts, and the state lives in files — an outputs/ directory per content project with one hidden manifest, a profiles/ directory per account, and underscore-prefixed system directories for login state, publish logs and analytics. Everything that touches the outside world either passes through a guard, the outbound content check before a real publish, or through a lock, one browser profile used by one process at a time, because the failure mode of an unattended social agent is not a wrong answer but a wrong action.
- easel/
- The Python command line: dispatch, an environment checker of 14,031 bytes and a connectivity check, gateway management, and the three modules that make the agent runtime addressable — one resolving gateway host, port and URL from the environment, the configuration file or a profile hash; one locating the runtime binary or its module entry point; one isolating a dedicated profile so an existing installation on the machine is never touched. Its own nine modules are 44 KB, with five more files under
commands/. - web/
- A 191,207-byte FastAPI application behind the console, its endpoints covering settings, sessions, media, publishing and statistics, with a React front end of twenty-three components and seven library modules — a 49,200-byte capability menu, a 29,109-byte API client, a 12,490-byte store — and prebuilt pages under
web/static/including a 76,045-byte product page and a 40,262-byte console shell. - skills/openclaw/
- Seven hundred and sixteen files, 12.9 MB, holding the capability set as directories rather than a menu. Each skill is a
SKILL.mdof under two hundred lines carrying only a name, a description and a pipeline layer, with optionalreferences/for domain knowledge that is loaded on demand andscripts/for work that never enters the prompt, grouped into the five stages and a general layer. The README badge counts 113 skills, and the two validators report 114 and 115 at points where pull requests were adding more. - skills/shared/
- Fifty-five files of cross-skill machinery and the place where the boundary with the platforms is drawn: an 85,581-byte multi-platform web publisher, a 76,086-byte Douyin publisher, a 59,517-byte multi-voice tool, a 48,656-byte comment handler, a 48,135-byte account statistics tool, a 47,674-byte Xiaohongshu publisher, a 42,058-byte video generator, a 41,251-byte readback module, a 36,706-byte image generator, a 33,769-byte WeChat Official Accounts reader and a 21,848-byte content guard that every publishing script calls.
- docs/ and profiles/
- Six documents: the skill interface specification, a 24,646-byte capability map, a note on how the prompt stack is layered, known issues in two languages, and a 10,681-byte acknowledgements file.
profiles/_template/defines an account in seven files — identity, style, audience, platforms, preferences, memory and a README — and a profile can be used across several logged-in platforms at once. - scripts/ and tests/
- Ten scripts including the two installers, 37,037 bytes for the shell one and 24,259 for the PowerShell one, the two skill validators, an output migration tool, a session repair script, and gateway launchers for both systems; plus thirteen test files covering the gateway endpoint, workspace resolution, the web security rules, the local write guard, the data tracker and the image client that needed a provider-specific branch.
Choices, and what they beat
Browser automation for the Chinese platforms, one aggregator API for the overseas ones over official developer APIs for every platform
The maintainers state that the domestic layer runs through browser automation or platform command lines, and the contributor proposing the overseas publishers writes why that was not repeated there: TikTok, Instagram, YouTube, LinkedIn and the rest each require a developer application, an OAuth flow and a review, which is expensive to do one by one, so the prototype routes all of them through a single upload service with no browser and no cookies and is offered as an addition rather than a replacement.
Leave an unresolved publish unresolved over retrying after a server error, a timeout or an unusable response
Because a duplicate post is worse than a missing one. The request identifier doubles as an idempotency key, the script re-queries that identifier instead of resending, and a result that cannot be resolved exits with its own code, is written into the calendar as pending, and is kept out of the publish log. The skill states that the publishing command must not be run again, and that only the status command may be used to check.
Fix the production path rather than the test fixture over redirecting the home directory inside the one test file that broke it
Two pull requests were open against the same bug at once, and the author of the second sets out both: one changes only the test fixture, while the other routes the five places in the web server that read and write the configuration directly through the workspace module that already has an isolation variable, which makes every future test isolated by setting one value. The second is offered as the root fix, with production behaviour unchanged; the first is described as treating the symptom.
Say a capability is unavailable rather than let it look available over listing every command line agent found on the machine as usable
The detection module separates three states — installed, supported, usable without a key — and only the last may be claimed to the user. Assistants with no backend in the runtime are explicitly marked unsupported with an explanation, on the principle the author writes down: it is better to say less than to let someone believe that having a tool installed is enough.
Resolve the gateway address instead of pinning or patching it over hardcoding the documented default port, or modifying the runtime
The runtime assigns a hash-derived port to a non-default profile, which is why the hardcoded address was wrong. The fix resolves host, port and URL in one module using the same priority order the runtime itself uses, and it deliberately neither pins the port nor patches the runtime, since the port may be occupied or chosen by the user. Thirteen call sites across seven files were moved onto it.
Keep the preview frame scriptable but on an opaque origin over granting scripts and same-origin access so that one button works
The preview frame disabled scripts, so the copy-to-platform button inside it did nothing. The fix grants script execution and clipboard write but withholds same-origin, so the frame still cannot reach the application. The companion fix inlines the local images as data in the preview, because a pasted page made the platform fetch images from a local address it cannot reach, which left the article broken.
Read fromdocs/SKILL-SPEC.md (5,439 characters), skills/openclaw/paper-explainer/references/slide-design.md (6,456), skills/openclaw/xhs-note-creator/references/output-spec.md (1,315), README.md (17,793 characters — the report printed the first 6,000 and the rest was fetched from the default branch on 2026-10-01), and the pull-request and issue bodies quoted in the log above.
Build log
6 stages- 01
Thirty-four days, four releases, and a repository that is mostly video
The repository was created on 2026-08-28 at 02:22 UTC and its two hundred and tenth commit landed on 2026-10-01, so the whole record covers thirty-four days: fifty commits in the three remaining days of August, a hundred and fifty-nine in September, one on the first of October. Four releases came out of that stretch —
v0.1.0on 2026-08-31,v0.1.1on 2026-09-14,v0.2.0on 2026-09-17 with video production built in and three tiers of transcription, andv0.2.1on 2026-09-24 covering the settings panel, faster conversation over the gateway and security hardening. Around them sit 2,633 stars, 402 forks, 14 watchers, 24 open issues and an Apache-2.0 licence. The tree holds 988 files and 330 MB, and almost none of that is code:assets/readme/is 59 files and 239 MB — nine complete films, the largest 57,580,062 bytes, with six-second previews and thumbnails beside them — andweb/static/adds another 206 MB of the same material for the product page. The skills are 716 files and 12.9 MB, the shared scripts 55 files and 1 MB, the React console 46 files and 519 KB, the tests 110 KB, the Python command line 44 KB, the documentation 52 KB. Sixty-five of the 210 commits carry a linked GitHub account and twelve carry a co-author trailer; seven of those trailers name a Claude model, six of them saying only “Claude”. - 02
Seven platforms, two ways in, and a dispatcher above both
Seven platforms support login, adaptation and publishing: Xiaohongshu, Douyin, Kuaishou, Zhihu, Bilibili, WeChat Channels and WeChat Official Accounts. A contributor proposing the overseas publishers states the domestic layer plainly — browser automation or the platform command line. Xiaohongshu is a 47,674-byte Playwright script. One pull request adds a cross-process lock in the browser profile,
msvcrton Windows andfcntlelsewhere, because a background login probe and a foreground publish could both launch Chromium against one directory and the process died with exit 21; the same change prefers an anti-detection kernel under~/.cloakbrowser, after headless mode kept meeting the platform’s 300012 challenge. Douyin is 76,086 bytes, the largest script here, with a headed login whose window must not be closed early and a keep-open mode forbidden from re-opening the browser context, since a second lock on one profile crashes. Bilibili callsbiliup, a third-party uploader. WeChat Official Accounts is the one that talks to a platform API: images in the body are uploaded through the material endpoint and replaced with CDN links before the draft is created, because the platform will not fetch a local path. Above thempublish_dispatch.pyholds the platform registry and routes an adapted piece to the right publisher. - 03
The gate before the real send, and what to do when the result is unknown
Every script that puts text on a public platform must call the outbound content guard before the real send. It is a 21,848-byte module with two levels: a blocking finding is real sensitive information — API keys, internal URLs and domains, proxy addresses, internal paths, environment names with their real values — and it stops the publish with exit code 7, failing closed; a warning is AI-flavoured wording or a model name, which in a paper explainer may well be legitimate, so it only warns. Dry runs never block, and overriding a hard block takes an explicit flag. Publishing is dry-run until an explicit switch is added. The harder decision is what to do when the outcome is unknown, and its author wrote the first version wrong and corrected it in public. Only an explicit 400, 401, 403 or 422 counts as a definite rejection. A 5xx, a timeout, a dropped connection, or a 2xx with an unusable body is not a failure, and the script does not resend: it queries the same request identifier it had sent as an idempotency key. If that finds the post it keeps tracking; if not, the attempt is reported as unresolved, written into the calendar as pending with the identifier in the note, and kept out of the publish log so nobody later reads it as a failure and posts twice. The skill states the rule: after an unresolved result, do not run the command again — use the status command to check.
- 04
Reading the numbers back is its own set of scripts, with its own bugs
The attribute stage is where the loop closes, and it is heavy: a 48,135-byte account statistics tool, a 41,251-byte readback module for the per-platform shapes, a 33,769-byte WeChat Official Accounts reader, a publish analytics skill with a follower-log schema, a performance review with a 7,640-byte benchmarks file, and a post scorer with a 4,084-byte script and a 4,888-byte criteria document. The bugs are instructive. One pull request reports that Xiaohongshu statistics were counting drafts: the creator centre opens on an all-notes tab, the script read that list, and untitled drafts and half-finished notes counted as published work. The fix clicks through to the published tab first, then filters out draft markers, empty titles and editor URLs. The same pull request protects Douyin’s readback, recording a plain instruction when a headed login waits for a scan and forbidding the keep-open path from re-opening the context to collect numbers. The loop reads plays, interactions, comments and performance and deposits the effective structures back into the account profile. The mechanism is a manifest contract: every layer records the paths it produced and a one-line summary into one hidden metadata file per project, so a downstream step reads the upstream file by path instead of re-deriving it, and a failed step is recorded as failed so a run can be resumed.
- 05
Two validators for the skills, and four bugs that were not about the agent
One script checks every skill directory — frontmatter, resource links, the output and publishing safety contracts. A second parses the Python commands named inside each
SKILL.mdand compares them with the scripts’ own argument definitions, to catch drift between prose and code. There are thirteen test files, a continuous integration workflow, and an offline self-test inside each publishing script. The failures in the bug list are plumbing, not judgement. Running the test suite overwrote the real configuration file with fixture values, so the primary model reference no longer matched the provider catalogue and every later conversation reported an unknown model; five places in the web server touched that file directly, bypassing the module that already had an isolation mechanism. The gateway address was hardcoded as 18789 in thirteen places across seven files while the dedicated profile listens on 37289, so the checker printed a failure while the gateway was healthy. On Windows, the runtime locator could land on an npm shim, and because such a file runs through the command interpreter a multi-line message was cut at its first newline: the agent received the eighty-seven-character profile prefix and nothing else. The fix resolves the shim’s sibling runtime and module entry point instead, and the same prompt then measured three hundred and fifty-five characters. - 06
Outside contributors, a full chat group, and a warning about one platform
Of the thirty issues and pull requests, most come from outside the laboratory. One contributor has eight, spread across the settings panel, the installer, the command line and the image pipeline. Another has four in a week: Xiaohongshu publishing, a statistics fix, images in WeChat Official Accounts drafts, and a security pass that tightened cross-origin access on the local port and blocked deletion of system directories. Two contributors filed two each, including a chat bug where pressing return halfway through typing pinyin sent the message, and a dark-mode toggle. One proposed feature opened an issue before the code and offered to be closed if the direction did not fit. The maintainers route support to a WeChat group — one issue is simply that the group passed two hundred members and could not be joined — and in one thread the main committer suggests contributors reach for a working agent to debug their own problems, since machines and configurations differ. The published roadmap has four items, and the community pull requests are already hitting the second, simpler installation. Finally there is the warning in the README itself, unusual in a launch document: automated publishing to Xiaohongshu may be detected by the platform and carries verification, rate-limiting or account risk, so preview and pre-publish checks with a human confirmation are recommended.
Adjacent records
All records →No. 033
MiroFish
A prediction engine that builds a parallel world of hundreds of AI agents out of a document you upload, then runs it forward to see what happens next.
No. 116
Lemmalog
A Rust Datalog engine that treats agent memory as a deductive database rather than a bigger vector store: facts asserted at the extraction boundary, stratified rules deriving closures and temporal views, provenance back to the source episode on every derived fact, and views maintained one epoch at a time — served to Claude Code and Kimi CLI as twelve MCP tools.
No. 114
Rome
A self-hosted agent runtime that treats the environment around a model as the thing worth growing: a Rome App packages an interface, executable actions, on-demand skills and a private database as git-tracked code an agent can reuse later, while the runtime gives every delegated subagent a child session of its own, forks a turn without letting it mutate its source, and fails a turn rather than quietly swapping the model that produced the conversation.