Skip to content

dorshitrit/x-publisher-private

v0.1.0

Publish original signed text posts to the X account configured locally by the owner.

ABot X Publisher

Publish original text posts to your configured X account from ABot Runtime. The model writes the content; the plugin handles credentials, verifies the account, and appends By ABot AI - runtime to every post.

This is an independently installed Marketplace plugin for ABot Runtime 1.7.0 or later. It is not bundled with the public Runtime or its npm package. The source package keeps private: true to prevent accidental npm publication; installation uses its GitHub repository and the prebuilt src/index.cjs.

The stable plugin ID is x-publisher-private. The historical suffix is retained to preserve existing installations and saved configuration; each installation uses its owner's account. There is no default username or shared credential.

Install from GitHub

When the repository has been published, use its URL in the Runtime's plugin installation flow. Review the selected commit, plugin permissions and bundled files, then choose Install, Configure, and Apply changes. For a reproducible installation, select a reviewed commit or release tag instead of a moving branch. Installation does not run npm, build scripts, or a test post.

If this plugin already exists in the installation, remove the existing copy before installing its reviewed replacement; saved configuration is retained. Two copies exposing x_publish_post cannot coexist, even under different plugin IDs.

Connect the account locally

Use an X app and OAuth 1.0a user tokens belonging to the account you intend to publish from. No hosted OAuth service or model-managed connection is needed.

  1. In X Developer Console, enable OAuth 1.0a / Read and write for the app. No Direct Message permission is needed. If permissions changed after you generated Access Token / Secret, regenerate those user tokens.

  2. In the Runtime Web UI, open Plugins > x-publisher-private > Configure. The same form is available during installation review.

  3. Set expectedUsername to the intended X username without @: 1–15 ASCII letters, digits or underscores. Surrounding whitespace is trimmed and account comparison is case-insensitive. There is no personal-account default.

  4. Enter the four secret fields locally:

    • apiKey: OAuth 1.0a Consumer Key / API Key.
    • apiSecret: Consumer Secret / API Secret.
    • accessToken: the account's OAuth 1.0a Access Token.
    • accessTokenSecret: its Access Token Secret.

    These are not the app-only Bearer Token or OAuth 2.0 Client ID / Client Secret. Do not put values in chat, screenshots, the manifest or source code.

  5. Choose Save changes, then Apply changes when the owning Runtime is idle. Saved UI values load into new Runtime owners through the existing plugin configuration mechanism; no process restart is needed just to reload these locally saved values. Existing owners retain their loaded configuration until Apply succeeds.

  6. Ensure the plugin and capability x_publish_post are enabled. Existing allow/deny rules and Runtime request permission policy still apply.

Installation and loading are allowed before setup. Publishing returns x_not_configured / not_sent until a valid username and all four keys are available. Identity is verified as part of each authorized publication; there is no separate connection-test tool and no automatic test post.

Existing installation: retain the plugin ID and set expectedUsername to your intended account in Configure, then save/apply before posting. Existing environment keys can remain in place. A missing setting blocks publication.

The Runtime stores UI values locally outside this package, shared across dev/prod in the same installation. Secret values are not returned to the UI or the model. An untouched secret retains its current value; replacement and clearing are explicit. Clearing a secret suppresses its environment fallback for this plugin. Removing the plugin retains saved configuration. This is the existing Runtime storage contract, not a plugin-specific secret store.

Existing environment-variable setup

The existing .env.example names remain supported when no local secret override exists: ABOT_X_API_KEY, ABOT_X_API_SECRET, ABOT_X_ACCESS_TOKEN and ABOT_X_ACCESS_TOKEN_SECRET. Put these in the owning host's environment or root .env locally, preserving existing entries. The example is not loaded automatically. Environment changes require restarting the owning process; Apply alone does not reload .env. The username still comes from Configure.

No scheduler or autonomous posting job is installed. Request a deliberate first post only when ready; automated tests use fake credentials and mocked HTTP.

Contract and failure behavior

  • One capability: x_publish_post; one mutating operation: publish_post.
  • No public control fields. The model supplies the text through the standard raw-text payload stage. Only this selected capability receives its skill.
  • Signature, mention blocking and final weighted 280-character validation are enforced in code using the official twitter-text parser. Ordinary Hebrew and Latin content has 258 characters left after the two newlines and signature. Long URLs count as 23. Text is never silently truncated.
  • The request body is constructed as { text } against the fixed endpoint https://api.x.com/2/tweets. No reply, quote, media, DM, repost, thread, deletion, arbitrary endpoint or account override is exposed. User mentions and lists (@user/list, including fullwidth at-signs) are rejected.
  • Before each post, the plugin calls GET https://api.x.com/2/users/me with the same OAuth credential snapshot used for posting. It requires HTTP 200, a valid account ID, and a username matching the installation's expectedUsername (case-insensitive). The username is captured from context.config at plugin load; it is never a model-supplied control. Verification is fresh per invocation, with no cached identity or extra model tool.
  • Credentials stay in the host/plugin layer. The model supplies only post text. One timeout/abort budget covers verification and posting; redirects and automatic retries remain disabled.
  • Missing credentials, invalid text, failed account lookup, or an account mismatch return not_sent: no post request is dispatched. x_account_unverified identifies a failed identity check; x_account_mismatch identifies a different account. After post dispatch, X rejection returns rejected; network failures, timeout, server errors and invalid success receipts return unknown: the post may exist. There are no automatic POST retries. Check the X profile before attempting a new post after an unknown outcome.
  • Success requires verified identity and a valid API creation receipt. It returns the post ID, URL, returned text, and account: { id, username }. Provider error bodies and transport exception messages are never passed back to the model.
  • Duplicate prevention across separate model invocations is not provided. Each authorized invocation can create one post. Keep the profile's Automated label and operator disclosure; retain API credits for use.

Develop and validate independently

source/ contains handwritten code; src/index.cjs is generated. The build uses only the published @abot-ai/runtime/plugin-sdk entrypoint, pinned through the lockfile, and bundles runtime dependencies. It does not import a Runtime checkout or require consumer node_modules at execution time. The SDK build dependency is 1.6.0; the installation/configuration flow targets the 1.7.0 host.

From this repository's root:

npm ci --ignore-scripts
npm run build
npm run validate

Tests use fake credentials and mocked HTTP. They exercise plugin configuration, account verification, text policy, OAuth signing, error handling, and the generated bundle. They do not test or import Runtime internals, persistence, or the Web UI. The CI workflow validates the committed bundle on Node 20 and 22.

Prepare a repository export

npm run export:repository -- --output /absolute/path/to/new-empty-directory

The exporter copies only the explicit list in repository-files.json and refuses an existing output directory or symlinked source files. It never copies Git history, credentials, installed dependencies, logs, or test artifacts. Inspect and validate the exported copy before creating or pushing the separate repository. Repository publication and a Marketplace listing are separate owner-approved actions.

MARKETPLACE.md contains the proposed listing and remaining release checks.

License

MIT. See LICENSE and THIRD_PARTY_NOTICES.md for bundled dependency notices.

Official references