@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.
- package/CHANGELOG.md +14 -1
- package/README.md +27 -26
- 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 +26 -31
- package/dist/commands/flow.d.ts +0 -46
- package/dist/commands/flow.js +11 -82
- package/dist/commands/mcp.d.ts +9 -42
- package/dist/commands/mcp.js +34 -84
- package/dist/commands/oauth-login.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/update-check.js +3 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ 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
|
+
|
|
10
21
|
## [0.6.9] — 2026-10-21
|
|
11
22
|
|
|
12
23
|
### Fixed
|
|
@@ -544,7 +555,9 @@ supports today; nothing here depends on a backend change.
|
|
|
544
555
|
- Config commands (`aisa config get|set|list|reset`) and auth
|
|
545
556
|
(`aisa login|logout|whoami`).
|
|
546
557
|
|
|
547
|
-
[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
|
|
548
561
|
[0.6.8]: https://github.com/AIsa-team/cli/compare/v0.6.7...v0.6.8
|
|
549
562
|
[0.6.7]: https://github.com/AIsa-team/cli/compare/v0.6.6...v0.6.7
|
|
550
563
|
[0.6.6]: https://github.com/AIsa-team/cli/compare/v0.6.5...v0.6.6
|
package/README.md
CHANGED
|
@@ -289,36 +289,37 @@ To publish a skill, open a pull request against
|
|
|
289
289
|
npx @aisa-one/cli connect
|
|
290
290
|
```
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
everything is connected. No daemon stays behind. `--no-open` prints the URL
|
|
303
|
-
instead of launching a browser; `--dry-run` shows what would be written.
|
|
304
|
-
|
|
305
|
-
### 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
|
|
306
302
|
|
|
307
303
|
```bash
|
|
308
|
-
aisa mcp setup
|
|
309
|
-
aisa mcp setup --
|
|
310
|
-
aisa mcp setup --
|
|
311
|
-
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
|
|
312
308
|
```
|
|
313
309
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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.
|
|
322
323
|
|
|
323
324
|
## Shell Completion
|
|
324
325
|
|
|
@@ -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">${T(categoryBlurb(cat, list)).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}</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; }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as readline from "node:readline/promises";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { AGENT_BADGE, AGENT_NOTES, BACKUP_COPY, CONFIRM, FILE_MODEL_FALLBACK, FILE_MODEL_NOTE, STEP_AGENT, STEP_CAPS, STEP_MODELS, STEP_TITLES, STEP_WELCOME, agentRank,
|
|
3
|
+
import { AGENT_BADGE, AGENT_NOTES, BACKUP_COPY, CONFIRM, FILE_MODEL_FALLBACK, FILE_MODEL_NOTE, STEP_AGENT, STEP_CAPS, STEP_MODELS, STEP_TITLES, STEP_WELCOME, agentRank, t, } from "./flow.js";
|
|
4
4
|
import { INSTALLERS } from "./install.js";
|
|
5
5
|
import { defaultModelsFor } from "./llm-config.js";
|
|
6
6
|
import { httpFetch } from "../utils/http.js";
|
|
@@ -612,104 +612,10 @@ export async function runTerminalFlow(o) {
|
|
|
612
612
|
// ── step 4: capabilities ──
|
|
613
613
|
if (from <= 4) {
|
|
614
614
|
console.log(header(4, o.lang));
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}));
|
|
620
|
-
console.log(dim("│"));
|
|
621
|
-
// Pull once more: the capability list is long, and a page open beside
|
|
622
|
-
// this one is the likelier place to have ticked through it.
|
|
623
|
-
const fresh = await pull(o);
|
|
624
|
-
if (fresh?.draft?.servers?.length)
|
|
625
|
-
draft.servers = fresh.draft.servers;
|
|
626
|
-
if (fresh)
|
|
627
|
-
rev = fresh.rev;
|
|
628
|
-
const chosen = new Set(draft.servers);
|
|
629
|
-
if (interactive()) {
|
|
630
|
-
// Sorted by area, with the area named once where it changes.
|
|
631
|
-
//
|
|
632
|
-
// Twenty-five servers over eight areas — and five of those areas hold
|
|
633
|
-
// exactly one server, so a heading per area produced "heading, row,
|
|
634
|
-
// heading, row", which is a hat on every line rather than a grouping.
|
|
635
|
-
// The name on the first row of each run says the same thing and costs
|
|
636
|
-
// no rows: what follows inherits it from the blank space beside it.
|
|
637
|
-
const ordered = [...o.servers].sort((a, b) => a.category.localeCompare(b.category) || a.slug.localeCompare(b.slug));
|
|
638
|
-
const rowFor = (slug) => ordered.findIndex((s) => s.slug === slug);
|
|
639
|
-
const initial = [...chosen].map(rowFor).filter((i) => i >= 0);
|
|
640
|
-
const a3 = await pickOrWatch(o, rev, 4, ordered.map((srv) => ({
|
|
641
|
-
label: srv.slug,
|
|
642
|
-
meta: `${srv.toolCount} ${t(STEP_CAPS.toolsWord, o.lang)}`,
|
|
643
|
-
tag: srv.category,
|
|
644
|
-
// Straight from the host, the same sentence the page shows. A
|
|
645
|
-
// hand-written table here would be a second source of truth for
|
|
646
|
-
// something that ships with the server.
|
|
647
|
-
detail: srv.description,
|
|
648
|
-
})), true, initial, o.lang === "zh"
|
|
649
|
-
? "↑↓ 移动 · 空格勾选 · a 全选/全不选 · 回车确认 · esc 返回上一步"
|
|
650
|
-
: "↑↓ move · space to tick · a for all · enter to confirm · esc to go back", 3, {
|
|
651
|
-
read: (d) => d.servers === undefined ? undefined : d.servers.map(rowFor).filter((i) => i >= 0),
|
|
652
|
-
write: async (indexes) => {
|
|
653
|
-
const next = indexes.map((i) => ordered[i]?.slug).filter((x) => Boolean(x));
|
|
654
|
-
({ rev } = await push(o, rev, { draft: { servers: next } }));
|
|
655
|
-
return rev;
|
|
656
|
-
},
|
|
657
|
-
});
|
|
658
|
-
// In a checklist the way back cannot be another row — a row is a
|
|
659
|
-
// thing you tick — so Escape is the gesture, and the hint says so.
|
|
660
|
-
if (a3.by === "back")
|
|
661
|
-
throw new Rewind(a3.to, a3.from === "here");
|
|
662
|
-
if (a3.by === "page") {
|
|
663
|
-
chosen.clear();
|
|
664
|
-
for (const slug of a3.draft.servers ?? [])
|
|
665
|
-
chosen.add(slug);
|
|
666
|
-
const s2 = await pull(o);
|
|
667
|
-
if (s2)
|
|
668
|
-
rev = s2.rev;
|
|
669
|
-
console.log("\n" + dim("│ ") + chalk.magenta(o.lang === "zh" ? "↩ 已在页面中选择" : "↩ chosen in the page"));
|
|
670
|
-
}
|
|
671
|
-
else {
|
|
672
|
-
chosen.clear();
|
|
673
|
-
for (const i of a3.picked ?? []) {
|
|
674
|
-
const srv = ordered[i];
|
|
675
|
-
if (srv)
|
|
676
|
-
chosen.add(srv.slug);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
o.servers.forEach((s, i) => {
|
|
682
|
-
const mark = chosen.has(s.slug) ? chalk.green("[x]") : dim("[ ]");
|
|
683
|
-
console.log(`${dim("│")} ${mark} ${bold(String(i + 1).padStart(2))}) ${s.slug.padEnd(24)} ${dim(String(s.toolCount) + " " + t(STEP_CAPS.toolsWord, o.lang))}`);
|
|
684
|
-
});
|
|
685
|
-
console.log(dim("│"));
|
|
686
|
-
say(o.lang === "zh"
|
|
687
|
-
? "输入编号切换选中(空格分隔),直接回车确认。"
|
|
688
|
-
: "Type numbers to toggle (space-separated), or press enter to confirm.");
|
|
689
|
-
for (;;) {
|
|
690
|
-
const answer = await askLine(bold.cyan("│ > "));
|
|
691
|
-
if (answer === "")
|
|
692
|
-
break;
|
|
693
|
-
for (const tok of answer.split(/[\s,]+/)) {
|
|
694
|
-
const n = Number(tok);
|
|
695
|
-
if (!Number.isInteger(n) || n < 1 || n > o.servers.length)
|
|
696
|
-
continue;
|
|
697
|
-
const slug = o.servers[n - 1].slug;
|
|
698
|
-
if (chosen.has(slug))
|
|
699
|
-
chosen.delete(slug);
|
|
700
|
-
else
|
|
701
|
-
chosen.add(slug);
|
|
702
|
-
}
|
|
703
|
-
console.log(dim("│ ") + chalk.green(`${chosen.size} selected: `) + dim([...chosen].join(", ")));
|
|
704
|
-
({ rev } = await push(o, rev, { draft: { servers: [...chosen] } }));
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
draft.servers = [...chosen];
|
|
708
|
-
if (draft.servers.length === 0) {
|
|
709
|
-
console.log(dim("└─ ") + chalk.yellow(o.lang === "zh" ? "至少选一个" : "Pick at least one"));
|
|
710
|
-
return undefined;
|
|
711
|
-
}
|
|
712
|
-
console.log(dim("└─ ") + chalk.green(`${draft.servers.length} ✓`));
|
|
615
|
+
say(t(STEP_CAPS.ledeTail, o.lang));
|
|
616
|
+
draft.servers = o.servers.map((s) => s.slug);
|
|
617
|
+
for (const server of o.servers)
|
|
618
|
+
console.log(dim("│ ") + server.name + " — " + server.endpoint);
|
|
713
619
|
// Publish the choice but NOT the step: arriving at step 5 is what makes
|
|
714
620
|
// the page start the run by itself, and the user has not confirmed yet.
|
|
715
621
|
// Announcing the step here applied everything without being asked.
|