Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[Unreleased]
Added
- Bulk media downloading (
xactions download). XActions could always find media and could not save it, so anyone building an archive reached for gallery-dl. Nowxactions download @nichxbt:all --archivewrites every photo, video and GIF from a profile, tweet, search or community, plus avatars and banners at original resolution rather than the 48px thumbnail X serves by default. Filename templates take 18 keys including{media_filename}, X's own CDN name, which gallery-dl users have an open request for (mikf/gallery-dl#7695). The JSONL archive makes re-runs incremental on two layers: identity, like gallery-dl's, and a content hash, which gallery-dl explicitly does not do, so one photo reached through a retweet, a quote and the media tab is hard-linked instead of stored three times. Interrupted downloads resume from a.partfile with aRangerequest, rate limits are retried with backoff, a 404 is not, and every item ends as downloaded, skipped, deduped, planned or failed with its reason. Also available as thexactions/mediamodule and thex_download_mediaMCP tool. Docs: docs/media-archive.md. - Reputation audit: AI risk-scores your own timeline and gives you a shareable score card. Every account accumulates posts nobody re-reads: the venting reply from three years ago, the joke that reads differently out of context.
scripts/reputationAudit.jsscores your recent posts across professional, hostile, legal, and spam risk with one LLM call per post, rolls the verdicts into a 0-100 reputation score and a letter grade, and renders it as a downloadable or clipboard-copyable 1200x675 PNG card. A post's score is the max across its dimensions rather than the average, because averaging washes out the one thing that matters about it; the account score blends average risk with the single worst post, so one severe outlier still pulls the grade down. Flagged and borderline posts get a one-click cleanup pass reusing the same DOM-verified delete logic asscripts/searchSweep.js, with its own dry run, and scanning itself never touches anything. Scores are cached locally for 14 days by post id and rubric, so a re-scan does not re-bill posts already scored. The engine (src/ai/reputationScorer.js) is exported from the package root asscorePost,scorePosts,summarizeReport,scoreToGradeandDIMENSIONS, runs in the terminal asxactions reputation <username>, and is served atPOST /api/ai/reputation/score(x402-priced, documented in/openapi.jsonunder the Reputation tag so an agent can price the call before making it). xAI works straight from the browser console because x.com's CSP allowsapi.x.ai; every other provider goes through the extension bridge or the CLI. Docs: docs/reputation-audit.md. - A hosted MCP server: give any AI agent live access to X with one URL.
https://xactions.app/mcpis a Model Context Protocol server running at the Cloudflare edge.claude mcp add --transport http xactions https://xactions.app/mcpand your agent can read profiles, posts, threads and videos from X, and search the entire XActions corpus while it writes automation code. There is no API key, no account, no OAuth, and nothing to install: it reads the same public data x.com serves a logged-out browser, through the guest-token GraphQL rail with the public embed endpoint as failover, so a read only fails when both do. Four tools are free;x_profileandx_postscost $0.001 and $0.005 per call, settled in USDC on Solana or Base over x402 at the moment of the call, which still means no account and no invoice. Six read-only tools (x_profile,x_posts,x_post,x_thread,x_video,xactions_docs), roughly 700 documentation resources, one per guide, skill and browser script, and five prompt templates that turn the tools into finished work: account audits, thread summaries, competitor comparisons, video pulls, and "build me this automation" grounded in the real docs. Transport is Streamable HTTP (MCP 2025-06-18, back-compatible with 2025-03-26 and 2024-11-05) and stateless, so consecutive calls can land in different isolates on different continents and still work. Nothing in it can post, follow, like or delete. Docs: docs/mcp-remote.md, with copy-paste config for Claude Code, Claude Desktop, Cursor, VS Code and Windsurf, and discovery at /.well-known/mcp.json. xactions-edge: the same server as a plain SDK, with zero dependencies.npm install xactions-edge, thenawait createClient().profile('nasa'). Typed methods for profiles, posts, single posts, threads, videos and docs search, plus the raw MCP surface (listTools,call,listPrompts,getPrompt,listResources,readResource) for anyone building agent tooling. It ships a complete Streamable HTTP MCP client in about 200 lines that works against any MCP server, not only this one. Runs unchanged in Node 18+, browsers, Cloudflare Workers, Deno and Bun; the only requirement isfetch. Failures arrive as a typedXActionsErrorthat says whether retrying is worth it, and retryable ones are already retried with backoff before you see them. A priced tool raises aPaymentRequiredErrorcarrying the price, the chains and the raw x402 terms to sign against, andawait client.prices()reads the live price of every tool so a caller can budget before it starts.npx xactions-edge profile nasareads X from a terminal with nothing installed, andnpx xactions-edge video <url> --save clip.mp4saves the file. See packages/xactions-edge.- Single-post and thread reads at the edge.
src/edge/postReader.jsnormalises a post across two independent rails onto one shape: text (including long-form past 280 characters), author, every public metric including views and bookmarks, media with direct URLs and the full video variant ladder, entities, and the quoted post. Threads walk up to the root exactly, at any age, then continue down through the author's own replies, and reporttruncated: truerather than implying a thread ended where the public timeline stops.
Security
- Every AI endpoint is now priced or explicitly free, and a check keeps it that way. 101 endpoints were served without payment while their siblings were charged, and 66 prices pointed at routes that do not exist and answered 404 to anyone who paid attention to the catalogue. Neither is visible by reading either file alone, which is why they drifted.
npm run check:x402reconciles the price table against the routes mounted under/api/ai, and fails on a price with no route, a route with neither a price nor aFREE_OPERATIONSdeclaration, or a free declaration for a route that is gone. It runs innpm run docs:checkand innpm test. The rule behind the prices: this API charges for X data and X actions, and does not charge you to manage your relationship with the API itself, sobilling:*,webhooks:*andaction:validate-sessionstay free with a written reason. - A three-segment route could not be priced at all. Every gate derived the operation from the first two path segments, so
/api/ai/monitor/alert/new-followersresolved tomonitor:alert, which has no price, and the endpoint was free no matter what the table said.operationForPath()andpathForOperation()in the config are now the one definition, used by the Express middleware, the Worker gate and the serverless gate alike. - The Cloudflare Worker treated any
X-PAYMENTheader as proof of payment.worker/index.jspassed a request carrying one straight through to the origin without decoding it or asking a facilitator, which would have made every priced endpoint free to anyone sendingX-PAYMENT: x. It was not the live deployment (xactions.app runs the Pages Functions path, which verifies correctly), butwrangler.tomlnames that file asmain, so one deploy would have published it. The gate now decodes the payload, matches the chain against the terms it published, verifies with the facilitator, and refuses when the facilitator rejects the payment or cannot be reached. - The x402 client signed whatever a server asked for.
createX402Clientread the amount and recipient out of the 402 response and signed, with no ceiling, budget, or allowlist: one hostile endpoint could drain the wallet. It now enforces a per-call limit (X402_MAX_PRICE_USD, default $1), a session budget (X402_MAX_TOTAL_USD, default $10), and an optional payee allowlist (X402_ALLOWED_PAYEES), and refuses a 402 whose amount it cannot parse. - A Solana-only deployment advertised Base terms payable to an empty address. The Express middleware registers the EVM scheme only, but an operator who set
X402_PAY_TO_ADDRESS_SOLANAalone passed validation and published{ network: "eip155:8453", payTo: "" }on every route. It now refuses to initialize rather than serve unpayable terms, and names the two ways to fix it. /api/ai/healthand/api/ai/pricingadvertised eleven chains the server rejects. Every route offers only the configured network; the documents now say so.- Registration of the EVM
exactscheme against Solana network ids is gone, the development bypass announces itself with anX-Payment-Bypassedheader instead of failing open silently, a failed middleware initialization is retried after a minute instead of answering 503 until restart, andapi/serverless.jsno longer tells agents "Payment accepted" for a payment it never verified.
Added
- docs/audits/2026-08-28-x402-audit.md — the full audit: all four payment paths, what each one verifies, the nine findings with reproductions, and what is worth keeping.
tests/x402-routes.test.jsandscripts/check-x402-routes.mjs— the price-to-route reconciliation, in the test suite and asnpm run check:x402.tests/x402-gate.test.js— 14 tests against the real gate and the real client, with the facilitator stubbed at the fetch boundary. The existing x402 suite tests a mock middleware defined inside its own test file, and its "valid payment" case asserts that0xMockSignaturereturns 200, so it encoded the bypass as correct behaviour.writer:commentis priced like every other writer operation.
Fixed
/api/ai/alert/new-followersfinally resolves. The/alertmount exists for backward compatibility with that path, but the handler was declared as/alert/new-followers, so the mount produced/api/ai/alert/alert/new-followersand the documented path never existed.- The x402 client no longer loads a signing library it may never use. viem is built on the first payment instead of at construction, so creating a client is instant and the spend limits hold whether or not a wallet was configured.
- The old x402 suite no longer asserts that a fake signature is accepted. Its "With valid payment" block attached
0xMockSignatureand expected a 200 from the mock middleware defined in the same file, which encoded the header-is-payment bypass as correct behaviour. Acceptance is tested against the real gates intests/x402-gate.test.js; what remains here is a contract test for the 402 document. npm run docs:checkno longer fails on scratch files.scripts/audit-docs.mjswalkedtmp/, which is gitignored, so a working note left there turned the docs gate red for everyone.
Added
- Sweeps run over any feed, not just a profile.
scripts/engageProfile.jsnow recognises a profile, its replies tab, search results, a list, a hashtag, and your home timeline, and keeps separate progress for each. It takes the permalink from the post's timestamp rather than the first/status/link in the row, so a post that quotes another post is no longer at risk of having the quoted post engaged instead. Author allow and block lists, keyword and skip-keyword matching, like floors and ceilings, and a skip-verified toggle keep a mixed-feed sweep aimed; your own posts are never engaged. XEngage.inspect(). Run it in the console and every post on screen comes back with eithereligible: trueor the exact filter that rejected it. A sweep that engages nothing used to be silent about why.x_engageMCP tool. Agents can run the same sweep: profile, search, or list, with the same filters and either templates or an LLM brief. It defaults todryRun: true, is registered as a write tool so the approval gate covers it, and is charged against the daily action caps once per enabled action class.xactions engage --searchand--list, plus--from,--skip-user,--keyword,--skip-keyword,--min-likes, and--max-likes. A run that engages nothing now prints a counted breakdown of why every post was skipped.xactions/engagemodule. The sweep engine (resolveSource,collectTweets,runEngage, per-feed state) is importable, and is the single implementation behind the CLI and the MCP tool, so they share progress files and cannot double-engage a post.
Changed
- Action caps can charge several classes for one call.
resolveActionChargemay now return a list, so a sweep that likes and replies draws on both budgets instead of only the first.
Changed
- The endpoint table and the browser fingerprint refresh themselves now, from x.com's own bundles. Both were hand-maintained lists, so both were only ever as current as the last person who remembered.
npm run sync:endpointsreads fa0311/TwitterInternalAPIDocument (MIT), whose bot statically analyses x.com's JavaScript once a day, and regeneratessrc/scrapers/twitter/http/x-endpoints.generated.jswith all 325 GraphQL operations, the 61 feature switches and the values x.com's own client sends for them, and the v1.1 REST dispatch table;endpoints.jskeeps the hand-decided half (which operations XActions tracks,QUERY_ID_PINS, request-variable shapes, rate limits) and buildsGRAPHQLfrom the generated data, so every existing export keeps working and runtime discovery still wins at runtime. The first live run confirmed all 43 pinned IDs still match x.com, and found the feature defaults had drifted: 12 switches missing, 5 sent with the wrong value, and 4 field toggles never sent at all. An operation XActions has no wrapper for now resolves anyway (resolveGraphQL('BirdwatchFetchNotes')for Community Notes), withoperationFeatures()giving the exact switches that operation declares.npm run sync:user-agentsdoes the same for the browser pool from fa0311/latest-user-agent (MIT): Chrome 120 and Firefox 121, roughly two years stale, became Chrome 151, Edge 151 and Firefox 153 across Windows, macOS and Linux, each with theSec-CH-UAclient hints that agree with it. The default also changed from a random string per request to one consistent profile per session, because a client that claims three different browsers from one IP inside one cookie jar is a stronger tell than one slightly old string; rotation stays available per call, per process, or throughXACTIONS_ROTATE_USER_AGENT=1. Both syncs record the upstream commit and fetch time, so a "last verified" claim has a source, and both have a--checkmode that fails when the committed table has fallen behind. Docs: docs/scraping-infrastructure.md.
Fixed
A web page could drive your X account through the MCP HTTP transport.
xactions-mcp --httpvalidated a bearer token but never theHostheader, and binding to 127.0.0.1 does not keep a browser out: any page you visit can POST tohttp://127.0.0.1:8787/mcp, and DNS rebinding lets a hostile hostname resolve there. The MCP spec requires validating Host for exactly this reason and the SDK already shipped the check, so it now runs before anything reads a request body. Verified: a request carryingHost: evil.example.comis refused with 403Invalid Host, while a normal loopback request is unaffected.Every MCP registry install landed unauthenticated.
server.json, and therefore the public MCP Registry listing and the generatedsmithery.yaml, advertisedX_COOKIES,X_USERNAMEandX_PASSWORD. The server reads none of those; it readsXACTIONS_SESSION_COOKIEandXACTIONS_CSRF_TOKEN. Anyone following the registry set three variables that did nothing and got a guest-tier server with no explanation. The manifest now declares what the code reads, andnode scripts/sync-registry.mjs --checkfails if a manifest ever advertises an environment variable that nothing undersrc/reads.xactions skillsdid not exist for anyone who installed from npm.package.json'sfileslist shippedsrc,typesand one config file, so theskills/catalogue and.claude-plugin/never reached the published tarball, andxactions skills listansweredunknown commandwhile the docs said it worked from a global install. Both directories now ship, along withTHIRD-PARTY-NOTICES.md.CI now runs the lint step its own header always promised, on every supported Node. The workflow was titled "Tests + Lint" and had no lint job, so
npm run lintonly ever ran when someone remembered locally. Tests also ran on Node 20 alone whileenginesclaims>=20, which makes 22 and 24 a claim rather than a guarantee; they now run on all three. Coverage was configured invitest.config.jsbut@vitest/coverage-v8was never installed, so--coveragecould not run at all: it is installed,npm run test:coverageexists, and CI reports the number (33.18 percent today) rather than gating on it, so the untested areas are visible instead of invisible.npm install xactionsno longer downloads about 413 MB it never uses. Remotion and its@rspack/corebundler sat independencies, but they build the promo videos undersite/video/, a directorypackage.json'sfileslist has never shipped, so every CLI user pulled 217 MB of measured bundler toolchain for code they do not receive. They are devDependencies now.googleapis(196 MB) moved to an optional peer dependency, which is what the google-sheets plugin already treated it as: it lazy-imports the module and prints "googleapis is not installed. Run: npm install googleapis" when it is absent, so the plugin behaves exactly as before for anyone who wants it. Nothing changes for the API server: Prisma, Bull, Redis, Socket.io, Stripe and Nodemailer stay independenciesbecause the Docker image installs with--omit=devand runs them.Long-running AI operations could never be polled, and four route groups were open to anyone. A new contract test (
tests/api/contract.test.js) boots the real Express app and walks every path in the OpenAPI spec. It found that twenty handlers underapi/routes/ai/importedgetJobStatusfrom the job queue, which never exported it, so every status poll threwgetJobStatus is not a function;getJobis the function they meant and is now exported under both names. It also found/api/workflows,/api/streams,/api/scheduleand/api/notificationsanswering without a token while/api/crmand/api/automationsrequire one, which on a public deployment let anyone list and trigger the owner's automations; all four now carry the same authentication. Finally,GET /api/twitter/status,GET /api/admin/statsandGET /api/notificationswere documented in the spec but never implemented, and now report real state: whether a saved X session exists, instance uptime and payment totals, and the configured notification channels with recent signed webhook deliveries.The video downloader on xactions.app works again.
POST /api/video/extractanswered a bare405with an empty body, so xactions.app/video could not extract anything. The cause was routing, not the extractor:dashboard/_redirectsforwarded/api/*to a Node backend on another origin, and Cloudflare Pages accepts a 200-rewrite only when it points at a relative path, so the rule was dropped at build time and every API request fell through to the static asset handler, which rejects any non-GET method. The three video endpoints now run as Cloudflare Pages Functions (functions/api/video/) alongside the site, with no backend, no database, and no browser:extractreturns the full mp4 ladder (360p through 4K, best first) with thumbnail, duration and text;downloadstreams the file back through xactions.app so the browser saves it with a real filename;extract-formkeeps the no-JavaScript path working. Extraction reads X's public syndication endpoint first and falls back to fxtwitter, with the guest-token GraphQL lane available whenTWITTER_BEARER_TOKENis set. Results are cached at the edge for an hour per tweet./api/healthnow answers for real (the/statuspage was reading a 404 as "degraded"), and every remaining/api/*route returns a JSON 503 naming what it needs instead of the site's HTML 404 page. Docs: functions/README.md.
Changed
- One extraction module for both surfaces.
src/video/edgeExtractor.jsholds the credential-free lanes, andapi/services/videoExtractor.jsnow imports them and keeps only its Puppeteer last resort, which removed 350 lines of duplicated parsing and fixed two bugs the copy had drifted into: a GraphQL query ID X rotated away from, and an fxtwitter parser that read one quality when the response carries the whole ladder. HLS playlists are filtered out of the variant list, since a.m3u8cannot be saved as a video file.
Added
- Installable by any Agent Skills installer, plus a CLI lane for agents and a
.mcpbbundle for Claude Desktop. The 49 skills already followed the Agent Skills spec except for one:a2a-multi-agentshipped with no frontmatter at all, so every third-party installer silently dropped it and resolved 48. It hasname,description,licenseandcompatibilitynow, andnpx skills add nirholas/XActionsinstalls the full catalogue (references/included) into whichever agent directories a machine has, alongside the existingxactions skills install. A rewritten root AGENTS.md tells an agent when to shell out instead of loading the MCP server: the tool list is 150 tools and about 60 KB of JSON on connect, which is a fair price for a long session or a write and a poor one for one lookup, so the file carries the decision table plus verified recipes for profile, tweets, search, followers, non-followers and analyze with the exact--compactand--fieldsflags (and the fact that--compactwins over--jsonwhen both are passed). Andnode scripts/build-mcpb.mjsbuildsxactions-<version>.mcpb, a bundle a user drags onto Claude Desktop > Settings > Extensions: the manifest prompts for the session cookie, the tool groups and the approval gate at install time, so nothing is typed into a JSON config. The build validates the manifest against the MCPB schema, then unpacks what it packed and starts the server from it, so a bundle that cannot launch fails at build time;.github/workflows/release-mcpb.ymlattaches it with a SHA-256 to everyv*tag. Docs: docs/skills.md, docs/mcp-setup.md. - Real-time events off x.com's own pipeline, instead of polling for them.
createLivePipeline()(xactions/streaming) holds one long-lived connection tohttps://api.x.com/live_pipeline/eventsopen and pushes tweet engagement counters, DM updates and typing indicators as they happen, in under a second, with no poll interval. Topics are built withTopic.tweetEngagement(id),Topic.dmUpdate(conversationId)andTopic.dmTyping(conversationId), andsubscribe()/unsubscribe()change what a running session watches without dropping the connection. Frames are normalised into{ type, topic, payload, receivedAt, raw }withtypeone ofengagement,dm,typing,config,unknown, so an unmodelled frame key surfaces with its raw body rather than disappearing. It rides out trouble: a silence watchdog tied to the heartbeat the server advertises, subscriptions re-asserted before their TTL expires, reconnect with exponential backoff and jitter that re-subscribes every live topic, and aclose()that resolves once nothing is left running. A session with noauth_tokenfails with a typedLivePipelineAuthErrorbefore a request is sent, because this endpoint rejects guest tokens.createStream({ transport: 'live', topics, cookies })runs a managed stream over it and emitsstream:engagement/stream:dm/stream:typingto Socket.IO clients;transportdefaults topoll, and any live stream that cannot connect, or gives up reconnecting, logs the reason once and falls back to polling. No new dependency: the transport is chunked HTTP, not a WebSocket, which is what x.com actually serves. Docs: docs/streaming.md. - Sweep an X search: delete, like, repost, or reply to every result. X gives you no bulk tool for "remove every reply I ever sent this account", but its search finds them (
from:you @someone).scripts/searchSweep.jstakes any search result list and applies one action to all of it, from the console, with a floating panel: a query builder that submits through X's own search box so the page never reloads out from under the script, a dry run, age / engagement / keyword filters that protect what you want to keep, DOM-verified actions, a two-minute rest every 25 posts, back-off after four failures, and a JSON export. Two things about X search it works around: results come back a slice at a time, so it re-runs the query for several passes and stops once two in a row find nothing; and the search index lags deletions by minutes, so every id it acts on is remembered inlocalStorageand skipped later. Deletion is refused on any post that is not yours, twice over, and delete is exclusive with the engagement actions. Replies come from templates or from an LLM given a one-line brief, the same two lanes asengageProfile.js. Docs: docs/search-sweep.md. - Multi-account rotation and resumable scrapes for the HTTP scraper.
createAccountPool()keeps any number of X sessions in~/.xactions/accounts.db(SQLite), tracks each account's rate-limit window per GraphQL operation from thex-rate-limit-remaining/x-rate-limit-resetheaders, and leases the least-recently-used account that can serve;createPooledClient(pool)is a drop-in forTwitterHttpClientthat retries a call on the next account after a 429 or a spent window and locks an account on 401/403. Accounts import from any cookie formatxactions login --cookies-fileaccepts, and each can carry its own proxy, which now actually routes requests (through undici'sProxyAgent).createCheckpoint({ key })saves{ cursor, count }atomically after every page; pass it as thecheckpointoption ofscrapeFollowers,scrapeFollowing,scrapeTweets,searchTweets, and the other paginated scrapers, and an interrupted--limit 50000run picks up at the saved cursor with the remaining budget. Docs: docs/scraping-infrastructure.md. xactions drafts,xactions archive, and two newdoctorlines.drafts list|show|approve|discard|clearputs the MCP approval queue (XACTIONS_MCP_REQUIRE_APPROVAL) in the terminal, withapprove --alland--jsonon every sub-command; approval replays the call through the server's ownexecuteTool, which is loaded only when needed.archive summary|export|migrate <zip-or-folder>reads the official X data export straight from disk: a counts-and-hashtags report, an export into thexactions exportlayout (--formats json,csv,md,html), and a Bluesky or Mastodon migration from the zip (dry run unless--execute), all with a progress spinner that goes quiet under--json.doctornow reports the GraphQL query-ID cache (count, age, stale warning) with--refresh-idsto rediscover them from x.com, and the multi-account pool (available, rate limited, locked) whenaccounts.dbexists.- Ask XActions. xactions.app/ask answers questions about the toolkit in plain language: "how do I unfollow all users" gets the exact script, where to paste it, and its safety features, with clickable sources. Answers are grounded in a retrieval index built from the docs, the 49 skills, the tutorials, every browser script's header, and the dashboard pages (
npm run ask:index, checked bynpm run docs:check), plus a live GitHub issue search, and stream through a chain of free LLM lanes: Groq, Cerebras, OpenRouter free models, xAI, Gemini, Mistral, and Cloudflare Workers AI when a key is configured, and LLM7, Pollinations, and OVH with no key at all. The model picker lets you lead with your own key, kept in your browser.POST /api/ask(Server-Sent Events) runs at the Cloudflare edge, in Express, and in the Vercel serverless entry; the page falls back to running the same engine in the browser when no API answers. History is local to the browser and nothing is stored server-side. Docs: docs/ask.md. - Pinned GraphQL query IDs refreshed against x.com (2026-08-27). The offline fallback table in
src/scrapers/twitter/http/endpoints.jsandsrc/client/api/graphqlQueries.jsnow carries the IDs x.com serves today, so a fresh install works before the first auto-discovery run.UserByScreenName,TweetDetail,SearchTimeline,Followers,Following,CreateTweetand 16 more had rotated. xactions/portabilitysubpath export soimport { importTwitterArchive } from 'xactions/portability'resolves from an npm install.- Competitive and structural audit at docs/audits/2026-08-27-competitive-and-structural-audit.md: 24 comparable projects surveyed, every gap listed by area, and the repo-health backlog with what this release already closed.
xactions login --cookies-file <path>andxactions login --from-browser [browser]. Login no longer means pastingauth_tokenout of DevTools.--cookies-fileimports a session from any common export: Netscapecookies.txt(including#HttpOnly_prefixes), Cookie-Editor / EditThisCookie JSON arrays, Playwright / PuppeteerstorageState, or a rawauth_token=...; ct0=...header string.--from-browserreads x.com cookies straight out of a locally installed browser: Firefox on every platform, and Chromium-family browsers (chrome, chromium, brave, edge, arc) on Linux (default keyring-less key) and macOS (via the Keychain). Unsupported combinations (Windows Chromium, or a Linux Chromium sealed by the system keyring) print the exact--cookies-fileexport path to use instead. Both paths extractauth_token+ct0(and any other x.com cookies) and save through the same session storage every later command reads. New reusable modulesrc/client/auth/cookieImport.jsexportsparseCookieInput,detectCookieFormat, andreadBrowserCookiesfrom the package root.- Every request the HTTP scraper sends is now signed the way x.com's own web client signs it. x.com attaches an
x-client-transaction-idheader to every GraphQL and internal REST call; XActions generated none, which is one of the cheapest ways to tell a scraper from a browser.src/scrapers/twitter/http/transactionId.jsbuilds the value per request from the HTTP method, the request path, the clock, and the{verification key, animation key}pair the x.com page itself carries, andTwitterHttpClientsigns inside its retry loop so no value is ever replayed.GuestTokenManager.getHeaders({ method, path })signs too. The keys are extracted once and cached under$XACTIONS_HOME/transaction-keys.jsonfor 24 hours, so signing costs one SHA-256 after the first call; a cold cache takes a published pair dictionary (about 200 ms, no bundle parsing) and falls back to parsing x.com's live page andondemand.schunk (about 350 ms) through the same webpack manifest parser query-ID discovery already uses. Signing never blocks a request: every failure path leaves the request unsigned rather than throwing, and a failed lookup is remembered for ten minutes. Off withXACTIONS_TRANSACTION_ID=0ornew TwitterHttpClient({ transactionId: false }). Measured on the guest tier the header makes no observable difference (200and the same 150-per-window budget signed or not); the reported benefit is to how long an authenticated session survives, which only an authenticated multi-day run can confirm. Also fixed while wiring it:GuestTokenManager.activate()sent no User-Agent, andguest/activate.jsonanswers such a request with a misleading404 Sorry, that page does not exist, so the HTTP scraper's guest lane could not get a token at all. Docs: docs/scraping-infrastructure.md.
Added
xactions skillsand--compact.xactions skills list|show|install|uninstallcopies the bundled SKILL.md catalogue into the place your agent reads:~/.claude/skills/or./.claude/skills/(Claude Code),./.cursor/rules/*.mdc(Cursor),~/.codex/skills/plus a managedAGENTS.mdblock (Codex), or./.windsurf/rules/(Windsurf), idempotently and with a per-skill report;doctornow counts installs per target. The new global--compactflag prints the read commands (profile,tweets,search,thread,followers,following,non-followers,hashtag,media,analyze) as one tab-separatedkey=valuerecord per line with only the essential fields, and--fields id,text,likespicks columns; both--jsonand--compactkeep the spinner silent when stdout is a pipe.- Sweep a whole profile: like, repost, and reply to every post. Doing this by hand on a hundred-post account is twenty minutes of clicking.
scripts/engageProfile.jsdoes it from the console with a floating panel: per-action toggles, speed presets, a 90-second rest every 20 posts, automatic back-off after three failures, DOM-verified actions, progress saved per profile so a reload resumes, undo for likes and reposts, and a JSON export. Replies come from templates ({author},{name}, never the same one twice in a row) or from an LLM given a one-line brief. Grok works straight from the console because x.com's CSP allowsapi.x.ai; every other provider goes through the extension. xactions engage <username>. The same sweep from the terminal over the HTTP client:--like --repost --comment,--promptfor AI replies with any provider (OpenRouter, OpenAI, xAI, Anthropic, Ollama, custom URL),--template/--templates-file,--since,--dry-run,--json, and resumable progress in~/.xactions/engage/. Rate limits pause the run and continue.- Extension LLM relay. The browser extension now answers
LLM_REQUESTmessages from page scripts by calling the provider from its service worker, which is not bound by x.com's Content-Security-Policy. Host permissions were added for OpenRouter, OpenAI, Anthropic, xAI, and localhost. POST /api/ai/writer/commentandcreateCommentGenerator()inxactions/ai: prompt-driven reply generation with no voice profile, shared by the CLI, the API, and the extension. Replies are sanitised and regenerated once when they open with boilerplate.
Added
- MCP server: tool filtering, Streamable HTTP, and an approval gate.
XACTIONS_MCP_TOOLS/XACTIONS_MCP_EXCLUDE(or--tools/--exclude) trim the 149-tool list by name, group (read,write,dm,analytics, ...), orx_get_*pattern, and a filtered tool is refused with the group to enable rather than a generic error.xactions-mcp --http [--port 8787] [--host 127.0.0.1]serves the MCP Streamable HTTP transport on/mcp, with bearer auth viaXACTIONS_MCP_TOKEN; stdio stays the default.XACTIONS_MCP_REQUIRE_APPROVAL=1(or--require-approval) holds every write tool as a draft in~/.xactions/mcp-drafts.jsoninstead of running it, and the always-availablex_list_drafts,x_draft_status,x_approve_draft, andx_discard_drafttools manage the queue. Also fixed:x_list_platformswas declared but never handled and now lists every scraper platform with its capabilities and the adapter registry; 32 tool descriptions that were too short to guide a model were rewritten; and a coverage test now fails the build if a declared tool has no handler. Docs: MCP setup guide.
Added
- GraphQL query IDs refresh themselves from x.com's bundles. The HTTP scraper pinned every GraphQL query ID to a value copied from twikit, and X rotates those IDs with each web release; on 2026-08-27, 22 of the 26 pinned IDs were stale, including
UserByScreenName,TweetDetailandSearchTimeline.src/scrapers/twitter/http/queryIds.jsnow loads an x.com page, reads its webpack chunk manifest, downloadsmainplus the feature chunks that carry our operations, and extracts the live{queryId, operationName}pairs into~/.xactions/query-ids.json(honoursXACTIONS_HOME).TwitterHttpClientprefers the cached ID over the pinned one, refreshes once and retries when a call answers404or a400naming the persisted query, and refreshes in the background when the cache is older than 24 hours. The pinned table stays as the offline fallback.queryIdStatus()exposes{cached, fetchedAt, count}forxactions doctor.
Added
- Import your official X archive.
importTwitterArchive(path)insrc/portabilityreads the data export X sends you (the GDPR zip, or the folder you extracted it to) and returns normalised tweets, likes, following, followers, blocks, mutes, DMs grouped by conversation, lists, account, profile and media references. Zips are streamed entry by entry withyauzlso multi-gigabyte archives never have to fit in memory; multi-part files (tweets-part1.js) are merged in order.summarizeArchiveandformatArchiveReportproduce a counts, date range, busiest year and top hashtag report;exportArchivewrites the import in the same JSON/CSV/Markdown/HTML layoutexportproduces, andmigrateacceptssource: 'twitterArchive'to go straight from the zip to a Bluesky or Mastodon dry run. See docs/portability.md.
[3.5.0] - 2026-08-04
Added
A CLI you can find your way around
- Commands are grouped by task. Running
xactionsprinted fifty-three commands in one flat alphabetical list, which tells a newcomer nothing about where to start and an experienced user nothing about what else exists. The root help now sorts them into eight task-shaped groups (Start here, Read an account, Followers and audience, Search and monitor, Write and grow, Automate, Move data, Low level) with an examples block and pointers toquickstart, per-command help, and completions. The grouping is reconciled against the live command tree, so a newly registered command still appears (under "More") and a group entry naming a deleted command is caught by a test rather than shipped. xactions quickstart. A guided first run that reads what you already have configured and prints the three commands that will produce a result on your machine, then the directions worth exploring next.--jsonreports the detected setup state (config directory, whether a session is saved, guest or session tier) for scripts and CI.xactions completion bash|zsh|fish. Tab completion for every command, sub-command, and flag, generated from the live Commander tree rather than a hand-maintained list, so it stays correct as commands are added. Descriptions are escaped per shell: an unescaped colon in zsh'sname:descriptionformat silently truncated half the descriptions, and an apostrophe would have terminated the quoted string early.
Changed
--jsonnow works on every read command. It was accepted byprofileandanalyzeonly;tweets,followers,following,non-followers,search,hashtag,thread, andmediarejected it witherror: unknown option '--json'despite the docs promising it worked everywhere. All eight now accept it.--jsonoutranks--outputand--google-sheets. Passing both used to write the file and print nothing, so a script that piped--jsonwhile a config supplied--outputsilently produced no output.--jsonis now an explicit "give me the data on stdout" and wins.
Documentation
- tutorial 05: Read any account like an analyst. Reading an account report properly (median versus mean, engagement per view versus per follower, lifetime versus recent cadence), comparing accounts, follower overlap, and tracking a series over time. Runs entirely on the guest tier.
- tutorial 06: Everything is JSON. XActions as a pipeline component: the real field names, jq filters, chaining commands, exit codes in cron and CI, and where to switch from shell to the Node client. Every snippet was run against live data before it was written down.
- CLI reference corrected. It carried a hardcoded
Version 3.0.0, claimed an X account was required (most reads need none), and steered new users toxactions login(paste cookies out of DevTools) rather thanxactions connect(log in through a real browser). It now documents the command groups,quickstart, andcompletion.
Security
packages/xactions-mcpraised off the vulnerable 3.4.4 dependency line, clearing the outstandingnpm auditadvisories. Released as 3.4.8 and carried into this version.
Fixed
Public reads work again, without a browser
Scraper.getProfile()returnedHTTP 404 {"message":"Query not found"}for every call. The repo carried two independent tables of X GraphQL query IDs, and 11 of the client's had gone stale while the shared map stayed current. Query IDs now have exactly one home (src/scrapers/twitter/http/endpoints.js), and a test fails if a second copy reappears.- The HTTP client could not obtain a guest token at all. X answers a request with no browser
User-Agentwith a misleadingHTTP 404 "Sorry, that page does not exist", which reads like a removed endpoint rather than a rejected client. Every request the client makes now carries one. xactions profileprintedFollowers: 0and exited 0. X stopped serving profile and timeline content to logged-out browsers, so the Puppeteer scrape found an empty page and the CLI reported the nothing it found as success.profile,tweets,followers,following,search, andnon-followersnow use the HTTP client, which is roughly an order of magnitude faster and needs no Chromium download.xactions non-followersreported your entire following list as non-followers. It filtered on afollowsBackflag the GraphQL follow lists do not carry, so the predicate matched everything. It now diffs the follower and following lists.- The MCP server answered AI agents with every field set to
null(issue #27).x_get_profileandx_get_tweetshad the same browser-path problem, which is worse in an agent context: an assistant cannot tell an empty result from a missing one, so it reports confidently wrong answers. Both now prefer the HTTP client and fall back to the browser. - Unauthenticated failures now say what to do. X restricts search, followers, likes, bookmarks, and DMs to logged-in sessions and answers a guest request with a bare
404. That surfaced asHTTP 404: Not Found, which sent people looking for a bug in XActions. It now raisesAUTH_REQUIREDnaming the endpoint and the fix. Errors also carryendpoint,httpStatus, andrateLimitReset, which positional constructor calls had been silently dropping. xactions loginnow capturesct0as well asauth_token. Without the CSRF token X treats the session as logged out, so session-tier endpoints kept failing after an apparently successful login.
Paid API
- Every
/api/ai/*endpoint returned500instead of402.@x402/corev2 moved payment terms behind anacceptskey; the flat v1 route shape made the SDK throw during route validation on every request. All 95 payment tests now pass against a live server.
Cross-platform
- Every Bluesky scrape threw
Cannot read properties of undefined (reading '_client'). The XRPC helper detached the SDK method from its namespace before calling it. Profiles, posts, and follower lists all work again. - Mastodon bios and posts contained raw HTML entities (
&,') after tag stripping.
Tests
tests/mcp/server.test.jsnever ran. It importeddescribe/itfromnode:test, which registers with Node's runner rather than Vitest, so the file reported "No test suite found" and all 144 tool definitions went unvalidated.tests/x402-integration.test.jsfailed for every contributor. Its skip condition was inverted: it skipped in CI and ran on laptops, so a clean checkout produced 21 redECONNREFUSEDfailures. It now probes for a server and skips when there is not one.- Two A2A tests asserted a hardcoded
3.1.0against the real version.
Added
Examples and tutorials
examples/— 8 runnable programs, each verified against the live API before release: profile lookup, timeline analysis, offline sentiment reports, a three-network comparison, CSV export, non-follower analysis, a keyword monitor, and an MCP client that drives the server over stdio.tutorials/— four guided walkthroughs: first scrape, MCP with Claude, cleaning up a following list, and building a brand monitor.
Documentation that cannot rot
npm run docs:checkfails on a dead relative link, a dead heading anchor, a referenced script that no longer exists, a stale version claim, a wrong MCP tool count, or a documented CLI command that does not exist. It is dependency-free and runs as its own CI job. It found 87 dead links, 25 stale version and tool-count claims, and 11 invented CLI commands on its first run; all are fixed.npm run docs:scriptsregenerates the browser-script catalog from the scripts themselves, so a 93-entry list cannot drift.npm run check:endpointsprobes every GraphQL endpoint and distinguishes a rotated query ID from an endpoint that merely needs a session, which is the failure that silently broke the client above.- Five docs the index had promised but never had: browser-scripts, configuration, database, skills, troubleshooting.
40 new browser tools, and the Command Center now covers 108
- Added a full wave of browser-console tools so the toolkit covers essentially every X action, and folded them all into the Command Center (now 108 tools across 11 categories, with two new categories: Create & Post and Lists):
- Create & Post: post a tweet, post a thread, schedule a post, create a poll, quote tweet, pin/unpin.
- Posting actions: auto-repost, auto-reply to mentions, vote in polls, and bulk-delete your own posts by age/keyword/engagement (dry-run by default).
- Scrape everything: followers, following, post likers, reposters + quote-tweeters, a user's likes, search results, a hashtag, a List, profile media, tweet replies, notifications, DMs, and Spaces. Each exports JSON + CSV.
- Lists: create/rename/delete a List, add users to a List, follow all List members.
- DMs & account: bulk/welcome DM, auto-reply DMs, edit full profile, privacy/settings toggles, manage muted words, notification cleaner.
- Grow & moderate: follow-back everyone, remove a follower (dry-run), block-list import/export + block-chaining (dry-run).
- Diagnostics: shadowban checker, tweet performance ranking, sentiment analyzer, audience overlap, trending monitor.
- Every new tool follows the same conventions as the rest: real
data-testidDOM automation (no fragile hardcoded API IDs), randomized rate limiting, awindow.stop<Tool>()switch on long loops, page guards, and JSON/CSV export on the scrapers. Bulk/irreversible tools default to a dry run. - Docs: each tool gets its own page at
/scripts, plus a Command Center tutorial (docs/examples/tutorials/command-center-tutorial.md).
Build fix
- Fixed a Command Center bundler bug where a tool containing a
$-anchored regex template literal (e.g.`/${x}/?$`) corrupted the generated file,String.replacewas interpreting the `$`` sequence as a special replacement pattern. The injector now uses a function replacer.
⚡ XActions Command Center: one script for every tool
- New
scripts/twitter/xactions-command-center.js: paste one script into the browser console and get a searchable command palette of all 68 browser tools, no more hunting for the right file. Search and arrow-key navigation, nine categories (Scrape, Analytics, Grow, Engage, Clean Up, Moderate, Communities, Profile, Utilities), favorites and recents, and a per-tool options form (rendered from each tool's own config, with an "Edit as JSON" mode) so you never edit source by hand. - Safety built in: every tool is tagged Safe / Writes / Bulk-irreversible, destructive tools require a second confirming click and show a warning, the palette tells you which page each tool expects (and warns if you're not on it), and a run dock lets you Stop long-running tools individually or all at once.
- Works within x.com's strict CSP: it bundles every tool directly (no remote fetch, no
eval). Reopen anytime with the floating ⚡ button or Cmd/Ctrl+K. - Generated by a new build (
scripts/build-toolkit.mjs+_command-center-shell.js) that stays in sync with the tool files and fails the build on any drift. Docs:scripts/twitter/README-command-center.md.
Fixed
Browser scripts: re-paste crash fixed repo-wide
- Converted the remaining 40 tools that still declared a top-level
const CONFIGtovar CONFIG. Pasting a script a second time into the same DevTools tab threwIdentifier 'CONFIG' has already been declaredand the script never ran, breaking the documented "run it again later" workflow. This finishes the fix an earlier pass started on part of the collection; every tool now re-pastes cleanly.
[3.4.0] - 2026-07-20
Fixed
Hosted API server crash on boot
api/routes/teams.jsdefault-importedauthMiddlewarefrom a module that only has named exports — in ESM that's a hardSyntaxErrorat startup, not a warning, so the hosted API server crashed before it could ever answer a health check. This is why xactions.app's dashboard pages (graph, analytics, unfollowers, admin, price-correlation) were showing "backend offline." Fixed and verified with a full local Docker build + boot against a real Postgres container: server starts cleanly, migrations run,/api/health, register, login, and authenticated reads all respond correctly. Swept the wholeapi/,src/, andworker/tree for the same class of bug — no other instances found.
Browser console scripts: 64 files audited
- Every script in
scripts/twitter/(beyond the two already rewritten) was read end-to-end and fixed where real bugs were found. Highlights: all scripts using top-levelconst CONFIGbroke on re-paste into an already-open DevTools console (aSyntaxError, sinceconst/letbindings persist across console pastes in the same tab) — fixed tovareverywhere. Added consistentwindow.stopX()abort switches to every long-running loop that lacked one. Fixed stale-DOM bugs inmass-unblock.js/mass-unmute.js(cached elements pointing at rows already removed from a virtualized list), a wrong-author-attribution bug on quote-tweets inbookmark-exporter.js, a duplicate-processing risk in the hashtag/location commenters, severalwindow.location.hrefreloads that silently killed the running script mid-workflow, and wired up half-implemented options (filters, reply templates, video quality selection) that were declared but never actually checked.
Added
Google Cloud Run deployment for the hosted API
deploy/gcp/provision-api.sh+deploy/gcp/cloudbuild-api.yaml: one-shot provisioning (Cloud SQL Postgres, Secret Manager, IAM) and build/deploy for thexactions-apiCloud Run service, reusing the existing Memorystore Redis instance instead of standing up new infra.api/services/jobQueue.js's Bull queue now namespaces its Redis keys so it can safely share that instance.
Cloudflare Workers Deployment
- Full-site Cloudflare deploy: one Worker serves the landing page, dashboard, docs, blog, and static assets from Workers static assets, replacing the Vercel deployment
- Edge API in the Worker:
/api/health,/api/ai/health,/api/ai/pricing,/openapi.json,/.well-known/x402, and the x402 402 payment gate for/api/ai/* API_ORIGINproxy: heavy API routes (auth, user, unfollowers, video) forward to the Node backend on Railway/Fly/Docker; a clear 503 with setup instructions when unsetnpm run build:cloudflareassemblesdist-cloudflare/fromsite/,dashboard/,public/, andllms*.txt, mirroring thevercel.jsonroute tablenpm run deploy:cloudflarebuilds and deploys viawrangler deploy
Browser extension install page + extension-first account actions
- New
/extensionpage: what the extension does, a 30-second load-unpacked install guide (Chrome/Edge/Brave/Firefox), all 11 automations, and why it runs locally (your X login never leaves your browser) - Wired into the integrations page, footer, and sitemap
- Hosted service no longer executes X account actions server-side: follow/unfollow/like/reply/post routes return
501pointing to the extension, so the service never custodies your session token or drives your account from a datacenter. Paid reads (scrape, analytics) are unaffected
[3.3.0] - 2026-07-19
Improved
Site-wide visual glow-up (X.com-clone kept)
- Enhanced the shared styling (common.css, components.css, docs.css, the injected sidebar) so ~400 pages level up at once: accent gradient + glow, depth shadows, active-nav gradient pill, glowing buttons, card hover lift, refined badges/tabs/inputs/code, ambient background glow, and load-in motion. Layout and blue identity unchanged.
- Landing page and every app page got the same treatment in their own styles.
Fixed
- App pages (agent, graph, monitor, analytics, thread, video, login, admin, team, unfollowers, price-correlation, and more) now degrade gracefully when the hosted API is offline: designed "backend offline" notices and empty states instead of infinite spinners or console error floods. Stopped runaway polling and socket reconnection. Fixed a broken element id, a stuck loading overlay, and graph's cross-origin CORS calls (now same-origin).
- Docs pages that embedded full script source no longer run 20,000px tall (long code scrolls in a capped box).
- Footer column headings no longer render inline with their first link.
- Repaired every broken documentation cross-link (664 .md links plus repo-file links) and rebuilt the sitemap from 47 stale URLs to 535 real ones.
[3.2.2] - 2026-07-19
Added
xactions.app is live again, on Cloudflare Pages (free)
deploy/cloudflare/: build script +_redirectsdeploying the full site (landing page, dashboard app, docs, tutorials, blog, scripts directory) to Cloudflare Pages, free of charge (the prior Vercel deployment was disabled and the domain has been down)- Live now at the Pages project URL;
xactions.appcustom domain pending the nameserver switch to Cloudflare at the registrar deploy/gcp/(Cloud Run + nginx) kept as a fallback path for environments without Cloudflare access
[3.2.1] - 2026-07-19
Fixed
Browser script audit (103 bugs across 52 files)
- Full audit of every paste-in-console script in
scripts/twitter/; report indocs/audits/2026-07-19-browser-scripts.md - Fatal bugs: 6 scripts killed themselves by navigating mid-run; 3 infinite loops; an action script that liked/followed whatever page was open; blind menu clicks that could trigger unintended actions
- Correctness: quoted-tweet ID misattribution (9 scripts), locale-dependent repost/reply detection (8), K/M/B engagement multiplier and NaN bugs, CSV corruption from unquoted dates, React value-tracker bugs that made update-bio and DM sending silently no-op, wrong-DM-recipient matching, false clipboard success claims
- Reliability: end-of-list stall detection that never fired, missing
videoComponentselectors, unrevoked Blob URLs, setInterval re-entrancy src/cli/index.js:awaitin a non-async SIGINT handler crashed the whole CLI on load
Added
Cloud Run deployment for xactions.app
deploy/gcp/: Dockerfile, nginx config, and Cloud Build pipeline serving the landing page, dashboard, docs, tutorials, and blog with the same clean-URL routing the Vercel deployment had (Vercel deployment is disabled and the domain has been down)
[3.2.0] - 2026-07-19
Added
Scraper Toolbox (browser console)
scripts/twitter/scraper-toolbox.js: interactive on-page control panel for scraping any X timeline (profile, search, list, likes, bookmarks, home)- Start / pause / resume / stop, live progress, draggable panel, settings persisted in localStorage
- Captures X's own GraphQL responses: exact like/repost/reply/view/bookmark counts, full text of long posts, media URLs, language codes; promoted posts skipped
- Live filters applied at export time: keywords (include/exclude), only/skip specific users, min likes/reposts/views, date range, repost/reply/quote/pinned toggles, media, language
- Exports: JSON, CSV, Markdown, TXT, HTML downloads plus clipboard copy (JSON or clear text)
- Console API:
window.XActionsToolbox - Docs:
scripts/twitter/README-scraper-toolbox.md
Fixed
scrape-profile-posts.js (v2.1.0)
- Elapsed time was reported 3x too small (divided by 3000 instead of 1000)
- HTML export table rendered at 300% width; text export separators were 300 chars wide
- Tweet IDs could be attributed to a quoted tweet's URL instead of the post itself
- Pinned posts were counted as reposts; repost/reply detection no longer depends on the English UI
- End-of-timeline detection never triggered when
verbose: false - Video attachments using the newer
videoComponenttestid were not detected
[3.1.0] - 2026-02-25
Added
Plugin System
- Community plugin architecture — create
xactions-plugin-*npm packages - Plugin loader, manager, and template in
src/plugins/ - CLI commands:
xactions plugin install/list/remove - MCP server auto-discovers and registers plugin tools
Real-Time Streaming
- Live event streams for tweets, followers, and mentions via Socket.IO
- Puppeteer-based polling with Redis deduplication and rate limit backoff
- Browser pool management (max 3 concurrent instances)
- MCP tools:
x_stream_start,x_stream_stop,x_stream_list
Workflow Engine
- Declarative JSON automation pipelines with triggers, actions, and conditions
- Cron scheduling, webhook triggers, event-based triggers
- 3 example workflows: competitor monitor, auto-engage keywords, follower growth report
- CLI:
xactions workflow create/run/list - MCP tools:
x_workflow_create,x_workflow_run,x_workflow_list
Cross-Platform Scrapers
- Unified scraper interface:
scrape(platform, type, options) - Bluesky support via AT Protocol (@atproto/api) — no Puppeteer needed
- Mastodon support via public REST API — any instance URL
- Threads support via Puppeteer
- Backward compatible — existing Twitter imports unchanged
Sentiment Analysis & Reputation Monitoring
- Built-in rule-based sentiment analyzer (works offline, zero dependencies)
- Optional LLM mode via OpenRouter for nuanced analysis
- Reputation monitoring with trend detection and anomaly alerts
- Alert delivery via webhook, Socket.IO, or console
- Daily/weekly reputation reports
Account Portability
- Full account export: profile, tweets, followers, following, bookmarks, likes
- Output formats: JSON, CSV, Markdown, self-contained HTML archive viewer
- Export diff tool — compare two snapshots to see changes
- Migration stubs for Bluesky and Mastodon
Social Graph Analysis
- Graph builder crawls N degrees from seed account
- Algorithms: mutual connections, bridge accounts, cluster detection, influence scoring
- Exports to D3.js JSON and Gephi GEXF formats
- Self-contained HTML visualization with force-directed layout
Browser Extension
- Manifest V3 Chrome/Firefox extension
- Popup UI to run automations without console access
- Content script injection, settings persistence, activity badge
Dashboard Enhancements
automations.html— automation control panel with start/stop togglesmonitor.html— real-time activity feed with Chart.js visualizationsworkflows.html— visual workflow builderanalytics.html— sentiment timeline, mention analysis, alert configuration- Full docs site generated at
dashboard/docs/
New API Routes
/api/streams— real-time stream management/api/workflows— workflow CRUD and execution/api/analytics— sentiment analysis and monitoring/api/portability— account export and migration/api/graph— social graph building and analysis/api/automations— automation start/stop control- 15+ additional routes for bookmarks, discovery, engagement, posting, etc.
New Browser Scripts
engagementBooster.js— systematic engagement with target accountssentimentAnalyzer.js— in-browser sentiment scoringshadowbanChecker.js— detect account restrictionsviralTweetDetector.js— find viral content earlyfollowerGrowthTracker.js— track growth over timetweetScheduleOptimizer.js— find best posting timeswelcomeNewFollowers.js— auto-welcome with templatesquoteTweetAutomation.js— strategic quote tweetingthreadComposer.js— multi-tweet thread buildercontentCalendar.js— plan and schedule contentaudienceDemographics.js— analyze follower demographicsaccountHealthMonitor.js— monitor account health signalspinTweetManager.js— manage pinned tweetsbulkDeleteTweets.js— mass delete old tweetsautoReply.js— automated reply with templates
Other
- TypeScript type declarations (
types/index.d.ts) - Docker support (Dockerfile + docker-compose)
- New npm exports:
xactions/streaming,xactions/analytics,xactions/plugins xactions-mcpandxactions-agentbin commands
Changed
- MCP server expanded from ~200 to 140+ registered tools
- Package exports updated for multi-platform scraper paths
- Dependencies updated: vitest 4.x, puppeteer 24.x, added node-cron, better-sqlite3, exceljs
[1.0.0] - 2026-02-11
Added
- Initial release