@aisa-one/cli 0.6.9 → 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.
@@ -2,58 +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, MCP_MANIFEST_URL, MCP_CATALOG_URL, DOCS_MCP_URL, MCP_DEFAULT_SLUGS } from "../constants.js";
6
- import { httpFetch, MAX_ATTEMPTS } from "../utils/http.js";
5
+ import { MCP_CONFIGS, MCP_URL } from "../constants.js";
6
+ import { httpFetch } from "../utils/http.js";
7
7
  import { getAccessToken } from "../config.js";
8
8
  import { clientConfigKey } from "../utils/client-key.js";
9
9
  import { detectClients } from "./connect.js";
10
10
  import { join } from "node:path";
11
11
  export const stripped = (name) => name.replace(/^AIsa\s+/i, "");
12
- function toLive(servers) {
13
- return servers
14
- .filter((s) => s.status === "live" && s.slug && s.transport?.endpoint)
15
- .map((s) => ({
16
- slug: s.slug,
17
- name: s.name ?? s.slug,
18
- endpoint: s.transport.endpoint,
19
- toolCount: s.toolCount ?? s.tools?.length ?? 0,
20
- description: s.description ?? "",
21
- category: s.category ?? "Other",
22
- }));
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
+ };
23
19
  }
24
- async function readSource(url, attempts) {
25
- const res = await httpFetch(url, { idempotent: true, maxAttempts: attempts, timeoutMs: 15_000 });
26
- if (!res.ok)
27
- throw new Error(`${url} answered HTTP ${res.status}`);
28
- const doc = (await res.json());
29
- const live = toLive(doc.servers ?? []);
30
- if (live.length === 0)
31
- throw new Error(`${url} lists no live servers`);
32
- return live;
20
+ export function isUnifiedMcpSelection(servers) {
21
+ return servers.length === 1 && servers[0].slug === "router" && servers[0].endpoint === MCP_URL;
33
22
  }
34
- /**
35
- * Which servers exist, asked of the host that runs them.
36
- *
37
- * The index is preferred and the manifest is the fallback, in that order for
38
- * one reason: the manifest is a snapshot committed by hand, and on 2026-08-25
39
- * it was three servers and 72 tools behind what the host was actually serving.
40
- * Anything derived from a running process cannot drift that way.
41
- *
42
- * The fallback is not defensive padding — the index is newer than some
43
- * deployments, and a self-hosted gateway configured through `baseUrl` may
44
- * never grow one. A user in either case should still get a working setup from
45
- * a slightly stale list rather than an error.
46
- */
47
- export async function fetchLiveServers() {
48
- try {
49
- // One attempt only: there is somewhere else to go, so spending a retry
50
- // budget here just delays the fallback. The manifest is the last resort
51
- // and gets the full three.
52
- return await readSource(MCP_CATALOG_URL, 1);
53
- }
54
- catch {
55
- return await readSource(MCP_MANIFEST_URL, MAX_ATTEMPTS);
56
- }
23
+ /** The unified entry is fixed; setup does not depend on a directory service. */
24
+ export function getMcpServers() {
25
+ return [unifiedMcpServer()];
57
26
  }
58
27
  /** One config entry in the shape the client actually executes. */
59
28
  export function buildEntry(shape, endpoint, key) {
@@ -70,15 +39,10 @@ export function buildEntry(shape, endpoint, key) {
70
39
  args.push("--transport", "http-only");
71
40
  return { command: "npx", args };
72
41
  }
73
- /** The dead entry every pre-v0.3 setup wrote. Remove it on sight. */
74
- const DEAD_URL = "https://docs.aisa.one/mcp";
75
- /**
76
- * Write the chosen servers (plus the docs server) into one client's config
77
- * file. Shared by `aisa mcp setup` and `aisa connect` so the two entry points
78
- * cannot drift in how they treat existing files: unparseable JSON is refused,
79
- * never replaced, and only the known-dead legacy entry is cleaned up.
80
- */
42
+ /** Add the unified entry without deleting old AIsa, docs, or unrelated entries. */
81
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" };
82
46
  const config = MCP_CONFIGS[agent];
83
47
  if (!config)
84
48
  return { ok: false, reason: `unknown client "${agent}"` };
@@ -98,20 +62,12 @@ export function writeClientConfig(agent, chosen, key) {
98
62
  ensureDir(join(filePath, ".."));
99
63
  }
100
64
  const entries = existing[config.key] || {};
101
- // Clean up the dead entry earlier releases wrote, and only that one: an
102
- // "aisa" entry the user pointed somewhere else on purpose stays.
103
- const stale = entries["aisa"];
104
- if (stale?.url === DEAD_URL)
105
- delete entries["aisa"];
106
65
  for (const s of chosen) {
107
66
  entries[`aisa-${s.slug}`] = buildEntry(config.shape, s.endpoint, key);
108
67
  }
109
- // The docs-search MCP is tiny, unauthenticated, and answers "how do I call
110
- // this" questions — always included.
111
- entries["aisa-docs"] = buildEntry(config.shape, DOCS_MCP_URL, undefined);
112
68
  existing[config.key] = entries;
113
69
  writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
114
- return { ok: true, written: chosen.length + 1 };
70
+ return { ok: true, written: chosen.length };
115
71
  }
116
72
  export async function mcpSetupAction(options) {
117
73
  const requested = options.agent && options.agent !== "all" ? [options.agent] : Object.keys(MCP_CONFIGS);
@@ -138,22 +94,10 @@ export async function mcpSetupAction(options) {
138
94
  process.exitCode = 1;
139
95
  return;
140
96
  }
141
- // Fetch before touching any file: a setup that cannot see the manifest has
142
- // nothing true to write.
143
- let servers;
144
- try {
145
- servers = await fetchLiveServers();
146
- }
147
- catch (e) {
148
- error(`Could not read the MCP manifest at ${MCP_MANIFEST_URL}: ${e.message}`);
149
- hint("Nothing was written. Check your network and try again.");
150
- process.exitCode = 1;
151
- return;
152
- }
153
- const chosen = options.all
154
- ? servers
155
- : servers.filter((s) => MCP_DEFAULT_SLUGS.includes(s.slug));
156
- const skipped = servers.length - chosen.length;
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.");
157
101
  const credential = await getAccessToken();
158
102
  if (credential) {
159
103
  info("Using your configured API key (Bearer). It will be written into each client's config file.");
@@ -172,7 +116,7 @@ export async function mcpSetupAction(options) {
172
116
  console.log(` ${chalk.cyan(MCP_CONFIGS[agent].path)} (${agent}, ${MCP_CONFIGS[agent].shape})`);
173
117
  }
174
118
  for (const s of chosen) {
175
- console.log(` ${chalk.gray(`aisa-${s.slug}`)} ${stripped(s.name)} (${s.toolCount} tools)`);
119
+ console.log(` ${chalk.gray(`aisa-${s.slug}`)} ${stripped(s.name)}`);
176
120
  }
177
121
  hint("Nothing was written. Re-run with --yes to apply, or use `aisa connect` to choose interactively.");
178
122
  return;
@@ -199,12 +143,10 @@ export async function mcpSetupAction(options) {
199
143
  }
200
144
  console.log(` ${chalk.green("✓")} ${agent}: ${result.written} servers (${config.shape}) → ${config.path}`);
201
145
  }
202
- success(`Configured ${chosen.length} live servers plus docs search for ${targets.length} client(s)`);
146
+ success(`Configured the unified AIsa Router MCP for ${targets.length} client(s)`);
203
147
  for (const s of chosen) {
204
- console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)} (${s.toolCount} tools)`);
148
+ console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)}`);
205
149
  }
