@freeappstore/cli
The fas CLI for FreeAppStore creators. Scaffold, check, publish, and manage your free apps.
Install
npm i -g @freeappstore/cli
Requires Node 22+.
Quick start
fas login # GitHub device-flow auth
fas init my-app # scaffold from the standalone template
cd my-app
pnpm install && pnpm dev
fas check # compliance checks
fas publish # provisions repo + hosting + DNS
git remote add upstream https://github.com/freeappstore-online/my-app.git
git push upstream main # follow the commands printed by fas publish
After the first successful code push and deploy, live at https://my-app.freeappstore.online.
Commands
| Command | What it does |
|---|---|
fas | Launch the interactive TUI (when stdout is a TTY). Falls back to --help otherwise. |
fas login | Sign in with GitHub via the device-authorization flow. Token cached at ~/.fas/config.json (0600). |
fas logout | Clear the cached session. |
fas whoami | Print the currently signed-in GitHub login. |
fas doctor | App health and stage report; --offline, --json, --verify, --artifact. |
fas init <app-id> | Generate standalone and connected apps from the canonical platform starter foundation; for games use the separate fgs CLI. Replaces app placeholders, runs git init, makes the first commit. |
fas check | Run compliance checks (no-tracking, brand fonts, manifest, bundle size). Exits non-zero on hard failures. |
fas publish | Provisions repo + hosting route + storefront entry, injects deploy.yml if missing and commits it (never overwrites an existing one unless --update-workflow; a workflow that reads secrets.R2_* or lacks id-token: write stops the command until you pass --update-workflow), then prints git remote add and git push instructions. Auto-runs fas check first. |
fas list (alias fas ls) | List apps you have published. --json for scripting, -v for recent commits. |
fas logs <app-id> | Show recent deployment runs and links for an app (GitHub Actions). |
fas secret set|list|rm | Manage server-side encrypted API keys for an app. |
fas proxy allow|list|deny | Manage the URL allowlist for the per-app secret-injecting proxy. |
fas screencheck | Build the app and run a browser at the manifest-declared minimum width in portrait and landscape to verify the layout fits without scrolling. |
fas quality [app-id] | Show the VCQA code quality report (score + grade) for an app. |
fas admin transfer-owner <app> | Admin-only. Move an app to a new owner when --from matches the current one, and write an audit record. |
fas login
Sign in with GitHub via the device-authorization flow. Opens a browser to authorize the CLI, then exchanges the GitHub token for a platform session token. Both are persisted to ~/.fas/config.json (mode 0600).
fas login
fas logout
Clear the cached platform session token.
fas logout
fas whoami
Print the GitHub login of the currently signed-in user. Exits non-zero if not authenticated.
fas whoami
# @yourname
fas doctor
Run local health checks: Node version, git, pnpm, config file, signed-in state, and API reachability.
fas doctor --offline
fas doctor --offline --verify --artifact
fas doctor --offline --json
fas init <app-id>
Scaffold a standalone or connected app from the same canonical starter foundation used by the builder. The presets share their routing, PWA, styling, and capability files; connected adds a private draft example while leaving public screens reachable. Games are scaffolded and published with the separate fgs CLI. The command substitutes app identity and creates the first git commit.
fas init my-app # default: standalone template
fas init my-app --template connected # uses platform backend (KV, rooms, etc.)
| Flag | Purpose |
|---|---|
-t, --template <name> | Template to use. Choices: standalone, connected. Default: standalone. |
fas check
Run compliance checks against the app in the current directory (or --dir). Checks include: no tracking SDKs, no payments, no ads, brand fonts present, no brand token overrides, PWA manifest valid, bundle size under limit, and no leftover APPNAME placeholders. Exits non-zero on hard failures.
fas check # check cwd
fas check --dir ../my-app
| Flag | Purpose |
|---|---|
--dir <path> | Repo directory to check. Defaults to cwd. |
fas publish
Provision and publish an app to FreeAppStore. Runs compliance checks, authenticates, creates a GitHub repo + R2 hosting route + storefront registry entry via the admin Worker, and injects a deploy workflow.
By default, publish is interactive — it asks for category, type, oneliner, and demo URL. Skip prompts by passing flag values; combine with --yes to fail fast in CI rather than hang on a missing field.
fas publish # interactive
fas publish \
--name my-app \
--category Utilities \
--type standalone \
--oneliner "Does the thing." \
--yes # non-interactive (CI)
| Flag | Purpose |
|---|---|
--name <id> | App id (lowercase, used as subdomain). Defaults to package.json#name. |
--category <name> | Storefront category. Case-insensitive (e.g. utilities, brain training). |
--type standalone|connected | Standalone (localStorage only) or Connected (uses platform backend). |
--oneliner <text> | One-line description shown on the storefront. |
--demo <url> | Optional demo URL. Pass "" to clear. |
--yes | Non-interactive: missing required fields abort instead of prompting. |
--update-workflow | Replace an existing .github/workflows/deploy.yml with the current canonical workflow. Without it a differing file is left untouched and the drift is listed. |
The generated deploy.yml is the same one fas init writes. It has two jobs: build (install, typecheck, tests, build, pinned @freeappstore/cli validation, pinned code-health scan — no secrets) and deploy (needs: build; downloads the build artifact and syncs it to R2 — runs no project code). Every action is pinned to a commit SHA and every npx to an exact version.
fas list (alias fas ls)
List apps you have published. Shows app id, category, live URL, and creation date.
fas list # human-readable table
fas list --json # full JSON with per-app commit history
fas list -v # verbose — show last 3 commits per app
| Flag | Purpose |
|---|---|
--json | Output JSON instead of a table (includes per-app commit history). |
-v, --verbose | Show recent commits per app (fetches last 3 from each app repo via GitHub API). |
fas logs <app-id>
Show recent GitHub Actions deploy runs for an app. Shows status, timestamps, and links to failed runs.
fas logs calculator
fas secret set|list|rm
Manage server-side encrypted API keys for an app. Secrets are stored on the platform and injected at runtime via the proxy. Values are encrypted at rest; list only shows names, never values.
fas secret set OPENWEATHER_KEY --from-env OPENWEATHER_KEY
printf '%s' "$OPENWEATHER_KEY" | fas secret set OPENWEATHER_KEY
fas secret set OPENWEATHER_KEY # hidden interactive prompt
fas secret list # list secret names
fas secret list --json # machine-readable
fas secret rm OPENWEATHER_KEY # delete a secret
| Subcommand | Usage | Description |
|---|---|---|
set | fas secret set <name> [--from-env <variable>] [--stdin] [--app <id>] | Store or replace an encrypted API key from an environment variable, stdin, or hidden interactive prompt. Name must be uppercase + underscores. |
list (alias ls) | fas secret list [--app <id>] [--json] | List secret names registered for an app. |
rm (alias remove) | fas secret rm <name> [--app <id>] | Delete a stored secret. |
All subcommands accept --app <id> to specify the app explicitly. If omitted, the app id is derived from the name field in the current directory's package.json.
fas proxy allow|list|deny
Manage the URL allowlist for the per-app secret-injecting proxy. The proxy intercepts outbound API calls from your app and injects the appropriate secret (as a query param, header, bearer token, or OAuth2 client credentials exchange) so that API keys never reach the browser.
# Allow the proxy to inject OPENWEATHER_KEY as a query param when calling the API
fas proxy allow "https://api.openweathermap.org/" \
--secret OPENWEATHER_KEY \
--inject "query:appid"
# Inject as a Bearer token
fas proxy allow "https://api.example.com/v1/" \
--secret EXAMPLE_TOKEN \
--inject bearer
# OAuth2 client credentials flow
fas proxy allow "https://api.spotify.com/v1/" \
--secret SPOTIFY_CLIENT_ID \
--secret2 SPOTIFY_CLIENT_SECRET \
--inject oauth2_cc \
--token-url "https://accounts.spotify.com/api/token"
# List current rules
fas proxy list
fas proxy list --json
# Remove a rule
fas proxy deny "https://api.openweathermap.org/"
| Subcommand | Usage | Description |
|---|---|---|
allow | fas proxy allow <pattern> --secret <name> --inject <spec> [flags] | Add an allowlist rule. <pattern> is a URL prefix (must start with https://). |
list (alias ls) | fas proxy list [--app <id>] [--json] | Show all proxy allowlist rules for the app. |
deny (alias rm) | fas proxy deny <pattern> [--app <id>] | Remove an allowlist rule by its exact pattern. |
allow flags:
| Flag | Purpose |
|---|---|
--secret <name> | (required) Name of a previously stored secret (see fas secret set). |
--inject <spec> | (required) How to inject: query:<name>, header:<name>, bearer, or oauth2_cc. |
--secret2 <name> | Second secret (the client_secret for oauth2_cc). |
--token-url <url> | OAuth2 token endpoint (required for oauth2_cc). |
--methods <list> | Comma-separated HTTP methods. Default: GET. |
--app <id> | App id (defaults to package.json name in cwd). |
fas screencheck
Build the app and run a headless Chromium browser at every reference viewport in the device matrix (portrait + landscape, from iPhone SE to iPad Pro). Reports whether the layout fits without scrolling or clipping, and computes device coverage percentages. Requires Playwright as a dev dependency in the app project.
fas screencheck # build + test local dist
fas screencheck --skip-build # test existing dist without rebuilding
fas screencheck --screenshots # save a PNG per viewport to ./screencheck-out/
fas screencheck --url https://my-app.freeappstore.online # test a live deployment
| Flag | Purpose |
|---|---|
--dir <path> | Repo directory to check. Defaults to cwd. |
--port <n> | Port for the local static server. Default: 4571. |
--skip-build | Skip pnpm build -- assume web/dist is already current. |
--screenshots | Save a PNG of every viewport to ./screencheck-out/. |
--url <url> | Check a live URL instead of building and serving locally. |
fas quality [app-id]
Show the VCQA (VibeCode QA) code quality report for an app. Fetches the latest report from the platform API and displays the composite score (0--100), letter grade (A--F), per-check breakdown, and top issues.
If app-id is omitted, the command auto-detects it from the current directory's package.json name, a *.freeappstore.online reference in CLAUDE.md, or the directory basename.
fas quality # auto-detect app id from cwd
fas quality timer # explicit app id
fas quality timer --json # raw JSON for scripting
| Flag | Purpose |
|---|---|
--json | Output the raw JSON report instead of the formatted summary. |
Configuration
| Env var | Purpose |
|---|---|
FAS_API_BASE | Override the API base URL. Defaults to https://api.freeappstore.online. Useful for local dev (http://localhost:8787). |
NO_COLOR | Set to 1 to disable ANSI colors in output. |
~/.fas/config.json (mode 0600) holds the GitHub OAuth token + the platform session token. fas logout deletes both.
Brand and UI rules (mandated)
Every app and game on the platform shares the same visual language — colors, fonts, spacing — so the storefront and detail pages stay predictable. fas check enforces:
| Check | Rule |
|---|---|
No template placeholders | Every APPNAME placeholder substituted before publish. |
No tracking SDKs | None of 8 known trackers (Google Analytics, Plausible, Mixpanel, …). |
No payments (free tier) | No Payment Request API, checkout SDKs or payment links. Paid apps go on ProAppStore. |
No ads | No ad network scripts, SDKs or packages (AdSense, Ad Manager, IMA, Carbon Ads, EthicalAds, Taboola, …). |
Brand fonts present | Manrope (body) + Fraunces (display) both referenced in CSS / HTML. |
No brand overrides | No app redefines a platform CSS token (--paper, --ink, --accent, --line, --line-strong, --panel, --muted) outside the canonical theme file; no non-brand font-family declarations. |
PWA manifest | web/public/manifest.json with name, display, start_url. |
Bundle size | Initial-load JS under 300 KB gzipped; lazy wasm/workers/dynamic chunks under 25 MB each. |
fas publish auto-runs fas check and aborts on hard failures. The platform also checks the built artifact before making a deploy live.
How fas publish works
- Compliance gate: runs the same checks as
fas check. Hard failures abort. - Auth check: confirms a valid session token (re-login if expired).
- Provision: POSTs to the platform API, which calls the admin Worker via service binding. Admin creates an empty GitHub repo, inserts a D1 hosting route (subdomain → R2 prefix), and appends the app to the storefront registry.
- Deploy workflow: injects
.github/workflows/deploy.ymllocally if missing, so the firstgit pushtriggers an R2 deploy. An existing file that differs is reported, not replaced — pass--update-workflowto regenerate it. - Ownership: records the app in the platform DB so
fas listreturns it. - Output: prints the live URL, repo URL, storefront listing URL, and the
git remote add+git pushcommands to populate the new repo.
If provisioning is unavailable, the command reports the error and exits. Nothing is queued and no submission form opens. Retry after checking the platform status; --issue is not a supported flag.
For AI Agents
Building with Claude Code, Cursor, or another AI tool? Connect the MCP server for tool-based access:
{
"mcpServers": {
"freeappstore": {
"command": "npx",
"args": ["mcp-remote", "https://mcp.freeappstore.online/mcp"]
}
}
}
Or read the full platform guide: freeappstore.online/skills.md
License
MIT.
Migrate an existing app to token utilities
Run fas migrate tailwind-tokens --dir <app-repo> to preview a unified diff and per-file counts. Add --write to apply it. The command scans web/src, adds missing Tailwind theme mappings for defined FAS tokens, and rewrites class attributes and cn/clsx/classnames calls while preserving variants and opacity. Radius and shadow mappings use rounded-fas, rounded-fas-sm, and shadow-fas.
Banned aliases are resolved from the app's own CSS; conflicting light/dark definitions, cycles, and unknown tokens are left in place and reported. Review the remaining CSS rules, hex colours, dark overrides, and inline-style counts in the report. Run the app build after applying the migration. Re-running the command is a no-op once migrated. This command does not convert static inline styles or add SDK @source directives.