@aisa-one/cli 0.2.1 → 0.2.3

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 (52) hide show
  1. package/CHANGELOG.md +144 -1
  2. package/README.md +249 -102
  3. package/dist/api.d.ts +46 -1
  4. package/dist/api.js +77 -8
  5. package/dist/cache.d.ts +18 -0
  6. package/dist/cache.js +65 -0
  7. package/dist/catalog.d.ts +90 -0
  8. package/dist/catalog.js +101 -0
  9. package/dist/commands/account.d.ts +4 -1
  10. package/dist/commands/account.js +36 -6
  11. package/dist/commands/api.d.ts +18 -4
  12. package/dist/commands/api.js +357 -24
  13. package/dist/commands/cacheCmd.d.ts +2 -0
  14. package/dist/commands/cacheCmd.js +10 -0
  15. package/dist/commands/completionCmd.d.ts +7 -0
  16. package/dist/commands/completionCmd.js +37 -0
  17. package/dist/commands/configCmd.js +19 -1
  18. package/dist/commands/finance.d.ts +4 -0
  19. package/dist/commands/finance.js +212 -29
  20. package/dist/commands/models.js +6 -0
  21. package/dist/commands/run.d.ts +21 -0
  22. package/dist/commands/run.js +113 -6
  23. package/dist/commands/search.js +8 -3
  24. package/dist/commands/skills.d.ts +6 -2
  25. package/dist/commands/skills.js +189 -134
  26. package/dist/commands/twitter.js +2 -3
  27. package/dist/commands/video.d.ts +40 -0
  28. package/dist/commands/video.js +272 -55
  29. package/dist/completion.d.ts +26 -0
  30. package/dist/completion.js +293 -0
  31. package/dist/constants.d.ts +55 -5
  32. package/dist/constants.js +136 -11
  33. package/dist/index.js +86 -23
  34. package/dist/skills-registry.d.ts +64 -0
  35. package/dist/skills-registry.js +219 -0
  36. package/dist/types.d.ts +32 -1
  37. package/dist/utils/file.d.ts +5 -1
  38. package/dist/utils/file.js +5 -1
  39. package/dist/utils/pool.d.ts +8 -0
  40. package/dist/utils/pool.js +27 -0
  41. package/package.json +9 -3
  42. package/dist/agent/env-setup.d.ts +0 -5
  43. package/dist/agent/env-setup.js +0 -49
  44. package/dist/agent/index-client.d.ts +0 -7
  45. package/dist/agent/index-client.js +0 -22
  46. package/dist/agent/installer.d.ts +0 -19
  47. package/dist/agent/installer.js +0 -70
  48. package/dist/agent/setup-python.d.ts +0 -15
  49. package/dist/agent/setup-python.js +0 -51
  50. package/dist/commands/agent.d.ts +0 -37
  51. package/dist/commands/agent.js +0 -129
  52. package/tests/streaming.test.ts +0 -73
@@ -1,26 +1,359 @@
1
+ import ora from "ora";
1
2
  import chalk from "chalk";