206
- if (skipped > 0)
207
- hint(`${skipped} more live servers available — run with --all to configure every one`);
208
150
  hint("Restart your agent/editor to activate");
209
151
  }
210
152
  /**
@@ -236,10 +178,18 @@ export async function mcpStatusAction() {
236
178
  console.log(` ${chalk.gray("○")} ${agent}: not configured`);
237
179
  continue;
238
180
  }
239
- console.log(` ${chalk.green("✓")} ${agent}: ${ours.length} entries`);
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)`);
240
186
  for (const [name, entry] of ours) {
241
187
  const e = entry;
242
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
+ }
243
193
  if (endpoint) {
244
194
  const users = endpoints.get(endpoint) ?? [];
245
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://tools.aisa.one/mcp.
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
  `;
@@ -1,42 +1,11 @@
1
- export declare const VERSION = "0.6.9";
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
- * The MCP discovery manifest — the single source of truth for which servers
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.9";
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
- * The MCP discovery manifest — the single source of truth for which servers
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 servers to your local coding agents via a one-shot local page")
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 AIsa MCP servers (from the live manifest) for AI agents")
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", "Configure every live server, not just the default set")
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
@@ -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
- * `connect` already makes a network call before it does anything else
9
- * (fetchLiveServers), so piggybacking one more cheap lookup costs nothing —
10
- * but only once a day: the npm-registry hit is cached like the API catalog
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.9",
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",