@openquok/auto-cli
v0.0.7
Published
Programmatic CLI for the Openquok scheduling API — designed for automation and AI agents. It is to automate social media posting, manage scheduled content, and upload media via the Openquok API across the social platforms you’ve connected to Openquok
Maintainers
Readme
What Is It For
@openquok/auto-cli is a Programmatic CLI for the Openquok scheduling API — designed for automation and AI agents. It is to automate social media posting, manage scheduled content, and upload media via the Openquok API across the social platforms you’ve connected to Openquok (e.g. Twitter/X and Instagram, Facebook).
- Create and schedule posts via the programmatic API
- List posts and flip draft ↔ scheduled (
posts:status) - Upload media for use in posts
- List programmatic integrations and trigger provider-specific tools (e.g. fetch subreddits, pages, playlists)
Quickstart
Programmatic CLI for the Openquok scheduling API — designed for automation and AI agents.
This package lives in this monorepo under agent/ and is published as @openquok/auto-cli.
Install
Requires Node.js 20.19+ (or 22.12+, or 23+). Older versions may install with warnings or fail at runtime.
npm install -g @openquok/auto-cli
# or
pnpm add -g @openquok/auto-cliAuthentication
Option 1: OAuth2 (Recommended)
openquok auth:loginThis uses a device flow via an auth helper server (see agent/server). The CLI will print a one-time code and a verification URL, then poll until the user completes authorization. The resulting access token and API base URL (when returned by the auth server) are stored in ~/.openquok/credentials.json.
By default the CLI uses the hosted device-flow server at https://cli-auth.openquok.com. For local development of agent/server, point the CLI at your machine:
export OPENQUOK_AUTH_SERVER="http://localhost:3111"
openquok auth:loginOption 2: API Key
Use environment variables (recommended for CI)
export OPENQUOK_API_KEY="opo_..."Store credentials locally
openquok auth:login --apiKey "your_token"
openquok auth:status
openquok auth:workspace
openquok auth:logoutNote: Stored credentials (from
openquok auth:login) take priority overOPENQUOK_API_KEYwhen both are present. Runopenquok auth:logoutto clear them if you want the env var to be used.
Commands
Config
openquok config:show- Prints resolved
api_urlandauth_server_url, whether you match hosted Openquok (deployment:openquok_cloud) or a custom / self-hosted setup (deployment:custom), and where each value came from (environment,credentials_file, ordefault). Does not print secrets.
Integrations
openquok integrations:list
openquok integrations:settings <id>
openquok integrations:trigger <id> <method> [--data '<json>']integrations:settingsreturns the provider's rules, max post length, settings schema, and the list of allow-listedtoolsyou can invoke viaintegrations:trigger.integrations:triggerdispatches a single allow-listed provider method (e.g.getSubreddits). The--datapayload, when present, must be a JSON object; its shape is provider-specific (see thedataSchemafield of the corresponding tool inintegrations:settings).- Connecting new channels (OAuth) is done from the web UI; the CLI consumes the resulting integration IDs.
Posts
openquok posts:list
openquok posts:list --start "2026-01-01T00:00:00Z" --end "2026-02-01T00:00:00Z"
openquok posts:create \
-c "Hello from Openquok" \
-s "2026-01-01T12:00:00Z" \
-i "uuid1,uuid2"
openquok posts:status <post-id> --status draft
openquok posts:status <post-id> -s schedule
openquok posts:delete <postId>
openquok posts:missing <postId>
openquok posts:connect <postId> --release-id <providerReleaseId>posts:listwithout flags defaults to 30 local calendar days before today through 30 local calendar days after today (JavaScriptDatelocal rules, then ISO UTC on the wire). Pass--start/--endor--startDate/--endDate; use-i/--integrations/--integrationIds <csv>and/or--customer/--customerGroupId(integration_customers.id) to narrow channels.posts:statustakes a post row id fromposts:list(same asposts:delete) and flipsdraft↔scheduledat the same stored publish time viaPUT /public/posts/{postId}/status.posts:deleteremoves a single post (and the post group it belongs to — a row never publishes in isolation).posts:missingandposts:connectare the workflow for posts whoserelease_idcame back as"missing": list provider-side candidates withposts:missing, then link the matching id withposts:connect --release-id <id>(or--releaseId/-r) to unlock per-post analytics.
Analytics
openquok analytics:platform <integrationId> [--days 7|30|90]
openquok analytics:post <postId> [--days 7|30|90]analytics:platformreturns platform-native metrics for a connected channel (followers, impressions, engagement, …).analytics:postreturns per-post metrics (likes, comments, shares, …) for a published post. Drafts/queued posts return[].- The
--dayswindow is one of7,30, or90(default7); the backend will reject any other value.
Media upload
openquok upload ./image.png
openquok upload-from-url "https://cdn.example.com/banner.png"uploadposts a local file as multipart form data.upload-from-urlinstructs the API to fetch a publicly reachable http(s) URL server-side (no local file needed); the response shape matchesupload.
Both return JSON including data.filePath and data.id, which you can pass into posts:create as --media.
Environment variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| OPENQUOK_API_KEY | No* | - | Programmatic API key / token (Bearer) |
| OPENQUOK_API_URL | No | https://api.openquok.com | Base URL (the CLI calls {OPENQUOK_API_URL}/api/v1/...) |
| OPENQUOK_AUTH_SERVER | No | https://cli-auth.openquok.com | OAuth2 device flow auth server origin (paths /device/*, /health). Use http://localhost:3111 when running agent/server locally. |
*Either OPENQUOK_API_KEY or openquok auth:login (stored credentials) is required for authenticated commands.
Development
Install once from the repo root, then iterate either from source (no build) or against the compiled bundle.
# From the repo root
pnpm install
pnpm --filter ./agent dev # tsx watch: re-prints --help on every save
pnpm --filter ./agent build # produces dist/index.js (the published bin)Unit tests (Vitest)
Pure CLI helpers (for example posts.logic.ts) are covered with pnpm --filter ./agent test:unit (from the repo root pnpm agent:test:unit). agent/run-vitest.mjs runs node web/node_modules/vitest/vitest.mjs (not a nested pnpm exec vitest), so agent/ does not add its own vitest dependency and avoids pnpm deadlocks when tests are started via pnpm agent:test. Use pnpm --filter ./agent test:watch during development.
End-to-end CLI tests (Vitest)
Scenario files live under agent/tests/e2e/ and use the suffix *.e2e.test.ts. Name them by surface and command (for example threads.schedule.post.e2e.test.ts). Each test builds an argv array that matches what a user would type after openquok, runs node agent/dist/index.js against a local mock HTTP server, and asserts on the recorded request body and JSON stdout. That matches the usual pattern for Node CLI integration tests (spawn node on the entry script; see this CLI test helper gist for the interactive stdin variant).
If agent/dist/index.js is missing, the harness runs tsup once via node agent/node_modules/tsup/dist/cli-default.js (stdio discarded so it does not fight Vitest’s worker stdio). The CLI subprocess uses async spawn with stdin ignored — spawnSync in a Vitest worker hung in practice, and a piped stdin can block CLIs that read /dev/stdin.
From the repo root: pnpm agent:test:e2e (e2e only) or pnpm agent:test (unit + e2e).
Run the CLI without installing
# Source-mode (recommended for daily dev) — picks up changes immediately
pnpm --filter ./agent cli -- --help
pnpm --filter ./agent cli -- analytics:platform --help
pnpm --filter ./agent cli -- posts:missing --help
# Compiled bundle — verifies the published bin behaves identically
pnpm --filter ./agent build
pnpm --filter ./agent start -- --helpThe bare
--separator is required:pnpmconsumes its own flags first, so without itpnpm --filter ./agent cli --helpprints pnpm's help, not the CLI's.
Smoke test the CLI surface
After adding or renaming commands, run these to confirm every group is wired into registerAllCommands:
# 1. Top-level `--help` should list config, auth, integrations, posts, analytics, upload, and upload-from-url verbs
pnpm --filter ./agent cli -- --help
# 2. Each command should respond to `--help` with its yargs `Examples:` block
pnpm --filter ./agent cli -- config:show --help
pnpm --filter ./agent cli -- analytics:platform --help
pnpm --filter ./agent cli -- analytics:post --help
pnpm --filter ./agent cli -- posts:status --help
pnpm --filter ./agent cli -- posts:delete --help
pnpm --filter ./agent cli -- posts:missing --help
pnpm --filter ./agent cli -- posts:connect --help
pnpm --filter ./agent cli -- upload-from-url --helpFor a connectivity smoke (requires a valid API key or stored credentials):
# 3. Confirm auth + workspace plumbing end-to-end
pnpm --filter ./agent cli -- auth:status
pnpm --filter ./agent cli -- integrations:list | jq '.[] | {id, identifier}'
pnpm --filter ./agent cli -- posts:list | jq '.success, (.data.posts | type)'Each command emits machine-readable JSON on stdout, so piping into jq is the recommended way to assert on shape during smoke runs and CI.
License
| Component | License |
|-----------|---------|
| CLI source and compiled dist/ (@openquok/auto-cli) | AGPL-3.0-or-later |
| skills/ (agent skill definitions) | MIT |
The npm package ships both: use AGPL terms for the CLI binary; use MIT for files under skills/ when copying or redistributing skill content only.