2
- import { requireApiKey } from "../config.js";
3
- import { hint } from "../utils/display.js";
4
- const COMING_SOON = chalk.yellow(" API catalog is not yet available.");
5
- const BROWSE_HINT = "Browse available APIs at https://docs.aisa.one/reference";
6
- export async function apiListAction(_options) {
7
- requireApiKey();
8
- console.log(COMING_SOON);
9
- hint(BROWSE_HINT);
10
- hint("Use 'aisa run <slug> <path>' to call APIs directly");
11
- }
12
- export async function apiSearchAction(_query, _options) {
13
- requireApiKey();
14
- console.log(COMING_SOON);
15
- hint(BROWSE_HINT);
16
- }
17
- export async function apiShowAction(_slug, _path) {
18
- requireApiKey();
19
- console.log(COMING_SOON);
20
- hint(BROWSE_HINT);
21
- }
22
- export async function apiCodeAction(_slug, _path, _options) {
23
- requireApiKey();
24
- console.log(COMING_SOON);
25
- hint(BROWSE_HINT);
3
+ import { error, formatJson, hint, table, truncate } from "../utils/display.js";
4
+ import { API_CATEGORIES, categoryOf } from "../constants.js";
5
+ import { resolveBases } from "../api.js";
6
+ import { getProviders, getProviderDetail, getHealth, getAllEndpoints, flatEndpoints, toRunPath, runSlugOf, formatPrice, } from "../catalog.js";
7
+ const DEFAULT_ENDPOINT_LIMIT = 40;
8
+ function statusBadge(status) {
9
+ switch (status) {
10
+ case "healthy":
11
+ return chalk.green("healthy");
12
+ case "warning":
13
+ return chalk.yellow("warning");
14
+ case "failed":
15
+ return chalk.red("failed");
16
+ case "not_tested":
17
+ return chalk.gray("not tested");
18
+ default:
19
+ return chalk.gray(status || "—");
20
+ }
21
+ }
22
+ function shortDate(iso) {
23
+ if (!iso)
24
+ return "—";
25
+ const d = new Date(iso);
26
+ return Number.isNaN(d.getTime()) ? "—" : d.toISOString().slice(0, 10);
27
+ }
28
+ export async function apiListAction(options) {
29
+ const spinner = ora("Loading API catalog...").start();
30
+ let providers = await getProviders({ refresh: options.refresh });
31
+ const health = options.health ? await getHealth({ refresh: options.refresh }) : [];
32
+ const healthById = new Map(health.map((h) => [h.id, h]));
33
+ spinner.stop();
34
+ if (options.category) {
35
+ const cat = options.category.toLowerCase();
36
+ providers = providers.filter((p) => categoryOf(p.id) === cat);
37
+ if (providers.length === 0) {
38
+ console.log(` No APIs in category "${options.category}".`);
39
+ hint(`Categories: ${API_CATEGORIES.join(", ")}`);
40
+ return;
41
+ }
42
+ }
43
+ if (options.json) {
44
+ console.log(formatJson(providers.map((p) => ({
45
+ ...p,
46
+ category: categoryOf(p.id),
47
+ ...(options.health ? { health: healthById.get(p.id)?.status } : {}),
48
+ }))));
49
+ return;
50
+ }
51
+ const headers = ["API", "CATEGORY", "ENDPOINTS", "FROM", "UPDATED"];
52
+ if (options.health)
53
+ headers.push("STATUS");
54
+ const rows = providers.map((p) => {
55
+ const row = [
56
+ p.id,
57
+ categoryOf(p.id),
58
+ String(p.endpoint_count),
59
+ formatPrice(p.pricing?.normal),
60
+ shortDate(p.updated_at),
61
+ ];
62
+ if (options.health)
63
+ row.push(statusBadge(healthById.get(p.id)?.status));
64
+ return row;
65
+ });
66
+ const total = providers.reduce((sum, p) => sum + p.endpoint_count, 0);
67
+ console.log();
68
+ console.log(table(headers, rows));
69
+ console.log();
70
+ console.log(chalk.gray(` ${providers.length} APIs · ${total} endpoints`));
71
+ hint("Details: aisa api show <api>");
72
+ hint("Search: aisa api search <query>");
73
+ }
74
+ /** Resolve a provider by id, or by the URL slug its endpoints are served under. */
75
+ async function findProvider(idOrSlug, refresh) {
76
+ try {
77
+ return await getProviderDetail(idOrSlug, { refresh });
78
+ }
79
+ catch (err) {
80
+ const providers = await getProviders({ refresh });
81
+ const candidates = providers.filter((p) => p.id.includes(idOrSlug) || idOrSlug.includes(p.id));
82
+ for (const candidate of candidates) {
83
+ const detail = await getProviderDetail(candidate.id, { refresh }).catch(() => undefined);
84
+ if (detail && runSlugOf(detail) === idOrSlug)
85
+ return detail;
86
+ }
87
+ throw err;
88
+ }
89
+ }
90
+ function printEndpointDetail(detail, endpoint) {
91
+ const runPath = toRunPath(endpoint.path);
92
+ const [slug, ...rest] = runPath.split("/");
93
+ console.log(`\n ${chalk.cyan.bold(endpoint.name || runPath)}`);
94
+ if (endpoint.description)
95
+ console.log(` ${endpoint.description}`);
96
+ console.log(`\n Path: ${endpoint.path}`);
97
+ console.log(` Provider: ${detail.id}`);
98
+ console.log(` Price: ${formatPrice(endpoint.pricing?.normal)} per request`);
99
+ const params = [...runPath.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]);
100
+ if (params.length > 0) {
101
+ console.log(` Path params: ${params.join(", ")}`);
102
+ }
103
+ console.log(`\n ${chalk.gray(`aisa run ${slug} /${rest.join("/")}`)}`);
104
+ hint(`Code: aisa api code ${slug} /${rest.join("/")}`);
105
+ }
106
+ export async function apiShowAction(idOrSlug, path, options = {}) {
107
+ const spinner = ora(`Loading ${idOrSlug}...`).start();
108
+ let detail;
109
+ try {
110
+ detail = await findProvider(idOrSlug, options.refresh);
111
+ }
112
+ catch (err) {
113
+ spinner.fail("Not found");
114
+ error(err.message);
115
+ hint("List all: aisa api list");
116
+ return;
117
+ }
118
+ spinner.stop();
119
+ const endpoints = flatEndpoints(detail);
120
+ if (path) {
121
+ const wanted = toRunPath(path).replace(/^\/+/, "");
122
+ // Exact wins outright; a suffix is only usable when it identifies one
123
+ // endpoint. Providers routinely repeat a trailing segment — brave has six
124
+ // endpoints ending in /search — and silently picking the first would show
125
+ // the wrong one.
126
+ const exact = endpoints.filter((e) => {
127
+ const p = toRunPath(e.path);
128
+ return p === wanted || p === `${detail.id}/${wanted}`;
129
+ });
130
+ const matches = exact.length > 0
131
+ ? exact
132
+ : endpoints.filter((e) => toRunPath(e.path).endsWith(`/${wanted}`));
133
+ if (matches.length === 0) {
134
+ error(`No endpoint matching "${path}" in ${detail.id}`);
135
+ hint(`Run 'aisa api show ${detail.id}' to list them`);
136
+ return;
137
+ }
138
+ if (matches.length > 1) {
139
+ error(`"${path}" matches ${matches.length} endpoints in ${detail.id} — be more specific:`);
140
+ for (const e of matches)
141
+ console.log(` ${toRunPath(e.path)}`);
142
+ return;
143
+ }
144
+ if (options.json) {
145
+ console.log(formatJson(matches[0]));
146
+ return;
147
+ }
148
+ printEndpointDetail(detail, matches[0]);
149
+ return;
150
+ }
151
+ if (options.json) {
152
+ console.log(formatJson(detail));
153
+ return;
154
+ }
155
+ const health = options.health
156
+ ? (await getHealth({ refresh: options.refresh })).find((h) => h.id === detail.id)
157
+ : undefined;
158
+ console.log(`\n ${chalk.cyan.bold(detail.id)} ${chalk.gray(`(${categoryOf(detail.id)})`)}`);
159
+ console.log(` ${detail.endpoint_count} endpoints · from ${formatPrice(detail.pricing?.normal)} per request`);
160
+ console.log(` Updated ${shortDate(detail.updated_at)}`);
161
+ if (health) {
162
+ // Health is tracked per provider, not per endpoint — the per-endpoint
163
+ // counts are the same value repeated, so only the provider verdict is real.
164
+ console.log(` Status: ${statusBadge(health.status)}${health.checked_at ? chalk.gray(` (checked ${shortDate(health.checked_at)})`) : ""}`);
165
+ }
166
+ console.log();
167
+ if (options.group) {
168
+ // Group labels come from an operator-entered column ("Zero", "default"),
169
+ // so this is opt-in rather than the default view.
170
+ for (const group of detail.endpoint_groups || []) {
171
+ console.log(` ${chalk.bold(group.name || group.id || "ungrouped")}`);
172
+ for (const e of group.endpoints || []) {
173
+ console.log(` ${chalk.gray(toRunPath(e.path))} ${truncate(e.name || "", 45)}`);
174
+ }
175
+ console.log();
176
+ }
177
+ }
178
+ else {
179
+ const shown = options.all ? endpoints : endpoints.slice(0, DEFAULT_ENDPOINT_LIMIT);
180
+ for (const e of shown) {
181
+ console.log(` ${toRunPath(e.path).padEnd(48)} ${chalk.gray(truncate(e.name || e.description || "", 40))}`);
182
+ }
183
+ if (shown.length < endpoints.length) {
184
+ console.log(chalk.gray(`\n … ${endpoints.length - shown.length} more — pass --all to see them`));
185
+ }
186
+ }
187
+ const slug = runSlugOf(detail);
188
+ if (slug) {
189
+ console.log();
190
+ hint(`Call: aisa run ${slug} <path>`);
191
+ hint(`Detail: aisa api show ${detail.id} <path>`);
192
+ }
193
+ }
194
+ export async function apiSearchAction(query, options) {
195
+ const limit = options.limit ? parseInt(options.limit) : 20;
196
+ const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
197
+ const spinner = ora("Indexing catalog...").start();
198
+ let endpoints;
199
+ let failed = [];
200
+ if (options.provider) {
201
+ const detail = await getProviderDetail(options.provider, { refresh: options.refresh }).catch(() => undefined);
202
+ if (!detail) {
203
+ spinner.fail(`Unknown API "${options.provider}"`);
204
+ return;
205
+ }
206
+ endpoints = flatEndpoints(detail).map((endpoint) => ({ provider: detail.id, endpoint }));
207
+ }
208
+ else {
209
+ const result = await getAllEndpoints({
210
+ refresh: options.refresh,
211
+ onProgress: (done, total) => {
212
+ spinner.text = `Indexing catalog (${done}/${total})...`;
213
+ },
214
+ });
215
+ endpoints = result.endpoints;
216
+ failed = result.failed;
217
+ }
218
+ const providers = await getProviders({ refresh: options.refresh });
219
+ spinner.stop();
220
+ const matchesAll = (haystack) => terms.every((t) => haystack.includes(t));
221
+ // --provider means "restrict to one API", so provider-level hits have to be
222
+ // scoped too — otherwise `search brave --provider financial` reports brave.
223
+ const providerHits = providers
224
+ .filter((p) => !options.provider || p.id === options.provider)
225
+ .filter((p) => matchesAll(p.id.toLowerCase()));
226
+ const scored = endpoints
227
+ .map(({ provider, endpoint }) => {
228
+ const name = (endpoint.name || "").toLowerCase();
229
+ const path = toRunPath(endpoint.path).toLowerCase();
230
+ const desc = (endpoint.description || "").toLowerCase();
231
+ let score = 0;
232
+ if (matchesAll(name))
233
+ score = 3;
234
+ else if (matchesAll(path))
235
+ score = 2;
236
+ else if (matchesAll(desc))
237
+ score = 1;
238
+ return { provider, endpoint, score };
239
+ })
240
+ .filter((r) => r.score > 0)
241
+ .sort((a, b) => b.score - a.score || a.endpoint.path.localeCompare(b.endpoint.path));
242
+ if (options.json) {
243
+ console.log(formatJson({
244
+ providers: providerHits.map((p) => p.id),
245
+ endpoints: scored.slice(0, limit).map((r) => ({
246
+ provider: r.provider,
247
+ path: r.endpoint.path,
248
+ name: r.endpoint.name,
249
+ price: r.endpoint.pricing?.normal,
250
+ })),
251
+ }));
252
+ return;
253
+ }
254
+ if (providerHits.length === 0 && scored.length === 0) {
255
+ console.log(` No matches for "${query}".`);
256
+ if (failed.length > 0) {
257
+ hint(`${failed.length} APIs could not be indexed: ${failed.join(", ")}`);
258
+ }
259
+ return;
260
+ }
261
+ if (providerHits.length > 0) {
262
+ console.log(chalk.bold(`\n Matching APIs\n`));
263
+ for (const p of providerHits) {
264
+ console.log(` ${chalk.cyan.bold(p.id)} ${chalk.gray(`${p.endpoint_count} endpoints · from ${formatPrice(p.pricing?.normal)}`)}`);
265
+ }
266
+ }
267
+ if (scored.length > 0) {
268
+ console.log(chalk.bold(`\n Matching endpoints${scored.length > limit ? ` (${limit} of ${scored.length})` : ""}\n`));
269
+ for (const { endpoint } of scored.slice(0, limit)) {
270
+ const runPath = toRunPath(endpoint.path);
271
+ const [slug, ...rest] = runPath.split("/");
272
+ console.log(` ${chalk.cyan(runPath)} ${chalk.gray(formatPrice(endpoint.pricing?.normal))}`);
273
+ if (endpoint.name)
274
+ console.log(` ${chalk.gray(truncate(endpoint.name, 78))}`);
275
+ console.log(chalk.gray(` aisa run ${slug} /${rest.join("/")}`));
276
+ console.log();
277
+ }
278
+ }
279
+ if (failed.length > 0) {
280
+ hint(`${failed.length} APIs unavailable and not searched: ${failed.join(", ")}`);
281
+ }
282
+ }
283
+ function generateCode(lang, url, method, note) {
284
+ const header = `# ${note}`;
285
+ switch (lang) {
286
+ case "curl":
287
+ return [
288
+ header,
289
+ `curl -X ${method} "${url}" \\`,
290
+ ` -H "Authorization: Bearer $AISA_API_KEY" \\`,
291
+ ` -H "Content-Type: application/json"`,
292
+ ].join("\n");
293
+ case "python":
294
+ return [
295
+ header,
296
+ `import os, requests`,
297
+ ``,
298
+ `resp = requests.${method.toLowerCase()}(`,
299
+ ` "${url}",`,
300
+ ` headers={"Authorization": f"Bearer {os.environ['AISA_API_KEY']}"},`,
301
+ method === "GET" ? ` params={},` : ` json={},`,
302
+ `)`,
303
+ `resp.raise_for_status()`,
304
+ `print(resp.json())`,
305
+ ].join("\n");
306
+ case "node":
307
+ case "typescript":
308
+ return [
309
+ `// ${note}`,
310
+ `const res = await fetch("${url}", {`,
311
+ ` method: "${method}",`,
312
+ ` headers: {`,
313
+ ` Authorization: \`Bearer \${process.env.AISA_API_KEY}\`,`,
314
+ ` "Content-Type": "application/json",`,
315
+ ` },`,
316
+ ...(method === "GET" ? [] : [` body: JSON.stringify({}),`]),
317
+ `});`,
318
+ `console.log(await res.json());`,
319
+ ].join("\n");
320
+ }
321
+ }
322
+ export async function apiCodeAction(slug, path, options) {
323
+ const lang = (options.lang || "curl").toLowerCase();
324
+ if (!["curl", "python", "node", "typescript"].includes(lang)) {
325
+ error(`Unknown language: ${lang}. Valid: curl, python, node, typescript`);
326
+ return;
327
+ }
328
+ const cleanSlug = slug.replace(/^\/+|\/+$/g, "");
329
+ const cleanPath = path.replace(/^\/+/, "");
330
+ const runPath = toRunPath(`${cleanSlug}/${cleanPath}`);
331
+ const url = `${resolveBases().domain}/${runPath}`;
332
+ // Look the endpoint up when possible for validation and pricing, but never
333
+ // let a cold cache or an offline machine block code generation.
334
+ let endpoint;
335
+ try {
336
+ const { endpoints } = await getAllEndpoints({ refresh: options.refresh });
337
+ endpoint = endpoints.find((e) => toRunPath(e.endpoint.path) === runPath)?.endpoint;
338
+ if (!endpoint) {
339
+ hint(`No catalog entry for "${runPath}" — generating anyway`);
340
+ hint(`Search: aisa api search ${cleanPath.split("/")[0]}`);
341
+ }
342
+ }
343
+ catch {
344
+ // offline: generate without validation
345
+ }
346
+ // The catalog hardcodes GET server-side, so it is a hint rather than a fact.
347
+ const method = (options.method || "GET").toUpperCase();
348
+ const price = endpoint?.pricing?.normal;
349
+ const note = [
350
+ endpoint?.name || runPath,
351
+ price != null ? `${formatPrice(price)} per request` : undefined,
352
+ "method is advisory — pass --method to override",
353
+ ]
354
+ .filter(Boolean)
355
+ .join(" · ");
356
+ console.log();
357
+ console.log(generateCode(lang, url, method, note));
358
+ console.log();
26
359
  }
