@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/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.10] — 2026-11-09
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Keep `connect` and its client/model setup flow, but configure only
|
|
15
|
+
`aisa-router` at `https://mcp.aisa.one/mcp`. Remove the directory-service dependency;
|
|
16
|
+
stop installing per-domain endpoints or an extra docs MCP.
|
|
17
|
+
- Preserve existing client entries for manual cleanup. `mcp setup --all` now
|
|
18
|
+
adds the same single Router entry; `mcp status` skips legacy endpoints.
|
|
19
|
+
- Reject resuming setup runs that still contain retired MCP endpoints.
|
|
20
|
+
|
|
21
|
+
## [0.6.9] — 2026-10-21
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `aisa connect` and `aisa mcp setup` write the long-lived "aisa cli" key
|
|
26
|
+
(minted via `/v1/keys/mint`) into third-party client configs instead of the
|
|
27
|
+
OAuth access token, which expired after 24 hours. Static keys are unchanged.
|
|
28
|
+
Re-run `aisa connect` to replace a config written by 0.6.0–0.6.8.
|
|
29
|
+
- Preserve Runtime's successful-request billing condition and the full reference
|
|
30
|
+
rate in catalog details. Unknown estimates are not free requests.
|
|
31
|
+
- Revalidate catalog references every five minutes, bypass legacy long-lived
|
|
32
|
+
caches, and label stale offline references. Completion uses the same cache.
|
|
33
|
+
- Display newly published MCP categories using live server names when curated
|
|
34
|
+
copy is unavailable.
|
|
35
|
+
|
|
10
36
|
## [0.6.8] — 2026-10-14
|
|
11
37
|
|
|
12
38
|
### Fixed
|
|
@@ -529,7 +555,9 @@ supports today; nothing here depends on a backend change.
|
|
|
529
555
|
- Config commands (`aisa config get|set|list|reset`) and auth
|
|
530
556
|
(`aisa login|logout|whoami`).
|
|
531
557
|
|
|
532
|
-
[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.6.
|
|
558
|
+
[Unreleased]: https://github.com/AIsa-team/cli/compare/v0.6.10...HEAD
|
|
559
|
+
[0.6.10]: https://github.com/AIsa-team/cli/compare/v0.6.9...v0.6.10
|
|
560
|
+
[0.6.9]: https://github.com/AIsa-team/cli/compare/v0.6.8...v0.6.9
|
|
533
561
|
[0.6.8]: https://github.com/AIsa-team/cli/compare/v0.6.7...v0.6.8
|
|
534
562
|
[0.6.7]: https://github.com/AIsa-team/cli/compare/v0.6.6...v0.6.7
|
|
535
563
|
[0.6.6]: https://github.com/AIsa-team/cli/compare/v0.6.5...v0.6.6
|
package/README.md
CHANGED
|
@@ -164,18 +164,26 @@ aisa api show dataforseo --all # long lists truncate to 40 by default
|
|
|
164
164
|
```
|
|
165
165
|
|
|
166
166
|
A provider **id is not always its URL slug** — `brave-search` serves
|
|
167
|
-
`/apis/v1/brave/...`, and `api show` prints the catalog path.
|
|
168
|
-
|
|
167
|
+
`/apis/v1/brave/...`, and `api show` prints the catalog path. Use the published
|
|
168
|
+
Router schema for methods and request parameters; legacy catalog entries may
|
|
169
|
+
still report an advisory method.
|
|
169
170
|
|
|
170
171
|
Prices with `type: metered` display as `usage-based` (billing depends on usage),
|
|
171
172
|
even when the catalog returns `normal: 0`. Details show `usage-based (cost varies)`.
|
|
172
173
|
Use `aisa quote` for a request-specific estimate; JSON keeps the original pricing metadata.
|
|
174
|
+
When Runtime declares `customer_pricing: fixed_success`, endpoint details say
|
|
175
|
+
`per successful request`. Other fixed catalog amounts remain references until
|
|
176
|
+
their charging conditions are checked. Unavailable estimates do not mean free,
|
|
177
|
+
and displayed rates retain the supplied amount without rounding.
|
|
173
178
|
In `--json` output, metered providers and endpoints also include `pricing_note`
|
|
174
179
|
with quote guidance, including endpoints nested in provider details. The list
|
|
175
180
|
remains an array, and the original `pricing` fields are unchanged.
|
|
176
181
|
|
|
177
|
-
The catalog is cached in `~/.aisa/cache` (override with
|
|
178
|
-
|
|
182
|
+
The catalog is cached for five minutes in `~/.aisa/cache` (override with
|
|
183
|
+
`AISA_CACHE_DIR`). Upgrading bypasses the old six-hour/day-long catalog cache.
|
|
184
|
+
If refresh fails, an explicitly labelled stale reference remains available;
|
|
185
|
+
schema and quote are still required before a call. Pass `--refresh` to bypass
|
|
186
|
+
the cache, or `aisa cache clear`.
|
|
179
187
|
|
|
180
188
|
## LLM Gateway
|
|
181
189
|
|
|
@@ -281,36 +289,37 @@ To publish a skill, open a pull request against
|
|
|
281
289
|
npx @aisa-one/cli connect
|
|
282
290
|
```
|
|
283
291
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
everything is connected. No daemon stays behind. `--no-open` prints the URL
|
|
295
|
-
instead of launching a browser; `--dry-run` shows what would be written.
|
|
296
|
-
|
|
297
|
-
### Scripted: `mcp setup`
|
|
292
|
+
The browser or terminal wizard connects your coding agent to the single AIsa
|
|
293
|
+
Router MCP at `https://mcp.aisa.one/mcp`, under the name `aisa-router`.
|
|
294
|
+
Client selection, installation, sign-in, model configuration and launch options
|
|
295
|
+
remain available. There is no per-domain server selection.
|
|
296
|
+
|
|
297
|
+
The CLI configures the fixed Router endpoint without requesting a directory
|
|
298
|
+
service. Available tools are discovered by the MCP client from the Router;
|
|
299
|
+
the CLI does not pin a tool count or inventory.
|
|
300
|
+
|
|
301
|
+
### Scripted setup and status
|
|
298
302
|
|
|
299
303
|
```bash
|
|
300
|
-
aisa mcp setup
|
|
301
|
-
aisa mcp setup --
|
|
302
|
-
aisa mcp setup --
|
|
303
|
-
aisa mcp status #
|
|
304
|
+
aisa mcp setup --agent cursor # preview the single Router entry
|
|
305
|
+
aisa mcp setup --agent cursor --yes # add it to the detected client
|
|
306
|
+
aisa mcp setup --all --yes # accepted for compatibility; still one MCP
|
|
307
|
+
aisa mcp status # inspect entries and probe the unified endpoint
|
|
304
308
|
```
|
|
305
309
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
310
|
+
Cursor uses an HTTP URL entry; Claude Desktop uses an `npx mcp-remote` stdio
|
|
311
|
+
bridge. With a configured credential, setup writes the durable API key as a
|
|
312
|
+
Bearer header. Without one, the client handles the MCP OAuth flow on first use.
|
|
313
|
+
The server's OAuth resource metadata stays on `mcp.aisa.one`.
|
|
314
|
+
|
|
315
|
+
Existing domain entries, `aisa-docs`, and unrelated configuration are preserved.
|
|
316
|
+
The CLI no longer adds a docs MCP automatically. After validating the new
|
|
317
|
+
connection, remove old domain entries manually in each client. Status lists
|
|
318
|
+
legacy entries without sending requests to their retired endpoints.
|
|
319
|
+
|
|
320
|
+
DNS cutover requires the Router ingress and TLS certificate to accept
|
|
321
|
+
`mcp.aisa.one`, and its OAuth metadata to describe that host's `/mcp` resource.
|
|
322
|
+
Updating the CLI alone does not migrate existing clients or change DNS.
|
|
314
323
|
|
|
315
324
|
## Shell Completion
|
|
316
325
|
|
|
@@ -366,8 +375,11 @@ Settings:
|
|
|
366
375
|
`aisa login` stores OAuth credentials in `~/.aisa/tokens.json`.
|
|
367
376
|
`aisa login --key <key>` stores a static credential without refresh metadata.
|
|
368
377
|
Legacy mirrors contain the current access token; older CLIs cannot refresh it.
|
|
369
|
-
Third-party client configurations written by `aisa connect`
|
|
370
|
-
|
|
378
|
+
Third-party client configurations written by `aisa connect` and
|
|
379
|
+
`aisa mcp setup` get the long-lived "aisa cli" API key, minted from your
|
|
380
|
+
sign-in, never the expiring OAuth access token. Configs written by 0.6.0–0.6.8
|
|
381
|
+
hold an access token that stops working after a day; run `aisa connect` again
|
|
382
|
+
to replace it.
|
|
371
383
|
Environment variables:
|
|
372
384
|
`AISA_API_KEY` takes precedence over the stored key.
|
|
373
385
|
`AISA_ROUTER_BASE_URL` is the Router origin/prefix before
|
|
@@ -398,8 +410,8 @@ configured `baseUrl`. It tolerates a value with either suffix already attached,
|
|
|
398
410
|
because the shipped default has always included `/v1` and is persisted in every
|
|
399
411
|
existing user's config.
|
|
400
412
|
|
|
401
|
-
**The catalog's shape has three traps.** `endpoints[].method`
|
|
402
|
-
|
|
413
|
+
**The catalog's shape has three traps.** Legacy `endpoints[].method` values can
|
|
414
|
+
be advisory. `endpoint_groups[].name` is an operator-entered label
|
|
403
415
|
(`Zero`, `One`, `default`) with no business meaning, which is why `api show`
|
|
404
416
|
flattens by default. And health is tracked per provider, not per endpoint, so
|
|
405
417
|
per-endpoint counts are one value repeated.
|
package/dist/catalog.d.ts
CHANGED
|
@@ -11,8 +11,14 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export interface CatalogPricing {
|
|
13
13
|
type: string;
|
|
14
|
-
/**
|
|
15
|
-
normal
|
|
14
|
+
/** Runtime reference estimate in dollars; unavailable estimates are not zero prices. */
|
|
15
|
+
normal?: number;
|
|
16
|
+
estimate_unavailable?: boolean;
|
|
17
|
+
/** Runtime's pricing declaration; provider costs do not determine customer charges. */
|
|
18
|
+
details?: {
|
|
19
|
+
customer_pricing?: string;
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
};
|
|
16
22
|
}
|
|
17
23
|
export interface CatalogProvider {
|
|
18
24
|
id: string;
|
|
@@ -69,6 +75,6 @@ export declare function runSlugOf(detail: CatalogDetail): string | undefined;
|
|
|
69
75
|
* Format catalog pricing, keeping metered billing distinct from a free request.
|
|
70
76
|
* Fixed prices span $0.000001 to $0.08, so a fixed
|
|
71
77
|
* number of decimals either rounds the cheap endpoints to $0.00 or pads the
|
|
72
|
-
* rest with noise
|
|
78
|
+
* rest with noise. Preserve the supplied amount without rounding its rate.
|
|
73
79
|
*/
|
|
74
80
|
export declare function formatPrice(pricing?: CatalogPricing): string;
|
package/dist/catalog.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { publicRequest, cacheScope } from "./api.js";
|
|
2
2
|
import { readCache, writeCache, touchCache } from "./cache.js";
|
|
3
|
-
const CATEGORY_TTL_MS =
|
|
4
|
-
const DETAIL_TTL_MS =
|
|
3
|
+
const CATEGORY_TTL_MS = 5 * 60 * 1000;
|
|
4
|
+
const DETAIL_TTL_MS = 5 * 60 * 1000;
|
|
5
5
|
const HEALTH_TTL_MS = 5 * 60 * 1000;
|
|
6
6
|
/** Fetch with disk caching and ETag revalidation; falls back to stale data offline. */
|
|
7
7
|
async function cachedGet(path, cacheKey, ttlMs, refresh) {
|
|
@@ -13,8 +13,10 @@ async function cachedGet(path, cacheKey, ttlMs, refresh) {
|
|
|
13
13
|
res = await publicRequest(path, { etag: refresh ? undefined : cached?.etag });
|
|
14
14
|
}
|
|
15
15
|
catch (err) {
|
|
16
|
-
if (cached)
|
|
16
|
+
if (cached) {
|
|
17
|
+
console.error("Warning: catalog refresh failed; showing a stale cached reference. Use aisa schema and aisa quote before calling.");
|
|
17
18
|
return cached.data;
|
|
19
|
+
}
|
|
18
20
|
throw err;
|
|
19
21
|
}
|
|
20
22
|
if (res.status === 304 && cached) {
|
|
@@ -22,19 +24,21 @@ async function cachedGet(path, cacheKey, ttlMs, refresh) {
|
|
|
22
24
|
return cached.data;
|
|
23
25
|
}
|
|
24
26
|
if (!res.data) {
|
|
25
|
-
if (cached)
|
|
27
|
+
if (cached) {
|
|
28
|
+
console.error("Warning: catalog refresh failed; showing a stale cached reference. Use aisa schema and aisa quote before calling.");
|
|
26
29
|
return cached.data;
|
|
30
|
+
}
|
|
27
31
|
throw new Error(`Catalog request failed: ${path} returned ${res.status}`);
|
|
28
32
|
}
|
|
29
33
|
writeCache(cacheKey, res.data, ttlMs, res.etag);
|
|
30
34
|
return res.data;
|
|
31
35
|
}
|
|
32
36
|
export async function getProviders(options = {}) {
|
|
33
|
-
const data = await cachedGet("info/apis/category", `catalog/${cacheScope()}/category.json`, CATEGORY_TTL_MS, options.refresh);
|
|
37
|
+
const data = await cachedGet("info/apis/category", `catalog-v2/${cacheScope()}/category.json`, CATEGORY_TTL_MS, options.refresh);
|
|
34
38
|
return data.apis || [];
|
|
35
39
|
}
|
|
36
40
|
export async function getProviderDetail(id, options = {}) {
|
|
37
|
-
const data = await cachedGet(`info/apis/${encodeURIComponent(id)}`, `catalog/${cacheScope()}/${id.replace(/[^\w.-]/g, "_")}.json`, DETAIL_TTL_MS, options.refresh);
|
|
41
|
+
const data = await cachedGet(`info/apis/${encodeURIComponent(id)}`, `catalog-v2/${cacheScope()}/${id.replace(/[^\w.-]/g, "_")}.json`, DETAIL_TTL_MS, options.refresh);
|
|
38
42
|
if (!data.api)
|
|
39
43
|
throw new Error(`Unknown API "${id}"`);
|
|
40
44
|
return data.api;
|
|
@@ -65,18 +69,29 @@ export function runSlugOf(detail) {
|
|
|
65
69
|
* Format catalog pricing, keeping metered billing distinct from a free request.
|
|
66
70
|
* Fixed prices span $0.000001 to $0.08, so a fixed
|
|
67
71
|
* number of decimals either rounds the cheap endpoints to $0.00 or pads the
|
|
68
|
-
* rest with noise
|
|
72
|
+
* rest with noise. Preserve the supplied amount without rounding its rate.
|
|
69
73
|
*/
|
|
70
74
|
export function formatPrice(pricing) {
|
|
71
75
|
if (pricing?.type === "metered")
|
|
72
76
|
return "usage-based";
|
|
77
|
+
if (pricing?.estimate_unavailable)
|
|
78
|
+
return "—";
|
|
73
79
|
const usd = pricing?.normal;
|
|
74
|
-
if (usd == null)
|
|
80
|
+
if (usd == null || !Number.isFinite(usd))
|
|
75
81
|
return "—";
|
|
76
82
|
if (usd === 0)
|
|
77
83
|
return "free";
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const text = usd.toString();
|
|
85
|
+
if (!text.includes("e"))
|
|
86
|
+
return `$${text}`;
|
|
87
|
+
// Expand the supplied numeric text without rounding tiny nonzero rates.
|
|
88
|
+
const [coefficient, exponent] = text.split("e");
|
|
89
|
+
const negative = coefficient.startsWith("-");
|
|
90
|
+
const [whole, fraction = ""] = (negative ? coefficient.slice(1) : coefficient).split(".");
|
|
91
|
+
const digits = whole + fraction;
|
|
92
|
+
const point = whole.length + Number(exponent);
|
|
93
|
+
const expanded = point <= 0 ? `0.${"0".repeat(-point)}${digits}`
|
|
94
|
+
: point >= digits.length ? digits + "0".repeat(point - digits.length)
|
|
95
|
+
: digits.slice(0, point) + "." + digits.slice(point);
|
|
96
|
+
return `$${negative ? "-" : ""}${expanded}`;
|
|
82
97
|
}
|
package/dist/commands/api.js
CHANGED
|
@@ -15,9 +15,13 @@ function pricingDescription(pricing, from = false) {
|
|
|
15
15
|
const price = formatPrice(pricing);
|
|
16
16
|
if (pricing?.type === "metered")
|
|
17
17
|
return `${price} (cost varies)`;
|
|
18
|
-
if (pricing?.normal == null)
|
|
18
|
+
if (pricing?.estimate_unavailable || pricing?.normal == null)
|
|
19
19
|
return price;
|
|
20
|
-
|
|
20
|
+
const reference = `${from && pricing.normal > 0 ? "from " : ""}${price}`;
|
|
21
|
+
if (pricing.details?.customer_pricing === "fixed_success") {
|
|
22
|
+
return `${reference} per successful request`;
|
|
23
|
+
}
|
|
24
|
+
return `${reference} (reference; check schema and quote for charging conditions)`;
|
|
21
25
|
}
|
|
22
26
|
function statusBadge(status) {
|
|
23
27
|
switch (status) {
|
|
@@ -70,7 +74,7 @@ export async function apiListAction(options) {
|
|
|
70
74
|
p.id,
|
|
71
75
|
categoryOf(p.id),
|
|
72
76
|
String(p.endpoint_count),
|
|
73
|
-
p.pricing?.type !== "metered" && p.pricing?.normal != null && p.pricing.normal > 0
|
|
77
|
+
p.pricing?.type !== "metered" && !p.pricing?.estimate_unavailable && p.pricing?.normal != null && p.pricing.normal > 0
|
|
74
78
|
? `from ${formatPrice(p.pricing)}`
|
|
75
79
|
: formatPrice(p.pricing),
|
|
76
80
|
shortDate(p.updated_at),
|
|
@@ -52,50 +52,7 @@ export const CATEGORY_ICON = {
|
|
|
52
52
|
* AIsa tools on its own and no naming is needed at all.
|
|
53
53
|
*/
|
|
54
54
|
export const EXAMPLES = {
|
|
55
|
-
|
|
56
|
-
"Using AIsa, search the web for this week's biggest AI news and summarize the top 3 results with links.",
|
|
57
|
-
"Using AIsa, research how teams are adopting MCP servers in production and give me a sourced brief.",
|
|
58
|
-
],
|
|
59
|
-
"twitter-api": [
|
|
60
|
-
"Using AIsa, fetch the latest tweets from @AnthropicAI and summarize the main themes.",
|
|
61
|
-
"Using AIsa, pull @sama's five most recent tweets with their engagement numbers.",
|
|
62
|
-
],
|
|
63
|
-
"crypto-market-data": [
|
|
64
|
-
"Using AIsa, get Bitcoin's current price and 24h change, then compare it with Ethereum.",
|
|
65
|
-
"Using AIsa's crypto data, list today's trending coins with prices and market caps.",
|
|
66
|
-
],
|
|
67
|
-
marketpulse: [
|
|
68
|
-
"Using AIsa, pull AAPL's latest income statement and summarize the revenue and margin trend.",
|
|
69
|
-
"Using AIsa's market data, screen for US stocks with a market cap above $1T and compare their P/E ratios.",
|
|
70
|
-
],
|
|
71
|
-
"stock-pulse": [
|
|
72
|
-
"Using AIsa, show what X/Twitter is saying about NVDA today, joined with its market data.",
|
|
73
|
-
"Using AIsa, find which tickers are trending on X right now and why.",
|
|
74
|
-
],
|
|
75
|
-
"prediction-market-data": [
|
|
76
|
-
"Using AIsa, list the most active prediction markets right now with their implied probabilities.",
|
|
77
|
-
"Using AIsa, compare what Polymarket and Kalshi imply about the next Fed rate decision.",
|
|
78
|
-
],
|
|
79
|
-
reddit: [
|
|
80
|
-
"Using AIsa, find today's top posts in r/MachineLearning and summarize the discussion.",
|
|
81
|
-
"Using AIsa, search Reddit for real-world Claude Code workflows people recommend.",
|
|
82
|
-
],
|
|
83
|
-
"youtube-search": [
|
|
84
|
-
"Using AIsa, find the three most relevant YouTube videos about MCP servers and list their channels.",
|
|
85
|
-
"Using AIsa, find beginner YouTube tutorials for Claude Code and pick the best one to start with.",
|
|
86
|
-
],
|
|
87
|
-
instagram: [
|
|
88
|
-
"Using AIsa, fetch the Instagram profile and recent posts of @nasa and describe their content strategy.",
|
|
89
|
-
"Using AIsa, compare the recent Instagram engagement of @natgeo and @nasa.",
|
|
90
|
-
],
|
|
91
|
-
pinterest: [
|
|
92
|
-
"Using AIsa, search Pinterest for 'mid-century interior' and summarize the visual trends.",
|
|
93
|
-
"Using AIsa, find trending home-office ideas on Pinterest and summarize them with links.",
|
|
94
|
-
],
|
|
95
|
-
apollo: [
|
|
96
|
-
"Using AIsa, enrich the company anthropic.com — size, industry, and key people.",
|
|
97
|
-
"Using AIsa, find five AI infrastructure startups in San Francisco with their CEOs.",
|
|
98
|
-
],
|
|
55
|
+
router: ["Using AIsa, research the latest developments in AI and cite your sources."],
|
|
99
56
|
};
|
|
100
57
|
export const FILE_CLIENT_LABELS = {
|
|
101
58
|
cursor: "Cursor",
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { MCP_DEFAULT_SLUGS } from "../constants.js";
|
|
2
1
|
import { INSTALLERS } from "./install.js";
|
|
3
2
|
import { defaultModelsFor } from "./llm-config.js";
|
|
4
3
|
import { stripped } from "./mcp.js";
|
|
5
4
|
import { BRAND_LOGOS } from "./brand-logos.js";
|
|
6
|
-
import { STEP_AGENT, AGENT_ORDER, agentRank, AGENT_BADGE, AGENT_SIDE_TITLES, AGENT_NOTES, AGENT_HAVE_NOTES, installOffer, t,
|
|
7
|
-
import { RED, RED_CTA, INK, PAPER, I, LOGO,
|
|
5
|
+
import { STEP_AGENT, AGENT_ORDER, agentRank, AGENT_BADGE, AGENT_SIDE_TITLES, AGENT_NOTES, AGENT_HAVE_NOTES, installOffer, t, LANGS, LANG_LABEL, NAV, STEP_TITLES, STEP_WELCOME, STEP_MODELS, STEP_CAPS, STEP_INSTALL, STEP_DONE, FILE_MODEL_NOTE, FILE_MODEL_FALLBACK, BACKUP_COPY, runtimeCopy, NEXT_WORD, SUPERSEDED, } from "./flow.js";
|
|
6
|
+
import { RED, RED_CTA, INK, PAPER, I, LOGO, EXAMPLES, CLIENT_LOGOS, CODEX_FACE, CLAUDE_BOT, OPENCODE_MARK, } from "./connect-shared.js";
|
|
8
7
|
import { AISA_PROVIDER_ID } from "../constants.js";
|
|
9
8
|
import { VSCODE_MODELS } from "./vscode.js";
|
|
10
9
|
/**
|
|
@@ -80,7 +79,6 @@ lang = "en") {
|
|
|
80
79
|
const T = (x) => t(x, lang);
|
|
81
80
|
/** A per-agent map of Text, flattened to the current language. */
|
|
82
81
|
const resolved = (m) => Object.fromEntries(Object.entries(m).map(([k, v]) => [k, t(v, lang)]));
|
|
83
|
-
const totalTools = servers.reduce((n, s) => n + s.toolCount, 0);
|
|
84
82
|
const cats = new Map();
|
|
85
83
|
for (const s of servers) {
|
|
86
84
|
const c = normCategory(s.category);
|
|
@@ -95,7 +93,6 @@ lang = "en") {
|
|
|
95
93
|
endpoint: s.endpoint,
|
|
96
94
|
name: stripped(s.name),
|
|
97
95
|
category: normCategory(s.category),
|
|
98
|
-
toolCount: s.toolCount,
|
|
99
96
|
description: s.description,
|
|
100
97
|
}));
|
|
101
98
|
// Windsurf is not part of this flow (its file path stays in T1). claude.ai
|
|
@@ -204,36 +201,22 @@ ${restChips}
|
|
|
204
201
|
<button type="button" class="mw-fix" id="modelfix">${T(STEP_MODELS.warn.fix)}</button>
|
|
205
202
|
</div>`;
|
|
206
203
|
// ── step 4: capabilities ──
|
|
207
|
-
const catTiles = catList
|
|
208
|
-
.map(([cat, list]) => {
|
|
209
|
-
const tools = list.reduce((n, s) => n + s.toolCount, 0);
|
|
210
|
-
return `<button type="button" class="ctile" data-cat="${cat}">
|
|
211
|
-
<span class="cico">${CATEGORY_ICON[cat] ?? I.sparkles}</span>
|
|
212
|
-
<span class="cname">${cat}</span>
|
|
213
|
-
<span class="cmeta">${fill(STEP_CAPS.tileMeta, lang, { n: list.length, noun: list.length > 1 ? T(STEP_CAPS.serversWord) : T(STEP_CAPS.serverWord), tools })}</span>
|
|
214
|
-
<span class="cblurb">${CATEGORY_BLURB[cat] ? T(CATEGORY_BLURB[cat]) : ""}</span>
|
|
215
|
-
<span class="csel" data-csel></span></button>`;
|
|
216
|
-
})
|
|
217
|
-
.join("");
|
|
218
204
|
const serverTiles = catList
|
|
219
205
|
.map(([cat, list]) => list
|
|
220
206
|
.map((s) => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<input type="checkbox" name="server" value="${s.slug}"${checked ? " checked" : ""}>
|
|
207
|
+
return `<label class="stile on" data-cat="${cat}">
|
|
208
|
+
<input type="checkbox" name="server" value="${s.slug}" checked disabled>
|
|
224
209
|
<span class="tbody"><span class="thead"><span class="tname">${stripped(s.name)}</span>
|
|
225
|
-
|
|
210
|
+
</span>
|
|
226
211
|
<span class="sdesc">${s.description}</span></span></label>`;
|
|
227
212
|
})
|
|
228
213
|
.join(""))
|
|
229
214
|
.join("");
|
|
230
215
|
const caps = `
|
|
231
216
|
<h1>${T(STEP_CAPS.h1).replace("reach", "<em>reach</em>")}</h1>
|
|
232
|
-
<p class="lede">${
|
|
233
|
-
<div class="cgrid">${catTiles}</div>
|
|
217
|
+
<p class="lede">${T(STEP_CAPS.counts)} ${T(STEP_CAPS.ledeTail)}</p>
|
|
234
218
|
<div class="spanel" id="spanel">
|
|
235
|
-
|
|
236
|
-
<div class="sgrid" id="sgrid">${serverTiles}</div>
|
|
219
|
+
<div class="sgrid" id="sgrid">${serverTiles}</div>
|
|
237
220
|
</div>
|
|
238
221
|
<div class="tally" id="tally"></div>`;
|
|
239
222
|
// ── step 5: install ──
|
|
@@ -757,42 +740,11 @@ ${restChips}
|
|
|
757
740
|
});
|
|
758
741
|
|
|
759
742
|
// ── step 4 ──
|
|
760
|
-
var activeCat = null;
|
|
761
743
|
function syncCaps() {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
var n = SERVERS.filter(function (s) { return s.category === cat && picked.indexOf(s.slug) !== -1; }).length;
|
|
766
|
-
var sel = t.querySelector("[data-csel]");
|
|
767
|
-
sel.textContent = n ? n + " selected" : "";
|
|
768
|
-
t.classList.toggle("has", n > 0);
|
|
769
|
-
t.classList.toggle("active", cat === activeCat);
|
|
770
|
-
});
|
|
771
|
-
$$(".stile").forEach(function (t) {
|
|
772
|
-
t.classList.toggle("on", t.querySelector("input").checked);
|
|
773
|
-
t.style.display = t.dataset.cat === activeCat ? "" : "none";
|
|
774
|
-
});
|
|
775
|
-
$("#spanel").classList.toggle("open", Boolean(activeCat));
|
|
776
|
-
$("#spTitle").textContent = activeCat ? activeCat : "Pick an area above";
|
|
777
|
-
var tools = picked.reduce(function (n, s) { return n + (BY_SLUG[s] ? BY_SLUG[s].toolCount : 0); }, 0);
|
|
778
|
-
$("#tally").innerHTML = picked.length
|
|
779
|
-
? "<b>" + picked.length + " server" + (picked.length > 1 ? "s" : "") + "</b> · " + tools + " tools selected"
|
|
780
|
-
: COPY.nothingSelected;
|
|
744
|
+
$$('input[name="server"]').forEach(function (i) { i.checked = true; i.disabled = true; });
|
|
745
|
+
$("#spanel").classList.add("open");
|
|
746
|
+
$("#tally").textContent = SERVERS[0].endpoint;
|
|
781
747
|
}
|
|
782
|
-
$$(".ctile").forEach(function (t) { t.addEventListener("click", function () {
|
|
783
|
-
activeCat = activeCat === t.dataset.cat ? null : t.dataset.cat; syncCaps();
|
|
784
|
-
if (activeCat) $("#spanel").scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
785
|
-
}); });
|
|
786
|
-
$$('input[name="server"]').forEach(function (c) { c.addEventListener("change", function () { syncCaps(); publishDraft(current); }); });
|
|
787
|
-
$("#spAll").addEventListener("click", function () {
|
|
788
|
-
if (!activeCat) return;
|
|
789
|
-
var boxes = $$('.stile[data-cat="' + activeCat + '"] input');
|
|
790
|
-
var all = boxes.every(function (b) { return b.checked; });
|
|
791
|
-
boxes.forEach(function (b) { b.checked = !all; });
|
|
792
|
-
syncCaps();
|
|
793
|
-
});
|
|
794
|
-
// Open the area holding the default selection so the page never starts blank.
|
|
795
|
-
(function () { var first = $('input[name="server"]:checked'); if (first) activeCat = first.closest(".stile").dataset.cat; })();
|
|
796
748
|
|
|
797
749
|
// ── next button ──
|
|
798
750
|
nextBtn.addEventListener("click", function () {
|
|
@@ -984,7 +936,6 @@ ${restChips}
|
|
|
984
936
|
locked = true;
|
|
985
937
|
paintRail();
|
|
986
938
|
$$('.pane[data-pane="2"] input, .pane[data-pane="3"] input, .pane[data-pane="4"] input').forEach(function (i) { i.disabled = true; });
|
|
987
|
-
$$(".ctile, #spAll").forEach(function (b) { b.disabled = false; });
|
|
988
939
|
document.body.classList.add("locked");
|
|
989
940
|
}
|
|
990
941
|
function start() {
|
|
@@ -1029,7 +980,6 @@ ${restChips}
|
|
|
1029
980
|
var sel = s.selection, steps = s.steps || [];
|
|
1030
981
|
var id = sel.clients[0], name = LABEL[id] || id;
|
|
1031
982
|
var chosen = sel.servers.map(function (x) { return BY_SLUG[x]; }).filter(Boolean);
|
|
1032
|
-
var tools = chosen.reduce(function (n, x) { return n + x.toolCount; }, 0);
|
|
1033
983
|
var failed = steps.filter(function (x) { return x.state === "fail"; });
|
|
1034
984
|
var mcpFailed = failed.some(function (x) { return x.id === "mcp"; });
|
|
1035
985
|
var installed = steps.filter(function (x) { return x.id.indexOf("install:") === 0 && x.id !== "install:aisa-cli" && x.state === "ok"; })
|
|
@@ -1041,8 +991,8 @@ ${restChips}
|
|
|
1041
991
|
var head = mcpFailed
|
|
1042
992
|
? "<div class='eyebrow'>Almost there</div><h1>Your agent is <em>not connected yet</em></h1><p class='lede'>The MCP entries could not be added to <b>" + name + "</b> — details below.</p>"
|
|
1043
993
|
: (installed.length
|
|
1044
|
-
? "<h1><em>Congratulations!</em> " + installed.join(" & ") + " is installed and armed with
|
|
1045
|
-
: "<h1><em>Congratulations!</em> Your agent just got
|
|
994
|
+
? "<h1><em>Congratulations!</em> " + installed.join(" & ") + " is installed and armed with AIsa tools<span class='h1check'>" + ICON_CHECK + "</span></h1><p class='lede'><b>" + installed.join(" & ") + "</b> is on this machine, signed in to AIsa" + (llmOk ? ", running on <b>" + MODEL_FOR[id] + "</b> through AIsa," : ",") + " with " + chosen.length + " MCP server" + (chosen.length > 1 ? "s" : "") + " wired in — a complete setup, nothing else to configure.</p>"
|
|
995
|
+
: "<h1><em>Congratulations!</em> Your agent just got access to AIsa tools.<span class='h1check'>" + ICON_CHECK + "</span></h1><p class='lede'>" + (id === "claude-ai"
|
|
1046
996
|
? chosen.length + " connector URL" + (chosen.length > 1 ? "s are" : " is") + " ready for <b>Claude.ai</b> — add them below, it takes about a minute."
|
|
1047
997
|
: id === "cursor" && s.deeplinks && s.deeplinks.length
|
|
1048
998
|
? chosen.length + " install link" + (chosen.length > 1 ? "s are" : " is") + " ready for <b>Cursor</b> — one click each, below."
|
|
@@ -1052,7 +1002,7 @@ ${restChips}
|
|
|
1052
1002
|
failed.map(function (x) { return "<li><b>" + x.label + "</b> — " + (x.detail || "failed") + "</li>"; }).join("") +
|
|
1053
1003
|
"</ul>Fix the above, then run <code>npx @aisa-one/cli connect</code> again — it is safe to re-run.</div></div>" : "";
|
|
1054
1004
|
var ICON = { ok: "✓", fail: "✕", skip: "–", pending: "·", running: "·" };
|
|
1055
|
-
var recap = "<h2>Everything you just gained</h2><div class='recap'><div class='rsum'>
|
|
1005
|
+
var recap = "<h2>Everything you just gained</h2><div class='recap'><div class='rsum'>AIsa Router MCP · " + name + "</div>" +
|
|
1056
1006
|
// The balance has its own card right below, so its row stays out of the recap.
|
|
1057
1007
|
steps.filter(function (x) { return x.id !== "balance"; }).map(function (x) { return "<div class='rrow " + x.state + "'><span class='rl'>" + x.label + "</span><span class='rd' title=\\"" + (x.detail || "").replace(/"/g, """) + "\\">" + (x.detail || "") + "</span><span class='ri'>" + ICON[x.state] + "</span></div>"; }).join("") + "</div>";
|
|
1058
1008
|
var bal = s.balanceMicros, low = bal !== null && bal !== undefined && bal < 5e6;
|
|
@@ -1065,11 +1015,11 @@ ${restChips}
|
|
|
1065
1015
|
var fileNote = "";
|
|
1066
1016
|
if (id === "claude-desktop") {
|
|
1067
1017
|
fileNote = "<div class='vsccard'><div class='vschead'><span class='blogo lg'>" + CLAUDE_DT_LOGO + "</span><div><b>Claude Desktop is ready</b>" +
|
|
1068
|
-
"<div class='fine' style='margin:0'>" + chosen.length + " MCP server" + (chosen.length > 1 ? "s" : "") + "
|
|
1018
|
+
"<div class='fine' style='margin:0'>" + chosen.length + " MCP server" + (chosen.length > 1 ? "s" : "") + " are in its config — one restart loads them.</div></div>" +
|
|
1069
1019
|
"<div class='termside'><button type='button' class='cta sm' id='launch'>Restart Claude Desktop →</button><span class='fine' id='launchnote'></span></div></div>" +
|
|
1070
1020
|
"<div class='vscgrid'>" +
|
|
1071
|
-
"<div class='vscitem'><span class='srv'>Tools</span>After the restart, open the <b>tools menu</b> (🔧) in a chat — the <code>aisa
|
|
1072
|
-
"<div class='vscitem'><span class='srv'>Across devices</span>Prefer one setup on every device? Add the same server
|
|
1021
|
+
"<div class='vscitem'><span class='srv'>Tools</span>After the restart, open the <b>tools menu</b> (🔧) in a chat — the <code>aisa-router</code> server is there. It runs as a small bridge that lives and dies with the app; no background service.</div>" +
|
|
1022
|
+
"<div class='vscitem'><span class='srv'>Across devices</span>Prefer one setup on every device? Add the same server URL as <b>custom Connectors</b> under Settings → Connectors — they sync with claude.ai through your account.</div>" +
|
|
1073
1023
|
"</div></div>";
|
|
1074
1024
|
}
|
|
1075
1025
|
if (id === "vscode") {
|
|
@@ -1081,7 +1031,7 @@ ${restChips}
|
|
|
1081
1031
|
"<div class='termside'><button type='button' class='cta sm' id='launch'>Open VS Code →</button><span class='fine' id='launchnote'></span></div></div>" +
|
|
1082
1032
|
"<div class='vscgrid'>" +
|
|
1083
1033
|
(modelsOn ? "<div class='vscitem'><span class='srv'>Models</span>Open <b>Chat</b> (<code>Ctrl+Cmd+I</code>), click the model name at the bottom-right of the input — the <b>AIsa</b> group lists Claude, GPT, DeepSeek, Kimi, GLM and Qwen. The AIsa extension already stored your key.</div>" : "") +
|
|
1084
|
-
"<div class='vscitem'><span class='srv'>Tools</span>Switch Chat to <b>Agent</b> mode and tick the <code>aisa
|
|
1034
|
+
"<div class='vscitem'><span class='srv'>Tools</span>Switch Chat to <b>Agent</b> mode and tick the <code>aisa-router</code> server in the tools picker (🔧). It is listed under <b>Extensions → MCP Servers</b>; if one is missing, run <b>Reload Window</b> once.</div>" +
|
|
1085
1035
|
"</div></div>";
|
|
1086
1036
|
if (sel.llmMode === "backup" && needsPaste) {
|
|
1087
1037
|
fileNote += "<h2>One paste in VS Code — your key</h2><div class='webcard'>" +
|
|
@@ -1163,7 +1113,7 @@ ${restChips}
|
|
|
1163
1113
|
$$('input[name="server"]').forEach(function (i) { i.checked = sel.servers.indexOf(i.value) !== -1; });
|
|
1164
1114
|
var fresh = sel.install.length > 0;
|
|
1165
1115
|
var r = $('input[name="' + (fresh ? "lmodeFresh" : "lmodeDet") + '"][value="' + sel.llmMode + '"]'); if (r) r.checked = true;
|
|
1166
|
-
|
|
1116
|
+
|
|
1167
1117
|
syncAgent(); syncModels(); syncCaps(); lockSelections();
|
|
1168
1118
|
phase = s.phase; serverSteps = s.steps; lastStatus = s;
|
|
1169
1119
|
var settled = phase === "done" || phase === "failed";
|
|
@@ -1465,21 +1415,6 @@ function shellT2(title, body) {
|
|
|
1465
1415
|
.mw-fix { margin-top: .7rem; background: var(--red-cta); color: #fff; border: 0; border-radius: 6px;
|
|
1466
1416
|
padding: .5rem 1.2rem; font: inherit; font-weight: 700; cursor: pointer; }
|
|
1467
1417
|
@keyframes mwshake { 0%,100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }
|
|
1468
|
-
/* capabilities: the area grid, then the servers of the open area */
|
|
1469
|
-
.cgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: .9rem; margin: 1.6rem 0 1rem; }
|
|
1470
|
-
.ctile { position: relative; display: flex; flex-direction: column; gap: .25rem; align-items: flex-start; text-align: left;
|
|
1471
|
-
background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 1.2rem 1.2rem 1.1rem;
|
|
1472
|
-
font: inherit; color: var(--ink); cursor: pointer; min-height: 168px; transition: border-color .15s, transform .15s; }
|
|
1473
|
-
.ctile:hover { border-color: color-mix(in srgb, var(--red) 45%, var(--line)); transform: translateY(-1px); }
|
|
1474
|
-
.ctile.active { border-color: var(--red); box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 18%, transparent); }
|
|
1475
|
-
.ctile.has { border-left: 3px solid var(--ok); }
|
|
1476
|
-
.cico { width: 40px; height: 40px; border-radius: 10px; background: var(--tint); color: var(--red);
|
|
1477
|
-
display: flex; align-items: center; justify-content: center; margin-bottom: .4rem; }
|
|
1478
|
-
.cico svg { width: 20px; height: 20px; }
|
|
1479
|
-
.cname { font-weight: 800; font-size: 1.05rem; }
|
|
1480
|
-
.cmeta { font-size: .78rem; color: var(--muted); }
|
|
1481
|
-
.cblurb { font-size: .86rem; color: var(--muted); margin-top: .3rem; }
|
|
1482
|
-
.csel { position: absolute; top: .8rem; right: .9rem; font-size: .74rem; font-weight: 700; color: var(--ok); }
|
|
1483
1418
|
.spanel { display: none; border: 1px solid var(--line); border-radius: 12px; background: color-mix(in srgb, var(--card) 60%, var(--paper));
|
|
1484
1419
|
padding: 1rem 1.2rem 1.2rem; }
|
|
1485
1420
|
.spanel.open { display: block; animation: fade .2s ease; }
|