
Square Post
Publish text, image, article, and video posts to Binance Square from Node scripts without npm installs.
Overview
Square Post is an agent skill for the Launch phase that publishes text, image, article, and video posts to Binance Square via local Node.js OpenAPI scripts.
Install
npx skills add https://github.com/binance/binance-skills-hub --skill square-postWhat is this skill?
- Four post types: text, image, article, and video via dedicated scripts in `scripts/`
- OpenAPI auth from `BINANCE_SQUARE_OPENAPI_KEY` or `~/.config/binance-square/openapi-key` (0600); `--key` CLI rejected
- Video flow uses ffmpeg for cover frame and ffprobe when duration is not supplied
- Node.js 18+ ES modules with native `fetch`—no npm package install required
- Presigned upload URLs and Square OpenAPI endpoints require outbound network
- Four post modalities: text, image, article, video
- Node.js 18+ required; no npm install for current scripts
Adoption & trust: 4k installs on skills.sh; 881 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You have content ready for Binance Square but no safe, repeatable way for an agent to publish across post types without hand-operating the UI or exposing API keys on the command line.
Who is it for?
Indie builders automating Binance Square distribution from a repo that already holds the Binance skills-hub scripts and a Square OpenAPI key.
Skip if: Teams that only need generic social scheduling without Binance Square, or environments that cannot install ffmpeg for video posts or allow OpenAPI network egress.
When should I use this skill?
User wants to publish or automate posts to Binance Square using the hub scripts, including saving OpenAPI keys or handling video cover/duration.
What do I get? / Deliverables
After the skill runs, posts are submitted through the documented scripts with keys read from env or `~/.config/binance-square/openapi-key`, including video covers and duration handling when ffmpeg/ffprobe are available.
- Published Square post (text, image, article, or video) via API
- Optional saved key file at `~/.config/binance-square/openapi-key` with 0600 permissions
Recommended Skills
Journey fit
Binance Square is a distribution channel—this skill executes publish workflows when you are ready to put content in front of an audience. Distribution subphase covers cross-posting and platform-native publishing, which is exactly what the Square OpenAPI scripts automate.
How it compares
Use this OpenAPI script skill for Binance Square specifically—not a generic MCP social connector or manual copy-paste publishing.
Common Questions / FAQ
Who is square-post for?
Solo and indie builders shipping content or product narratives on Binance Square who want agent-driven publishing from Node scripts with documented auth and media handling.
When should I use square-post?
Use it in Launch (distribution) when you need to push text, image, article, or video posts via Square OpenAPI; also when an agent must save or rotate keys with `save-key.mjs` before scheduled posts.
Is square-post safe to install?
Review the Security Audits panel on this Prism page for published audit results; the skill itself stresses never passing API keys as CLI args and storing keys at 0600 under `~/.config/binance-square/`.
SKILL.md
READMESKILL.md - Square Post
# Square Post Skill Publish text, image, article, and video posts to Binance Square through the local Node.js scripts in `scripts/`. ## Dependencies ### Runtime - Node.js 18 or newer. The scripts use native ES modules and the built-in `fetch` API. - Bash-capable shell for running the scripts through the agent tool. ### System Tools - `ffmpeg` is required for video posts. `scripts/post-video.mjs` extracts the first frame from the source video and uploads it as the post cover. - `ffprobe` is required when the user does not provide a video duration. The agent uses it to determine the duration before calling `post-video.mjs`. ### External Services - Binance Square OpenAPI access through `BINANCE_SQUARE_OPENAPI_KEY` or the local saved key file. - Network access to the Square OpenAPI endpoints and to presigned upload URLs returned by the API. No npm package install is required for the current scripts; they only use Node.js built-in modules. ## Authentication Scripts read the OpenAPI key in this order: 1. `BINANCE_SQUARE_OPENAPI_KEY` 2. The saved key file at `~/.config/binance-square/openapi-key` Do not pass API keys as CLI arguments. `--key` is rejected because command-line arguments can appear in process listings and shell history. To save a key for future runs, explicitly run: ```bash BINANCE_SQUARE_OPENAPI_KEY=<apiKey> node scripts/save-key.mjs ``` The saved key file is written with `0600` permissions. To remove it, delete `~/.config/binance-square/openapi-key`. ## Directory Structure ``` square-post/ ├── SKILL.md # Skill instructions and publishing workflow ├── README.md # Directory overview ├── scripts/ │ ├── lib.mjs # Shared API, upload, polling, and publish helpers │ ├── save-key.mjs # Saves the OpenAPI key to a local private config file │ ├── post-text.mjs # Text and article publishing script │ ├── post-image.mjs # Image post and article-with-cover publishing script │ └── post-video.mjs # Video publishing script with generated cover ``` import fs from "fs"; import os from "os"; import path from "path"; const BASE_URL_V1 = "https://www.binance.com/bapi/composite/v1/public/pgc/openApi"; const BASE_URL_V2 = "https://www.binance.com/bapi/composite/v2/public/pgc/openApi"; const POLL_INTERVAL_MS = 3000; const MAX_POLL_RETRIES = 10; const CONFIG_DIR = path.join(os.homedir(), ".config", "binance-square"); const CONFIG_FILE = path.join(CONFIG_DIR, "openapi-key"); const CONTENT_TYPE_MAP = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", mp4: "video/mp4", mov: "video/quicktime", avi: "video/x-msvideo", webm: "video/webm", }; export function getContentType(filePath) { const ext = path.extname(filePath).slice(1).toLowerCase(); return CONTENT_TYPE_MAP[ext] || "application/octet-stream"; } export function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } export function getConfigFilePath() { return CONFIG_FILE; } export function maskApiKey(apiKey) { if (!apiKey) return ""; if (apiKey.length <= 9) return `${apiKey.slice(0, 2)}...`; return `${apiKey.slice(0, 5)}...${apiKey.slice(-4)}`; } export function readSavedApiKey() { const keyFile = getConfigFilePath(); if (!fs.existsSync(keyFile)) return ""; return fs.readFileSync(keyFile, "utf8").trim(); } export function saveApiKey(apiKey) { const key = apiKey.trim(); if (!key) { throw new Error("Missing Square OpenAPI key"); } const keyFile = getConfigFilePath(); fs.mkdirSync(path.dirname(keyFile), { recursive: true, mode: 0o700 }); fs.writeFileSync(keyFile, `${key}\n`, { mode: 0o600 }); fs.chmodSync(keyFile, 0o600); return keyFile; } export function resolveApiKey(args = []) { if (args.includes("--key")) { throw new Error("Do not pass API keys with --key. Set BINANCE_SQUARE_OPENAPI_KEY or save the key locally first."); } const envKey = process.env.BINANCE_SQUARE_O