@aisa-one/cli 0.0.1 → 0.1.1

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 (45) hide show
  1. package/README.md +203 -0
  2. package/dist/api.d.ts +11 -0
  3. package/dist/api.js +61 -0
  4. package/dist/commands/account.d.ts +5 -0
  5. package/dist/commands/account.js +47 -0
  6. package/dist/commands/api.d.ts +10 -0
  7. package/dist/commands/api.js +116 -0
  8. package/dist/commands/auth.d.ts +5 -0
  9. package/dist/commands/auth.js +29 -0
  10. package/dist/commands/chat.d.ts +8 -0
  11. package/dist/commands/chat.js +78 -0
  12. package/dist/commands/configCmd.d.ts +4 -0
  13. package/dist/commands/configCmd.js +36 -0
  14. package/dist/commands/finance.d.ts +13 -0
  15. package/dist/commands/finance.js +85 -0
  16. package/dist/commands/mcp.d.ts +4 -0
  17. package/dist/commands/mcp.js +67 -0
  18. package/dist/commands/models.d.ts +4 -0
  19. package/dist/commands/models.js +65 -0
  20. package/dist/commands/run.d.ts +7 -0
  21. package/dist/commands/run.js +70 -0
  22. package/dist/commands/search.d.ts +9 -0
  23. package/dist/commands/search.js +43 -0
  24. package/dist/commands/skills.d.ts +22 -0
  25. package/dist/commands/skills.js +555 -0
  26. package/dist/commands/twitter.d.ts +14 -0
  27. package/dist/commands/twitter.js +85 -0
  28. package/dist/commands/video.d.ts +9 -0
  29. package/dist/commands/video.js +73 -0
  30. package/dist/config.d.ts +10 -0
  31. package/dist/config.js +56 -0
  32. package/dist/constants.d.ts +14 -0
  33. package/dist/constants.js +43 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +291 -0
  36. package/dist/types.d.ts +100 -0
  37. package/dist/types.js +1 -0
  38. package/dist/utils/display.d.ts +8 -0
  39. package/dist/utils/display.js +33 -0
  40. package/dist/utils/file.d.ts +12 -0
  41. package/dist/utils/file.js +70 -0
  42. package/dist/utils/streaming.d.ts +5 -0
  43. package/dist/utils/streaming.js +37 -0
  44. package/package.json +53 -5
  45. package/index.js +0 -3