@@ -0,0 +1,2 @@
1
+ export declare function cacheClearAction(): void;
2
+ export declare function cachePathAction(): void;
@@ -0,0 +1,10 @@
1
+ import { cacheDir, clearCache } from "../cache.js";
2
+ import { success, hint } from "../utils/display.js";
3
+ export function cacheClearAction() {
4
+ const removed = clearCache();
5
+ success(removed === 0 ? "Cache already empty." : `Cleared ${removed} cached file(s).`);
6
+ hint(cacheDir());
7
+ }
8
+ export function cachePathAction() {
9
+ console.log(cacheDir());
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { Command } from "commander";
2
+ export declare function completionAction(shell: string | undefined): void;
3
+ /**
4
+ * Hidden helper the shell scripts call on every Tab press. Prints one candidate
5
+ * per line as "value<TAB>description"; the shell does the prefix filtering.
6
+ */
7
+ export declare function completeAction(program: Command, words: string[]): void;
@@ -0,0 +1,37 @@
1
+ import { basename } from "node:path";
2
+ import { complete, completionScript, isShell } from "../completion.js";
3
+ import { error, hint } from "../utils/display.js";
4
+ /** Guess the user's shell from $SHELL so `aisa completion` can work bare. */
5
+ function detectShell() {
6
+ const name = basename(process.env.SHELL || "");
7
+ return isShell(name) ? name : undefined;
8
+ }
9
+ export function completionAction(shell) {
10
+ const target = shell || detectShell();
11
+ if (!target) {
12
+ error("Could not detect your shell.");
13
+ hint("Run: aisa completion <bash|zsh|fish>");
14
+ process.exit(1);
15
+ }
16
+ if (!isShell(target)) {
17
+ error(`Unsupported shell: ${target}. Supported: bash, zsh, fish`);
18
+ process.exit(1);
19
+ }
20
+ console.log(completionScript(target));
21
+ }
22
+ /**
23
+ * Hidden helper the shell scripts call on every Tab press. Prints one candidate
24
+ * per line as "value<TAB>description"; the shell does the prefix filtering.
25
+ */
26
+ export function completeAction(program, words) {
27
+ try {
28
+ for (const candidate of complete(program, words)) {
29
+ console.log(candidate.description
30
+ ? `${candidate.value}\t${candidate.description.replace(/\s+/g, " ")}`
31
+ : candidate.value);
32
+ }
33
+ }
34
+ catch {
35
+ // Never let a completion failure print noise into the user's prompt.
36
+ }
37
+ }
@@ -1,6 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { getConfig, setConfig, listConfig, resetConfig } from "../config.js";
3
- import { success, info } from "../utils/display.js";
3
+ import { success, info, hint } from "../utils/display.js";
4
+ import { resolveBases } from "../api.js";
4
5
  const VALID_KEYS = ["defaultModel", "baseUrl", "outputFormat", "twitterCookies", "twitterProxy"];
5
6
  export function configSetAction(key, value) {
6
7
  if (!VALID_KEYS.includes(key)) {
@@ -8,8 +9,18 @@ export function configSetAction(key, value) {
8
9
  console.error(`Valid keys: ${VALID_KEYS.join(", ")}`);
9
10
  process.exit(1);
10
11
  }
12
+ if (key === "baseUrl" && !/^https?:\/\/[^/]+/.test(value.trim())) {
13
+ console.error(`baseUrl must be an absolute http(s) URL, got: ${value}`);
14
+ process.exit(1);
15
+ }
11
16
  setConfig(key, value);
12
17
  success(`${key} = ${value}`);
18
+ if (key === "baseUrl") {
19
+ const bases = resolveBases();
20
+ hint(`LLM: ${bases.llm}`);
21
+ hint(`Integration: ${bases.domain}`);
22
+ hint(`Catalog: ${bases.info}`);
23
+ }
13
24
  }
14
25
  export function configGetAction(key) {
15
26
  const value = getConfig(key);
@@ -29,6 +40,13 @@ export function configListAction() {
29
40
  for (const [k, v] of Object.entries(display)) {
30
41
  console.log(` ${chalk.cyan(k)} = ${v}`);
31
42
  }
43
+ // baseUrl configures a root that three different bases are derived from;
44
+ // showing them makes a self-hosted setup verifiable.
45
+ const bases = resolveBases();
46
+ console.log(`\n ${chalk.gray("Derived from baseUrl:")}`);
47
+ console.log(` ${chalk.gray(`LLM ${bases.llm}`)}`);
48
+ console.log(` ${chalk.gray(`Integration ${bases.domain}`)}`);
49
+ console.log(` ${chalk.gray(`Catalog ${bases.info}`)}`);
32
50
  }
33
51
  export function configResetAction() {
34
52
  resetConfig();
@@ -5,9 +5,13 @@ export declare function stockAction(symbol: string, options: {
5
5
  export declare function cryptoAction(symbol: string, options: {
6
6
  period?: string;
7
7
  raw?: boolean;
8
+ id?: string;
9
+ source?: string;
8
10
  }): Promise<void>;
9
11
  export declare function screenerAction(options: {
10
12
  sector?: string;
13
+ minMarketCap?: string;
14
+ filter?: string[];
11
15
  limit?: string;
12
16
  raw?: boolean;
13
17
  }): Promise<void>;