farseer-cli
v1.10.1
Published
CLI tool for syncing Farseer App scripts between local folder and Farseer instances
Maintainers
Readme
Farseer CLI
Command-line tool for managing Farseer Apps and scripts. Includes AI assistant integration via MCP (Model Context Protocol).
Features
- Bidirectional Sync - Pull/push scripts and apps between local folder and Farseer instances
- AI Assistant Integration - Native MCP support for Claude, Cursor, and other AI tools
- App Management - Create, configure, and run Farseer apps from CLI
- Model Inspection - Explore dimensions, variables, and formulas
- Multiple Auth Methods - Browser OAuth (JWT) and API key authentication
- Local Development - Run apps locally with automatic credential injection
Table of Contents
- Installation
- Quick Start
- MCP Setup (AI Assistants)
- Authentication
- Commands Reference
- App Management
- Model Inspection
- MCP Tools & Resources
- File Structure
- Configuration
- Troubleshooting
Installation
Global Installation (Recommended)
npm install -g farseer-cliVerify installation:
farseer --versionLocal Installation (for CI/CD)
npm install farseer-cli
npx farseer --versionImportant: All CLI commands must be run from the repository root — the directory that contains the
farseer-apps/folder. The CLI resolves tenant directories relative toprocess.cwd(). Running from insidefarseer-apps/<tenant>/or any other subdirectory will cause "folder not found" errors.
Quick Start
1. Login
farseer loginThis opens your browser for OAuth authentication. After successful login, your credentials are saved locally.
2. Set Up a Tenant
farseer pull my-tenant # or: farseer pull my-org my-tenant (if org differs)The first pull verifies auth, confirms the org owns the tenant, scaffolds farseer-apps/my-tenant/, and downloads all scripts and apps.
3. Select the Tenant for This Shell
export FARSEER_TENANT=my-tenant # add FARSEER_ORG=my-org if the org differsCommands take their target from an explicit argument (farseer push my-tenant) or from FARSEER_TENANT — there is no machine-global default, so a command errors rather than guessing. Each shell is independent: different terminals can target different tenants at the same time.
4. Install Dependencies
farseer installInstalls npm packages required for local script execution.
5. List Apps
farseer app list6. Run an App
farseer run "My Import App"MCP Setup (AI Assistants)
Enable AI assistants (Claude, Cursor) to directly interact with Farseer.
Prerequisite: Install the CLI globally first:
npm install -g farseer-cliClaude Code (CLI) — Recommended
Run one command:
claude mcp add --scope user -t stdio farseer -- farseer mcp-serverVerify it's connected:
claude mcp listYou should see farseer: farseer mcp-server - ✓ Connected. Then type /mcp inside Claude Code to see it alongside your other MCP servers.
Claude Desktop
Add to config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"farseer": {
"command": "farseer",
"args": ["mcp-server", "--tenant", "my-tenant"]
}
}
}Restart Claude Desktop.
Cursor
Add to .cursor/mcp_config.json (project) or ~/.cursor/mcp_config.json (global):
{
"mcpServers": {
"farseer": {
"command": "farseer",
"args": ["mcp-server", "--tenant", "my-tenant"]
}
}
}Once configured, you can ask the AI assistant things like:
- "List my Farseer apps"
- "Pull latest scripts from my-tenant"
- "Create an import script for Sales Amount variable"
See MCP Tools & Resources for full list of available tools.
Tenant binding: each mcp-server process works on a single tenant. Pass it at launch — --tenant <slug> or the FARSEER_TENANT env (e.g. "args": ["mcp-server", "--tenant", "my-tenant"]) — to run one server per tenant, isolated. Launched without one, the server connects and the agent binds it once per session via farseer_select_tenant (then it's locked; restart the server to switch tenants).
Authentication
Farseer CLI supports two authentication methods:
Browser Login (JWT) - Recommended
# Default realm (master)
farseer login
# Specific realm (e.g., acme, corp)
farseer login --realm acmeOpens Keycloak login in browser. Features:
- Automatic token refresh
- Access to all tenants you have permissions for
- Full functionality (scripts + apps)
API Key Login
farseer login --api-keyInteractive prompt for tenant, organisation, and API key. Useful for:
- CI/CD environments
- Headless servers
- Service accounts
Note: API keys can only sync files. For app management, use browser login.
Check Authentication Status
farseer whoamiOutput:
Authentication Status
User Token:
✓ Valid JWT token
Email: [email protected]
Expires: 2025-01-28 10:00:00
Configured Tenants:
✓ my-tenant (API key)Logout
# Clear JWT token
farseer logout
# Remove specific tenant credentials
farseer logout my-tenant
# Remove all credentials
farseer logout --allCommands Reference
Tenant Selection
Every command resolves its target as: explicit argument → FARSEER_TENANT env → error. There is no persisted global default, so a command never silently retargets a stale tenant, and two shells can work on two tenants at once.
# Explicit per command
farseer pull my-tenant
# Or set it once for the shell (org resolved from the saved mapping;
# add FARSEER_ORG=my-org if the org differs from the tenant)
export FARSEER_TENANT=my-tenant
farseer pullSync Operations
Sync commands take the tenant from an argument or FARSEER_TENANT (see Tenant Selection above).
farseer pull [org] [tenant]
Download workspace files and apps from Farseer instance. The first pull for a tenant also sets up its workspace (scaffolds farseer-apps/<tenant>/) and — on the browser-login path — confirms the org owns the tenant before it writes anything.
# Pull for the FARSEER_TENANT tenant (asks before merging into a dirty tree)
farseer pull
# Skip the dirty-tree confirmation prompt
farseer pull -y
# Pull for specific tenant
farseer pull other-tenant
# Download every file in the workspace, not just developer files
farseer pull --all
# Hard-reset local to match remote (overwrite edits, delete local-only)
farseer pull --force
# Pull default-scope files + specific extra extensions
farseer pull --include-ext .json --include-ext .txt
# Pull default-scope files + all files from a folder
farseer pull --include-folder Config --include-folder "App Logs"Default behaviour:
- Fetches files from
Files/folder on instance. - Downloads app configurations.
- If local has uncommitted changes, asks before merging so it doesn't silently mix remote into your work in progress (use
-yto skip the prompt). - Per-file conflict prompts when local and remote both moved — see Conflict resolution below.
- Saves to
farseer-apps/<tenant>/files/andfarseer-apps/<tenant>/apps/, createspackage.jsonif missing, updates sync tracking file.
--force (destructive):
- Hard reset local to remote within the current sync scope: overwrites local edits, restores files you deleted locally, AND removes local-only files.
- Scope-aware: a default
pull --forceonly mirrors developer files (.ts,.js,.py,.yml,.yaml,.md,.json) outside hidden directories and the folder blocklist. Data files (.csv,.xlsx,.parquet, …) are backend-accepted but excluded from the default — opt in via--include-ext .csvorfarseer config include --ext .csv. Previously-pulled out-of-scope files (e.g. a file insidenode_modules/you grabbed once with--include-folder node_modules) survive a defaultpull --forcebecause they're invisible to this run. To wipe everything, pass--allor pass the same--include-ext/--include-folderflags you used to pull them. - Always asks for confirmation —
-ydoes NOT bypass this.
Conflict resolution
When farseer pull finds a file edited on BOTH sides, it prompts you per file. The choice list depends on the conflict type — both-modified gets the full menu, delete-vs-edit gets only the two choices that actually make sense.
Both modified (or both newly added at the same path):
| Choice | What happens |
|---|---|
| Keep local version | Your local file wins; sync state records that you've taken a stance, next push uploads your version. |
| Use remote version (overwrite local) | Remote wins; your local edit is discarded. |
| Keep both (save remote as .remote file) | Local stays untouched, remote version saved alongside as <name>.remote.<ext>. You decide later. |
| Merge with markers (git-style) | File is rewritten with <<<<<<< LOCAL / ======= / >>>>>>> REMOTE blocks around both versions. Only offered for text files — binaries skip this option to avoid corruption. Open in your editor, pick what you want, remove the marker lines, then farseer push. The push pre-flight refuses any file that still contains LOCAL or REMOTE markers, so accidentally-unfinished resolves never reach the backend. |
Locally modified, remote deleted (only two choices — there is no "remote version" to keep):
| Choice | What happens | |---|---| | Keep local | Your modified file stays; next push will recreate it on remote. | | Accept deletion | Local file is removed to match remote. |
Locally deleted, remote modified (mirror image — only two choices):
| Choice | What happens | |---|---| | Keep deleted | Stay deleted; next push will delete it on remote too. | | Restore remote version | The remote version is downloaded back into your tree. |
After a merge_markers resolve, farseer push works without --force — the drift check sees only "local diverged from baseline" since the pull just ran. --force is only needed if someone else pushed AGAIN in the meantime.
farseer push [tenant]
Upload local changes to Farseer instance.
# Push changes (asks for confirmation by default)
farseer push
# Skip the confirmation prompt for non-destructive pushes
farseer push -y
# Mirror local → remote: overwrite drift AND delete remote-only files
farseer push --force
# Include extra file types in the push set
farseer push --include-ext .json
# Surgical push: only upload these specific files, leave everything else alone
farseer push --only Scripts/syncGkFreezeBs.ts
farseer push --only Scripts/foo.ts --only Scripts/bar.tsDefault behaviour:
Pre-flight — refuses any local file the backend would refuse anyway:
- Extension not on the backend whitelist.
- Filename longer than 256 chars or empty after trimming.
- File body empty (0 bytes — common cause of opaque
BadRequest). - Unresolved git-style merge markers (
<<<<<<< LOCAL/=======/>>>>>>> REMOTE) left over from amerge_markerspull — see Conflict resolution.
Reported up-front in a single grouped section; nothing leaves your machine until those are fixed.
Drift check — if the remote has changes you haven't pulled (modified, added, or deleted files within your sync scope), push refuses and lists the exact files that are blocking, plus the matching
farseer pullcommand (with the same--include-ext/--include-folderflags so it actually fetches them). Remote-side content edits are caught via a deep preflight — pass--shallowto fall back to listing-only diff if you trust the recorded baseline.Plan preview — shows added/modified/deleted files (and any conflicts) using the same buckets
farseer statusshows. Asks for confirmation (-yskips).Uploads/updates/deletes files on instance, then app configurations.
Conflict-only push exits non-zero — if every candidate file hits a service-side conflict (someone else edited the same file content), push prints Push did nothing — N conflict(s) blocked the upload and exits 1. CI scripts can branch on this.
Out-of-scope files are invisible — a file with an extension outside the default developer-files scope (.ts, .js, .py, .yml, .yaml, .md, .json), or one sitting inside node_modules/, dist/, or another blocklisted folder, won't be pushed under default farseer push. Data files like .csv, .xlsx, .parquet are backend-accepted but excluded from the default — opt in via --include-ext .csv. The push CLI does NOT have an --all flag (only pull and status do); broaden the push set with --include-ext / --include-folder instead. Hidden paths (any segment starting with ., like .vscode/) are skipped even by pull --all — they require explicit --include-folder .vscode to be picked up, on either side.
--force (destructive — true mirror):
- Skips the drift check AND deletes remote files that have no local counterpart (within the scope you declared via
--include-ext/--include-folder). - Always asks for confirmation —
-ydoes NOT bypass this.
--only <path> (surgical single-file push):
- Whitelists exact paths (relative to the tenant files dir, case-sensitive, repeatable). Anything not listed is out-of-scope: not pushed, not compared, not deleted.
- Narrows the drift check to the listed paths and skips the apps push. The drift gate still fires if any
--onlypath has unpulled remote changes (modified, added, or deleted) — that's the dangerous overlap. Unrelated working-tree edits outside the whitelist are ignored, and the apps sync (which has its own diff path) stays out of the way. - Pre-flight validates that every
--onlypath resolves to a real file under the tenant files dir — typos exit with a list of unmatched paths instead of silently succeeding as a no-op. - Use when a colleague's edit drifted on remote and you only want to land your unrelated fix without reconciling theirs first, or when you don't want a stale local file to sneak into the push.
farseer status [tenant]
Compare local files with remote instance.
# Default: hash every remote file so remote-side content edits show up
farseer status
# Faster: trust the last sync for remote-side state (will MISS remote edits)
farseer status --shallow
# Check all files
farseer status --all
# Check default-scope files + specific extensions
farseer status --include-ext .json --include-folder ConfigOutput uses explicit add/modify/delete buckets per side, so a deletion never shows up as M:
Comparing local (apps/<tenant>/files/) with remote (Files/)...
Local changes:
+ Scripts/new-script.ts
M Scripts/import-sales.ts
- Scripts/old-helper.ts
Remote changes:
+ Data/from-other-dev.xlsx
M Scripts/sync-actuals.ts (by Matej)
- Scripts/legacy.tsRemote-modified entries pick up a (by <name>) suffix when the recorded sync metadata knows who last uploaded the file; everything else is bare + / M / -. The legend is consistent across status and the push plan preview.
By default the engine hashes every remote file so it catches both add/delete changes and content edits. Pass --shallow to skip the remote hash and trust the recorded baseline — much faster on large tenants, but it cannot see remote-side content edits to files that still exist remotely.
farseer status and the preview that farseer push shows before its confirmation prompt come from the same status engine, so they always agree on which files are added / modified / deleted. If status says nothing is changed, push will say "Nothing to push"; if status lists three modified files, push lists the same three.
farseer diff [tenant] [file]
Show file differences.
# Diff all changed files
farseer diff
# Diff specific file
farseer diff Scripts/import-sales.ts
# Show only remote version
farseer diff Scripts/import-sales.ts --remotefarseer files list [tenant]
List files on remote instance.
# List script files only
farseer files list
# List all files
farseer files list --allApp Management
App names with spaces don't need quotes when the tenant comes from FARSEER_TENANT.
farseer app list [tenant]
List all apps.
# Simple list
farseer app list
# With details (entrypoint, scripts, arguments)
farseer app list --detailsfarseer app show <name>
Show app details.
farseer app show My Import AppOutput:
{
"name": "My Import App",
"description": "Imports sales data",
"entrypoint": "import-sales.ts",
"arguments": [
{ "name": "Year", "defaultValue": "2025" },
{ "name": "Version", "defaultValue": "Plan" }
]
}farseer app create <name>
Create new app.
farseer app create My New AppCreates JSON file in apps/<tenant>/apps/. Use push to upload to instance.
farseer app configure <name>
Configure app settings.
An app's scripts are derived from its source folder, and the entrypoint must be one of those files. Set the source folder first, then pick an entrypoint inside it.
# Set the source folder (path under Files/) the app's scripts come from
farseer app configure My App --source-folder "Scripts/MyApp"
# Set the entrypoint (must be a file in the source folder)
farseer app configure My App --entrypoint index.ts
# Add argument
farseer app configure My App --add-arg "Year=2025"
# Set description
farseer app configure My App --description "Imports data"Settings you don't pass are preserved — configure only changes what you specify.
If you move the source folder to one that doesn't contain the app's current
entrypoint, pass --entrypoint in the same command — configure errors rather
than silently clearing or relocating the entrypoint.
farseer app configure My App --source-folder "Scripts/NewFolder" --entrypoint index.tsfarseer app delete <name>
Delete app (requires --force).
farseer app delete My App --forcefarseer run <app>
Run app locally with injected credentials.
# Run app
farseer run My Import App
# Override argument
farseer run My Import App --arg "Year=2026"
# Multiple arguments
farseer run My Import App --arg "Year=2026" --arg "Version=Forecast"Prerequisites:
- Run
farseer installfirst - Credentials configured (
farseer login)
farseer install [tenant]
Install npm dependencies for local script execution. Auto-creates package.json with farseer-client if it doesn't exist.
# Install for the FARSEER_TENANT tenant
farseer install
# Install for specific tenant
farseer install my-tenantIf the tenant directory has no package.json, one is created automatically with farseer-client as a dependency. If package.json exists but lacks farseer-client, the dependency is added automatically.
Model Inspection
Explore your Farseer data model from the command line.
farseer model [tenant]
# Show model overview
farseer model
# List all variables
farseer model --variables
# List dimension tables
farseer model --tables
# Show variable details
farseer model --variable "Sales Amount"farseer dimensions members <table>
# List members of a dimension table
farseer dimensions members Product
# Export to CSV (useful for large tables)
farseer dimensions members Customers --output customers.csv
# Limit output
farseer dimensions members Product --limit 50farseer variables update
# Update formula
farseer variables update --name "Revenue" --formula "Price * Quantity"
# Change dimensions
farseer variables update --name "Revenue" --dimensions "Product,Region,Months"
# Change rollup type
farseer variables update --name "Revenue" --rollup Averagefarseer variable export <name>
# Export variable data to CSV
farseer variable export "Revenue"
# With row limit
farseer variable export "Revenue" --limit 1000 --output revenue.csvSheets & Dashboards
# List all sheets
farseer sheets list
# Show sheet configuration
farseer sheets show "P&L"
# List all dashboards
farseer dashboards list
# Show dashboard with tile details
farseer dashboards show "KPI Dashboard" --tilesMCP Tools & Resources
Farseer CLI implements the Model Context Protocol (MCP). For setup instructions, see MCP Setup.
Available Tools
Once configured, AI assistants have access to:
Authentication & tenant:
farseer_whoami- Check auth status and the tenant this session is bound tofarseer_select_tenant- Bind this session to a tenant (once per session, then locked)
Sync Operations:
farseer_pull- Pull scripts and apps from remote.force=trueoverwrites local edits with remote on conflicts (does NOT wipe local-only files — that destructive step lives behind the CLI's interactive confirm; usefarseer pull --forcefrom a shell when you actually want a hard reset). Per-file conflict resolutions (keep_local / keep_remote / keep_both / merge_markers) are CLI-only because they require an interactive prompt; the MCP path either lets the conflict bubble up in the response (conflicts[]field) orforce=trueto overwrite.farseer_push- Push local changes to remote.force=trueskips the drift check;mirror=true(separate parameter) also deletes remote files that have no local counterpart. The two are independent — combine when you want both. Returnsfiles.rejected[]for any files the backend pre-flight refused (extension whitelist, name length, empty body, unresolved git-style conflict markers from a priormerge_markerspull). If every candidate was rejected and nothing was uploaded/updated/deleted, the call returnsVALIDATION_ERRORinstead of a silent no-op success.farseer_status- Compare local vs remote state. Default is deep — every remote file is hashed so the response surfaces remote-side content edits made since the last pull. Passshallow=trueto skip the hash and trust the recorded baseline (faster on big tenants; cannot detect content edits to existing remote files). The response is the same view thatfarseer pushshows in its confirmation preview — the two commands are guaranteed not to disagree.farseer_diff- Show file differencesfarseer_list_files- List remote files
App Management:
farseer_list_apps- List all appsfarseer_get_app- Get app detailsfarseer_create_app- Create new appfarseer_configure_app- Configure app (scripts can be referenced by filename or full path)farseer_delete_app- Delete appfarseer_run_app- Run app locallyfarseer_install- Install dependencies (auto-creates package.json, supports adding extra npm packages)
Model Inspection:
farseer_export_model- Export full model structure (auto-saves to temp file for large models 20KB+)farseer_list_tables- List dimension tablesfarseer_list_variables- List variablesfarseer_get_table- Get table detailsfarseer_get_variable- Get variable detailsfarseer_evaluate_formula- Evaluate a formula expression with dimension filters. Variable names with spaces or special characters (e.g., "P&L") are auto-quoted.farseer_list_dimension_members- List members of a dimension table (auto-CSV for 500+)farseer_manage_dimension_members- Create or delete dimension membersfarseer_create_variable- Create a new variable with dimensions and formula. Accepts rollupType in any case (e.g., "Sum", "average", "CLOSING STATE").farseer_update_variable- Update variable (formula, rollup, dimensions, description). Fetches current state and applies partial updates.
Sheets & Dashboards:
farseer_list_sheets- List all sheetsfarseer_get_sheet- Get sheet configuration (by name or ID)farseer_get_sheet_data- Get data from a sheetfarseer_set_sheet_data- Update cell values in a sheetfarseer_create_sheet- Create a new sheetfarseer_list_dashboards- List all dashboardsfarseer_get_dashboard- Get dashboard details
Data Operations:
farseer_export_variable_data- Export variable data with optional dimension filters (auto-CSV for 500+)farseer_load_variable_data- Load variable data as CSV (auto-CSV for 500+)farseer_create_import_job- Create and execute data import job with audit trail and undo supportfarseer_create_file- Create/update files on remote instance
Cells:
farseer_query_cells- Query cell values by variable and dimensions. To write data, usefarseer_create_import_jobinstead.
Documentation Resources
AI assistants can read comprehensive documentation:
- How Farseer Works (Dimensions, Variables, Formulas)
- CLI Guide
- API Reference (farseer-client)
- New App Template
- Troubleshooting
- Import Workflows
- Arquero Patterns
- Query & Formulas
- Model Export
Workflow Prompts
Guided workflows for common tasks:
- setup_new_tenant - Setup new tenant from scratch
- create_new_app - Create new app with guidance
- understand_model - Explore data model
- write_import_script - Write import script
- copy_app - Copy and modify existing app
- debug_sync_issues - Troubleshoot sync problems
Example Conversations
List apps:
"Show me all my Farseer apps"
Create import script:
"Create a new import script for Sales Amount variable that reads from CSV"
The AI will:
- Check what dimensions Sales Amount has
- Create TypeScript import script
- Push script to Farseer
- Create and configure app
Debug issues:
"Pull latest changes and show me what's different"
File Structure
apps/
└── my-tenant/
├── package.json # Auto-generated dependencies
├── .farseer-sync.json # Sync tracking
├── files/ # Maps to Files/ folder on instance
│ ├── Scripts/
│ │ ├── import-sales.ts
│ │ └── helpers/
│ │ └── utils.ts
│ └── Data/
│ └── config.json
└── apps/ # App configurations (JSON)
├── Import Sales.json
└── Sync Actuals.jsonApp JSON Format
{
"name": "My Import App",
"description": "Imports sales data",
"entrypoint": "import-sales.ts",
"arguments": [
{ "name": "Year", "defaultValue": "2025" },
{ "name": "Version", "defaultValue": "Plan" }
],
"_remote": {
"id": 123,
"reference": "123"
}
}Important:
- For new apps: Don't add
_remote- CLI adds it after first push - For existing apps:
_remotelinks local JSON to remote app - Never edit
_remotemanually
Configuration
Config File Location
~/.farseer/config.json
Config Structure
{
"credentials": {
"my-tenant": {
"type": "apiKey",
"tenantId": "my-tenant",
"apiKey": "sk_xxx123",
"basePath": "https://my-org.farseer.io/api/v3"
}
},
"userAuth": {
"accessToken": "...",
"refreshToken": "...",
"expiresAt": "2025-01-28T10:00:00Z",
"realm": "master"
},
"tenantOrgMapping": {
"my-tenant": "my-org"
},
"syncIncludes": {
"my-tenant": {
"extensions": [".json", ".txt"],
"folders": ["Config", "App Logs"]
}
}
}Sync Includes (Extra File Types)
By default, pull/push/status sync developer files (.ts, .js, .py, .yml, .yaml, .md, .json) outside hidden directories and the folder blocklist (node_modules/, dist/, …). Data files (.csv, .xlsx, .parquet, .pdf, images, …) are backend-accepted but EXCLUDED from the default — large binaries no one edits in a code editor would dominate first-pull time and clutter status. Opt in via --include-ext .csv or farseer config include --ext .csv. To broaden the set:
pull --all/status --allsyncs everything (push CLI does not have--all; use--include-ext/--include-folderto broaden a push).--include-ext <ext>/--include-folder <folder>adds inline scope to a single command (works on all four sync verbs: pull, push, status, refresh).- Persistent includes via
farseer config include(below) apply to every run without re-typing the flags.
Hidden files (anything with a path segment starting with . — .env, .git/, .vscode/, …) require explicit opt-in via --include-ext or --include-folder even under --all. This protects against a stray credential file in the working tree leaking on a force push. The CLI never picks them up just because they happen to have a whitelisted extension.
Persistent config (set once, always applies):
# Add extra extensions
farseer config include --ext .json --ext .txt
# Add folders (all files inside get synced)
farseer config include --folder Config --folder "App Logs"
# Show current includes
farseer config include
# Remove specific items
farseer config include --remove-ext .txt
farseer config include --remove-folder Config
# Clear all includes
farseer config include --clearInline on any sync command (one-time, additive to config):
farseer pull --include-ext .json --include-folder Config
farseer status --include-ext .json
farseer push --include-ext .json
farseer pull my-tenant --include-ext .json --include-folder ConfigManaging Credentials
# Set API key
farseer config set my-tenant --api-key sk_xxx123
# Set with custom organisation
farseer config set my-tenant --api-key sk_xxx123 --org my-org
# List configured tenants
farseer config list
# Show credentials (masked)
farseer config show my-tenant
# Test connection
farseer config test my-tenant
# Remove credentials
farseer config remove my-tenantTroubleshooting
"No credentials found for tenant"
# Login with browser
farseer login
# Or set API key
farseer config set my-tenant --api-key sk_xxx123"No tenant specified"
Pass the tenant as an argument, or set it for the shell:
farseer <command> my-tenant
# or
export FARSEER_TENANT=my-tenant # + FARSEER_ORG=my-org if the org differs"Token expired"
JWT tokens auto-refresh. If refresh fails:
farseer login"Remote has changes that are not synced locally"
Someone edited files on Farseer UI. Pull first:
farseer pull
farseer push"Cannot find module 'farseer-client'"
farseer install"Entry point needs to be defined"
farseer app configure "My App" --entrypoint index.tsLogin Issues with Different Realm
# Try specific realm
farseer login --realm acme
farseer login --realm corpRequirements
- Node.js >= 18
- Access to Farseer instance
Releasing
The CLI is published to npm via GitHub Actions when a cli-v* tag is pushed.
To release a new version:
- Bump
versionincli/package.json - Commit and push to
dev - Tag and push:
git tag cli-v1.0.10 git push origin cli-v1.0.10
CI will typecheck, lint, format-check, build, smoke-test, and publish to npm.
The tag version must match package.json or the pipeline will fail.
License
ISC
