@aisa-one/cli 0.6.8 → 0.6.10
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 +29 -1
- package/README.md +46 -34
- package/dist/catalog.d.ts +9 -3
- package/dist/catalog.js +27 -12
- package/dist/commands/api.js +7 -3
- package/dist/commands/connect-shared.js +1 -44
- package/dist/commands/connect-t2.js +18 -83
- package/dist/commands/connect-terminal.js +5 -99
- package/dist/commands/connect.js +66 -40
- package/dist/commands/flow.d.ts +0 -42
- package/dist/commands/flow.js +13 -77
- package/dist/commands/mcp.d.ts +9 -42
- package/dist/commands/mcp.js +49 -86
- package/dist/commands/oauth-login.js +1 -1
- package/dist/completion.js +1 -1
- package/dist/constants.d.ts +3 -50
- package/dist/constants.js +3 -50
- package/dist/index.js +3 -3
- package/dist/utils/client-key.d.ts +1 -0
- package/dist/utils/client-key.js +36 -0
- package/dist/utils/update-check.js +3 -5
- package/package.json +1 -1
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -1,47 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `aisa mcp setup` — write real, per-client MCP entries from the live manifest.
|
|
3
|
-
*
|
|
4
|
-
* Three rules, each one a lesson from the version this replaces:
|
|
5
|
-
*
|
|
6
|
-
* 1. The server list comes from the platform's discovery manifest at setup
|
|
7
|
-
* time, never from this file. Servers ship without a CLI release.
|
|
8
|
-
* 2. Nothing is written unless the manifest was actually fetched. The old
|
|
9
|
-
* command wrote first and verified never, which is how a dead hostname
|
|
10
|
-
* sat in users' configs across releases.
|
|
11
|
-
* 3. Each client gets the shape it executes: `{url}` for clients that dial
|
|
12
|
-
* HTTP themselves, an `npx mcp-remote` stdio bridge for the ones that only
|
|
13
|
-
* spawn processes. One shape for all three was the old bug.
|
|
14
|
-
*
|
|
15
|
-
* Auth is a choice the user already made: with an API key configured, entries
|
|
16
|
-
* carry it as a Bearer header (zero-interaction, but the key lands in each
|
|
17
|
-
* client's config file); without one, entries carry no credentials and the
|
|
18
|
-
* server's 401 challenge drives the client through the Clerk OAuth flow.
|
|
19
|
-
*/
|
|
1
|
+
/** Configure the unified Router MCP. Existing client entries are preserved. */
|
|
20
2
|
export interface LiveServer {
|
|
21
3
|
slug: string;
|
|
22
4
|
name: string;
|
|
23
5
|
endpoint: string;
|
|
24
|
-
toolCount
|
|
25
|
-
/**
|
|
26
|
-
* source of truth for what a server does; rendered on the connect page. */
|
|
6
|
+
toolCount?: number;
|
|
7
|
+
/** Capability description rendered on the connect page. */
|
|
27
8
|
description: string;
|
|
28
9
|
category: string;
|
|
29
10
|
}
|
|
30
11
|
export declare const stripped: (name: string) => string;
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* it was three servers and 72 tools behind what the host was actually serving.
|
|
37
|
-
* Anything derived from a running process cannot drift that way.
|
|
38
|
-
*
|
|
39
|
-
* The fallback is not defensive padding — the index is newer than some
|
|
40
|
-
* deployments, and a self-hosted gateway configured through `baseUrl` may
|
|
41
|
-
* never grow one. A user in either case should still get a working setup from
|
|
42
|
-
* a slightly stale list rather than an error.
|
|
43
|
-
*/
|
|
44
|
-
export declare function fetchLiveServers(): Promise<LiveServer[]>;
|
|
12
|
+
/** Router's fixed public MCP contract, independent of the legacy module catalog. */
|
|
13
|
+
export declare function unifiedMcpServer(): LiveServer;
|
|
14
|
+
export declare function isUnifiedMcpSelection(servers: LiveServer[]): boolean;
|
|
15
|
+
/** The unified entry is fixed; setup does not depend on a directory service. */
|
|
16
|
+
export declare function getMcpServers(): LiveServer[];
|
|
45
17
|
/** One config entry in the shape the client actually executes. */
|
|
46
18
|
export declare function buildEntry(shape: "url" | "stdio", endpoint: string, key: string | undefined): Record<string, unknown>;
|
|
47
19
|
export type WriteResult = {
|
|
@@ -51,12 +23,7 @@ export type WriteResult = {
|
|
|
51
23
|
ok: false;
|
|
52
24
|
reason: string;
|
|
53
25
|
};
|
|
54
|
-
/**
|
|
55
|
-
* Write the chosen servers (plus the docs server) into one client's config
|
|
56
|
-
* file. Shared by `aisa mcp setup` and `aisa connect` so the two entry points
|
|
57
|
-
* cannot drift in how they treat existing files: unparseable JSON is refused,
|
|
58
|
-
* never replaced, and only the known-dead legacy entry is cleaned up.
|
|
59
|
-
*/
|
|
26
|
+
/** Add the unified entry without deleting old AIsa, docs, or unrelated entries. */
|
|
60
27
|
export declare function writeClientConfig(agent: string, chosen: LiveServer[], key: string | undefined): WriteResult;
|
|
61
28
|
export declare function mcpSetupAction(options: {
|
|
62
29
|
agent?: string;
|
package/dist/commands/mcp.js
CHANGED
|
@@ -2,57 +2,27 @@ 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,
|
|
6
|
-
import { httpFetch
|
|
5
|
+
import { MCP_CONFIGS, MCP_URL } from "../constants.js";
|
|
6
|
+
import { httpFetch } from "../utils/http.js";
|
|
7
7
|
import { getAccessToken } from "../config.js";
|
|
8
|
+
import { clientConfigKey } from "../utils/client-key.js";
|
|
8
9
|
import { detectClients } from "./connect.js";
|
|
9
10
|
import { join } from "node:path";
|
|
10
11
|
export const stripped = (name) => name.replace(/^AIsa\s+/i, "");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
toolCount: s.toolCount ?? s.tools?.length ?? 0,
|
|
19
|
-
description: s.description ?? "",
|
|
20
|
-
category: s.category ?? "Other",
|
|
21
|
-
}));
|
|
12
|
+
/** Router's fixed public MCP contract, independent of the legacy module catalog. */
|
|
13
|
+
export function unifiedMcpServer() {
|
|
14
|
+
return {
|
|
15
|
+
slug: "router", name: "AIsa Router", endpoint: MCP_URL,
|
|
16
|
+
description: "One AIsa connection to discover and use tools through the Router.",
|
|
17
|
+
category: "AIsa",
|
|
18
|
+
};
|
|
22
19
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!res.ok)
|
|
26
|
-
throw new Error(`${url} answered HTTP ${res.status}`);
|
|
27
|
-
const doc = (await res.json());
|
|
28
|
-
const live = toLive(doc.servers ?? []);
|
|
29
|
-
if (live.length === 0)
|
|
30
|
-
throw new Error(`${url} lists no live servers`);
|
|
31
|
-
return live;
|
|
20
|
+
export function isUnifiedMcpSelection(servers) {
|
|
21
|
+
return servers.length === 1 && servers[0].slug === "router" && servers[0].endpoint === MCP_URL;
|
|
32
22
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* The index is preferred and the manifest is the fallback, in that order for
|
|
37
|
-
* one reason: the manifest is a snapshot committed by hand, and on 2026-08-25
|
|
38
|
-
* it was three servers and 72 tools behind what the host was actually serving.
|
|
39
|
-
* Anything derived from a running process cannot drift that way.
|
|
40
|
-
*
|
|
41
|
-
* The fallback is not defensive padding — the index is newer than some
|
|
42
|
-
* deployments, and a self-hosted gateway configured through `baseUrl` may
|
|
43
|
-
* never grow one. A user in either case should still get a working setup from
|
|
44
|
-
* a slightly stale list rather than an error.
|
|
45
|
-
*/
|
|
46
|
-
export async function fetchLiveServers() {
|
|
47
|
-
try {
|
|
48
|
-
// One attempt only: there is somewhere else to go, so spending a retry
|
|
49
|
-
// budget here just delays the fallback. The manifest is the last resort
|
|
50
|
-
// and gets the full three.
|
|
51
|
-
return await readSource(MCP_CATALOG_URL, 1);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return await readSource(MCP_MANIFEST_URL, MAX_ATTEMPTS);
|
|
55
|
-
}
|
|
23
|
+
/** The unified entry is fixed; setup does not depend on a directory service. */
|
|
24
|
+
export function getMcpServers() {
|
|
25
|
+
return [unifiedMcpServer()];
|
|
56
26
|
}
|
|
57
27
|
/** One config entry in the shape the client actually executes. */
|
|
58
28
|
export function buildEntry(shape, endpoint, key) {
|
|
@@ -69,15 +39,10 @@ export function buildEntry(shape, endpoint, key) {
|
|
|
69
39
|
args.push("--transport", "http-only");
|
|
70
40
|
return { command: "npx", args };
|
|
71
41
|
}
|
|
72
|
-
/**
|
|
73
|
-
const DEAD_URL = "https://docs.aisa.one/mcp";
|
|
74
|
-
/**
|
|
75
|
-
* Write the chosen servers (plus the docs server) into one client's config
|
|
76
|
-
* file. Shared by `aisa mcp setup` and `aisa connect` so the two entry points
|
|
77
|
-
* cannot drift in how they treat existing files: unparseable JSON is refused,
|
|
78
|
-
* never replaced, and only the known-dead legacy entry is cleaned up.
|
|
79
|
-
*/
|
|
42
|
+
/** Add the unified entry without deleting old AIsa, docs, or unrelated entries. */
|
|
80
43
|
export function writeClientConfig(agent, chosen, key) {
|
|
44
|
+
if (!isUnifiedMcpSelection(chosen))
|
|
45
|
+
return { ok: false, reason: "Only the unified AIsa Router MCP can be configured" };
|
|
81
46
|
const config = MCP_CONFIGS[agent];
|
|
82
47
|
if (!config)
|
|
83
48
|
return { ok: false, reason: `unknown client "${agent}"` };
|
|
@@ -97,20 +62,12 @@ export function writeClientConfig(agent, chosen, key) {
|
|
|
97
62
|
ensureDir(join(filePath, ".."));
|
|
98
63
|
}
|
|
99
64
|
const entries = existing[config.key] || {};
|
|
100
|
-
// Clean up the dead entry earlier releases wrote, and only that one: an
|
|
101
|
-
// "aisa" entry the user pointed somewhere else on purpose stays.
|
|
102
|
-
const stale = entries["aisa"];
|
|
103
|
-
if (stale?.url === DEAD_URL)
|
|
104
|
-
delete entries["aisa"];
|
|
105
65
|
for (const s of chosen) {
|
|
106
66
|
entries[`aisa-${s.slug}`] = buildEntry(config.shape, s.endpoint, key);
|
|
107
67
|
}
|
|
108
|
-
// The docs-search MCP is tiny, unauthenticated, and answers "how do I call
|
|
109
|
-
// this" questions — always included.
|
|
110
|
-
entries["aisa-docs"] = buildEntry(config.shape, DOCS_MCP_URL, undefined);
|
|
111
68
|
existing[config.key] = entries;
|
|
112
69
|
writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
113
|
-
return { ok: true, written: chosen.length
|
|
70
|
+
return { ok: true, written: chosen.length };
|
|
114
71
|
}
|
|
115
72
|
export async function mcpSetupAction(options) {
|
|
116
73
|
const requested = options.agent && options.agent !== "all" ? [options.agent] : Object.keys(MCP_CONFIGS);
|
|
@@ -137,24 +94,12 @@ export async function mcpSetupAction(options) {
|
|
|
137
94
|
process.exitCode = 1;
|
|
138
95
|
return;
|
|
139
96
|
}
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
catch (e) {
|
|
147
|
-
error(`Could not read the MCP manifest at ${MCP_MANIFEST_URL}: ${e.message}`);
|
|
148
|
-
hint("Nothing was written. Check your network and try again.");
|
|
149
|
-
process.exitCode = 1;
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const chosen = options.all
|
|
153
|
-
? servers
|
|
154
|
-
: servers.filter((s) => MCP_DEFAULT_SLUGS.includes(s.slug));
|
|
155
|
-
const skipped = servers.length - chosen.length;
|
|
156
|
-
const key = await getAccessToken();
|
|
157
|
-
if (key) {
|
|
97
|
+
const servers = getMcpServers();
|
|
98
|
+
// --all remains accepted for existing scripts; there is only one entry.
|
|
99
|
+
const chosen = servers;
|
|
100
|
+
info("Adds aisa-router only. Existing MCP entries are preserved; remove old domain entries manually.");
|
|
101
|
+
const credential = await getAccessToken();
|
|
102
|
+
if (credential) {
|
|
158
103
|
info("Using your configured API key (Bearer). It will be written into each client's config file.");
|
|
159
104
|
}
|
|
160
105
|
else {
|
|
@@ -171,11 +116,23 @@ export async function mcpSetupAction(options) {
|
|
|
171
116
|
console.log(` ${chalk.cyan(MCP_CONFIGS[agent].path)} (${agent}, ${MCP_CONFIGS[agent].shape})`);
|
|
172
117
|
}
|
|
173
118
|
for (const s of chosen) {
|
|
174
|
-
console.log(` ${chalk.gray(`aisa-${s.slug}`)} ${stripped(s.name)}
|
|
119
|
+
console.log(` ${chalk.gray(`aisa-${s.slug}`)} ${stripped(s.name)}`);
|
|
175
120
|
}
|
|
176
121
|
hint("Nothing was written. Re-run with --yes to apply, or use `aisa connect` to choose interactively.");
|
|
177
122
|
return;
|
|
178
123
|
}
|
|
124
|
+
// A client config never refreshes an OAuth token, so it gets the durable
|
|
125
|
+
// key instead — otherwise every entry stops working a day from now.
|
|
126
|
+
let key;
|
|
127
|
+
try {
|
|
128
|
+
key = credential ? await clientConfigKey(credential) : undefined;
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
error(e.message);
|
|
132
|
+
hint("Nothing was written.");
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
179
136
|
for (const agent of targets) {
|
|
180
137
|
const config = MCP_CONFIGS[agent];
|
|
181
138
|
const result = writeClientConfig(agent, chosen, key);
|
|
@@ -186,12 +143,10 @@ export async function mcpSetupAction(options) {
|
|
|
186
143
|
}
|
|
187
144
|
console.log(` ${chalk.green("✓")} ${agent}: ${result.written} servers (${config.shape}) → ${config.path}`);
|
|
188
145
|
}
|
|
189
|
-
success(`Configured
|
|
146
|
+
success(`Configured the unified AIsa Router MCP for ${targets.length} client(s)`);
|
|
190
147
|
for (const s of chosen) {
|
|
191
|
-
console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)}
|
|
148
|
+
console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)}`);
|
|
192
149
|
}
|
|
193
|
-
if (skipped > 0)
|
|
194
|
-
hint(`${skipped} more live servers available — run with --all to configure every one`);
|
|
195
150
|
hint("Restart your agent/editor to activate");
|
|
196
151
|
}
|
|
197
152
|
/**
|
|
@@ -223,10 +178,18 @@ export async function mcpStatusAction() {
|
|
|
223
178
|
console.log(` ${chalk.gray("○")} ${agent}: not configured`);
|
|
224
179
|
continue;
|
|
225
180
|
}
|
|
226
|
-
|
|
181
|
+
const unified = ours.filter(([, entry]) => {
|
|
182
|
+
const e = entry;
|
|
183
|
+
return (e.url ?? e.args?.find((a) => a.startsWith("http"))) === MCP_URL;
|
|
184
|
+
}).length;
|
|
185
|
+
console.log(` ${unified ? chalk.green("✓") : chalk.yellow("!")} ${agent}: ${unified} unified, ${ours.length - unified} other AIsa entries (not checked)`);
|
|
227
186
|
for (const [name, entry] of ours) {
|
|
228
187
|
const e = entry;
|
|
229
188
|
const endpoint = e.url ?? e.args?.find((a) => a.startsWith("http"));
|
|
189
|
+
if (endpoint && endpoint !== MCP_URL) {
|
|
190
|
+
console.log(` ${name}: ${endpoint} — existing entry preserved (not checked); remove manually from ${filePath} if no longer needed`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
230
193
|
if (endpoint) {
|
|
231
194
|
const users = endpoints.get(endpoint) ?? [];
|
|
232
195
|
users.push(`${agent}/${name}`);
|
|
@@ -307,7 +307,7 @@ Default: open a browser this person can actually use (SSH/CI/no display skip the
|
|
|
307
307
|
|
|
308
308
|
If this process already timed out, or the URL/code is from an older run, start a fresh aisa login and use the new URL and its result. Do not reuse a stale paste.
|
|
309
309
|
|
|
310
|
-
No browser and no TTY: this CLI cannot complete OAuth here. Normal setup is native MCP OAuth at https://
|
|
310
|
+
No browser and no TTY: this CLI cannot complete OAuth here. Normal setup is native MCP OAuth at https://mcp.aisa.one/mcp.
|
|
311
311
|
|
|
312
312
|
Do not report connected unless login stored credentials and the following balance check succeeded. aisa whoami and stored local credentials are not proof. Scripts/CI: AISA_API_KEY or --key.
|
|
313
313
|
`;
|
package/dist/completion.js
CHANGED
|
@@ -7,7 +7,7 @@ export function isShell(value) {
|
|
|
7
7
|
}
|
|
8
8
|
// ── Dynamic candidates, all cache-only ───────────────────────────────────────
|
|
9
9
|
function cachedProviders() {
|
|
10
|
-
return readCache(`catalog/${cacheScope()}/category.json`)?.data.apis || [];
|
|
10
|
+
return readCache(`catalog-v2/${cacheScope()}/category.json`)?.data.apis || [];
|
|
11
11
|
}
|
|
12
12
|
function providerIds() {
|
|
13
13
|
return cachedProviders().map((p) => ({
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,42 +1,11 @@
|
|
|
1
|
-
export declare const VERSION = "0.6.
|
|
1
|
+
export declare const VERSION = "0.6.10";
|
|
2
2
|
/** Default for chat when no model is configured or supplied explicitly. */
|
|
3
3
|
export declare const DEFAULT_CHAT_MODEL = "deepseek-v4.1-flash";
|
|
4
4
|
/** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
|
|
5
5
|
export declare const BASE_URL = "https://api.aisa.one";
|
|
6
6
|
export declare const ENV_VAR_NAME = "AISA_API_KEY";
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
* exist and where they live. The CLI deliberately knows no server list of its
|
|
10
|
-
* own: servers ship and retire on the platform side, and reading the manifest
|
|
11
|
-
* at setup time means a new server works without a CLI release (the same
|
|
12
|
-
* philosophy as live catalog/MCP discovery, applied to the fast-moving side).
|
|
13
|
-
*
|
|
14
|
-
* History: until v0.2.4 this file hardcoded MCP_URL = "https://docs.aisa.one/mcp",
|
|
15
|
-
* a host that does not resolve — every `aisa mcp setup` wrote a dead entry into
|
|
16
|
-
* users' configs, and nothing noticed because the command had no tests.
|
|
17
|
-
*/
|
|
18
|
-
export declare const MCP_MANIFEST_URL = "https://aisa.one/.well-known/mcp.json";
|
|
19
|
-
/**
|
|
20
|
-
* The live server index, served by the MCP host itself.
|
|
21
|
-
*
|
|
22
|
-
* Preferred over the manifest above, which is generated offline and committed
|
|
23
|
-
* by hand: being a snapshot it drifts. Measured 2026-08-25 it named 11 servers
|
|
24
|
-
* and 188 tools while mcp.aisa.one was serving 14 and 260 — agentmail (49
|
|
25
|
-
* tools), seo (21) and creator-discovery (2) missing outright, so every user
|
|
26
|
-
* of this CLI was offered 72 fewer tools than exist.
|
|
27
|
-
*
|
|
28
|
-
* It is also 12 KB against the manifest's 137 KB. Most of that difference is
|
|
29
|
-
* tool names, of which this CLI uses exactly one thing: `tools.length`, to
|
|
30
|
-
* print a count. The index carries `toolCount` instead; a client that wants
|
|
31
|
-
* the names is about to connect anyway, and `tools/list` is authoritative.
|
|
32
|
-
*
|
|
33
|
-
* The manifest stays as the fallback — this endpoint is newer than some
|
|
34
|
-
* deployments — and as the public document for readers who want the roadmap
|
|
35
|
-
* of planned servers, which a running process cannot honestly report.
|
|
36
|
-
*/
|
|
37
|
-
export declare const MCP_CATALOG_URL = "https://mcp.aisa.one/servers";
|
|
38
|
-
/** Mintlify's docs-search MCP (3 tools: search/read/feedback). Unauthenticated. */
|
|
39
|
-
export declare const DOCS_MCP_URL = "https://aisa.one/docs/mcp";
|
|
7
|
+
/** The only business MCP resource configured by this CLI. */
|
|
8
|
+
export declare const MCP_URL = "https://mcp.aisa.one/mcp";
|
|
40
9
|
/** Where credit is added. Payment always finishes in a browser — Stripe's
|
|
41
10
|
* hosted page owns the card details, and 3-D Secure needs one. */
|
|
42
11
|
/** The account surface. Printed where someone can act on it, not linked. */
|
|
@@ -50,22 +19,6 @@ export declare const LLM_RESPONSES_BASE_URL = "https://api.aisa.one/v1";
|
|
|
50
19
|
/** The provider name written into Codex's config.toml. Frozen: renaming it
|
|
51
20
|
* orphans the block we would otherwise remove cleanly. */
|
|
52
21
|
export declare const AISA_PROVIDER_ID = "aisa";
|
|
53
|
-
/**
|
|
54
|
-
* Servers configured by default. Eleven entries per client is a lot of config;
|
|
55
|
-
* these five cover the broadest agent needs, and `--all` configures everything
|
|
56
|
-
* the manifest lists as live.
|
|
57
|
-
*/
|
|
58
|
-
export declare const MCP_DEFAULT_SLUGS: readonly string[];
|
|
59
|
-
/**
|
|
60
|
-
* Why one and not five.
|
|
61
|
-
*
|
|
62
|
-
* Each server is its own OAuth resource — the authorization URL carries
|
|
63
|
-
* `resource=https://mcp.aisa.one/<slug>/mcp` (RFC 8707) — so a keyless
|
|
64
|
-
* install authorises once per server. Defaulting to five meant five browser
|
|
65
|
-
* round trips before anything worked, observed 2026-08-20. Web search is the
|
|
66
|
-
* one every agent reaches for; the rest are one tick away, and a configured
|
|
67
|
-
* API key skips authorization for all of them anyway.
|
|
68
|
-
*/
|
|
69
22
|
export declare const AGENT_DIRS: Record<string, string>;
|
|
70
23
|
/**
|
|
71
24
|
* Per-client config shape. This is the part v0.2.4 got wrong even apart from
|
package/dist/constants.js
CHANGED
|
@@ -1,42 +1,11 @@
|
|
|
1
|
-
export const VERSION = "0.6.
|
|
1
|
+
export const VERSION = "0.6.10";
|
|
2
2
|
/** Default for chat when no model is configured or supplied explicitly. */
|
|
3
3
|
export const DEFAULT_CHAT_MODEL = "deepseek-v4.1-flash";
|
|
4
4
|
/** Root of the platform. Per-surface bases are derived in api.ts#resolveBases. */
|
|
5
5
|
export const BASE_URL = "https://api.aisa.one";
|
|
6
6
|
export const ENV_VAR_NAME = "AISA_API_KEY";
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
* exist and where they live. The CLI deliberately knows no server list of its
|
|
10
|
-
* own: servers ship and retire on the platform side, and reading the manifest
|
|
11
|
-
* at setup time means a new server works without a CLI release (the same
|
|
12
|
-
* philosophy as live catalog/MCP discovery, applied to the fast-moving side).
|
|
13
|
-
*
|
|
14
|
-
* History: until v0.2.4 this file hardcoded MCP_URL = "https://docs.aisa.one/mcp",
|
|
15
|
-
* a host that does not resolve — every `aisa mcp setup` wrote a dead entry into
|
|
16
|
-
* users' configs, and nothing noticed because the command had no tests.
|
|
17
|
-
*/
|
|
18
|
-
export const MCP_MANIFEST_URL = "https://aisa.one/.well-known/mcp.json";
|
|
19
|
-
/**
|
|
20
|
-
* The live server index, served by the MCP host itself.
|
|
21
|
-
*
|
|
22
|
-
* Preferred over the manifest above, which is generated offline and committed
|
|
23
|
-
* by hand: being a snapshot it drifts. Measured 2026-08-25 it named 11 servers
|
|
24
|
-
* and 188 tools while mcp.aisa.one was serving 14 and 260 — agentmail (49
|
|
25
|
-
* tools), seo (21) and creator-discovery (2) missing outright, so every user
|
|
26
|
-
* of this CLI was offered 72 fewer tools than exist.
|
|
27
|
-
*
|
|
28
|
-
* It is also 12 KB against the manifest's 137 KB. Most of that difference is
|
|
29
|
-
* tool names, of which this CLI uses exactly one thing: `tools.length`, to
|
|
30
|
-
* print a count. The index carries `toolCount` instead; a client that wants
|
|
31
|
-
* the names is about to connect anyway, and `tools/list` is authoritative.
|
|
32
|
-
*
|
|
33
|
-
* The manifest stays as the fallback — this endpoint is newer than some
|
|
34
|
-
* deployments — and as the public document for readers who want the roadmap
|
|
35
|
-
* of planned servers, which a running process cannot honestly report.
|
|
36
|
-
*/
|
|
37
|
-
export const MCP_CATALOG_URL = "https://mcp.aisa.one/servers";
|
|
38
|
-
/** Mintlify's docs-search MCP (3 tools: search/read/feedback). Unauthenticated. */
|
|
39
|
-
export const DOCS_MCP_URL = "https://aisa.one/docs/mcp";
|
|
7
|
+
/** The only business MCP resource configured by this CLI. */
|
|
8
|
+
export const MCP_URL = "https://mcp.aisa.one/mcp";
|
|
40
9
|
/** Where credit is added. Payment always finishes in a browser — Stripe's
|
|
41
10
|
* hosted page owns the card details, and 3-D Secure needs one. */
|
|
42
11
|
/** The account surface. Printed where someone can act on it, not linked. */
|
|
@@ -50,22 +19,6 @@ export const LLM_RESPONSES_BASE_URL = "https://api.aisa.one/v1";
|
|
|
50
19
|
/** The provider name written into Codex's config.toml. Frozen: renaming it
|
|
51
20
|
* orphans the block we would otherwise remove cleanly. */
|
|
52
21
|
export const AISA_PROVIDER_ID = "aisa";
|
|
53
|
-
/**
|
|
54
|
-
* Servers configured by default. Eleven entries per client is a lot of config;
|
|
55
|
-
* these five cover the broadest agent needs, and `--all` configures everything
|
|
56
|
-
* the manifest lists as live.
|
|
57
|
-
*/
|
|
58
|
-
export const MCP_DEFAULT_SLUGS = ["web-search"];
|
|
59
|
-
/**
|
|
60
|
-
* Why one and not five.
|
|
61
|
-
*
|
|
62
|
-
* Each server is its own OAuth resource — the authorization URL carries
|
|
63
|
-
* `resource=https://mcp.aisa.one/<slug>/mcp` (RFC 8707) — so a keyless
|
|
64
|
-
* install authorises once per server. Defaulting to five meant five browser
|
|
65
|
-
* round trips before anything worked, observed 2026-08-20. Web search is the
|
|
66
|
-
* one every agent reaches for; the rest are one tick away, and a configured
|
|
67
|
-
* API key skips authorization for all of them anyway.
|
|
68
|
-
*/
|
|
69
22
|
export const AGENT_DIRS = {
|
|
70
23
|
claude: "~/.claude/skills/",
|
|
71
24
|
cursor: "~/.cursor/skills/",
|
package/dist/index.js
CHANGED
|
@@ -229,7 +229,7 @@ skills
|
|
|
229
229
|
// Top-level so the zero-install one-liner works: `npx @aisa-one/cli connect`.
|
|
230
230
|
program
|
|
231
231
|
.command("connect")
|
|
232
|
-
.description("Connect AIsa MCP
|
|
232
|
+
.description("Connect the unified AIsa Router MCP and models to your coding agents")
|
|
233
233
|
.option("--no-open", "Print the URL instead of opening the browser")
|
|
234
234
|
.option("--port <port>", "Bind a specific port (default: random)")
|
|
235
235
|
.option("--dry-run", "Show what would be configured without writing anything")
|
|
@@ -248,9 +248,9 @@ program
|
|
|
248
248
|
const mcp = program.command("mcp").description("MCP server integration");
|
|
249
249
|
mcp
|
|
250
250
|
.command("setup")
|
|
251
|
-
.description("Configure
|
|
251
|
+
.description("Configure the unified AIsa Router MCP for AI agents")
|
|
252
252
|
.option("--agent <agent>", "Target agent: cursor, claude-desktop, all")
|
|
253
|
-
.option("--all", "
|
|
253
|
+
.option("--all", "Compatibility option: configures the single AIsa Router MCP")
|
|
254
254
|
.option("--yes", "Write the files. Without it, print what would change and stop")
|
|
255
255
|
.action(wrap(mcpSetupAction));
|
|
256
256
|
mcp
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function clientConfigKey(credential: string): Promise<string>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BASE_URL } from "../constants.js";
|
|
2
|
+
import { httpFetch } from "./http.js";
|
|
3
|
+
/**
|
|
4
|
+
* The credential a third-party client config gets.
|
|
5
|
+
*
|
|
6
|
+
* Since 0.6.0 browser login stores an OAuth session: this CLI refreshes the
|
|
7
|
+
* access token, but a token copied into Claude Code, Codex or Cursor stays
|
|
8
|
+
* the snapshot it was — and expires in 24 hours, taking every MCP entry and
|
|
9
|
+
* model provider with it the next day. Those configs need an API key, so an
|
|
10
|
+
* OAuth access token is traded for the durable "aisa cli" key at
|
|
11
|
+
* /v1/keys/mint. The server returns the same managed key on every call, so
|
|
12
|
+
* nothing piles up; the CLI's own session is left as it is.
|
|
13
|
+
*
|
|
14
|
+
* A static key (`login --key`, AISA_API_KEY) already is one and passes
|
|
15
|
+
* through untouched — the mint endpoint refuses API keys anyway.
|
|
16
|
+
*/
|
|
17
|
+
const MINT_URL = `${BASE_URL}/v1/keys/mint`;
|
|
18
|
+
export async function clientConfigKey(credential) {
|
|
19
|
+
if (credential.startsWith("sk-"))
|
|
20
|
+
return credential;
|
|
21
|
+
const res = await httpFetch(MINT_URL, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: { Authorization: `Bearer ${credential}`, "Content-Type": "application/json" },
|
|
24
|
+
// Named explicitly; no body means the same thing to the server.
|
|
25
|
+
body: JSON.stringify({ client: "cli" }),
|
|
26
|
+
timeoutMs: 20_000,
|
|
27
|
+
});
|
|
28
|
+
if (res.status === 404) {
|
|
29
|
+
throw new Error("this deployment cannot issue CLI keys — copy one from https://console.aisa.one/api-keys and run: aisa login --key <key>");
|
|
30
|
+
}
|
|
31
|
+
const minted = (await res.json().catch(() => ({})));
|
|
32
|
+
if (!res.ok || !minted.key) {
|
|
33
|
+
throw new Error(`could not issue a key for your clients: ${minted.error ?? `HTTP ${res.status}`}`);
|
|
34
|
+
}
|
|
35
|
+
return minted.key;
|
|
36
|
+
}
|
|
@@ -5,11 +5,9 @@ import { httpFetch } from "./http.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Throttled, best-effort check for a newer published version.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* (
|
|
10
|
-
*
|
|
11
|
-
* and skills index (see cache.ts), so every other run within the window
|
|
12
|
-
* reads the last answer off disk instead of asking again.
|
|
8
|
+
* The npm-registry lookup is cached once a day like the API catalog
|
|
9
|
+
* and skills index (see cache.ts), so runs within the window
|
|
10
|
+
* read the last answer off disk instead of asking again.
|
|
13
11
|
*
|
|
14
12
|
* Never throws and never blocks longer than FETCH_TIMEOUT_MS. Offline, a
|
|
15
13
|
* timeout, or a non-200 all resolve to "nothing to report" rather than an
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aisa-one/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.10",
|
|
4
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",
|