@kibibot/cli 1.0.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.
@@ -0,0 +1,33 @@
1
+ import chalk from 'chalk';
2
+ import { agent, handleApiError } from '../lib/api.js';
3
+ import { printCard, fmtDate } from '../lib/display.js';
4
+ export function registerStatus(program) {
5
+ program
6
+ .command('status <jobId>')
7
+ .description('Check the status of a token creation job')
8
+ .action(async (jobId, _, cmd) => {
9
+ try {
10
+ const job = await agent.jobStatus(jobId);
11
+ const isJson = cmd.optsWithGlobals().json;
12
+ if (isJson) {
13
+ console.log(JSON.stringify(job, null, 2));
14
+ return;
15
+ }
16
+ const statusColor = job.status === 'completed' ? chalk.green
17
+ : job.status === 'failed' ? chalk.red
18
+ : job.status === 'processing' ? chalk.yellow
19
+ : chalk.dim;
20
+ printCard(`Job ${job.job_id}`, [
21
+ ['Status', statusColor(job.status)],
22
+ ['Chain', job.chain],
23
+ ['Token', job.token_address],
24
+ ['Error', job.error ? chalk.red(job.error) : undefined],
25
+ ['Created', fmtDate(job.created_at)],
26
+ ['Completed', fmtDate(job.completed_at)],
27
+ ]);
28
+ }
29
+ catch (err) {
30
+ handleApiError(err);
31
+ }
32
+ });
33
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * kibi token create / kibi token info
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function registerToken(program: Command): void;
@@ -0,0 +1,140 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { agent, handleApiError } from '../lib/api.js';
4
+ import { printCard, fmtUsd, fmtDate } from '../lib/display.js';
5
+ const POLL_INTERVAL_MS = 2000;
6
+ const POLL_TIMEOUT_MS = 120_000;
7
+ const EXPLORER_URLS = {
8
+ base: 'https://basescan.org/token/',
9
+ bsc: 'https://bscscan.com/token/',
10
+ solana: 'https://solscan.io/token/',
11
+ };
12
+ export function registerToken(program) {
13
+ const tokenCmd = program
14
+ .command('token')
15
+ .description('Token creation and info');
16
+ // ── token create ──────────────────────────────────────
17
+ tokenCmd
18
+ .command('create')
19
+ .description('Deploy a new token')
20
+ .option('--name <name>', 'Token name')
21
+ .option('--symbol <symbol>', 'Token symbol')
22
+ .option('--chain <chain>', 'Chain: base, bsc, or solana')
23
+ .option('--description <desc>', 'Token description')
24
+ .option('--image-url <url>', 'Image URL (HTTPS or IPFS)')
25
+ .option('--platform <platform>', 'Platform override')
26
+ .option('--no-wait', 'Return job ID without waiting for deployment')
27
+ .action(async (opts, cmd) => {
28
+ try {
29
+ let { name, symbol, chain } = opts;
30
+ const isJson = cmd.optsWithGlobals().json;
31
+ // Interactive prompts for missing required fields
32
+ if (!name || !symbol || !chain) {
33
+ const readline = await import('node:readline');
34
+ const rl = readline.createInterface({
35
+ input: process.stdin,
36
+ output: process.stdout,
37
+ });
38
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
39
+ if (!name)
40
+ name = await ask('Token name: ');
41
+ if (!symbol)
42
+ symbol = await ask('Symbol: ');
43
+ if (!chain)
44
+ chain = await ask('Chain (base/bsc/solana): ');
45
+ rl.close();
46
+ }
47
+ // Validate chain
48
+ chain = chain.toLowerCase();
49
+ const VALID_CHAINS = ['base', 'bsc', 'solana'];
50
+ if (!VALID_CHAINS.includes(chain)) {
51
+ console.error(`Error: Chain must be one of: ${VALID_CHAINS.join(', ')}`);
52
+ process.exit(1);
53
+ }
54
+ const spinner = ora('Creating token...').start();
55
+ const result = await agent.tokenCreate({
56
+ name,
57
+ symbol: symbol.toUpperCase(),
58
+ chain,
59
+ description: opts.description,
60
+ image_url: opts.imageUrl,
61
+ platform: opts.platform,
62
+ });
63
+ spinner.succeed(`Job created (ID: ${result.job_id})`);
64
+ if (!opts.wait) {
65
+ if (isJson) {
66
+ console.log(JSON.stringify(result, null, 2));
67
+ }
68
+ else {
69
+ console.log(` Check status with: ${chalk.cyan(`kibi status ${result.job_id}`)}`);
70
+ }
71
+ return;
72
+ }
73
+ // Poll for completion
74
+ const pollSpinner = ora('Waiting for deployment...').start();
75
+ const startTime = Date.now();
76
+ while (Date.now() - startTime < POLL_TIMEOUT_MS) {
77
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
78
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
79
+ pollSpinner.text = `Waiting for deployment... (${elapsed}s)`;
80
+ const job = await agent.jobStatus(String(result.job_id));
81
+ if (job.status === 'completed' && job.token_address) {
82
+ pollSpinner.succeed('Token deployed!');
83
+ if (isJson) {
84
+ console.log(JSON.stringify(job, null, 2));
85
+ return;
86
+ }
87
+ const explorerBase = EXPLORER_URLS[chain] || '';
88
+ printCard('Token Deployed', [
89
+ ['Address', job.token_address],
90
+ ['Chain', chain.charAt(0).toUpperCase() + chain.slice(1)],
91
+ ['View', explorerBase ? `${explorerBase}${job.token_address}` : undefined],
92
+ ]);
93
+ return;
94
+ }
95
+ if (job.status === 'failed') {
96
+ pollSpinner.fail('Token creation failed');
97
+ console.error(` ${job.error || 'Unknown error'}`);
98
+ process.exit(1);
99
+ }
100
+ }
101
+ // Timeout
102
+ pollSpinner.warn('Token creation still in progress');
103
+ console.log(` Check status with: ${chalk.cyan(`kibi status ${result.job_id}`)}`);
104
+ }
105
+ catch (err) {
106
+ handleApiError(err);
107
+ }
108
+ });
109
+ // ── token info ────────────────────────────────────────
110
+ tokenCmd
111
+ .command('info <address>')
112
+ .description('Get token details')
113
+ .option('--chain <chain>', 'Chain hint (base, bsc, solana)')
114
+ .action(async (address, opts, cmd) => {
115
+ try {
116
+ const res = await agent.tokenInfo(address, opts.chain);
117
+ const isJson = cmd.optsWithGlobals().json;
118
+ if (isJson) {
119
+ console.log(JSON.stringify(res, null, 2));
120
+ return;
121
+ }
122
+ const explorerBase = EXPLORER_URLS[res.chain] || '';
123
+ printCard(`${res.symbol} — ${res.name}`, [
124
+ ['Address', res.token_address],
125
+ ['Chain', res.chain],
126
+ ['Platform', res.platform],
127
+ ['Creator', res.creator_twitter_username ? `@${res.creator_twitter_username}` : undefined],
128
+ ['Price', fmtUsd(res.price_usd)],
129
+ ['Market Cap', fmtUsd(res.market_cap_usd)],
130
+ ['24h Volume', fmtUsd(res.volume_24h_usd)],
131
+ ['Creator Reward', fmtUsd(res.creator_reward_usd)],
132
+ ['Created', fmtDate(res.created_at)],
133
+ ['Explorer', explorerBase ? `${explorerBase}${res.token_address}` : undefined],
134
+ ]);
135
+ }
136
+ catch (err) {
137
+ handleApiError(err);
138
+ }
139
+ });
140
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * kibi tokens created
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function registerTokens(program: Command): void;
@@ -0,0 +1,45 @@
1
+ import chalk from 'chalk';
2
+ import { agent, handleApiError } from '../lib/api.js';
3
+ import { printTable, shortAddr, fmtDate } from '../lib/display.js';
4
+ export function registerTokens(program) {
5
+ const tokensCmd = program
6
+ .command('tokens')
7
+ .description('Token listing commands');
8
+ tokensCmd
9
+ .command('created')
10
+ .description('List tokens you have created')
11
+ .option('--page <n>', 'Page number', '1')
12
+ .option('--page-size <n>', 'Items per page', '20')
13
+ .action(async (opts, cmd) => {
14
+ try {
15
+ const page = parseInt(opts.page, 10);
16
+ const pageSize = parseInt(opts.pageSize, 10);
17
+ const res = await agent.tokensCreated(page, pageSize);
18
+ const isJson = cmd.optsWithGlobals().json;
19
+ if (isJson) {
20
+ console.log(JSON.stringify(res, null, 2));
21
+ return;
22
+ }
23
+ if (res.tokens.length === 0) {
24
+ console.log('\nNo tokens created yet.\n');
25
+ return;
26
+ }
27
+ console.log(chalk.dim(`\nShowing ${res.tokens.length} of ${res.total} tokens (page ${page})\n`));
28
+ printTable(['Symbol', 'Name', 'Chain', 'Platform', 'Address', 'Created'], res.tokens.map((t) => [
29
+ chalk.bold(t.symbol),
30
+ t.name,
31
+ t.chain,
32
+ t.platform || '—',
33
+ shortAddr(t.token_address),
34
+ fmtDate(t.created_at),
35
+ ]));
36
+ if (res.has_more) {
37
+ console.log(chalk.dim(` Next page: kibi tokens created --page ${page + 1}`));
38
+ console.log();
39
+ }
40
+ }
41
+ catch (err) {
42
+ handleApiError(err);
43
+ }
44
+ });
45
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * kibi whoami
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function registerWhoami(program: Command): void;
@@ -0,0 +1,25 @@
1
+ import { agent, handleApiError } from '../lib/api.js';
2
+ import { printCard, fmtDate } from '../lib/display.js';
3
+ export function registerWhoami(program) {
4
+ program
5
+ .command('whoami')
6
+ .description('Show your account info')
7
+ .action(async (_, cmd) => {
8
+ try {
9
+ const me = await agent.me();
10
+ const isJson = cmd.optsWithGlobals().json;
11
+ if (isJson) {
12
+ console.log(JSON.stringify(me, null, 2));
13
+ return;
14
+ }
15
+ printCard('Account', [
16
+ ['Twitter', me.twitter_username ? `@${me.twitter_username}` : me.twitter_user_id],
17
+ ['Followers', me.followers_count?.toLocaleString()],
18
+ ['Joined', fmtDate(me.joined_at)],
19
+ ]);
20
+ }
21
+ catch (err) {
22
+ handleApiError(err);
23
+ }
24
+ });
25
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * KibiBot CLI — deploy tokens, check balances, and manage your AI agent.
4
+ *
5
+ * @package @kibibot/cli
6
+ */
7
+ import { Command } from 'commander';
8
+ import { readFileSync } from 'node:fs';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { dirname, join } from 'node:path';
11
+ import { enableDebug } from './lib/api.js';
12
+ import { registerLogin } from './commands/login.js';
13
+ import { registerWhoami } from './commands/whoami.js';
14
+ import { registerTokens } from './commands/tokens.js';
15
+ import { registerToken } from './commands/token.js';
16
+ import { registerStatus } from './commands/status.js';
17
+ import { registerBalances } from './commands/balances.js';
18
+ import { registerQuota } from './commands/quota.js';
19
+ import { registerSkills } from './commands/skills.js';
20
+ import { registerLlm } from './commands/llm.js';
21
+ import { registerConfig } from './commands/config.js';
22
+ // Read version from package.json
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = dirname(__filename);
25
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
26
+ const program = new Command();
27
+ program
28
+ .name('kibi')
29
+ .description('KibiBot CLI — deploy tokens, check balances, and manage your AI agent')
30
+ .version(pkg.version)
31
+ .option('--json', 'Output raw JSON (machine-readable)')
32
+ .option('--debug', 'Verbose HTTP request/response logging')
33
+ .hook('preAction', (thisCommand) => {
34
+ const opts = thisCommand.optsWithGlobals();
35
+ if (opts.debug) {
36
+ enableDebug();
37
+ }
38
+ });
39
+ // Register all commands
40
+ registerLogin(program);
41
+ registerWhoami(program);
42
+ registerTokens(program);
43
+ registerToken(program);
44
+ registerStatus(program);
45
+ registerBalances(program);
46
+ registerQuota(program);
47
+ registerSkills(program);
48
+ registerLlm(program);
49
+ registerConfig(program);
50
+ program.parse();
@@ -0,0 +1,28 @@
1
+ import type { ApiErrorResponse } from '../types.js';
2
+ export declare function enableDebug(): void;
3
+ export declare class ApiError extends Error {
4
+ statusCode: number;
5
+ statusText: string;
6
+ body?: ApiErrorResponse | undefined;
7
+ constructor(statusCode: number, statusText: string, body?: ApiErrorResponse | undefined);
8
+ }
9
+ /**
10
+ * Print a friendly error message to stderr and exit.
11
+ */
12
+ export declare function handleApiError(err: unknown): never;
13
+ export declare const agent: {
14
+ me: () => Promise<import("../types.js").MeResponse>;
15
+ tokensCreated: (page?: number, pageSize?: number) => Promise<import("../types.js").CreatedTokensResponse>;
16
+ tokenCreate: (body: import("../types.js").TokenCreateRequest) => Promise<import("../types.js").TokenCreateResponse>;
17
+ tokenInfo: (address: string, chain?: string) => Promise<import("../types.js").TokenInfoResponse>;
18
+ jobStatus: (jobId: string) => Promise<import("../types.js").JobStatusResponse>;
19
+ walletBalance: () => Promise<import("../types.js").WalletBalanceResponse>;
20
+ kibiCreditBalance: () => Promise<import("../types.js").KibiCreditBalanceResponse>;
21
+ reloadKibiCredits: () => Promise<import("../types.js").AgentReloadResponse>;
22
+ disableReload: () => Promise<void>;
23
+ quota: () => Promise<import("../types.js").QuotaResponse>;
24
+ skills: () => Promise<import("../types.js").SkillsResponse>;
25
+ };
26
+ export declare const llm: {
27
+ models: () => Promise<import("../types.js").LlmModelsResponse>;
28
+ };
@@ -0,0 +1,152 @@
1
+ /**
2
+ * HTTP client for the KibiBot Agent API + LLM Gateway.
3
+ *
4
+ * Uses native fetch (Node 18+). Handles auth headers, error mapping,
5
+ * and --debug logging.
6
+ */
7
+ import { readConfig, requireApiKey } from './config.js';
8
+ let debugEnabled = false;
9
+ export function enableDebug() {
10
+ debugEnabled = true;
11
+ }
12
+ // ─── Error Classes ───────────────────────────────────────
13
+ export class ApiError extends Error {
14
+ statusCode;
15
+ statusText;
16
+ body;
17
+ constructor(statusCode, statusText, body) {
18
+ const detail = body?.detail || body?.message || statusText;
19
+ super(detail);
20
+ this.statusCode = statusCode;
21
+ this.statusText = statusText;
22
+ this.body = body;
23
+ this.name = 'ApiError';
24
+ }
25
+ }
26
+ // ─── Friendly error messages ─────────────────────────────
27
+ function friendlyError(err) {
28
+ switch (err.statusCode) {
29
+ case 401:
30
+ return "Invalid or expired API key. Run 'kibi login' to re-authenticate.";
31
+ case 403:
32
+ return err.body?.detail || 'Access denied. Your API key may not have the required permissions.';
33
+ case 404:
34
+ return err.body?.detail || 'Not found.';
35
+ case 429:
36
+ return 'Rate limited. Try again later.';
37
+ default:
38
+ if (err.statusCode >= 500) {
39
+ return 'Server error. Try again later.';
40
+ }
41
+ return err.body?.detail || err.message || `HTTP ${err.statusCode}`;
42
+ }
43
+ }
44
+ /**
45
+ * Print a friendly error message to stderr and exit.
46
+ */
47
+ export function handleApiError(err) {
48
+ if (err instanceof ApiError) {
49
+ console.error(`Error: ${friendlyError(err)}`);
50
+ if (debugEnabled) {
51
+ console.error(` Status: ${err.statusCode} ${err.statusText}`);
52
+ if (err.body)
53
+ console.error(` Body: ${JSON.stringify(err.body)}`);
54
+ }
55
+ }
56
+ else if (err instanceof TypeError && err.cause) {
57
+ // Network errors (fetch rejects with TypeError on connection failure)
58
+ console.error('Error: Could not reach KibiBot API. Check your connection.');
59
+ if (debugEnabled)
60
+ console.error(` ${err.message}`);
61
+ }
62
+ else {
63
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
64
+ }
65
+ process.exit(1);
66
+ }
67
+ async function request(path, opts = {}) {
68
+ const { method = 'GET', body, auth = true, baseLlm = false } = opts;
69
+ const config = readConfig();
70
+ const baseUrl = baseLlm
71
+ ? (config.llmUrl || 'https://llm.kibi.bot/v1')
72
+ : (config.apiUrl || 'https://kibi.bot');
73
+ const url = `${baseUrl}${path}`;
74
+ const headers = {
75
+ 'Accept': 'application/json',
76
+ };
77
+ if (auth) {
78
+ const apiKey = requireApiKey();
79
+ headers['X-API-Key'] = apiKey;
80
+ }
81
+ if (body) {
82
+ headers['Content-Type'] = 'application/json';
83
+ }
84
+ if (debugEnabled) {
85
+ console.error(`[debug] ${method} ${url}`);
86
+ if (body)
87
+ console.error(`[debug] Body: ${JSON.stringify(body)}`);
88
+ }
89
+ const response = await fetch(url, {
90
+ method,
91
+ headers,
92
+ body: body ? JSON.stringify(body) : undefined,
93
+ });
94
+ if (debugEnabled) {
95
+ console.error(`[debug] Response: ${response.status} ${response.statusText}`);
96
+ }
97
+ if (!response.ok) {
98
+ let errorBody;
99
+ try {
100
+ errorBody = await response.json();
101
+ if (debugEnabled)
102
+ console.error(`[debug] Error body: ${JSON.stringify(errorBody)}`);
103
+ }
104
+ catch {
105
+ // non-JSON error response
106
+ }
107
+ throw new ApiError(response.status, response.statusText, errorBody);
108
+ }
109
+ // Handle 204 No Content (empty body)
110
+ if (response.status === 204) {
111
+ return undefined;
112
+ }
113
+ const data = await response.json();
114
+ if (debugEnabled) {
115
+ console.error(`[debug] Response body: ${JSON.stringify(data).slice(0, 500)}`);
116
+ }
117
+ return data;
118
+ }
119
+ // ─── Public API Methods ──────────────────────────────────
120
+ // Agent API (base: /agent/v1)
121
+ export const agent = {
122
+ me: () => request('/agent/v1/me'),
123
+ tokensCreated: (page = 1, pageSize = 20) => request(`/agent/v1/tokens/created?page=${page}&page_size=${pageSize}`),
124
+ tokenCreate: (body) => request('/agent/v1/token/create', {
125
+ method: 'POST',
126
+ body,
127
+ }),
128
+ tokenInfo: (address, chain) => {
129
+ const params = chain ? `?chain=${chain}` : '';
130
+ return request(`/agent/v1/token/${address}${params}`);
131
+ },
132
+ jobStatus: (jobId) => request(`/agent/v1/jobs/${jobId}`),
133
+ walletBalance: () => request('/agent/v1/balance/wallet'),
134
+ kibiCreditBalance: () => request('/agent/v1/balance/credits'),
135
+ reloadKibiCredits: () => request('/agent/v1/balance/credits/reload', {
136
+ method: 'POST',
137
+ body: {},
138
+ }),
139
+ disableReload: () => request('/agent/v1/balance/credits/reload/disable', {
140
+ method: 'POST',
141
+ body: {},
142
+ }),
143
+ quota: () => request('/agent/v1/quota'),
144
+ skills: () => request('/agent/v1/skills', { auth: false }),
145
+ };
146
+ // LLM Gateway (base: /llm/v1)
147
+ export const llm = {
148
+ models: () => request('/models', {
149
+ baseLlm: true,
150
+ auth: false,
151
+ }),
152
+ };
@@ -0,0 +1,31 @@
1
+ import type { KibiConfig } from '../types.js';
2
+ declare const CONFIG_DIR: string;
3
+ declare const CONFIG_PATH: string;
4
+ declare const DEFAULT_API_URL = "https://kibi.bot";
5
+ declare const DEFAULT_LLM_URL = "https://llm.kibi.bot/v1";
6
+ /**
7
+ * Read config from disk. Returns defaults for missing values.
8
+ */
9
+ export declare function readConfig(): KibiConfig;
10
+ /**
11
+ * Write config to disk. Merges with existing config.
12
+ * Creates ~/.kibi/ if needed. Sets file permissions to 0600.
13
+ */
14
+ export declare function writeConfig(updates: Partial<KibiConfig>): void;
15
+ /**
16
+ * Delete config file (logout).
17
+ */
18
+ export declare function clearConfig(): void;
19
+ /**
20
+ * Get a specific config value.
21
+ */
22
+ export declare function getConfigValue(key: keyof KibiConfig): string | undefined;
23
+ /**
24
+ * Set a specific config value.
25
+ */
26
+ export declare function setConfigValue(key: keyof KibiConfig, value: string): void;
27
+ /**
28
+ * Get the API key or throw a helpful error.
29
+ */
30
+ export declare function requireApiKey(): string;
31
+ export { CONFIG_PATH, CONFIG_DIR, DEFAULT_API_URL, DEFAULT_LLM_URL };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Config file management — read/write ~/.kibi/config.json
3
+ */
4
+ import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { homedir } from 'node:os';
7
+ const CONFIG_DIR = join(homedir(), '.kibi');
8
+ const CONFIG_PATH = join(CONFIG_DIR, 'config.json');
9
+ const DEFAULT_API_URL = 'https://kibi.bot';
10
+ const DEFAULT_LLM_URL = 'https://llm.kibi.bot/v1';
11
+ /**
12
+ * Read config from disk. Returns defaults for missing values.
13
+ */
14
+ export function readConfig() {
15
+ try {
16
+ if (!existsSync(CONFIG_PATH)) {
17
+ return { apiUrl: DEFAULT_API_URL, llmUrl: DEFAULT_LLM_URL };
18
+ }
19
+ const raw = readFileSync(CONFIG_PATH, 'utf-8');
20
+ const parsed = JSON.parse(raw);
21
+ return {
22
+ apiUrl: DEFAULT_API_URL,
23
+ llmUrl: DEFAULT_LLM_URL,
24
+ ...parsed,
25
+ };
26
+ }
27
+ catch {
28
+ return { apiUrl: DEFAULT_API_URL, llmUrl: DEFAULT_LLM_URL };
29
+ }
30
+ }
31
+ /**
32
+ * Write config to disk. Merges with existing config.
33
+ * Creates ~/.kibi/ if needed. Sets file permissions to 0600.
34
+ */
35
+ export function writeConfig(updates) {
36
+ const current = readConfig();
37
+ const merged = { ...current, ...updates };
38
+ if (!existsSync(CONFIG_DIR)) {
39
+ mkdirSync(CONFIG_DIR, { recursive: true });
40
+ }
41
+ writeFileSync(CONFIG_PATH, JSON.stringify(merged, null, 2) + '\n', 'utf-8');
42
+ chmodSync(CONFIG_PATH, 0o600);
43
+ }
44
+ /**
45
+ * Delete config file (logout).
46
+ */
47
+ export function clearConfig() {
48
+ try {
49
+ if (existsSync(CONFIG_PATH)) {
50
+ writeFileSync(CONFIG_PATH, '{}\n', 'utf-8');
51
+ chmodSync(CONFIG_PATH, 0o600);
52
+ }
53
+ }
54
+ catch {
55
+ // ignore
56
+ }
57
+ }
58
+ /**
59
+ * Get a specific config value.
60
+ */
61
+ export function getConfigValue(key) {
62
+ const config = readConfig();
63
+ return config[key];
64
+ }
65
+ /**
66
+ * Set a specific config value.
67
+ */
68
+ export function setConfigValue(key, value) {
69
+ writeConfig({ [key]: value });
70
+ }
71
+ /**
72
+ * Get the API key or throw a helpful error.
73
+ */
74
+ export function requireApiKey() {
75
+ // Env var takes precedence
76
+ const envKey = process.env['KIBI_API_KEY'];
77
+ if (envKey)
78
+ return envKey;
79
+ const config = readConfig();
80
+ if (!config.apiKey) {
81
+ console.error('Error: Not logged in. Run `kibi login` first.');
82
+ process.exit(1);
83
+ }
84
+ return config.apiKey;
85
+ }
86
+ export { CONFIG_PATH, CONFIG_DIR, DEFAULT_API_URL, DEFAULT_LLM_URL };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Print a key-value card.
3
+ */
4
+ export declare function printCard(title: string, fields: [string, string | undefined][]): void;
5
+ /**
6
+ * Print a simple table with headers.
7
+ */
8
+ export declare function printTable(headers: string[], rows: string[][]): void;
9
+ /**
10
+ * Truncate an address for display.
11
+ */
12
+ export declare function shortAddr(addr: string | undefined | null, chars?: number): string;
13
+ /**
14
+ * Format a balance value.
15
+ */
16
+ export declare function fmtBalance(value: string | undefined | null, symbol: string): string;
17
+ /**
18
+ * Format a USD value.
19
+ */
20
+ export declare function fmtUsd(value: string | undefined | null): string;
21
+ /**
22
+ * Format a date string.
23
+ */
24
+ export declare function fmtDate(dateStr: string | undefined | null): string;
25
+ /**
26
+ * Print a success message.
27
+ */
28
+ export declare function success(msg: string): void;
29
+ /**
30
+ * Print an error message to stderr.
31
+ */
32
+ export declare function error(msg: string): void;
33
+ /**
34
+ * Print a warning message.
35
+ */
36
+ export declare function warn(msg: string): void;