@aisa-one/cli
Command-line access to AIsa: one API key for
Claude, GPT, Gemini, DeepSeek, Kimi, GLM and published Tool Router
operations. Browse the public provider/endpoint catalog separately;
catalog metadata is not a substitute for schema or quote.
Install
npm install -g @aisa-one/cli
Quick Start
# Sign in (browser; stores OAuth tokens — no key to copy)
aisa login
# Discover published tools (Router; search/schema may be anonymous)
aisa search "company facts" --json
aisa schema get_financial_company_facts --json
# Browse the read-only provider/endpoint catalog
aisa api list
aisa api show financial
# Quote a published Router tool (does not execute)
aisa quote --input '{"calls":[{"call_id":"c1","tool":"get_financial_company_facts","arguments":{"ticker":"AAPL"}}]}' --json
aisa login opens a browser, signs you in, and stores OAuth tokens. You do not
need to create or paste a key from the console. For CI or scripts, set
AISA_API_KEY or run aisa login --key <key>. New accounts receive $1 in
free credits.
This first block does not run aisa chat or aisa call. Quote is a price
observation, not authorization to execute. See
Published tools for the quote/approval
contract before a billable call.
Root help lists 21 explicit commands plus implicit help. Removed domain
shortcuts and raw execution names are unknown commands — not aliases and
not forwarded to aisa call.
Published tools (Tool Router)
These four commands are a thin HTTP client for the same Router service MCP uses. They do not search the local catalog cache and do not call providers directly.
--json prints the unmodified application body (MCP identifiers stay).
Human output maps only those four identifiers onto CLI names. aisa manifest
and aisa manifest search / schema / quote / call expose mcp, auth,
enforced, safety, exits, and parseable examples.
Recommended sequence: discover a tool → aisa schema when
has_full_schema=false → aisa quote → aisa call. Quote and call share
one request shape. The first search may omit session_id; the Router returns
one. Reuse that ID for every later search, schema, quote, and call in the same
task, using --session-id <id> or a top-level session_id in --input/-f
JSON. The CLI does not save it between commands. For related quote/call
requests, also carry the Search search_id; it identifies the search, while
session_id groups the task. Standalone commands may omit either ID.
Enforced: invalid local input exits 2 and is not sent;
quote and call refuse to run without an OAuth session or static AIsa API key. Not
enforced: the CLI does not record quotes, approvals, or budget caps and
does not reject an unquoted call. Instruction: do not execute unquoted
calls; the caller must ensure a matching quote and approval. Quote is a
price observation, not authorization. A data request or credentials alone is
not spending approval. Do not invent tool names or guess required values.
aisa call is billable. A missing or failed quote is never free. Estimated
cost is not a limit. If a hard monetary cap is required, do not execute
calls with no guaranteed maximum. A partial quote is not a full-batch total;
call only an independently approved successful subset, and do not silently
retry. Without an OAuth session or static AIsa API key, do not invent a business result.
--input is inline JSON (no file required). Documented shell examples use
POSIX single quotes so apostrophes, Unicode, $(), and backticks stay
literal.
get_financial_company_facts is a published tool whose schema includes
ticker. Do not invent unpublished tool names. Router search does not
guarantee every former domain-shortcut function.
aisa search "company facts" --json
aisa search --input '{"query":"company facts","limit":5}' --json
aisa search --input '{"query":"company facts","known_fields":{"name":"O'\''Reilly — 苹果"}}' --json
aisa search -f request.json --json
aisa search -f - --json < request.json
aisa schema get_financial_company_facts --json
aisa schema --input '{"tools":["get_financial_company_facts"]}' --json
aisa quote --input '{"calls":[{"call_id":"c1","tool":"get_financial_company_facts","arguments":{"ticker":"AAPL"}}]}' --json
aisa quote -f request.json --json
aisa call --input '{"calls":[{"call_id":"c1","tool":"get_financial_company_facts","arguments":{"ticker":"AAPL"}}]}' --json
aisa call -f - --json < request.json
For a multi-step task, copy the actual session_id from the first search
response (shown in normal output or --json) and pass it on later commands:
aisa search "company facts"
aisa search "company revenue" --session-id "$SESSION_ID"
aisa schema get_financial_company_facts --session-id "$SESSION_ID"
aisa quote --input '{"search_id":"SEARCH_ID","calls":[{"call_id":"c1","tool":"get_financial_company_facts","arguments":{"ticker":"AAPL"}}]}' --session-id "$SESSION_ID" --json
aisa call --input '{"search_id":"SEARCH_ID","calls":[{"call_id":"c1","tool":"get_financial_company_facts","arguments":{"ticker":"AAPL"}}]}' --session-id "$SESSION_ID" --json
Set SESSION_ID and replace SEARCH_ID with values returned by your search;
quote the same calls before an authorized call. If JSON already contains
session_id, omit --session-id to avoid conflicting sources.
--json keeps large integer tokens. Diagnostics go to stderr. Exit 2 means
local input was invalid and nothing was sent; 1 is transport, auth, or an
HTTP error; 3 means the Router returned a batch with at least one failed
item.
search and schema may be anonymous. quote and call require a
stored OAuth session or static API key. aisa login stores access and refresh
tokens in ~/.aisa/tokens.json (0600), along with the OAuth client ID and
expiresAt (Unix milliseconds). Access tokens refresh within 60 seconds of
expiry; a 401 triggers one refresh and retry. AISA_API_KEY takes precedence
and never refreshes. Legacy ~/.aisa/key files migrate on first read; conf
apiKey is a write-only compatibility mirror.
For CI, set AISA_API_KEY or use aisa login --key <key>. The default Router
origin is https://tools.aisa.one (independent of baseUrl /
https://api.aisa.one). Point a test Router at AISA_ROUTER_BASE_URL (origin
or prefix before /v1/tool-router/...), or aisa config set routerUrl. There
is no origin fallback.
quote never executes. Router requests do not follow HTTP redirects, so a
307/308 cannot turn quote into call. There is no automatic quote-to-call sequence and no
retry.
HTTP requests default to a 600-second (10-minute) timeout per attempt, matching the Router's default downstream request budget. Explicit shorter timeouts, including 10 seconds for public catalog reads, still apply.
API Catalog
api list and api show are the supported read-only catalog. They browse
public provider and endpoint metadata (--json, --refresh, --health,
--category, path filters). They are not deprecated and are not Router
search / schema. Catalog paths and prices are browsing metadata, not a
substitute for schema or quote, and not a way to execute an endpoint.
aisa api list # all catalog providers
aisa api list --category finance # finance, search, social, productivity, other
aisa api list --health # include provider health
aisa api show financial # endpoints in one provider
aisa api show financial /news # one endpoint: params and catalog price
aisa api show dataforseo --all # long lists truncate to 40 by default
A provider id is not always its URL slug — brave-search serves
/apis/v1/brave/..., and api show prints the catalog path. Use the published
Router schema for methods and request parameters; legacy catalog entries may
still report an advisory method.
Prices with type: metered display as usage-based (billing depends on usage),
even when the catalog returns normal: 0. Details show usage-based (cost varies).
Use aisa quote for a request-specific estimate; JSON keeps the original pricing metadata.
When Runtime declares customer_pricing: fixed_success, endpoint details say
per successful request. Other fixed catalog amounts remain references until
their charging conditions are checked. Unavailable estimates do not mean free,
and displayed rates retain the supplied amount without rounding.
In --json output, metered providers and endpoints also include pricing_note
with quote guidance, including endpoints nested in provider details. The list
remains an array, and the original pricing fields are unchanged.
The catalog is cached for five minutes in ~/.aisa/cache (override with
AISA_CACHE_DIR). Upgrading bypasses the old six-hour/day-long catalog cache.
If refresh fails, an explicitly labelled stale reference remains available;
schema and quote are still required before a call. Pass --refresh to bypass
the cache, or aisa cache clear.
LLM Gateway
GPT, Claude, Gemini, DeepSeek, Kimi, GLM, Qwen and the rest behind one OpenAI-compatible endpoint.
aisa chat "your message" --model deepseek-v4.1-flash
aisa chat "explain this" --model claude-opus-4-6
aisa chat "respond in JSON" --model gemini-3.5-flash --json
echo "summarize this" | aisa chat # pipe support
aisa models # list all models
aisa models --provider anthropic # filter by provider
aisa models show gpt-4.1-mini # model details
Streaming is on by default; pass --no-stream to disable it.
Account
aisa balance # wallet and API key credit balance
aisa balance --json
aisa usage # last 7 days for this API key
aisa usage --days 31 --scope account
aisa usage --limit 3 # table shows only the latest 3 days
aisa usage --json
aisa topup # open the console billing page to add credit
aisa topup 20 # same, deep-linked to $20 (minimum $10)
aisa usage calls GET /v1/usage and prints one row per day — requests,
failed requests, input and output tokens, and charged USD — plus a total.
--days accepts 1–31 and defaults to 7. --scope key (the default) is the
current credential; --scope account sums the whole account. --json prints
the gateway response. --limit shortens the table only.
Payment always finishes in the browser: card details belong to Stripe's hosted
page, not to us, and a bank's 3-D Secure step needs one. topup opens the
right page; --no-open prints the URL instead. The minimum top-up is $10.
Skills
Skills are markdown files that teach AI coding agents (Claude Code, Cursor, Copilot, …) how to use AIsa. They come from the agent-skills repository.
aisa skills list # all skills
aisa skills list --category financial # one category
aisa skills search "financial analysis"
aisa skills show marketpulse # bare name or financial/marketpulse
aisa skills install marketpulse # install to detected agent directories
aisa skills install marketpulse --force # replace whatever occupies that directory
aisa skills remove marketpulse
Installing replaces the target directory rather than merging into it, so a
previous skill's scripts and assets cannot linger and keep being loaded, and a
partial download aborts without touching what is already there. Each install
writes a small .aisa-skill.json recording which skill owns the directory —
that marker is what lets the CLI tell two same-named skills apart.
Naming a category (financial/marketpulse) means "this specific skill", so the
CLI checks the marker before replacing or removing anything. A directory
installed before markers existed cannot be verified that way, so those need
--force. A bare name (marketpulse) means "whatever holds that directory" and
always works — it only resolves when the leaf name is unique across the repo.
Skills install to whichever agent directories exist on your machine:
Create Skills
aisa skills init my-skill # default template
aisa skills init my-skill --template finance # finance, llm, search, twitter, video
Bundled templates may keep those domain labels. Their runnable steps use
search / schema / quote / call (and optional api list / api show).
They do not invent tool names or treat Router as a replacement for every
removed shortcut. The llm template stays on chat / models.
To publish a skill, open a pull request against AIsa-team/agent-skills.
MCP Server
One-liner: connect
npx @aisa-one/cli connect
Opens a small local page (served by this process on 127.0.0.1, shut down
when finished) where you tick the AIsa MCP servers you want and the coding
agents to install them into. Claude Code is configured through its own
claude mcp add (user scope), then signed in through its own OAuth: connect
runs claude mcp login per server, your browser opens the AIsa
authorization, and the tokens live in Claude Code's own store where it also
refreshes them — no API key, nothing pasted. Cursor, Claude Desktop and
Windsurf get config entries and run the same OAuth themselves on first use.
The page matches the AIsa Console style, reports authorization progress
live, and a success page with copy-paste try-it-now prompts opens when
everything is connected. No daemon stays behind. --no-open prints the URL
instead of launching a browser; --dry-run shows what would be written.
Scripted: mcp setup
aisa mcp setup # configure the default servers for every detected client
aisa mcp setup --all # every live server, not just the defaults
aisa mcp setup --agent cursor # one client only
aisa mcp status # list entries and ping each configured endpoint
setup reads the platform's discovery manifest (aisa.one/.well-known/mcp.json)
at run time and writes one entry per live server, in the shape each client
executes: a url entry for Cursor, an npx mcp-remote stdio bridge for
Claude Desktop and Windsurf. With an API key configured the entries carry it
as a Bearer header; without one they carry no credentials and the server's
OAuth flow opens in your browser on first use. A config file that exists but
does not parse is never overwritten. The docs-search MCP is always included
as aisa-docs.
Shell Completion
# zsh
aisa completion zsh > "${fpath[1]}/_aisa" # then restart your shell
# or, without touching fpath:
echo 'eval "$(aisa completion zsh)"' >> ~/.zshrc
# bash
aisa completion bash > /usr/local/etc/bash_completion.d/aisa
# or:
echo 'eval "$(aisa completion bash)"' >> ~/.bashrc
# fish
aisa completion fish > ~/.config/fish/completions/aisa.fish
aisa completion with no argument detects your shell from $SHELL.
Completion covers commands, subcommands, and options, plus values pulled from the local cache:
aisa api show <TAB> → provider ids
aisa api list --category <TAB> → finance, search, social, productivity, other
aisa skills show <TAB> → skill names
aisa chat --model <TAB> → model ids
The cache-backed suggestions only appear once the relevant command has been run
at least once — completion never makes a network request, so a cold cache
completes commands and flags but no dynamic values. Run aisa api list,
aisa models, and aisa skills list once to warm everything up.
Configuration
aisa config set defaultModel claude-opus-4-6
aisa config get defaultModel
aisa config list # also shows derived base URLs
aisa config reset
Settings:
defaultModel— default model foraisa chat(default:deepseek-v4.1-flash). Existing saved model settings are preserved.baseUrl— platform root; the LLM (/v1), integration (/apis/v1), and catalog bases are all derived from itrouterUrl— Tool Router origin (defaulthttps://tools.aisa.one, independent ofbaseUrl); overridden byAISA_ROUTER_BASE_URLoutputFormat—textorjson
aisa login stores OAuth credentials in ~/.aisa/tokens.json.
aisa login --key <key> stores a static credential without refresh metadata.
Legacy mirrors contain the current access token; older CLIs cannot refresh it.
Third-party client configurations written by aisa connect and
aisa mcp setup get the long-lived "aisa cli" API key, minted from your
sign-in, never the expiring OAuth access token. Configs written by 0.6.0–0.6.8
hold an access token that stops working after a day; run aisa connect again
to replace it.
Environment variables:
AISA_API_KEY takes precedence over the stored key.
AISA_ROUTER_BASE_URL is the Router origin/prefix before
/v1/tool-router/... and overrides the default https://tools.aisa.one.
AISA_CACHE_DIR relocates the cache. GITHUB_TOKEN
raises the GitHub rate limit for skills commands.
Development
git clone https://github.com/AIsa-team/cli.git
cd cli
npm install
npm run build # compile TypeScript
npm run dev # watch mode
npm test # run tests
npm run package:smoke # clean pack, isolated install, installed-bin checks
Release metadata and the exact merge/tag publish path (not an authorization
to publish) are in docs/release.md.
Appendix: Notes for Contributors
Three bases, one root. resolveBases() in src/api.ts derives the LLM base
(/v1), the integration base (/apis/v1), and the catalog root from a single
configured baseUrl. It tolerates a value with either suffix already attached,
because the shipped default has always included /v1 and is persisted in every
existing user's config.
The catalog's shape has three traps. Legacy endpoints[].method values can
be advisory. endpoint_groups[].name is an operator-entered label
(Zero, One, default) with no business meaning, which is why api show
flattens by default. And health is tracked per provider, not per endpoint, so
per-endpoint counts are one value repeated.
Parameter naming varies by endpoint. Scholar uses query, finance uses
ticker (not symbol), Twitter uses userName. aisa api show <api> <path>
prints each endpoint's description and path parameters. That listing is
catalog metadata, not an execution recipe.
License
MIT. See LICENSE. Copyright (c) 2026 AIsa Team.
aisa logout revokes the stored OAuth refresh token at Clerk before deleting
local credentials and compatibility mirrors. If revocation fails, it exits
with an error and retains the credentials so you can retry. Already-issued
JWT access tokens remain valid until expiry. Static API keys are only removed
locally; an AISA_API_KEY environment variable must be unset separately.
Credential reads and changes use a cross-process file lock with heartbeat and
stale-lock recovery. Re-running aisa login or switching to --key revokes
the previous stored OAuth grant before replacing it. Failed replacements
retain pending credentials in a private .pending-tokens.json recovery file;
the next login or logout cleans up those grants before completing.
If a login cannot acquire the credential lock, the newly issued grant is kept
in a private .pending-login-*.json file for the next login/logout to clean up.
Refresh persists the new credentials before optional rotation hints and legacy
mirrors; failure to save the primary token file is reported explicitly.