@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.
@@ -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: number;
25
- /** One-paragraph capability description from the manifest — the single
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
- * Which servers exist, asked of the host that runs them.
33
- *
34
- * The index is preferred and the manifest is the fallback, in that order for
35
- * one reason: the manifest is a snapshot committed by hand, and on 2026-08-25
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;
@@ -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, 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
+ 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
- function toLive(servers) {
12
- return servers
13
- .filter((s) => s.status === "live" && s.slug && s.transport?.endpoint)
14
- .map((s) => ({
15
- slug: s.slug,
16
- name: s.name ?? s.slug,
17
- endpoint: s.transport.endpoint,
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
- async function readSource(url, attempts) {
24
- const res = await httpFetch(url, { idempotent: true, maxAttempts: attempts, timeoutMs: 15_000 });
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
- * Which servers exist, asked of the host that runs them.
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
- /** The dead entry every pre-v0.3 setup wrote. Remove it on sight. */
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 + 1 };
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
- // Fetch before touching any file: a setup that cannot see the manifest has
141
- // nothing true to write.
142
- let servers;
143
- try {
144
- servers = await fetchLiveServers();
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)} (${s.toolCount} tools)`);
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 ${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)`);
190
147
  for (const s of chosen) {
191
- console.log(` ${chalk.cyan(`aisa-${s.slug}`)} ${stripped(s.name)} (${s.toolCount} tools)`);
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
- 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)`);
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://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
  `;
@@ -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) => ({
@@ -1,42 +1,11 @@
1
- export declare const VERSION = "0.6.8";
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.8";
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
@@ -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
- * `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.8",
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",