Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.6.0] — 2026-09-10

### Changed

- Browser login stores OAuth access/refresh tokens and the client ID instead
of minting a permanent API key. Refresh near expiry and retry once after 401.
- Serialize credential refresh, replacement, and logout across CLI processes.
Logout and replacement revoke the previous refresh token; preserve pending
grants for cleanup when login or persistence fails.
- Keep `AISA_API_KEY` and `login --key` static; migrate `~/.aisa/key` and retain
legacy credential mirrors. Credentials are stored atomically with mode 0600.
- Handle async credential errors consistently in logout, whoami, and MCP setup;
reject incomplete OAuth login responses before replacing existing credentials.

### Known limitations

- Already-issued access tokens remain valid until expiry after logout.
- Third-party client configurations receive a fixed access-token snapshot;
CLI refresh does not update those configurations.

## [0.5.2] — 2026-09-10

Compatible patch on published `0.5.1`. Expanded `aisa login --help` for Agent
Expand Down Expand Up @@ -424,7 +444,8 @@ supports today; nothing here depends on a backend change.
- Config commands (`aisa config get|set|list|reset`) and auth
(`aisa login|logout|whoami`).

[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.5.2...HEAD
[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.6.0...HEAD
[0.6.0]: https://github.com/AIsa-team/cli/compare/v0.5.2...v0.6.0
[0.5.2]: https://github.com/AIsa-team/cli/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/AIsa-team/cli/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/AIsa-team/cli/compare/v0.3.0...v0.5.0
Expand Down
41 changes: 33 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ npm install -g @aisa-one/cli
## Quick Start

```bash
# Sign in (browser; stores a CLI key — no key to copy)
# Sign in (browser; stores OAuth tokens — no key to copy)
aisa login

# Discover published tools (Router; search/schema may be anonymous)
Expand All @@ -29,7 +29,7 @@ aisa api show financial
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 a CLI key. You do not
`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 $5 in
free credits.
Expand Down Expand Up @@ -64,7 +64,7 @@ and `aisa manifest search` / `schema` / `quote` / `call` expose `mcp`, `auth`,
Recommended sequence: discover a tool → `aisa schema` when
`has_full_schema=false` → `aisa quote` → `aisa call`. Quote and call share
one request shape. **Enforced:** invalid local input exits 2 and is not sent;
quote and call refuse to run without a configured AIsa API key. **Not
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
Expand All @@ -74,7 +74,7 @@ not spending approval. Do not invent tool names or guess required values.
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 a configured AIsa API key, do not invent a business result.
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
Expand Down Expand Up @@ -104,9 +104,12 @@ 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
configured AIsa API key. Sign in with `aisa login` first; it mints and stores
a CLI key. Resolution order is unchanged: `AISA_API_KEY`, then `~/.aisa/key`,
then legacy login. `AISA_API_KEY` still takes precedence over the stored key.
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
Expand Down Expand Up @@ -323,7 +326,12 @@ Settings:
independent of `baseUrl`); overridden by `AISA_ROUTER_BASE_URL`
- `outputFormat` — `text` or `json`

`aisa login` stores a CLI key in `~/.aisa/key`. Environment variables:
`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` also contain a
snapshot of the credential, not a refresh-capable OAuth session.
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`.
Expand Down Expand Up @@ -367,3 +375,20 @@ catalog metadata, not an execution recipe.
## License

MIT. See [LICENSE](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.
22 changes: 11 additions & 11 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ that commit is merged and reviewed. A push to `main` runs CI only;

| Item | Value |
| --- | --- |
| Version | `0.5.2` (release target) |
| Version | `0.6.0` (release target) |
| Command surface | 22 root help entries including implicit `help`; `api` is `list`/`show` only |
| Registry latest | `0.5.1` on `https://registry.npmjs.org` (baseline at this preparation; recheck before tagging) |
| Registry latest | `0.5.2` on `https://registry.npmjs.org` (baseline at this preparation; recheck before tagging) |
| Default Router origin | `https://tools.aisa.one` |
| LLM / catalog host | `https://api.aisa.one` |
| Node | `engines` `>=18`. CI on Ubuntu: 18/20 legacy compatibility, 22/24 maintained, 26 current. Publish job uses Node 24 and npm `11.6.0`. |

`package.json`, `package-lock.json` (root / `packages[""]`),
`src/constants.ts` `VERSION`, installed `aisa --version`, and
`CHANGELOG.md` `## [0.5.2]` must agree. Confirm with
`CHANGELOG.md` `## [0.6.0]` must agree. Confirm with
`node scripts/package-smoke.mjs` (or `--tarball` of the candidate
archive). The VS Code extension is not version-bumped with this CLI
release unless its own packaging requires it.
Expand Down Expand Up @@ -45,17 +45,17 @@ official registry agree.
```bash
# Official registry only — do not use a mirror as the source of truth.
npm view @aisa-one/cli version --registry https://registry.npmjs.org
# baseline at this preparation: 0.5.1 — recheck before tagging
# baseline at this preparation: 0.5.2 — recheck before tagging
# 0.4.0 is the unpublished main baseline, not a registry release.

git checkout main
git pull origin main
# Confirm this commit is the reviewed merge of the 0.5.2 candidate.
node -p "require('./package.json').version" # 0.5.2
grep -E '^export const VERSION' src/constants.ts # "0.5.2"
# Confirm this commit is the reviewed merge of the 0.6.0 candidate.
node -p "require('./package.json').version" # 0.6.0
grep -E '^export const VERSION' src/constants.ts # "0.6.0"

git tag -a v0.5.2 -m "v0.5.2"
git push origin v0.5.2
git tag -a v0.6.0 -m "v0.6.0"
git push origin v0.6.0
```

Do not tag a worktree or unmerged branch. Do not run `npm publish` on a
Expand All @@ -78,14 +78,14 @@ again via `prepack`). It:
Local smoke of an existing archive:

