@aisa-one/cli 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -1
- package/README.md +18 -12
- package/dist/commands/account.js +1 -1
- package/dist/commands/auth.js +1 -1
- package/dist/commands/mcp.d.ts +20 -2
- package/dist/commands/mcp.js +172 -36
- package/dist/commands/skills.js +10 -10
- package/dist/constants.d.ts +29 -2
- package/dist/constants.js +37 -3
- package/dist/index.js +6 -5
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] — 2026-08-18
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`aisa mcp setup` wrote a dead URL.** It hardcoded
|
|
15
|
+
`https://docs.aisa.one/mcp`, a hostname with no DNS record, and wrote it
|
|
16
|
+
into every client's config. Setup now reads the live discovery manifest at
|
|
17
|
+
`aisa.one/.well-known/mcp.json` and writes one entry per live server
|
|
18
|
+
(default set of five; `--all` for everything), so new servers work without
|
|
19
|
+
a CLI release. If the manifest cannot be fetched, nothing is written.
|
|
20
|
+
- **`{url}` entries were written for clients that cannot execute them.**
|
|
21
|
+
Claude Desktop and Windsurf spawn stdio processes and silently ignore url
|
|
22
|
+
entries; they now get an `npx mcp-remote` bridge, verified end to end
|
|
23
|
+
against production.
|
|
24
|
+
- **A config file that failed to parse was replaced with `{}`.** A
|
|
25
|
+
hand-edited `mcp.json` with comments or a trailing comma was wiped by
|
|
26
|
+
setup. It is now left untouched, with an error.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Entries carry the configured API key as a Bearer header; without a key
|
|
31
|
+
they are written credential-less and the server's OAuth challenge drives
|
|
32
|
+
the browser flow on first use.
|
|
33
|
+
- `aisa mcp status` now pings every configured endpoint with a real
|
|
34
|
+
initialize request instead of checking that a JSON key exists — a 401 is
|
|
35
|
+
reported as healthy (the auth challenge), an unresolvable hostname as the
|
|
36
|
+
failure it is.
|
|
37
|
+
- The stale dead entry earlier releases wrote is removed on the next setup.
|
|
38
|
+
|
|
39
|
+
## [0.2.4] — 2026-08-07
|
|
40
|
+
|
|
41
|
+
Text-only follow-up to 0.2.3: no behavioural change, but the URL it printed
|
|
42
|
+
sent people to a 404.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- **`aisa usage` and `aisa login` pointed at `aisa.one/dashboard`, which
|
|
47
|
+
returns 404.** The customer console is served at `console.aisa.one`; the
|
|
48
|
+
API-key and usage-log pages are `/api-keys` and `/logs`. Both commands and
|
|
49
|
+
both README references now say so.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- **The brand is written `AIsa`, not `AISA`,** in `--help` output, the npm
|
|
54
|
+
package description, the skill templates, and the README. The `AISA_*`
|
|
55
|
+
environment variables and `X-AISA-*` response headers are technical
|
|
56
|
+
identifiers and keep their existing casing.
|
|
57
|
+
|
|
10
58
|
## [0.2.3] — 2026-08-07
|
|
11
59
|
|
|
12
60
|
Every command documented in the README was checked against the live gateway.
|
|
@@ -198,7 +246,8 @@ supports today; nothing here depends on a backend change.
|
|
|
198
246
|
- Config commands (`aisa config get|set|list|reset`) and auth
|
|
199
247
|
(`aisa login|logout|whoami`).
|
|
200
248
|
|
|
201
|
-
[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.2.
|
|
249
|
+
[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.2.4...HEAD
|
|
250
|
+
[0.2.4]: https://github.com/AIsa-team/cli/compare/v0.2.3...v0.2.4
|
|
202
251
|
[0.2.3]: https://github.com/AIsa-team/cli/compare/v0.2.2...v0.2.3
|
|
203
252
|
[0.2.2]: https://github.com/AIsa-team/cli/compare/v0.2.1...v0.2.2
|
|
204
253
|
[0.2.1]: https://github.com/AIsa-team/cli/releases/tag/v0.2.1
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @aisa-one/cli
|
|
2
2
|
|
|
3
|
-
Command-line access to [
|
|
3
|
+
Command-line access to [AIsa](https://aisa.one): **one API key for 80+ LLMs and
|
|
4
4
|
950+ endpoints** across 29 API providers — finance, web search, social,
|
|
5
5
|
research, and video generation.
|
|
6
6
|
|
|
@@ -33,8 +33,9 @@ aisa web-search "latest AI research"
|
|
|
33
33
|
aisa run financial /insider-trades -q "ticker=AAPL"
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
Get your API key at
|
|
37
|
-
|
|
36
|
+
Get your API key at
|
|
37
|
+
[console.aisa.one/api-keys](https://console.aisa.one/api-keys). New accounts
|
|
38
|
+
receive $5 in free credits.
|
|
38
39
|
|
|
39
40
|
## API Catalog
|
|
40
41
|
|
|
@@ -192,13 +193,13 @@ aisa balance --json
|
|
|
192
193
|
|
|
193
194
|
`aisa usage` is not available yet — the gateway does not serve
|
|
194
195
|
`GET /v1/credits/usage` (it 404s, while `/v1/credits/balance` on the same route
|
|
195
|
-
group works). Use the [
|
|
196
|
+
group works). Use the [console](https://console.aisa.one/logs) for usage history
|
|
196
197
|
in the meantime.
|
|
197
198
|
|
|
198
199
|
## Skills
|
|
199
200
|
|
|
200
201
|
Skills are markdown files that teach AI coding agents (Claude Code, Cursor,
|
|
201
|
-
Copilot, …) how to use
|
|
202
|
+
Copilot, …) how to use AIsa APIs. They come from the
|
|
202
203
|
[agent-skills](https://github.com/AIsa-team/agent-skills) repository — 42 skills
|
|
203
204
|
across six categories.
|
|
204
205
|
|
|
@@ -249,15 +250,20 @@ To publish a skill, open a pull request against
|
|
|
249
250
|
## MCP Server
|
|
250
251
|
|
|
251
252
|
```bash
|
|
252
|
-
aisa mcp setup # configure detected
|
|
253
|
-
aisa mcp
|
|
253
|
+
aisa mcp setup # configure the default servers for every detected client
|
|
254
|
+
aisa mcp setup --all # every live server, not just the defaults
|
|
255
|
+
aisa mcp setup --agent cursor # one client only
|
|
256
|
+
aisa mcp status # list entries and ping each configured endpoint
|
|
254
257
|
```
|
|
255
258
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
259
|
+
`setup` reads the platform's discovery manifest (`aisa.one/.well-known/mcp.json`)
|
|
260
|
+
at run time and writes one entry per live server, in the shape each client
|
|
261
|
+
executes: a `url` entry for Cursor, an `npx mcp-remote` stdio bridge for
|
|
262
|
+
Claude Desktop and Windsurf. With an API key configured the entries carry it
|
|
263
|
+
as a Bearer header; without one they carry no credentials and the server's
|
|
264
|
+
OAuth flow opens in your browser on first use. A config file that exists but
|
|
265
|
+
does not parse is never overwritten. The docs-search MCP is always included
|
|
266
|
+
as `aisa-docs`.
|
|
261
267
|
|
|
262
268
|
## Shell Completion
|
|
263
269
|
|
package/dist/commands/account.js
CHANGED
|
@@ -39,5 +39,5 @@ export async function usageAction(_options) {
|
|
|
39
39
|
// The gateway does not serve /v1/credits/usage yet — it 404s in production
|
|
40
40
|
// even though /v1/credits/balance on the same route group works.
|
|
41
41
|
console.log(chalk.yellow(" Usage API is not yet available on the gateway."));
|
|
42
|
-
hint("View usage history at https://aisa.one/
|
|
42
|
+
hint("View usage history at https://console.aisa.one/logs");
|
|
43
43
|
}
|
package/dist/commands/auth.js
CHANGED
|
@@ -10,7 +10,7 @@ export function loginAction(options) {
|
|
|
10
10
|
}
|
|
11
11
|
setApiKey(key);
|
|
12
12
|
success(`Authenticated: ${maskKey(key)}`);
|
|
13
|
-
console.log(chalk.gray(" Get your API key at https://aisa.one/
|
|
13
|
+
console.log(chalk.gray(" Get your API key at https://console.aisa.one/api-keys"));
|
|
14
14
|
}
|
|
15
15
|
export function logoutAction() {
|
|
16
16
|
clearApiKey();
|
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
/** One config entry in the shape the client actually executes. */
|
|
2
|
+
export declare function buildEntry(shape: "url" | "stdio", endpoint: string, key: string | undefined): Record<string, unknown>;
|
|
1
3
|
export declare function mcpSetupAction(options: {
|
|
2
4
|
agent?: string;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
all?: boolean;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* `aisa mcp status` — what is configured, and does it actually answer.
|
|
9
|
+
*
|
|
10
|
+
* The old status only checked that a config file contained an "aisa" key,
|
|
11
|
+
* which is exactly how a dead URL looked healthy. Reachability means a real
|
|
12
|
+
* HTTP exchange with each configured endpoint.
|
|
13
|
+
*/
|
|
14
|
+
export declare function mcpStatusAction(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* One initialize POST, no credentials. What each answer means:
|
|
17
|
+
* 200 -> alive and open (the docs MCP)
|
|
18
|
+
* 401 -> alive, auth required — the expected answer from mcp.aisa.one
|
|
19
|
+
* anything else -> alive but odd; say so
|
|
20
|
+
* network error -> the dead-hostname class this command exists to catch
|
|
21
|
+
*/
|
|
22
|
+
export declare function pingEndpoint(endpoint: string): Promise<string>;
|
package/dist/commands/mcp.js
CHANGED
|
@@ -2,19 +2,78 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { success, error, info, hint } from "../utils/display.js";
|
|
4
4
|
import { expandHome, ensureDir } from "../utils/file.js";
|
|
5
|
-
import { MCP_CONFIGS,
|
|
5
|
+
import { MCP_CONFIGS, MCP_MANIFEST_URL, DOCS_MCP_URL, MCP_DEFAULT_SLUGS } from "../constants.js";
|
|
6
|
+
import { getApiKey } from "../config.js";
|
|
6
7
|
import { join } from "node:path";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const stripped = (name) => name.replace(/^AIsa\s+/i, "");
|
|
9
|
+
async function fetchLiveServers() {
|
|
10
|
+
const res = await fetch(MCP_MANIFEST_URL, { signal: AbortSignal.timeout(15_000) });
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new Error(`manifest answered HTTP ${res.status}`);
|
|
13
|
+
const manifest = (await res.json());
|
|
14
|
+
const live = (manifest.servers ?? [])
|
|
15
|
+
.filter((s) => s.status === "live" && s.slug && s.transport?.endpoint)
|
|
16
|
+
.map((s) => ({
|
|
17
|
+
slug: s.slug,
|
|
18
|
+
name: s.name ?? s.slug,
|
|
19
|
+
endpoint: s.transport.endpoint,
|
|
20
|
+
toolCount: s.tools?.length ?? 0,
|
|
21
|
+
}));
|
|
22
|
+
if (live.length === 0)
|
|
23
|
+
throw new Error("manifest lists no live servers");
|
|
24
|
+
return live;
|
|
25
|
+
}
|
|
26
|
+
/** One config entry in the shape the client actually executes. */
|
|
27
|
+
export function buildEntry(shape, endpoint, key) {
|
|
28
|
+
if (shape === "url") {
|
|
29
|
+
return key ? { url: endpoint, headers: { Authorization: `Bearer ${key}` } } : { url: endpoint };
|
|
30
|
+
}
|
|
31
|
+
// The exact invocation verified against production on 2026-08-18. The
|
|
32
|
+
// header value splits on the first colon, so no space after "Authorization:".
|
|
33
|
+
// Without a key, mcp-remote answers the server's 401 challenge by running
|
|
34
|
+
// the OAuth flow itself and caching tokens under ~/.mcp-auth.
|
|
35
|
+
const args = ["-y", "mcp-remote", endpoint];
|
|
36
|
+
if (key)
|
|
37
|
+
args.push("--header", `Authorization:Bearer ${key}`);
|
|
38
|
+
args.push("--transport", "http-only");
|
|
39
|
+
return { command: "npx", args };
|
|
40
|
+
}
|
|
41
|
+
/** The dead entry every pre-v0.3 setup wrote. Remove it on sight. */
|
|
42
|
+
const DEAD_URL = "https://docs.aisa.one/mcp";
|
|
43
|
+
export async function mcpSetupAction(options) {
|
|
44
|
+
const targets = options.agent && options.agent !== "all" ? [options.agent] : Object.keys(MCP_CONFIGS);
|
|
12
45
|
for (const agent of targets) {
|
|
13
|
-
|
|
14
|
-
if (!config) {
|
|
46
|
+
if (!MCP_CONFIGS[agent]) {
|
|
15
47
|
error(`Unknown agent: ${agent}. Valid: ${Object.keys(MCP_CONFIGS).join(", ")}, all`);
|
|
16
48
|
return;
|
|
17
49
|
}
|
|
50
|
+
}
|
|
51
|
+
// Fetch before touching any file: a setup that cannot see the manifest has
|
|
52
|
+
// nothing true to write.
|
|
53
|
+
let servers;
|
|
54
|
+
try {
|
|
55
|
+
servers = await fetchLiveServers();
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
error(`Could not read the MCP manifest at ${MCP_MANIFEST_URL}: ${e.message}`);
|
|
59
|
+
hint("Nothing was written. Check your network and try again.");
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const chosen = options.all
|
|
64
|
+
? servers
|
|
65
|
+
: servers.filter((s) => MCP_DEFAULT_SLUGS.includes(s.slug));
|
|
66
|
+
const skipped = servers.length - chosen.length;
|
|
67
|
+
const key = getApiKey();
|
|
68
|
+
if (key) {
|
|
69
|
+
info("Using your configured API key (Bearer). It will be written into each client's config file.");
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
info("No API key configured — entries are written without credentials, and the");
|
|
73
|
+
info("server's OAuth flow will open in your browser on first use.");
|
|
74
|
+
}
|
|
75
|
+
for (const agent of targets) {
|
|
76
|
+
const config = MCP_CONFIGS[agent];
|
|
18
77
|
const filePath = expandHome(config.path);
|
|
19
78
|
let existing = {};
|
|
20
79
|
if (existsSync(filePath)) {
|
|
@@ -22,46 +81,123 @@ export function mcpSetupAction(options) {
|
|
|
22
81
|
existing = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
23
82
|
}
|
|
24
83
|
catch {
|
|
25
|
-
|
|
84
|
+
// A file we cannot parse is a file we do not own. The old command
|
|
85
|
+
// replaced it with {} — a user's hand-edited config (comments,
|
|
86
|
+
// trailing commas) was wiped by a "setup". Refuse instead.
|
|
87
|
+
error(`${agent}: ${config.path} exists but is not valid JSON — refusing to overwrite it.`);
|
|
88
|
+
hint("Fix or remove the file, then re-run setup.");
|
|
89
|
+
continue;
|
|
26
90
|
}
|
|
27
91
|
}
|
|
28
92
|
else {
|
|
29
93
|
ensureDir(join(filePath, ".."));
|
|
30
94
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
95
|
+
const entries = existing[config.key] || {};
|
|
96
|
+
// Clean up the dead entry earlier releases wrote, and only that one:
|
|
97
|
+
// an "aisa" entry the user pointed somewhere else on purpose stays.
|
|
98
|
+
const stale = entries["aisa"];
|
|
99
|
+
if (stale?.url === DEAD_URL)
|
|
100
|
+
delete entries["aisa"];
|
|
101
|
+
for (const s of chosen) {
|
|
102
|
+
entries[`aisa-${s.slug}`] = buildEntry(config.shape, s.endpoint, key);
|
|
103
|
+
}
|
|
104
|
+
// The docs-search MCP is tiny, unauthenticated, and answers "how do I call
|
|
105
|
+
// this" questions — always included.
|
|
106
|
+
entries["aisa-docs"] = buildEntry(config.shape, DOCS_MCP_URL, undefined);
|
|
107
|
+
existing[config.key] = entries;
|
|
34
108
|
writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
35
|
-
console.log(` ${chalk.green("✓")} ${agent}: ${config.path}`);
|
|
36
|
-
configured++;
|
|
109
|
+
console.log(` ${chalk.green("✓")} ${agent}: ${chosen.length + 1} servers (${config.shape}) → ${config.path}`);
|
|
37
110
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
111
|
+
success(`Configured ${chosen.length} live servers plus docs search for ${targets.length} client(s)`);
|
|
112
|
+
for (const s of chosen) {
|
|
113
|
+
console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)} (${s.toolCount} tools)`);
|
|
41
114
|
}
|
|
115
|
+
if (skipped > 0)
|
|
116
|
+
hint(`${skipped} more live servers available — run with --all to configure every one`);
|
|
117
|
+
hint("Restart your agent/editor to activate");
|
|
42
118
|
}
|
|
43
|
-
|
|
44
|
-
|
|
119
|
+
/**
|
|
120
|
+
* `aisa mcp status` — what is configured, and does it actually answer.
|
|
121
|
+
*
|
|
122
|
+
* The old status only checked that a config file contained an "aisa" key,
|
|
123
|
+
* which is exactly how a dead URL looked healthy. Reachability means a real
|
|
124
|
+
* HTTP exchange with each configured endpoint.
|
|
125
|
+
*/
|
|
126
|
+
export async function mcpStatusAction() {
|
|
127
|
+
const endpoints = new Map(); // endpoint -> [client/entry]
|
|
45
128
|
for (const [agent, config] of Object.entries(MCP_CONFIGS)) {
|
|
46
129
|
const filePath = expandHome(config.path);
|
|
47
|
-
if (existsSync(filePath)) {
|
|
48
|
-
try {
|
|
49
|
-
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
50
|
-
const servers = data[config.key];
|
|
51
|
-
if (servers?.["aisa"]) {
|
|
52
|
-
console.log(` ${chalk.green("✓")} ${agent}: configured`);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
console.log(` ${chalk.gray("○")} ${agent}: not configured`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
console.log(` ${chalk.gray("○")} ${agent}: config unreadable`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
130
|
+
if (!existsSync(filePath)) {
|
|
63
131
|
console.log(` ${chalk.gray("○")} ${agent}: not installed`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
let entries = {};
|
|
135
|
+
try {
|
|
136
|
+
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
137
|
+
entries = data[config.key] || {};
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
console.log(` ${chalk.gray("○")} ${agent}: config unreadable`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const ours = Object.entries(entries).filter(([name]) => name.startsWith("aisa"));
|
|
144
|
+
if (ours.length === 0) {
|
|
145
|
+
console.log(` ${chalk.gray("○")} ${agent}: not configured`);
|
|
146
|
+
continue;
|
|
64
147
|
}
|
|
148
|
+
console.log(` ${chalk.green("✓")} ${agent}: ${ours.length} entries`);
|
|
149
|
+
for (const [name, entry] of ours) {
|
|
150
|
+
const e = entry;
|
|
151
|
+
const endpoint = e.url ?? e.args?.find((a) => a.startsWith("http"));
|
|
152
|
+
if (endpoint) {
|
|
153
|
+
const users = endpoints.get(endpoint) ?? [];
|
|
154
|
+
users.push(`${agent}/${name}`);
|
|
155
|
+
endpoints.set(endpoint, users);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (endpoints.size === 0) {
|
|
160
|
+
hint("Run 'aisa mcp setup' to configure");
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log("");
|
|
164
|
+
info("Checking each configured endpoint:");
|
|
165
|
+
for (const [endpoint, users] of endpoints) {
|
|
166
|
+
console.log(` ${endpoint} ${chalk.gray(`(${users.length} entr${users.length > 1 ? "ies" : "y"})`)}`);
|
|
167
|
+
console.log(` ${await pingEndpoint(endpoint)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* One initialize POST, no credentials. What each answer means:
|
|
172
|
+
* 200 -> alive and open (the docs MCP)
|
|
173
|
+
* 401 -> alive, auth required — the expected answer from mcp.aisa.one
|
|
174
|
+
* anything else -> alive but odd; say so
|
|
175
|
+
* network error -> the dead-hostname class this command exists to catch
|
|
176
|
+
*/
|
|
177
|
+
export async function pingEndpoint(endpoint) {
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetch(endpoint, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
182
|
+
body: JSON.stringify({
|
|
183
|
+
jsonrpc: "2.0",
|
|
184
|
+
id: 1,
|
|
185
|
+
method: "initialize",
|
|
186
|
+
params: {
|
|
187
|
+
protocolVersion: "2025-06-18",
|
|
188
|
+
capabilities: {},
|
|
189
|
+
clientInfo: { name: "aisa-cli", version: "status" },
|
|
190
|
+
},
|
|
191
|
+
}),
|
|
192
|
+
signal: AbortSignal.timeout(8_000),
|
|
193
|
+
});
|
|
194
|
+
if (res.status === 200)
|
|
195
|
+
return `${chalk.green("✓")} reachable (open)`;
|
|
196
|
+
if (res.status === 401)
|
|
197
|
+
return `${chalk.green("✓")} reachable (auth required — normal)`;
|
|
198
|
+
return `${chalk.yellow("⚠")} reachable but answered HTTP ${res.status}`;
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
return `${chalk.red("✗")} unreachable: ${e.message}`;
|
|
65
202
|
}
|
|
66
|
-
hint("Run 'aisa mcp setup' to configure");
|
|
67
203
|
}
|
package/dist/commands/skills.js
CHANGED
|
@@ -33,14 +33,14 @@ aisa run <slug> <path> -q "param=value"
|
|
|
33
33
|
`,
|
|
34
34
|
llm: `---
|
|
35
35
|
name: llm-assistant
|
|
36
|
-
description: "Use
|
|
36
|
+
description: "Use AIsa's unified LLM gateway to chat with 70+ AI models."
|
|
37
37
|
homepage: https://aisa.one
|
|
38
38
|
metadata: {"aisa":{"emoji":"🤖","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
|
|
39
39
|
---
|
|
40
40
|
|
|
41
41
|
# LLM Assistant Skill
|
|
42
42
|
|
|
43
|
-
Use the
|
|
43
|
+
Use the AIsa unified gateway to access 70+ language models through a single API.
|
|
44
44
|
|
|
45
45
|
## Authentication
|
|
46
46
|
|
|
@@ -69,14 +69,14 @@ aisa models --provider anthropic
|
|
|
69
69
|
`,
|
|
70
70
|
search: `---
|
|
71
71
|
name: web-search
|
|
72
|
-
description: "Search the web, YouTube, and academic papers via
|
|
72
|
+
description: "Search the web, YouTube, and academic papers via AIsa APIs."
|
|
73
73
|
homepage: https://aisa.one
|
|
74
74
|
metadata: {"aisa":{"emoji":"🔍","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
|
|
75
75
|
---
|
|
76
76
|
|
|
77
77
|
# Web Search Skill
|
|
78
78
|
|
|
79
|
-
Search the web, YouTube, and academic papers through
|
|
79
|
+
Search the web, YouTube, and academic papers through AIsa's unified search APIs.
|
|
80
80
|
|
|
81
81
|
## Authentication
|
|
82
82
|
|
|
@@ -104,14 +104,14 @@ aisa scholar "transformer architecture"
|
|
|
104
104
|
`,
|
|
105
105
|
finance: `---
|
|
106
106
|
name: finance-analyst
|
|
107
|
-
description: "Access stock prices, earnings, SEC filings, and financial data via
|
|
107
|
+
description: "Access stock prices, earnings, SEC filings, and financial data via AIsa."
|
|
108
108
|
homepage: https://aisa.one
|
|
109
109
|
metadata: {"aisa":{"emoji":"📊","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
|
|
110
110
|
---
|
|
111
111
|
|
|
112
112
|
# Finance Analyst Skill
|
|
113
113
|
|
|
114
|
-
Access real-time and historical financial data through
|
|
114
|
+
Access real-time and historical financial data through AIsa's finance APIs.
|
|
115
115
|
|
|
116
116
|
## Authentication
|
|
117
117
|
|
|
@@ -136,14 +136,14 @@ aisa crypto ETH --period 30d
|
|
|
136
136
|
`,
|
|
137
137
|
twitter: `---
|
|
138
138
|
name: twitter-manager
|
|
139
|
-
description: "Search Twitter, get user profiles and trends via
|
|
139
|
+
description: "Search Twitter, get user profiles and trends via AIsa."
|
|
140
140
|
homepage: https://aisa.one
|
|
141
141
|
metadata: {"aisa":{"emoji":"🐦","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
|
|
142
142
|
---
|
|
143
143
|
|
|
144
144
|
# Twitter Manager Skill
|
|
145
145
|
|
|
146
|
-
Interact with Twitter/X through
|
|
146
|
+
Interact with Twitter/X through AIsa's Twitter APIs.
|
|
147
147
|
|
|
148
148
|
## Authentication
|
|
149
149
|
|
|
@@ -171,14 +171,14 @@ aisa twitter trends
|
|
|
171
171
|
`,
|
|
172
172
|
video: `---
|
|
173
173
|
name: video-generator
|
|
174
|
-
description: "Generate videos from text prompts using
|
|
174
|
+
description: "Generate videos from text prompts using AIsa's video synthesis API."
|
|
175
175
|
homepage: https://aisa.one
|
|
176
176
|
metadata: {"aisa":{"emoji":"🎬","requires":{"bins":["curl"],"env":["AISA_API_KEY"]},"primaryEnv":"AISA_API_KEY","compatibility":["openclaw","claude-code","hermes"]}}
|
|
177
177
|
---
|
|
178
178
|
|
|
179
179
|
# Video Generator Skill
|
|
180
180
|
|
|
181
|
-
Generate videos from text prompts using
|
|
181
|
+
Generate videos from text prompts using AIsa's AI video generation APIs.
|
|
182
182
|
|
|
183
183
|
## Authentication
|
|
184
184
|
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.3.0";
|
|
2
2
|
/** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
|
|
3
3
|
export declare const BASE_URL = "https://api.aisa.one";
|
|
4
4
|
export declare const ENV_VAR_NAME = "AISA_API_KEY";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The MCP discovery manifest — the single source of truth for which servers
|
|
7
|
+
* exist and where they live. The CLI deliberately knows no server list of its
|
|
8
|
+
* own: servers ship and retire on the platform side, and reading the manifest
|
|
9
|
+
* at setup time means a new server works without a CLI release (the same
|
|
10
|
+
* philosophy as LLM_ROUTE_PREFIXES below, applied to the fast-moving side).
|
|
11
|
+
*
|
|
12
|
+
* History: until v0.2.4 this file hardcoded MCP_URL = "https://docs.aisa.one/mcp",
|
|
13
|
+
* a host that does not resolve — every `aisa mcp setup` wrote a dead entry into
|
|
14
|
+
* users' configs, and nothing noticed because the command had no tests.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MCP_MANIFEST_URL = "https://aisa.one/.well-known/mcp.json";
|
|
17
|
+
/** Mintlify's docs-search MCP (3 tools: search/read/feedback). Unauthenticated. */
|
|
18
|
+
export declare const DOCS_MCP_URL = "https://aisa.one/docs/mcp";
|
|
19
|
+
/**
|
|
20
|
+
* Servers configured by default. Eleven entries per client is a lot of config;
|
|
21
|
+
* these five cover the broadest agent needs, and `--all` configures everything
|
|
22
|
+
* the manifest lists as live.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MCP_DEFAULT_SLUGS: readonly string[];
|
|
6
25
|
/**
|
|
7
26
|
* First path segment of every route the LLM gateway serves under /v1 (plus
|
|
8
27
|
* /v1beta for Gemini). Everything else is an integration API under /apis/v1.
|
|
@@ -15,9 +34,17 @@ export declare const MCP_URL = "https://docs.aisa.one/mcp";
|
|
|
15
34
|
*/
|
|
16
35
|
export declare const LLM_ROUTE_PREFIXES: readonly string[];
|
|
17
36
|
export declare const AGENT_DIRS: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Per-client config shape. This is the part v0.2.4 got wrong even apart from
|
|
39
|
+
* the dead URL: it wrote `{url}` everywhere, but Claude Desktop's mcpServers
|
|
40
|
+
* only executes command/args (stdio) entries and silently ignores url ones.
|
|
41
|
+
* Stdio clients get an `npx mcp-remote` bridge instead — verified end to end
|
|
42
|
+
* against mcp.aisa.one (initialize -> tools/list -> tools/call) on 2026-08-18.
|
|
43
|
+
*/
|
|
18
44
|
export declare const MCP_CONFIGS: Record<string, {
|
|
19
45
|
path: string;
|
|
20
46
|
key: string;
|
|
47
|
+
shape: "url" | "stdio";
|
|
21
48
|
}>;
|
|
22
49
|
/**
|
|
23
50
|
* Ticker symbol → CoinGecko coin id.
|
package/dist/constants.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.3.0";
|
|
2
2
|
/** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
|
|
3
3
|
export const BASE_URL = "https://api.aisa.one";
|
|
4
4
|
export const ENV_VAR_NAME = "AISA_API_KEY";
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The MCP discovery manifest — the single source of truth for which servers
|
|
7
|
+
* exist and where they live. The CLI deliberately knows no server list of its
|
|
8
|
+
* own: servers ship and retire on the platform side, and reading the manifest
|
|
9
|
+
* at setup time means a new server works without a CLI release (the same
|
|
10
|
+
* philosophy as LLM_ROUTE_PREFIXES below, applied to the fast-moving side).
|
|
11
|
+
*
|
|
12
|
+
* History: until v0.2.4 this file hardcoded MCP_URL = "https://docs.aisa.one/mcp",
|
|
13
|
+
* a host that does not resolve — every `aisa mcp setup` wrote a dead entry into
|
|
14
|
+
* users' configs, and nothing noticed because the command had no tests.
|
|
15
|
+
*/
|
|
16
|
+
export const MCP_MANIFEST_URL = "https://aisa.one/.well-known/mcp.json";
|
|
17
|
+
/** Mintlify's docs-search MCP (3 tools: search/read/feedback). Unauthenticated. */
|
|
18
|
+
export const DOCS_MCP_URL = "https://aisa.one/docs/mcp";
|
|
19
|
+
/**
|
|
20
|
+
* Servers configured by default. Eleven entries per client is a lot of config;
|
|
21
|
+
* these five cover the broadest agent needs, and `--all` configures everything
|
|
22
|
+
* the manifest lists as live.
|
|
23
|
+
*/
|
|
24
|
+
export const MCP_DEFAULT_SLUGS = [
|
|
25
|
+
"web-search",
|
|
26
|
+
"twitter-api",
|
|
27
|
+
"crypto-market-data",
|
|
28
|
+
"marketpulse",
|
|
29
|
+
"stock-pulse",
|
|
30
|
+
];
|
|
6
31
|
/**
|
|
7
32
|
* First path segment of every route the LLM gateway serves under /v1 (plus
|
|
8
33
|
* /v1beta for Gemini). Everything else is an integration API under /apis/v1.
|
|
@@ -35,15 +60,24 @@ export const AGENT_DIRS = {
|
|
|
35
60
|
gemini: "~/.gemini/skills/",
|
|
36
61
|
openclaw: "~/.openclaw/skills/",
|
|
37
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Per-client config shape. This is the part v0.2.4 got wrong even apart from
|
|
65
|
+
* the dead URL: it wrote `{url}` everywhere, but Claude Desktop's mcpServers
|
|
66
|
+
* only executes command/args (stdio) entries and silently ignores url ones.
|
|
67
|
+
* Stdio clients get an `npx mcp-remote` bridge instead — verified end to end
|
|
68
|
+
* against mcp.aisa.one (initialize -> tools/list -> tools/call) on 2026-08-18.
|
|
69
|
+
*/
|
|
38
70
|
export const MCP_CONFIGS = {
|
|
39
|
-
cursor: { path: "~/.cursor/mcp.json", key: "mcpServers" },
|
|
71
|
+
cursor: { path: "~/.cursor/mcp.json", key: "mcpServers", shape: "url" },
|
|
40
72
|
"claude-desktop": {
|
|
41
73
|
path: "~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
42
74
|
key: "mcpServers",
|
|
75
|
+
shape: "stdio",
|
|
43
76
|
},
|
|
44
77
|
windsurf: {
|
|
45
78
|
path: "~/.codeium/windsurf/mcp_config.json",
|
|
46
79
|
key: "mcpServers",
|
|
80
|
+
shape: "stdio",
|
|
47
81
|
},
|
|
48
82
|
};
|
|
49
83
|
/**
|
package/dist/index.js
CHANGED
|
@@ -43,12 +43,12 @@ function wrap(fn) {
|
|
|
43
43
|
const program = new Command();
|
|
44
44
|
program
|
|
45
45
|
.name("aisa")
|
|
46
|
-
.description("
|
|
46
|
+
.description("AIsa CLI - Unified AI infrastructure platform")
|
|
47
47
|
.version(VERSION);
|
|
48
48
|
// ── Auth ──
|
|
49
49
|
program
|
|
50
50
|
.command("login")
|
|
51
|
-
.description("Authenticate with your
|
|
51
|
+
.description("Authenticate with your AIsa API key")
|
|
52
52
|
.option("--key <key>", "API key")
|
|
53
53
|
.action(loginAction);
|
|
54
54
|
program
|
|
@@ -130,7 +130,7 @@ program
|
|
|
130
130
|
// ── Chat (LLM Gateway) ──
|
|
131
131
|
program
|
|
132
132
|
.command("chat [message]")
|
|
133
|
-
.description("Chat with AI models via the
|
|
133
|
+
.description("Chat with AI models via the AIsa gateway")
|
|
134
134
|
.option("--model <model>", "Model ID (default: gpt-4.1-mini)")
|
|
135
135
|
.option("--system <prompt>", "System prompt")
|
|
136
136
|
.option("--no-stream", "Disable streaming")
|
|
@@ -472,12 +472,13 @@ skills
|
|
|
472
472
|
const mcp = program.command("mcp").description("MCP server integration");
|
|
473
473
|
mcp
|
|
474
474
|
.command("setup")
|
|
475
|
-
.description("Configure
|
|
475
|
+
.description("Configure AIsa MCP servers (from the live manifest) for AI agents")
|
|
476
476
|
.option("--agent <agent>", "Target agent: cursor, claude-desktop, windsurf, all")
|
|
477
|
+
.option("--all", "Configure every live server, not just the default set")
|
|
477
478
|
.action(mcpSetupAction);
|
|
478
479
|
mcp
|
|
479
480
|
.command("status")
|
|
480
|
-
.description("Check MCP
|
|
481
|
+
.description("Check MCP configuration and ping each configured endpoint")
|
|
481
482
|
.action(mcpStatusAction);
|
|
482
483
|
// ── Config ──
|
|
483
484
|
const configCmd = program.command("config").description("Manage CLI configuration");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aisa-one/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI for the
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"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",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"url": "https://github.com/AIsa-team/cli/issues"
|
|
61
61
|
},
|
|
62
62
|
"homepage": "https://aisa.one",
|
|
63
|
-
"author": "
|
|
63
|
+
"author": "AIsa Team <support@aisa.one> (https://aisa.one)"
|
|
64
64
|
}
|