skyloom 1.15.5 → 1.16.0

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.
Files changed (39) hide show
  1. package/dist/cli/command_args.d.ts +74 -0
  2. package/dist/cli/command_args.d.ts.map +1 -0
  3. package/dist/cli/command_args.js +129 -0
  4. package/dist/cli/command_args.js.map +1 -0
  5. package/dist/cli/loom.d.ts +20 -0
  6. package/dist/cli/loom.d.ts.map +1 -1
  7. package/dist/cli/loom.js +202 -24
  8. package/dist/cli/loom.js.map +1 -1
  9. package/dist/cli/loom_chat.d.ts.map +1 -1
  10. package/dist/cli/loom_chat.js +39 -0
  11. package/dist/cli/loom_chat.js.map +1 -1
  12. package/dist/core/agent.js +2 -2
  13. package/dist/core/agent.js.map +1 -1
  14. package/dist/core/security.d.ts.map +1 -1
  15. package/dist/core/security.js +1 -0
  16. package/dist/core/security.js.map +1 -1
  17. package/dist/core/tool_router.d.ts.map +1 -1
  18. package/dist/core/tool_router.js +11 -3
  19. package/dist/core/tool_router.js.map +1 -1
  20. package/dist/tools/builtin.d.ts.map +1 -1
  21. package/dist/tools/builtin.js +38 -192
  22. package/dist/tools/builtin.js.map +1 -1
  23. package/dist/tools/websearch.d.ts +92 -0
  24. package/dist/tools/websearch.d.ts.map +1 -0
  25. package/dist/tools/websearch.js +343 -0
  26. package/dist/tools/websearch.js.map +1 -0
  27. package/package.json +1 -1
  28. package/src/cli/command_args.ts +159 -0
  29. package/src/cli/loom.ts +155 -17
  30. package/src/cli/loom_chat.ts +33 -0
  31. package/src/core/agent.ts +2 -2
  32. package/src/core/security.ts +1 -0
  33. package/src/core/tool_router.ts +11 -3
  34. package/src/tools/builtin.ts +38 -190
  35. package/src/tools/websearch.ts +368 -0
  36. package/tests/command_args.test.ts +115 -0
  37. package/tests/loom.test.ts +74 -0
  38. package/tests/tool_router.test.ts +15 -0
  39. package/tests/websearch.test.ts +190 -0
