Upload, publish, rollout, and manage Chrome extensions — a fast, lightweight, CLI powered by the latest V2 API.
or curl -fsSL https://null3000.github.io/cws-cli/install.sh | bash
Everything you need to manage Chrome Web Store extensions, nothing you don't.
No runtime, no npm install, no dependencies. Download and run.
Built on the latest Chrome Web Store API — not the deprecated V1 that other tools still use.
cws init walks you through OAuth config — no more copy-pasting env vars from docs.
Upload, publish, status, staged rollouts, and submission cancellation — all from one tool.
Env vars, meaningful exit codes, and zero dependencies means it drops into any pipeline.
TOML file, env vars, CLI flags — use any combination. Local or global config, your choice.
Three steps to publish your extension.
cws init
Interactive wizard that configures OAuth2 credentials, refresh token, and Publisher ID. Writes a cws.toml config file.
cws upload ./dist
Zips the directory (excluding .git/, node_modules/, etc.) and uploads it.
cws publish
Before using cws, set up API access in the Google Cloud Console. Follow the official Chrome Web Store API guide:
Everything available in cws help
| Command | Description |
|---|---|
cws init | Interactive credential setup wizard |
cws validate [source] | Pre-flight validation (manifest, version, size) |
cws upload [source] | Validate, zip, and upload a package |
cws publish | Publish the latest uploaded version |
cws status | Check extension status |
cws rollout <pct> | Set deploy percentage (10k+ users required) |
cws cancel | Cancel a pending submission |
cws version | Print CLI version |
# Full validation (local + remote checks)
cws validate ./dist
# Local checks only (no credentials needed)
cws validate ./dist --local
# Validate a pre-built zip
cws validate extension.zip
Checks: manifest.json validity, required fields, version format, package size, version > published, no pending submission. Also runs automatically before every cws upload.
# Upload a directory (zips automatically)
cws upload ./dist
# Upload a pre-built zip
cws upload extension.zip
# Upload and publish in one step
cws upload ./dist --publish
# Specify extension ID
cws upload ./dist -e abcdefghijklmnopabcdefghijklmnop
# Don't wait for processing
cws upload ./dist --wait=false
# Skip pre-upload validation
cws upload ./dist --skip-validate
# Publish immediately after review
cws publish
# Stage for review without auto-publishing
cws publish --staged
# Human-readable output
cws status
# Raw JSON (for scripting)
cws status --json
# Set to 50% rollout
cws rollout 50
# Full rollout
cws rollout 100
# Cancel a pending submission under review
cws cancel
# Cancel for a specific extension
cws cancel -e abcdefghijklmnopabcdefghijklmnop
# Set up credentials in current directory
cws init
# Save credentials globally (~/.config/cws/cws.toml)
cws init --global
Interactive wizard that guides you through configuring OAuth2 Client ID, Client Secret, Refresh Token, and Publisher ID. Validates credentials before saving.
| Exit Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error (API error, validation failure, invalid config, timeout, etc.) |
TOML file, env vars, and CLI flags — use any combination.
cws init writes credentials to ./cws.toml. This file contains secrets — do not commit it.
publisher_id = "abc1234567890"
[auth]
client_id = "xxxxxxxxxxxx.apps.googleusercontent.com"
client_secret = "GOCSPX-xxxxxxxxxxxx"
refresh_token = "1//xxxxxxxxxxxx"
[extensions.default]
id = "abcdefghijklmnopabcdefghijklmnop"
source = "./dist"
Use cws init --global to write credentials to ~/.config/cws/cws.toml instead, keeping secrets out of your project. Then create a minimal per-project config (safe to commit):
[extensions.default]
id = "abcdefghijklmnopabcdefghijklmnop"
source = "./dist"
| Variable | Description |
|---|---|
| CWS_CLIENT_ID | OAuth2 Client ID |
| CWS_CLIENT_SECRET | OAuth2 Client Secret |
| CWS_REFRESH_TOKEN | OAuth2 Refresh Token |
| CWS_PUBLISHER_ID | Publisher ID |
| CWS_EXTENSION_ID | Default Extension ID |
--extension-id, etc.)CWS_*)cws.toml (current directory)~/.config/cws/cws.tomlDrop the binary into any pipeline — zero dependencies.
# .github/workflows/publish.yml
- name: Install cws
run: curl -fsSL https://null3000.github.io/cws-cli/install.sh | bash
- name: Upload and publish extension
env:
CWS_CLIENT_ID: ${{ secrets.CWS_CLIENT_ID }}
CWS_CLIENT_SECRET: ${{ secrets.CWS_CLIENT_SECRET }}
CWS_REFRESH_TOKEN: ${{ secrets.CWS_REFRESH_TOKEN }}
CWS_PUBLISHER_ID: ${{ secrets.CWS_PUBLISHER_ID }}
run: cws upload ./dist -e ${{ vars.EXTENSION_ID }} --publish
The go-to tool has been chrome-webstore-upload-cli. Here's how they compare.
| cws | chrome-webstore-upload-cli | |
|---|---|---|
| Runtime | Single binary — no dependencies | Requires Node.js + npm |
| API | V2 | V1 |
| Setup | Interactive wizard | Manual env var config |
| Commands | validate, upload, publish, status, rollout, cancel | upload, publish |
| Config | TOML + env vars + CLI flags | Env vars only |
| Pre-upload validation | Built-in — manifest, version, size checks before upload | None |
| CI/CD | Drop in a binary | Requires Node.js in CI image |
Multiple ways to install — pick what works for you.
brew install null3000/tap/cws
curl -fsSL https://null3000.github.io/cws-cli/install.sh | bash
go install github.com/null3000/cws-cli/cmd/cws@latest
Make sure $GOPATH/bin is in your PATH.
Download the latest binary for your platform from Releases.
Invoke-WebRequest -Uri https://github.com/null3000/cws-cli/releases/latest/download/cws_windows_amd64.zip -OutFile cws.zip
Expand-Archive cws.zip -DestinationPath .
Move-Item cws.exe C:\Windows\System32\