```bash
node scripts/package-smoke.mjs --tarball /path/to/aisa-one-cli-0.5.2.tgz
node scripts/package-smoke.mjs --tarball /path/to/aisa-one-cli-0.6.0.tgz
```

`prepack` (`npm run build`) is what puts `dist/` into a clean `npm pack`.
CI still runs an explicit `npm run build` before `npm test`.

## After the tag

Watch the Release workflow. Success is `0.5.2` on
Watch the Release workflow. Success is `0.6.0` on
`https://registry.npmjs.org/@aisa-one/cli`. Recheck the official registry
before assuming the tag published. Never move an existing release tag.
97 changes: 95 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aisa-one/cli",
"version": "0.5.2",
"version": "0.6.0",
"description": "CLI for the AIsa unified AI infrastructure platform - one API key for 80+ LLMs and 900+ endpoints across finance, search, social, and video APIs",
"type": "module",
"main": "dist/index.js",
Expand Down Expand Up @@ -36,10 +36,12 @@
"commander": "^12.0.0",
"conf": "^12.0.0",
"ora": "^8.0.1",
"proper-lockfile": "^4.1.2",
"smol-toml": "^1.8.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"@types/proper-lockfile": "^4.1.4",
"typescript": "^5.0.0",
"vitest": "^3.2.4"
},
Expand Down
2 changes: 1 addition & 1 deletion scripts/package-smoke.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(here, "..");
const FAKE_KEY = "local-smoke-key";
const MISSING_KEY = /No API key found[\s\S]*Run "aisa login"[\s\S]*AISA_API_KEY/;
const MISSING_KEY = /Not authenticated\.[\s\S]*Run "aisa login"[\s\S]*AISA_API_KEY/;
const BIG = "9007199254740993";

const SEARCH_REQ = '{"query":"company facts","limit":3}';
Expand Down
Loading
Loading