package/README.md ADDED
@@ -0,0 +1,203 @@
1
+ # @aisa-one/cli
2
+
3
+ CLI for the [AISA](https://aisa.one) unified AI infrastructure platform. Access 70+ LLMs, web search, financial data, Twitter/X, and video generation APIs — all from one command line.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @aisa-one/cli
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Authenticate
15
+ aisa login --key sk-your-api-key
16
+
17
+ # Chat with any LLM
18
+ aisa chat "Explain quantum computing" --model claude-opus-4-6
19
+
20
+ # Search the web
21
+ aisa web-search "latest AI research"
22
+
23
+ # Look up stock data
24
+ aisa stock AAPL
25
+
26
+ # Post a tweet
27
+ aisa tweet "Hello from AISA CLI!"
28
+
29
+ # Generate a video
30
+ aisa video create "A cat playing piano" --wait
31
+ ```
32
+
33
+ Get your API key at [aisa.one/dashboard](https://aisa.one/dashboard). New accounts receive $5 in free credits.
34
+
35
+ ## Commands
36
+
37
+ ### Authentication
38
+
39
+ ```bash
40
+ aisa login --key <key> # Store API key (also reads AISA_API_KEY env var)
41
+ aisa logout # Remove stored key
42
+ aisa whoami # Show auth status
43
+ ```
44
+
45
+ ### LLM Gateway
46
+
47
+ Chat with 70+ models (GPT, Claude, Gemini, Qwen, Deepseek, Grok) through a single OpenAI-compatible endpoint.
48
+
49
+ ```bash
50
+ aisa chat "your message" --model gpt-4.1
51
+ aisa chat "explain this" --model claude-opus-4-6 --stream
52
+ aisa chat "respond in JSON" --model gemini-2.5-pro --json
53
+ echo "summarize this" | aisa chat # pipe support
54
+
55
+ aisa models # list all models
56
+ aisa models --provider anthropic # filter by provider
57
+ aisa models show gpt-4.1 # model details + pricing
58
+ ```
59
+
60
+ ### API Discovery & Execution
61
+
62
+ ```bash
63
+ aisa api list # list all API endpoints
64
+ aisa api list --category finance # filter by category
65
+ aisa api search "email finder" # natural language search
66
+ aisa api show finance /stock/price # endpoint details + params
67
+ aisa api code finance /stock/price --lang python # generate code snippet
68
+
69
+ # Execute any API
70
+ aisa run finance /stock/price -q "symbol=AAPL"
71
+ aisa run tavily /search -d '{"query": "AI news"}'
72
+ aisa run twitter /tweet/advanced-search -q "query=AI agents" --raw
73
+ ```
74
+
75
+ ### Web Search
76
+
77
+ ```bash
78
+ aisa web-search "query" # smart search (default)
79
+ aisa web-search "query" --type full # full-text search
80
+ aisa web-search "query" --type youtube # YouTube search
81
+ aisa web-search "query" --type tavily # Tavily deep search
82
+ aisa scholar "transformer architecture" # academic papers
83
+ ```
84
+
85
+ ### Finance
86
+
87
+ ```bash
88
+ aisa stock AAPL # stock price
89
+ aisa stock MSFT --field earnings # earnings data
90
+ aisa stock TSLA --field filings # SEC filings
91
+ aisa stock NVDA --field insider # insider trades
92
+ aisa crypto BTC # crypto price
93
+ aisa crypto ETH --period 30d # historical
94
+ aisa screener --sector Technology # stock screener
95
+ ```
96
+
97
+ ### Twitter/X
98
+
99
+ ```bash
100
+ aisa tweet "Hello world!" # post a tweet
101
+ aisa tweet "Reply" --reply-to 123456 # reply
102
+ aisa twitter search "AI agents" --limit 20 # search tweets
103
+ aisa twitter user elonmusk # user profile
104
+ aisa twitter trends # trending topics
105
+ ```
106
+
107
+ ### Video Generation
108
+
109
+ ```bash
110
+ aisa video create "A sunset timelapse" # create task
111
+ aisa video create "Dancing robot" --wait # wait for result
112
+ aisa video status <task-id> # check status
113
+ ```
114
+
115
+ ### Account
116
+
117
+ ```bash
118
+ aisa balance # credit balance
119
+ aisa usage --limit 20 # usage history
120
+ aisa usage --days 7 # last 7 days
121
+ ```
122
+
123
+ ## Skills
124
+
125
+ Skills are markdown files that teach AI coding agents (Claude Code, Cursor, Copilot, etc.) how to use AISA APIs.
126
+
127
+ ### Browse & Install
128
+
129
+ ```bash
130
+ aisa skills list # browse skills
131
+ aisa skills search "financial analysis" # search
132
+ aisa skills show aisa-team/finance-analyst # details
133
+ aisa skills add aisa-team/finance-analyst # install to all agents
134
+ aisa skills add aisa-team/llm-assistant --agent claude # specific agent
135
+ aisa skills remove aisa-team/finance-analyst # uninstall
136
+ ```
137
+
138
+ Skills install to agent directories automatically:
139
+
140
+ | Agent | Directory |
141
+ |-------|-----------|
142
+ | Claude Code | `~/.claude/skills/` |
143
+ | Cursor | `~/.cursor/skills/` |
144
+ | GitHub Copilot | `~/.github/skills/` |
145
+ | Windsurf | `~/.codeium/windsurf/skills/` |
146
+ | Codex | `~/.agents/skills/` |
147
+ | Gemini | `~/.gemini/skills/` |
148
+ | OpenClaw | `~/.openclaw/skills/` |
149
+
150
+ ### Create & Publish
151
+
152
+ ```bash
153
+ aisa skills init my-skill # create from default template
154
+ aisa skills init my-skill --template finance # finance template
155
+ aisa skills init my-skill --template llm # LLM template
156
+ aisa skills submit ./my-skill # submit to AISA
157
+ aisa skills push owner/my-skill # push updates
158
+ aisa skills request-verification owner/my-skill # request review
159
+ ```
160
+
161
+ Available templates: `default`, `llm`, `search`, `finance`, `twitter`, `video`
162
+
163
+ ## MCP Server
164
+
165
+ Auto-configure AISA's MCP server for your AI agents:
166
+
167
+ ```bash
168
+ aisa mcp setup # configure all detected agents
169
+ aisa mcp setup --agent cursor # Cursor only
170
+ aisa mcp setup --agent claude-desktop # Claude Desktop only
171
+ aisa mcp status # check configuration
172
+ ```
173
+
174
+ ## Configuration
175
+
176
+ ```bash
177
+ aisa config set defaultModel claude-opus-4-6 # change default model
178
+ aisa config get defaultModel # read a value
179
+ aisa config list # show all settings
180
+ aisa config reset # reset to defaults
181
+ ```
182
+
183
+ Settings:
184
+ - `defaultModel` — default model for `aisa chat` (default: `gpt-4.1`)
185
+ - `baseUrl` — API base URL (default: `https://api.aisa.one`)
186
+ - `outputFormat` — output format: `text` or `json`
187
+
188
+ Environment variable `AISA_API_KEY` takes precedence over stored key.
189
+
190
+ ## Development
191
+
192
+ ```bash
193
+ git clone https://github.com/AIsa-team/cli.git
194
+ cd cli
195
+ npm install
196
+ npm run build # compile TypeScript
197
+ npm run dev # watch mode
198
+ npm test # run tests
199
+ ```
200
+
201
+ ## License
202
+
203
+ MIT
package/dist/api.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { type Response } from "node-fetch";
2
+ import type { ApiResponse } from "./types.js";
3
+ export interface RequestOptions {
4
+ method?: "GET" | "POST" | "PUT" | "DELETE";
5
+ query?: Record<string, string>;
6
+ body?: unknown;
7
+ stream?: boolean;
8
+ headers?: Record<string, string>;
9
+ }
10
+ export declare function apiRequest<T = unknown>(apiKey: string, endpoint: string, options?: RequestOptions): Promise<ApiResponse<T>>;
11
+ export declare function apiRequestRaw(apiKey: string, endpoint: string, options?: RequestOptions): Promise<Response>;
package/dist/api.js ADDED
@@ -0,0 +1,61 @@
1
+ import fetch from "node-fetch";
2
+ import { CLI_BASE_URL } from "./constants.js";
3
+ import { getConfig } from "./config.js";
4
+ function getBaseUrl() {
5
+ const custom = getConfig("baseUrl");
6
+ return custom || CLI_BASE_URL;
7
+ }
8
+ export async function apiRequest(apiKey, endpoint, options = {}) {
9
+ const { method = "GET", query, body, headers: extraHeaders } = options;
10
+ const baseUrl = getBaseUrl();
11
+ let url = `${baseUrl}/${endpoint}`;
12
+ if (query) {
13
+ const params = new URLSearchParams(query);
14
+ url += `?${params.toString()}`;
15
+ }
16
+ const headers = {
17
+ Authorization: `Bearer ${apiKey}`,
18
+ "Content-Type": "application/json",
19
+ "x-aisa-source": "cli",
20
+ ...extraHeaders,
21
+ };
22
+ const res = await fetch(url, {
23
+ method,
24
+ headers,
25
+ body: body ? JSON.stringify(body) : undefined,
26
+ });
27
+ if (!res.ok) {
28
+ const text = await res.text();
29
+ let errorMsg;
30
+ try {
31
+ const json = JSON.parse(text);
32
+ errorMsg = json.error?.message || json.error || json.message || text;
33
+ }
34
+ catch {
35
+ errorMsg = text;
36
+ }
37
+ return { success: false, error: `${res.status}: ${errorMsg}` };
38
+ }
39
+ const data = (await res.json());
40
+ return { success: true, data };
41
+ }
42
+ export async function apiRequestRaw(apiKey, endpoint, options = {}) {
43
+ const { method = "GET", query, body, headers: extraHeaders } = options;
44
+ const baseUrl = getBaseUrl();
45
+ let url = `${baseUrl}/${endpoint}`;
46
+ if (query) {
47
+ const params = new URLSearchParams(query);
48
+ url += `?${params.toString()}`;
49
+ }
50
+ const headers = {
51
+ Authorization: `Bearer ${apiKey}`,
52
+ "Content-Type": "application/json",
53
+ "x-aisa-source": "cli",
54
+ ...extraHeaders,
55
+ };
56
+ return fetch(url, {
57
+ method,
58
+ headers,
59
+ body: body ? JSON.stringify(body) : undefined,
60
+ });
61
+ }
@@ -0,0 +1,5 @@
1
+ export declare function balanceAction(): Promise<void>;
2
+ export declare function usageAction(options: {
3
+ limit?: string;
4
+ days?: string;
5
+ }): Promise<void>;
@@ -0,0 +1,47 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { requireApiKey } from "../config.js";
4
+ import { apiRequest } from "../api.js";
5
+ import { error, table } from "../utils/display.js";
6
+ export async function balanceAction() {
7
+ const key = requireApiKey();
8
+ const spinner = ora("Fetching balance...").start();
9
+ const res = await apiRequest(key, "credits/balance");
10
+ if (!res.success || !res.data) {
11
+ spinner.fail("Failed to fetch balance");
12
+ error(res.error || "Unknown error");
13
+ return;
14
+ }
15
+ spinner.stop();
16
+ console.log(` Balance: ${chalk.green("$" + res.data.balance.toFixed(2))} ${res.data.currency}`);
17
+ console.log(chalk.gray(" Add credits at https://aisa.one/dashboard/wallet"));
18
+ }
19
+ export async function usageAction(options) {
20
+ const key = requireApiKey();
21
+ const spinner = ora("Fetching usage...").start();
22
+ const query = {};
23
+ if (options.limit)
24
+ query.limit = options.limit;
25
+ if (options.days)
26
+ query.days = options.days;
27
+ const res = await apiRequest(key, "credits/usage", { query });
28
+ if (!res.success || !res.data) {
29
+ spinner.fail("Failed to fetch usage");
30
+ error(res.error || "Unknown error");
31
+ return;
32
+ }
33
+ spinner.stop();
34
+ const { records } = res.data;
35
+ if (records.length === 0) {
36
+ console.log(" No usage records found.");
37
+ return;
38
+ }
39
+ const rows = records.map((r) => [
40
+ new Date(r.timestamp).toLocaleDateString(),
41
+ r.api,
42
+ r.endpoint,
43
+ `$${r.cost.toFixed(4)}`,
44
+ r.status,
45
+ ]);
46
+ console.log(table(["Date", "API", "Endpoint", "Cost", "Status"], rows));
47
+ }
@@ -0,0 +1,10 @@
1
+ export declare function apiListAction(options: {
2
+ category?: string;
3
+ }): Promise<void>;
4
+ export declare function apiSearchAction(query: string, options: {
5
+ limit?: string;
6
+ }): Promise<void>;
7
+ export declare function apiShowAction(slug: string, path?: string): Promise<void>;
8
+ export declare function apiCodeAction(slug: string, path: string, options: {
9
+ lang?: string;
10
+ }): Promise<void>;
@@ -0,0 +1,116 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { requireApiKey } from "../config.js";
4
+ import { apiRequest } from "../api.js";
5
+ import { error, badge, table, hint, truncate } from "../utils/display.js";
6
+ export async function apiListAction(options) {
7
+ const key = requireApiKey();
8
+ const spinner = ora("Listing APIs...").start();
9
+ const query = {};
10
+ if (options.category)
11
+ query.category = options.category;
12
+ const res = await apiRequest(key, "cli/apis", { query });
13
+ if (!res.success || !res.data) {
14
+ spinner.fail("Failed to list APIs");
15
+ error(res.error || "Unknown error");
16
+ return;
17
+ }
18
+ spinner.stop();
19
+ const { apis } = res.data;
20
+ if (apis.length === 0) {
21
+ console.log(" No APIs found.");
22
+ return;
23
+ }
24
+ for (const api of apis) {
25
+ console.log(`\n ${chalk.cyan.bold(api.name)} ${chalk.gray(api.slug)} ${badge(api.category)}`);
26
+ console.log(` ${chalk.gray(api.description)}`);
27
+ const shown = api.endpoints.slice(0, 3);
28
+ for (const ep of shown) {
29
+ console.log(` ${chalk.yellow(ep.method.padEnd(6))} ${ep.path} ${chalk.gray("- " + truncate(ep.description, 50))}`);
30
+ }
31
+ if (api.endpoints.length > 3) {
32
+ hint(`+${api.endpoints.length - 3} more endpoints`);
33
+ }
34
+ }
35
+ console.log(chalk.gray("\n Run 'aisa api show <slug>' for endpoint details"));
36
+ }
37
+ export async function apiSearchAction(query, options) {
38
+ const key = requireApiKey();
39
+ const spinner = ora(`Searching "${query}"...`).start();
40
+ const res = await apiRequest(key, "cli/apis/search", {
41
+ method: "POST",
42
+ body: { query, limit: parseInt(options.limit || "10") },
43
+ });
44
+ if (!res.success || !res.data) {
45
+ spinner.fail("Search failed");
46
+ error(res.error || "Unknown error");
47
+ return;
48
+ }
49
+ spinner.stop();
50
+ const { apis } = res.data;
51
+ if (apis.length === 0) {
52
+ console.log(` No APIs found for "${query}".`);
53
+ return;
54
+ }
55
+ for (const api of apis) {
56
+ console.log(`\n ${chalk.cyan.bold(api.name)} ${chalk.gray(api.slug)} ${badge(api.category)}`);
57
+ console.log(` ${chalk.gray(api.description)}`);
58
+ for (const ep of api.endpoints.slice(0, 2)) {
59
+ console.log(` ${chalk.yellow(ep.method.padEnd(6))} ${ep.path}`);
60
+ }
61
+ }
62
+ console.log(chalk.gray("\n Run 'aisa run <slug> <path>' to execute"));
63
+ }
64
+ export async function apiShowAction(slug, path) {
65
+ const key = requireApiKey();
66
+ const spinner = ora(`Loading ${slug}${path ? " " + path : ""}...`).start();
67
+ const endpoint = path ? `cli/apis/${slug}${path}` : `cli/apis/${slug}`;
68
+ const res = await apiRequest(key, endpoint);
69
+ if (!res.success || !res.data) {
70
+ spinner.fail("Failed to load API details");
71
+ error(res.error || "Unknown error");
72
+ return;
73
+ }
74
+ spinner.stop();
75
+ const data = res.data;
76
+ if ("endpoints" in data) {
77
+ // ApiGroup
78
+ console.log(`\n ${chalk.cyan.bold(data.name)} ${badge(data.category)}`);
79
+ console.log(` ${data.description}\n`);
80
+ for (const ep of data.endpoints) {
81
+ console.log(` ${chalk.yellow(ep.method.padEnd(6))} ${ep.path}`);
82
+ console.log(` ${chalk.gray(ep.description)}`);
83
+ }
84
+ }
85
+ else {
86
+ // ApiEndpoint with parameters
87
+ console.log(`\n ${chalk.yellow(data.method)} ${data.path}`);
88
+ console.log(` ${data.description}\n`);
89
+ if (data.parameters && data.parameters.length > 0) {
90
+ const rows = data.parameters.map((p) => [
91
+ p.name,
92
+ p.type,
93
+ p.required ? chalk.red("required") : "optional",
94
+ truncate(p.description, 40),
95
+ ]);
96
+ console.log(table(["Name", "Type", "Required", "Description"], rows));
97
+ }
98
+ console.log(chalk.gray(`\n Example: aisa run ${slug} ${data.path} -q "param=value"`));
99
+ }
100
+ }
101
+ export async function apiCodeAction(slug, path, options) {
102
+ const key = requireApiKey();
103
+ const lang = options.lang || "typescript";
104
+ const spinner = ora(`Generating ${lang} code...`).start();
105
+ const res = await apiRequest(key, "cli/apis/code", {
106
+ method: "POST",
107
+ body: { slug, path, language: lang },
108
+ });
109
+ if (!res.success || !res.data) {
110
+ spinner.fail("Failed to generate code");
111
+ error(res.error || "Unknown error");
112
+ return;
113
+ }
114
+ spinner.stop();
115
+ console.log(`\n${res.data.code}`);
116
+ }
@@ -0,0 +1,5 @@
1
+ export declare function loginAction(options: {
2
+ key?: string;
3
+ }): void;
4
+ export declare function logoutAction(): void;
5
+ export declare function whoamiAction(): void;
@@ -0,0 +1,29 @@
1
+ import chalk from "chalk";
2
+ import { setApiKey, clearApiKey, getApiKey, getKeySource, maskKey } from "../config.js";
3
+ import { success, error, info } from "../utils/display.js";
4
+ import { ENV_VAR_NAME } from "../constants.js";
5
+ export function loginAction(options) {
6
+ const key = options.key || process.env[ENV_VAR_NAME];
7
+ if (!key) {
8
+ error(`Provide a key with --key or set ${ENV_VAR_NAME}`);
9
+ process.exit(1);
10
+ }
11
+ setApiKey(key);
12
+ success(`Authenticated: ${maskKey(key)}`);
13
+ console.log(chalk.gray(" Get your API key at https://aisa.one/dashboard"));
14
+ }
15
+ export function logoutAction() {
16
+ clearApiKey();
17
+ success("Logged out. API key removed.");
18
+ }
19
+ export function whoamiAction() {
20
+ const key = getApiKey();
21
+ const source = getKeySource();
22
+ if (!key) {
23
+ info("Not authenticated.");
24
+ console.log(chalk.gray(` Run "aisa login --key <key>" or set ${ENV_VAR_NAME}`));
25
+ return;
26
+ }
27
+ console.log(` Key: ${maskKey(key)}`);
28
+ console.log(` Source: ${source === "env" ? `${ENV_VAR_NAME} env var` : "stored config"}`);
29
+ }
@@ -0,0 +1,8 @@
1
+ export declare function chatAction(message: string | undefined, options: {
2
+ model?: string;
3
+ system?: string;
4
+ stream?: boolean;
5
+ json?: boolean;
6
+ maxTokens?: string;
7
+ temperature?: string;
8
+ }): Promise<void>;
@@ -0,0 +1,78 @@
1
+ import ora from "ora";
2
+ import chalk from "chalk";
3
+ import { requireApiKey, getConfig } from "../config.js";
4
+ import { apiRequest, apiRequestRaw } from "../api.js";
5
+ import { error } from "../utils/display.js";
6
+ import { handleSSEStream } from "../utils/streaming.js";
7
+ export async function chatAction(message, options) {
8
+ const key = requireApiKey();
9
+ // Read from stdin if no message provided
10
+ let text = message;
11
+ if (!text) {
12
+ if (process.stdin.isTTY) {
13
+ error("Provide a message or pipe input: echo 'hello' | aisa chat");
14
+ process.exit(1);
15
+ }
16
+ const chunks = [];
17
+ for await (const chunk of process.stdin) {
18
+ chunks.push(chunk);
19
+ }
20
+ text = Buffer.concat(chunks).toString("utf-8").trim();
21
+ }
22
+ if (!text) {
23
+ error("Empty message");
24
+ process.exit(1);
25
+ }
26
+ const model = options.model || getConfig("defaultModel") || "gpt-4.1";
27
+ const stream = options.stream !== false; // default true
28
+ const messages = [];
29
+ if (options.system) {
30
+ messages.push({ role: "system", content: options.system });
31
+ }
32
+ messages.push({ role: "user", content: text });
33
+ const body = { model, messages, stream };
34
+ if (options.maxTokens)
35
+ body.max_tokens = parseInt(options.maxTokens);
36
+ if (options.temperature)
37
+ body.temperature = parseFloat(options.temperature);
38
+ if (stream) {
39
+ const res = await apiRequestRaw(key, "chat/completions", {
40
+ method: "POST",
41
+ body,
42
+ headers: { Accept: "text/event-stream" },
43
+ });
44
+ if (!res.ok) {
45
+ const text = await res.text();
46
+ error(`${res.status}: ${text}`);
47
+ return;
48
+ }
49
+ await handleSSEStream(res, (token) => process.stdout.write(token), (usage) => {
50
+ console.log();
51
+ if (usage) {
52
+ console.log(chalk.gray(` ${model} · ${usage.prompt_tokens} in / ${usage.completion_tokens} out`));
53
+ }
54
+ });
55
+ }
56
+ else {
57
+ const spinner = ora(`${model}...`).start();
58
+ const res = await apiRequest(key, "chat/completions", {
59
+ method: "POST",
60
+ body,
61
+ });
62
+ if (!res.success || !res.data) {
63
+ spinner.fail("Chat failed");
64
+ error(res.error || "Unknown error");
65
+ return;
66
+ }
67
+ spinner.stop();
68
+ if (options.json) {
69
+ console.log(JSON.stringify(res.data, null, 2));
70
+ }
71
+ else {
72
+ console.log(res.data.choices[0].message.content);
73
+ if (res.data.usage) {
74
+ console.log(chalk.gray(`\n ${model} · ${res.data.usage.prompt_tokens} in / ${res.data.usage.completion_tokens} out`));
75
+ }
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,4 @@
1
+ export declare function configSetAction(key: string, value: string): void;
2
+ export declare function configGetAction(key: string): void;
3
+ export declare function configListAction(): void;
4
+ export declare function configResetAction(): void;
@@ -0,0 +1,36 @@
1
+ import chalk from "chalk";
2
+ import { getConfig, setConfig, listConfig, resetConfig } from "../config.js";
3
+ import { success, info } from "../utils/display.js";
4
+ const VALID_KEYS = ["defaultModel", "baseUrl", "outputFormat"];
5
+ export function configSetAction(key, value) {
6
+ if (!VALID_KEYS.includes(key)) {
7
+ console.error(`Unknown config key: ${key}`);
8
+ console.error(`Valid keys: ${VALID_KEYS.join(", ")}`);
9
+ process.exit(1);
10
+ }
11
+ setConfig(key, value);
12
+ success(`${key} = ${value}`);
13
+ }
14
+ export function configGetAction(key) {
15
+ const value = getConfig(key);
16
+ if (value === undefined) {
17
+ info(`${key} is not set.`);
18
+ }
19
+ else {
20
+ console.log(` ${key} = ${value}`);
21
+ }
22
+ }
23
+ export function configListAction() {
24
+ const all = listConfig();
25
+ const display = { ...all };
26
+ if (display.apiKey) {
27
+ display.apiKey = "****";
28
+ }
29
+ for (const [k, v] of Object.entries(display)) {
30
+ console.log(` ${chalk.cyan(k)} = ${v}`);
31
+ }
32
+ }
33
+ export function configResetAction() {
34
+ resetConfig();
35
+ success("Config reset to defaults.");
36
+ }
@@ -0,0 +1,13 @@
1
+ export declare function stockAction(symbol: string, options: {
2
+ field?: string;
3
+ raw?: boolean;
4
+ }): Promise<void>;
5
+ export declare function cryptoAction(symbol: string, options: {
6
+ period?: string;
7
+ raw?: boolean;
8
+ }): Promise<void>;
9
+ export declare function screenerAction(options: {
10
+ sector?: string;
11
+ limit?: string;
12
+ raw?: boolean;
13
+ }): Promise<void>;