@@ -0,0 +1,343 @@
1
+ "use strict";
2
+ /**
3
+ * 联网搜索 · Web search with a provider waterfall.
4
+ *
5
+ * Why this module exists: the old web_search scraped DuckDuckGo/Bing/Baidu/Sogou
6
+ * HTML. Scraping breaks constantly — engines change markup, block bot
7
+ * user-agents, throw CAPTCHAs, and rate-limit — so "search doesn't work" was the
8
+ * norm. This replaces it with a waterfall that prefers reliable JSON APIs and
9
+ * only falls back to scraping as a last resort:
10
+ *
11
+ * 1. Tavily (TAVILY_API_KEY) — purpose-built for LLM agents, returns an answer
12
+ * 2. Brave (BRAVE_API_KEY) — independent index, clean JSON
13
+ * 3. Serper (SERPER_API_KEY) — Google results as JSON
14
+ * 4. SearXNG (SEARXNG_URL) — self-hosted metasearch JSON
15
+ * 5. Jina (keyless) — s.jina.ai, free, LLM-optimized — works with NO setup
16
+ * 6. Scrape (last resort) — the legacy HTML scrapers
17
+ *
18
+ * The headline win: even with zero configuration, Jina's keyless endpoint gives
19
+ * results that actually return — no API key, no scraping fragility. Set any of
20
+ * the API keys above for enterprise-grade reliability and higher rate limits.
21
+ *
22
+ * The HTTP layer is injectable so the orchestration and every parser are
23
+ * unit-testable without a network.
24
+ */
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.defaultHttp = void 0;
30
+ exports.decodeHtmlEntities = decodeHtmlEntities;
31
+ exports.stripTags = stripTags;
32
+ exports.resolveProviders = resolveProviders;
33
+ exports.webSearch = webSearch;
34
+ exports.formatSearchResults = formatSearchResults;
35
+ exports.readPage = readPage;
36
+ const axios_1 = __importDefault(require("axios"));
37
+ const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36';
38
+ const DEFAULT_TIMEOUT = 15000;
39
+ /** Default HTTP client backed by axios. */
40
+ exports.defaultHttp = {
41
+ async getJson(url, opts) {
42
+ const res = await axios_1.default.get(url, {
43
+ headers: { 'User-Agent': UA, Accept: 'application/json', ...(opts?.headers || {}) },
44
+ timeout: opts?.timeoutMs ?? DEFAULT_TIMEOUT,
45
+ maxRedirects: 5,
46
+ validateStatus: (s) => s >= 200 && s < 300,
47
+ });
48
+ return res.data;
49
+ },
50
+ async postJson(url, body, opts) {
51
+ const res = await axios_1.default.post(url, body, {
52
+ headers: { 'User-Agent': UA, Accept: 'application/json', 'Content-Type': 'application/json', ...(opts?.headers || {}) },
53
+ timeout: opts?.timeoutMs ?? DEFAULT_TIMEOUT,
54
+ maxRedirects: 5,
55
+ validateStatus: (s) => s >= 200 && s < 300,
56
+ });
57
+ return res.data;
58
+ },
59
+ async getText(url, opts) {
60
+ const res = await axios_1.default.get(url, {
61
+ headers: {
62
+ 'User-Agent': UA,
63
+ Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
64
+ 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
65
+ ...(opts?.headers || {}),
66
+ },
67
+ timeout: opts?.timeoutMs ?? DEFAULT_TIMEOUT,
68
+ maxRedirects: 5,
69
+ validateStatus: (s) => s >= 200 && s < 300,
70
+ responseType: 'text',
71
+ transformResponse: [(d) => d],
72
+ });
73
+ return res.data;
74
+ },
75
+ };
76
+ /* ── HTML helpers (shared by the scrape provider) ── */
77
+ function decodeHtmlEntities(s) {
78
+ return s
79
+ .replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
80
+ .replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, ' ')
81
+ .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(parseInt(n, 10)))
82
+ .replace(/&#x([0-9a-f]+);/gi, (_, n) => String.fromCharCode(parseInt(n, 16)));
83
+ }
84
+ function stripTags(s) {
85
+ return decodeHtmlEntities(s.replace(/<[^>]+>/g, '')).replace(/\s+/g, ' ').trim();
86
+ }
87
+ function unwrapDdgRedirect(href) {
88
+ const m = href.match(/[?&]uddg=([^&]+)/);
89
+ if (m) {
90
+ try {
91
+ return decodeURIComponent(m[1]);
92
+ }
93
+ catch { /* fall through */ }
94
+ }
95
+ if (href.startsWith('//'))
96
+ return 'https:' + href;
97
+ return href;
98
+ }
99
+ function clean(results, max) {
100
+ const seen = new Set();
101
+ const out = [];
102
+ for (const r of results) {
103
+ if (!r || !r.title || !/^https?:\/\//i.test(r.url || ''))
104
+ continue;
105
+ if (seen.has(r.url))
106
+ continue;
107
+ seen.add(r.url);
108
+ out.push({ title: r.title.trim(), url: r.url.trim(), snippet: (r.snippet || '').trim() });
109
+ if (out.length >= max)
110
+ break;
111
+ }
112
+ return out;
113
+ }
114
+ /* ════════════════════════════════════════════════════════════
115
+ API providers (preferred — reliable JSON)
116
+ ════════════════════════════════════════════════════════════ */
117
+ async function tavily(http, key, query, max) {
118
+ const data = await http.postJson('https://api.tavily.com/search', {
119
+ query, max_results: max, search_depth: 'basic', include_answer: true,
120
+ }, { headers: { Authorization: `Bearer ${key}` } });
121
+ const results = (data?.results || []).map((r) => ({
122
+ title: r.title || '', url: r.url || '', snippet: r.content || '',
123
+ }));
124
+ return { provider: 'tavily', results: clean(results, max), answer: data?.answer || undefined };
125
+ }
126
+ async function brave(http, key, query, max) {
127
+ const data = await http.getJson(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${max}`, { headers: { 'X-Subscription-Token': key, Accept: 'application/json' } });
128
+ const results = (data?.web?.results || []).map((r) => ({
129
+ title: r.title || '', url: r.url || '', snippet: r.description || '',
130
+ }));
131
+ return { provider: 'brave', results: clean(results, max) };
132
+ }
133
+ async function serper(http, key, query, max) {
134
+ const data = await http.postJson('https://google.serper.dev/search', { q: query, num: max }, { headers: { 'X-API-KEY': key } });
135
+ const results = (data?.organic || []).map((r) => ({
136
+ title: r.title || '', url: r.link || '', snippet: r.snippet || '',
137
+ }));
138
+ const answer = data?.answerBox?.answer || data?.answerBox?.snippet || data?.knowledgeGraph?.description || undefined;
139
+ return { provider: 'serper', results: clean(results, max), answer };
140
+ }
141
+ async function searxng(http, baseUrl, query, max) {
142
+ const base = baseUrl.replace(/\/+$/, '');
143
+ const data = await http.getJson(`${base}/search?q=${encodeURIComponent(query)}&format=json&language=zh-CN`);
144
+ const results = (data?.results || []).map((r) => ({
145
+ title: r.title || '', url: r.url || '', snippet: r.content || '',
146
+ }));
147
+ return { provider: 'searxng', results: clean(results, max) };
148
+ }
149
+ async function jina(http, key, query, max) {
150
+ // s.jina.ai returns the SERP for a query. `X-Respond-With: no-content` skips
151
+ // fetching each page body (faster, fewer tokens — we only want the listing).
152
+ // Keyless works (shared rate pool); a JINA_API_KEY raises the limit.
153
+ const headers = { Accept: 'application/json', 'X-Respond-With': 'no-content' };
154
+ if (key)
155
+ headers.Authorization = `Bearer ${key}`;
156
+ const data = await http.getJson(`https://s.jina.ai/?q=${encodeURIComponent(query)}`, { headers });
157
+ const rows = Array.isArray(data?.data) ? data.data : Array.isArray(data) ? data : [];
158
+ const results = rows.map((r) => ({
159
+ title: r.title || '', url: r.url || '', snippet: r.description || r.content || r.snippet || '',
160
+ }));
161
+ return { provider: 'jina', results: clean(results, max) };
162
+ }
163
+ /* ════════════════════════════════════════════════════════════
164
+ Scrape provider (last resort — fragile HTML parsing)
165
+ ════════════════════════════════════════════════════════════ */
166
+ async function scrapeDuckDuckGo(http, query, max) {
167
+ const html = await http.getText(`https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`);
168
+ const out = [];
169
+ const re = /<a[^>]+class="[^"]*result__a[^"]*"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<a[^>]+class="[^"]*result__snippet[^"]*"[^>]*>([\s\S]*?)<\/a>/gi;
170
+ let m;
171
+ while ((m = re.exec(html)) && out.length < max) {
172
+ out.push({ url: unwrapDdgRedirect(m[1]), title: stripTags(m[2]), snippet: stripTags(m[3]) });
173
+ }
174
+ return out;
175
+ }
176
+ async function scrapeBing(http, query, max) {
177
+ const html = await http.getText(`https://www.bing.com/search?q=${encodeURIComponent(query)}&setlang=zh-cn`);
178
+ const out = [];
179
+ for (const item of html.match(/<li class="b_algo"[\s\S]*?<\/li>/gi) || []) {
180
+ if (out.length >= max)
181
+ break;
182
+ const a = item.match(/<h2[^>]*>\s*<a[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i);
183
+ if (!a)
184
+ continue;
185
+ const snip = item.match(/<p class="b_lineclamp[^"]*"[^>]*>([\s\S]*?)<\/p>/i) || item.match(/<p[^>]*>([\s\S]*?)<\/p>/i);
186
+ out.push({ url: a[1], title: stripTags(a[2]), snippet: snip ? stripTags(snip[1]) : '' });
187
+ }
188
+ return out;
189
+ }
190
+ async function scrapeBaidu(http, query, max) {
191
+ const html = await http.getText(`https://www.baidu.com/s?wd=${encodeURIComponent(query)}`);
192
+ const out = [];
193
+ const re = /<h3[^>]*>[\s\S]{0,500}?<a[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/gi;
194
+ let m;
195
+ while ((m = re.exec(html)) && out.length < max) {
196
+ const url = m[1];
197
+ const title = stripTags(m[2]);
198
+ if (!title || !/^https?:\/\//.test(url))
199
+ continue;
200
+ const after = html.slice(re.lastIndex, re.lastIndex + 4000);
201
+ const snip = after.match(/<span class="content-right[^"]*"[^>]*>([\s\S]*?)<\/span>/i)
202
+ || after.match(/<div class="c-abstract[^"]*"[^>]*>([\s\S]*?)<\/div>/i)
203
+ || after.match(/<p[^>]*>([\s\S]{20,400}?)<\/p>/i);
204
+ out.push({ url, title, snippet: snip ? stripTags(snip[1]) : '' });
205
+ }
206
+ return out;
207
+ }
208
+ async function scrapeSogou(http, query, max) {
209
+ const html = await http.getText(`https://www.sogou.com/web?query=${encodeURIComponent(query)}`);
210
+ const out = [];
211
+ for (const item of html.match(/<div[^>]+class="vrwrap"[\s\S]*?(?=<div[^>]+class="vrwrap"|$)/gi) || []) {
212
+ if (out.length >= max)
213
+ break;
214
+ const a = item.match(/<h3[^>]*>[\s\S]*?<a[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i);
215
+ if (!a)
216
+ continue;
217
+ let url = a[1];
218
+ if (url.startsWith('/link?'))
219
+ url = 'https://www.sogou.com' + url;
220
+ const snip = item.match(/<div[^>]+class="(?:str_info|fz-mid|space-txt)[^"]*"[^>]*>([\s\S]*?)<\/div>/i) || item.match(/<p[^>]*>([\s\S]{20,400}?)<\/p>/i);
221
+ out.push({ url, title: stripTags(a[2]), snippet: snip ? stripTags(snip[1]) : '' });
222
+ }
223
+ return out;
224
+ }
225
+ const SCRAPE_ENGINES = ['duckduckgo', 'bing', 'baidu', 'sogou'];
226
+ async function scrape(http, engine, query, max) {
227
+ const fn = engine === 'bing' ? scrapeBing : engine === 'baidu' ? scrapeBaidu : engine === 'sogou' ? scrapeSogou : scrapeDuckDuckGo;
228
+ return { provider: engine, results: clean(await fn(http, query, max), max) };
229
+ }
230
+ /** Resolve the ordered provider list for a given env + optional pinned engine. */
231
+ function resolveProviders(env, pinned) {
232
+ const p = (pinned || '').trim().toLowerCase();
233
+ const tavilyP = env.TAVILY_API_KEY
234
+ ? { id: 'tavily', run: (h, e, q, m) => tavily(h, e.TAVILY_API_KEY, q, m) } : null;
235
+ const braveKey = env.BRAVE_API_KEY || env.BRAVE_SEARCH_API_KEY;
236
+ const braveP = braveKey
237
+ ? { id: 'brave', run: (h, _e, q, m) => brave(h, braveKey, q, m) } : null;
238
+ const serperP = env.SERPER_API_KEY
239
+ ? { id: 'serper', run: (h, e, q, m) => serper(h, e.SERPER_API_KEY, q, m) } : null;
240
+ const searxngP = env.SEARXNG_URL
241
+ ? { id: 'searxng', run: (h, e, q, m) => searxng(h, e.SEARXNG_URL, q, m) } : null;
242
+ const jinaP = { id: 'jina', run: (h, e, q, m) => jina(h, e.JINA_API_KEY, q, m) };
243
+ const scrapeP = (eng) => ({ id: eng, run: (h, _e, q, m) => scrape(h, eng, q, m) });
244
+ // Explicit pin (tool arg or SKYLOOM_SEARCH_ENGINE) — use only that provider.
245
+ if (p) {
246
+ if (p === 'tavily')
247
+ return tavilyP ? [tavilyP] : [];
248
+ if (p === 'brave')
249
+ return braveP ? [braveP] : [];
250
+ if (p === 'serper')
251
+ return serperP ? [serperP] : [];
252
+ if (p === 'searxng')
253
+ return searxngP ? [searxngP] : [];
254
+ if (p === 'jina')
255
+ return [jinaP];
256
+ if (p === 'ddg' || p === 'duckduckgo')
257
+ return [scrapeP('duckduckgo')];
258
+ if (SCRAPE_ENGINES.includes(p))
259
+ return [scrapeP(p)];
260
+ // Unknown pin → fall through to auto.
261
+ }
262
+ // Auto waterfall: keyed providers first (best), then keyless Jina, then scrape.
263
+ const order = [];
264
+ for (const cand of [tavilyP, braveP, serperP, searxngP])
265
+ if (cand)
266
+ order.push(cand);
267
+ order.push(jinaP);
268
+ for (const eng of SCRAPE_ENGINES)
269
+ order.push(scrapeP(eng));
270
+ return order;
271
+ }
272
+ /**
273
+ * Run a web search through the provider waterfall. Returns the first provider
274
+ * that yields results, or a response with an empty result set + the list of
275
+ * providers that were tried.
276
+ */
277
+ async function webSearch(query, opts = {}) {
278
+ const q = (query || '').trim();
279
+ if (!q)
280
+ throw new Error('query is required');
281
+ const max = Math.max(1, Math.min(20, Math.floor(opts.max ?? 8)));
282
+ const env = opts.env ?? process.env;
283
+ const http = opts.http ?? exports.defaultHttp;
284
+ const pinned = (opts.engine || env.SKYLOOM_SEARCH_ENGINE || '').trim();
285
+ const providers = resolveProviders(env, pinned);
286
+ const tried = [];
287
+ for (const provider of providers) {
288
+ tried.push(provider.id);
289
+ try {
290
+ const res = await provider.run(http, env, q, max);
291
+ if (res.results.length > 0 || res.answer)
292
+ return { ...res, tried };
293
+ }
294
+ catch (e) {
295
+ opts.onProviderError?.(provider.id, String(e?.message || e));
296
+ }
297
+ }
298
+ return { provider: 'none', results: [], tried };
299
+ }
300
+ /** Format a SearchResponse as compact text for an LLM tool result. */
301
+ function formatSearchResults(res) {
302
+ if (!res.results.length && !res.answer) {
303
+ const tried = res.tried?.length ? ` (tried: ${res.tried.join(', ')})` : '';
304
+ return `No search results found${tried}. Try a simpler query, or set a search API key (TAVILY_API_KEY / BRAVE_API_KEY / SERPER_API_KEY) for more reliable results.`;
305
+ }
306
+ const parts = [];
307
+ if (res.answer)
308
+ parts.push(`Answer: ${res.answer}\n`);
309
+ parts.push(`Search results (${res.provider}, ${res.results.length}):`);
310
+ parts.push(res.results.map((r, i) => `${i + 1}. ${r.title}\n ${r.url}${r.snippet ? `\n ${r.snippet}` : ''}`).join('\n'));
311
+ return parts.join('\n');
312
+ }
313
+ /* ════════════════════════════════════════════════════════════
314
+ Page reader — clean, LLM-ready content from a URL
315
+ ════════════════════════════════════════════════════════════ */
316
+ /**
317
+ * Fetch a URL as clean, readable text. Uses Jina's r.jina.ai reader (strips
318
+ * nav/ads, returns markdown) when reachable, falling back to a raw fetch. This
319
+ * is what makes "read the top news article" actually usable — raw HTML is
320
+ * mostly boilerplate.
321
+ */
322
+ async function readPage(url, opts = {}) {
323
+ const env = opts.env ?? process.env;
324
+ const http = opts.http ?? exports.defaultHttp;
325
+ const maxChars = opts.maxChars ?? 12000;
326
+ if (!/^https?:\/\//i.test(url))
327
+ throw new Error('url must be http(s)');
328
+ const headers = { Accept: 'text/plain' };
329
+ if (env.JINA_API_KEY)
330
+ headers.Authorization = `Bearer ${env.JINA_API_KEY}`;
331
+ try {
332
+ const text = await http.getText(`https://r.jina.ai/${url}`, { headers, timeoutMs: 20000 });
333
+ if (text && text.trim())
334
+ return clip(text, maxChars);
335
+ }
336
+ catch { /* fall through to raw fetch */ }
337
+ const raw = await http.getText(url, { timeoutMs: 15000 });
338
+ return clip(stripTags(raw), maxChars);
339
+ }
340
+ function clip(s, max) {
341
+ return s.length > max ? s.slice(0, max) + `\n...[truncated, ${s.length - max} more chars]` : s;
342
+ }
343
+ //# sourceMappingURL=websearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websearch.js","sourceRoot":"","sources":["../../src/tools/websearch.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skyloom",
3
- "version": "1.15.5",
3
+ "version": "1.16.0",
4
4
  "description": "天空织机 Skyloom — 6 weather-themed AI agents: Fog, Rain, Frost, Snow, Dew, Fair",
5
5
  "preferGlobal": true,
6
6
  "type": "commonjs",
@@ -0,0 +1,159 @@
1
+ /**
2
+ * 斜杠命令向导 · Cascading argument wizard for slash commands.
3
+ *
4
+ * After a slash command that takes structured arguments is chosen in the loom
5
+ * palette, the TUI walks the user through its arguments one level at a time:
6
+ * pick a provider, then paste a key; pick a model; pick a session. Each level is
7
+ * navigable with ↑/↓ and filterable by typing — the same affordance as the
8
+ * command palette itself, extended to arguments.
9
+ *
10
+ * This module is the pure brain of that flow (no I/O, no terminal) so it is
11
+ * fully unit-testable: given a command, the values chosen so far, and a snapshot
12
+ * of runtime context, it returns the next step — or null when the command is
13
+ * complete and ready to submit.
14
+ */
15
+
16
+ export interface ArgChoice {
17
+ /** The value contributed to the final command line. */
18
+ value: string;
19
+ /** Display label in the list. */
20
+ label: string;
21
+ /** Optional dim hint shown after the label. */
22
+ hint?: string;
23
+ /** Optional group heading (e.g. provider name) for sectioned lists. */
24
+ group?: string;
25
+ }
26
+
27
+ export interface WizardStep {
28
+ kind: 'choice' | 'freeform';
29
+ /** Heading shown above the list / prompt. */
30
+ title: string;
31
+ /** Choices for a 'choice' step (already ordered). */
32
+ choices: ArgChoice[];
33
+ /** A 'choice' step may also accept a typed value not in the list. */
34
+ allowFreeform: boolean;
35
+ /** Placeholder for a 'freeform' step (or a free-typed choice). */
36
+ placeholder?: string;
37
+ /** Mask typed input (API keys). */
38
+ secret?: boolean;
39
+ }
40
+
41
+ export interface WizardProvider { id: string; label: string; configured: boolean; envVar?: string }
42
+ export interface WizardModel { id: string; provider: string; label: string; hint?: string }
43
+ export interface WizardSession { id: string; label: string }
44
+
45
+ export interface WizardContext {
46
+ providers: WizardProvider[];
47
+ models: WizardModel[];
48
+ sessions: WizardSession[];
49
+ }
50
+
51
+ /** Commands that drive a guided wizard (base name without the leading slash). */
52
+ const WIZARD_COMMANDS = new Set(['model', 'apikey', 'connect', 'resume']);
53
+
54
+ /** Does this base command (with or without leading slash) have a wizard? */
55
+ export function hasWizard(command: string): boolean {
56
+ return WIZARD_COMMANDS.has(command.replace(/^\//, '').trim().toLowerCase());
57
+ }
58
+
59
+ function providerChoices(ctx: WizardContext): ArgChoice[] {
60
+ return ctx.providers.map((p) => ({
61
+ value: p.id,
62
+ label: p.label,
63
+ hint: p.configured ? '✓ 已配置' : (p.envVar ? `需 ${p.envVar}` : '未配置'),
64
+ }));
65
+ }
66
+
67
+ function modelChoices(ctx: WizardContext): ArgChoice[] {
68
+ return ctx.models.map((m) => ({
69
+ value: m.id,
70
+ label: m.id,
71
+ hint: m.hint,
72
+ group: m.provider,
73
+ }));
74
+ }
75
+
76
+ /**
77
+ * The next step for `command` given the values already chosen, or null when the
78
+ * command is complete (ready to submit via {@link buildCommandLine}).
79
+ */
80
+ export function nextWizardStep(command: string, prior: string[], ctx: WizardContext): WizardStep | null {
81
+ const cmd = command.replace(/^\//, '').trim().toLowerCase();
82
+
83
+ switch (cmd) {
84
+ case 'model': {
85
+ if (prior.length >= 1) return null;
86
+ const choices: ArgChoice[] = [
87
+ { value: 'reset', label: '↺ reset', hint: '回到统一默认模型' },
88
+ ...modelChoices(ctx),
89
+ ];
90
+ return { kind: 'choice', title: '选择模型(输入可筛选)', choices, allowFreeform: true, placeholder: '模型 id' };
91
+ }
92
+
93
+ case 'connect': {
94
+ if (prior.length >= 1) return null;
95
+ return { kind: 'choice', title: '选择 Provider', choices: providerChoices(ctx), allowFreeform: true, placeholder: 'provider' };
96
+ }
97
+
98
+ case 'apikey': {
99
+ // step 0: provider · step 1: the key
100
+ if (prior.length === 0) {
101
+ return { kind: 'choice', title: '为哪个 Provider 配置 API Key', choices: providerChoices(ctx), allowFreeform: true, placeholder: 'provider' };
102
+ }
103
+ if (prior.length === 1) {
104
+ return { kind: 'freeform', title: `粘贴 ${prior[0]} 的 API Key`, choices: [], allowFreeform: true, placeholder: 'sk-…(回车保存)', secret: true };
105
+ }
106
+ return null;
107
+ }
108
+
109
+ case 'resume': {
110
+ if (prior.length >= 1) return null;
111
+ const choices: ArgChoice[] = ctx.sessions.map((s, i) => ({ value: String(i + 1), label: `${i + 1}. ${s.label}`, hint: s.id.slice(0, 8) }));
112
+ return { kind: 'choice', title: choices.length ? '选择要恢复的会话' : '暂无历史会话', choices, allowFreeform: true, placeholder: '序号或 id' };
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+
118
+ /** Assemble the final command line from the base command + chosen values. */
119
+ export function buildCommandLine(command: string, values: string[]): string {
120
+ const cmd = command.replace(/^\//, '').trim().toLowerCase();
121
+ const v = values.filter((x) => x !== undefined && x !== null);
122
+ switch (cmd) {
123
+ case 'apikey':
124
+ // /apikey set <provider> <key>
125
+ return `/apikey set ${v.join(' ')}`.trim();
126
+ case 'model':
127
+ return `/model ${v.join(' ')}`.trim();
128
+ case 'connect':
129
+ return `/connect ${v.join(' ')}`.trim();
130
+ case 'resume':
131
+ return `/resume ${v.join(' ')}`.trim();
132
+ default:
133
+ return `/${cmd} ${v.join(' ')}`.trim();
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Filter + rank choices by a typed query (case-insensitive substring on value,
139
+ * label, and group). Empty query returns the list unchanged. Exact value/label
140
+ * prefix matches sort first so the obvious pick lands at the top.
141
+ */
142
+ export function filterChoices(choices: ArgChoice[], typed: string): ArgChoice[] {
143
+ const q = typed.trim().toLowerCase();
144
+ if (!q) return choices;
145
+ const scored: Array<{ c: ArgChoice; rank: number }> = [];
146
+ for (const c of choices) {
147
+ const value = c.value.toLowerCase();
148
+ const label = c.label.toLowerCase();
149
+ const group = (c.group || '').toLowerCase();
150
+ let rank = -1;
151
+ if (value === q || label === q) rank = 0;
152
+ else if (value.startsWith(q) || label.startsWith(q)) rank = 1;
153
+ else if (value.includes(q) || label.includes(q)) rank = 2;
154
+ else if (group.includes(q)) rank = 3;
155
+ if (rank >= 0) scored.push({ c, rank });
156
+ }
157
+ scored.sort((a, b) => a.rank - b.rank); // stable within equal ranks
158
+ return scored.map((s) => s.c);
159
+ }