@bfun-bot/cli 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -0
- package/bin/bfunbot.js +2 -0
- package/dist/commands/about.d.ts +5 -0
- package/dist/commands/about.js +62 -0
- package/dist/commands/balances.d.ts +5 -0
- package/dist/commands/balances.js +48 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.js +53 -0
- package/dist/commands/fees.d.ts +7 -0
- package/dist/commands/fees.js +99 -0
- package/dist/commands/llm.d.ts +5 -0
- package/dist/commands/llm.js +216 -0
- package/dist/commands/login.d.ts +5 -0
- package/dist/commands/login.js +109 -0
- package/dist/commands/quota.d.ts +5 -0
- package/dist/commands/quota.js +28 -0
- package/dist/commands/skills.d.ts +5 -0
- package/dist/commands/skills.js +32 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +33 -0
- package/dist/commands/token.d.ts +5 -0
- package/dist/commands/token.js +150 -0
- package/dist/commands/tokens.d.ts +5 -0
- package/dist/commands/tokens.js +45 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.js +25 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +86 -0
- package/dist/lib/api.d.ts +32 -0
- package/dist/lib/api.js +172 -0
- package/dist/lib/config.d.ts +31 -0
- package/dist/lib/config.js +86 -0
- package/dist/lib/display.d.ts +36 -0
- package/dist/lib/display.js +120 -0
- package/dist/types.d.ts +172 -0
- package/dist/types.js +4 -0
- package/package.json +44 -0
- package/scripts/postinstall.js +34 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { writeConfig, clearConfig, CONFIG_PATH } from '../lib/config.js';
|
|
3
|
+
import { agent, handleApiError } from '../lib/api.js';
|
|
4
|
+
import { success } from '../lib/display.js';
|
|
5
|
+
export function registerLogin(program) {
|
|
6
|
+
program
|
|
7
|
+
.command('login')
|
|
8
|
+
.description('Authenticate with your BFunBot API key')
|
|
9
|
+
.option('--api-key <key>', 'API key (bfbot_...)')
|
|
10
|
+
.option('--url', 'Open the BFunBot API keys page in your browser')
|
|
11
|
+
.action(async (opts) => {
|
|
12
|
+
if (opts.url) {
|
|
13
|
+
const url = 'https://bfun.bot/settings/api-keys';
|
|
14
|
+
console.log(`Open this URL to get your API key:\n\n ${chalk.cyan(url)}\n`);
|
|
15
|
+
// Try to open browser
|
|
16
|
+
try {
|
|
17
|
+
const { exec } = await import('node:child_process');
|
|
18
|
+
const cmd = process.platform === 'darwin' ? 'open'
|
|
19
|
+
: process.platform === 'win32' ? 'start'
|
|
20
|
+
: 'xdg-open';
|
|
21
|
+
exec(`${cmd} ${url}`);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// browser open is best-effort
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
let apiKey = opts.apiKey;
|
|
29
|
+
if (!apiKey) {
|
|
30
|
+
// Interactive prompt with masked input (first 8 chars visible)
|
|
31
|
+
const VISIBLE_PREFIX = 8;
|
|
32
|
+
const prompt = 'Enter your API key: ';
|
|
33
|
+
process.stdout.write(prompt);
|
|
34
|
+
apiKey = await new Promise((resolve) => {
|
|
35
|
+
let input = '';
|
|
36
|
+
const stdin = process.stdin;
|
|
37
|
+
const wasRaw = stdin.isRaw;
|
|
38
|
+
if (stdin.isTTY) {
|
|
39
|
+
stdin.setRawMode(true);
|
|
40
|
+
}
|
|
41
|
+
stdin.resume();
|
|
42
|
+
stdin.setEncoding('utf8');
|
|
43
|
+
function redraw() {
|
|
44
|
+
process.stdout.write(`\r${prompt}`);
|
|
45
|
+
if (input.length <= VISIBLE_PREFIX) {
|
|
46
|
+
process.stdout.write(input);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
process.stdout.write(input.slice(0, VISIBLE_PREFIX) + '*'.repeat(input.length - VISIBLE_PREFIX));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const onData = (ch) => {
|
|
53
|
+
const c = ch.toString();
|
|
54
|
+
if (c === '\n' || c === '\r') {
|
|
55
|
+
stdin.removeListener('data', onData);
|
|
56
|
+
if (stdin.isTTY)
|
|
57
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
58
|
+
stdin.pause();
|
|
59
|
+
process.stdout.write('\n');
|
|
60
|
+
resolve(input.trim());
|
|
61
|
+
}
|
|
62
|
+
else if (c === '\u0003') {
|
|
63
|
+
process.stdout.write('\n');
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
else if (c === '\u007f' || c === '\b') {
|
|
67
|
+
if (input.length > 0) {
|
|
68
|
+
input = input.slice(0, -1);
|
|
69
|
+
process.stdout.write(`\r${prompt}${' '.repeat(input.length + 1)}`);
|
|
70
|
+
redraw();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
for (const char of c) {
|
|
75
|
+
if (char >= ' ')
|
|
76
|
+
input += char;
|
|
77
|
+
}
|
|
78
|
+
redraw();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
stdin.on('data', onData);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (!apiKey || !apiKey.startsWith('bfbot_')) {
|
|
85
|
+
console.error('Error: Invalid API key. Keys start with bfbot_');
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
// Save key first so the API call can use it
|
|
89
|
+
writeConfig({ apiKey });
|
|
90
|
+
// Verify the key works
|
|
91
|
+
try {
|
|
92
|
+
const me = await agent.me();
|
|
93
|
+
success(`Logged in as ${chalk.bold('@' + (me.twitter_username || me.twitter_user_id))}`);
|
|
94
|
+
console.log(` Config saved to ${chalk.dim(CONFIG_PATH)}`);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
// Key is bad — clear it
|
|
98
|
+
clearConfig();
|
|
99
|
+
handleApiError(err);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
program
|
|
103
|
+
.command('logout')
|
|
104
|
+
.description('Clear stored credentials')
|
|
105
|
+
.action(() => {
|
|
106
|
+
clearConfig();
|
|
107
|
+
success('Logged out. Credentials cleared.');
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { agent, handleApiError } from '../lib/api.js';
|
|
3
|
+
import { printTable } from '../lib/display.js';
|
|
4
|
+
export function registerQuota(program) {
|
|
5
|
+
program
|
|
6
|
+
.command('quota')
|
|
7
|
+
.description('Show daily token creation quota per chain')
|
|
8
|
+
.action(async (_, cmd) => {
|
|
9
|
+
try {
|
|
10
|
+
const res = await agent.quota();
|
|
11
|
+
const isJson = cmd.optsWithGlobals().json;
|
|
12
|
+
if (isJson) {
|
|
13
|
+
console.log(JSON.stringify(res, null, 2));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
printTable(['Chain', 'Free Used', 'Free Limit', 'Sponsored', 'Can Create'], res.chains.map((c) => [
|
|
17
|
+
chalk.bold(c.chain.charAt(0).toUpperCase() + c.chain.slice(1)),
|
|
18
|
+
`${c.free_used_today}`,
|
|
19
|
+
`${c.free_limit}`,
|
|
20
|
+
`${c.sponsored_remaining}`,
|
|
21
|
+
c.can_create_paid ? chalk.green('Yes') : chalk.dim('No'),
|
|
22
|
+
]));
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
handleApiError(err);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { agent, handleApiError } from '../lib/api.js';
|
|
3
|
+
export function registerSkills(program) {
|
|
4
|
+
program
|
|
5
|
+
.command('skills')
|
|
6
|
+
.description('List available BFunBot agent capabilities')
|
|
7
|
+
.action(async (_, cmd) => {
|
|
8
|
+
try {
|
|
9
|
+
const res = await agent.skills();
|
|
10
|
+
const isJson = cmd.optsWithGlobals().json;
|
|
11
|
+
if (isJson) {
|
|
12
|
+
console.log(JSON.stringify(res, null, 2));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
console.log();
|
|
16
|
+
console.log(chalk.bold.cyan(`Available Skills (${res.total})`));
|
|
17
|
+
console.log(chalk.dim('─'.repeat(50)));
|
|
18
|
+
for (const skill of res.skills) {
|
|
19
|
+
console.log();
|
|
20
|
+
console.log(` ${chalk.bold(skill.name)}`);
|
|
21
|
+
console.log(` ${skill.description}`);
|
|
22
|
+
if (skill.example) {
|
|
23
|
+
console.log(` ${chalk.dim(skill.example)}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
console.log();
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
handleApiError(err);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -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,150 @@
|
|
|
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
|
+
bsc: 'https://bscscan.com/token/',
|
|
9
|
+
};
|
|
10
|
+
export function registerToken(program) {
|
|
11
|
+
const tokenCmd = program
|
|
12
|
+
.command('token')
|
|
13
|
+
.description('Token creation and info');
|
|
14
|
+
// ── token create ──────────────────────────────────────
|
|
15
|
+
tokenCmd
|
|
16
|
+
.command('create')
|
|
17
|
+
.description('Deploy a new token')
|
|
18
|
+
.option('--name <name>', 'Token name')
|
|
19
|
+
.option('--symbol <symbol>', 'Token symbol')
|
|
20
|
+
.option('--description <desc>', 'Token description')
|
|
21
|
+
.option('--source <url>', 'Twitter/X post URL (insight source). Tweet image used as token image if --image-url not provided')
|
|
22
|
+
.option('--image-url <url>', 'Image URL (HTTPS or IPFS) — overrides source tweet image')
|
|
23
|
+
.option('--platform <platform>', 'Platform override (flap or fourmeme)')
|
|
24
|
+
.option('--no-wait', 'Return job ID without waiting for deployment')
|
|
25
|
+
.action(async (opts, cmd) => {
|
|
26
|
+
try {
|
|
27
|
+
let { name, symbol } = opts;
|
|
28
|
+
const chain = 'bsc'; // BFunBot is BSC-only
|
|
29
|
+
const isJson = cmd.optsWithGlobals().json;
|
|
30
|
+
// Interactive prompts for missing required fields
|
|
31
|
+
if (!name || !symbol) {
|
|
32
|
+
const readline = await import('node:readline');
|
|
33
|
+
const rl = readline.createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
output: process.stdout,
|
|
36
|
+
});
|
|
37
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
|
|
38
|
+
if (!name)
|
|
39
|
+
name = await ask('Token name: ');
|
|
40
|
+
if (!symbol)
|
|
41
|
+
symbol = await ask('Symbol: ');
|
|
42
|
+
if (!opts.source) {
|
|
43
|
+
const srcAnswer = await ask('Source tweet URL (optional, press Enter to skip): ');
|
|
44
|
+
if (srcAnswer)
|
|
45
|
+
opts.source = srcAnswer;
|
|
46
|
+
}
|
|
47
|
+
if (!opts.imageUrl) {
|
|
48
|
+
const imgHint = opts.source ? ' (overrides source tweet image)' : '';
|
|
49
|
+
const imgAnswer = await ask(`Token image URL${imgHint} (optional, press Enter to skip): `);
|
|
50
|
+
if (imgAnswer)
|
|
51
|
+
opts.imageUrl = imgAnswer;
|
|
52
|
+
}
|
|
53
|
+
rl.close();
|
|
54
|
+
}
|
|
55
|
+
// Validate source URL if provided
|
|
56
|
+
if (opts.source) {
|
|
57
|
+
const twitterPattern = /^https?:\/\/(?:(?:www\.)?twitter\.com|x\.com)\/[A-Za-z0-9_]+\/status\/\d+/;
|
|
58
|
+
if (!twitterPattern.test(opts.source)) {
|
|
59
|
+
console.error(chalk.red('Error:') + ' --source must be a valid Twitter/X post URL (e.g. https://x.com/user/status/123)');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const spinner = ora('Creating token...').start();
|
|
64
|
+
const result = await agent.tokenCreate({
|
|
65
|
+
name,
|
|
66
|
+
symbol: symbol.toUpperCase(),
|
|
67
|
+
chain,
|
|
68
|
+
description: opts.description,
|
|
69
|
+
source_url: opts.source,
|
|
70
|
+
image_url: opts.imageUrl,
|
|
71
|
+
platform: opts.platform,
|
|
72
|
+
});
|
|
73
|
+
spinner.succeed(`Job created (ID: ${result.job_id})`);
|
|
74
|
+
if (!opts.wait) {
|
|
75
|
+
if (isJson) {
|
|
76
|
+
console.log(JSON.stringify(result, null, 2));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(` Check status with: ${chalk.cyan(`bfunbot status ${result.job_id}`)}`);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Poll for completion
|
|
84
|
+
const pollSpinner = ora('Waiting for deployment...').start();
|
|
85
|
+
const startTime = Date.now();
|
|
86
|
+
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
87
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
88
|
+
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
89
|
+
pollSpinner.text = `Waiting for deployment... (${elapsed}s)`;
|
|
90
|
+
const job = await agent.jobStatus(String(result.job_id));
|
|
91
|
+
if (job.status === 'completed' && job.token_address) {
|
|
92
|
+
pollSpinner.succeed('Token deployed!');
|
|
93
|
+
if (isJson) {
|
|
94
|
+
console.log(JSON.stringify(job, null, 2));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
printCard('Token Deployed', [
|
|
98
|
+
['Address', job.token_address],
|
|
99
|
+
['Chain', 'BSC'],
|
|
100
|
+
['View', `${EXPLORER_URLS.bsc}${job.token_address}`],
|
|
101
|
+
]);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (job.status === 'failed') {
|
|
105
|
+
pollSpinner.fail('Token creation failed');
|
|
106
|
+
console.error(` ${job.error || 'Unknown error'}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Timeout
|
|
111
|
+
pollSpinner.warn('Token creation still in progress');
|
|
112
|
+
console.log(` Check status with: ${chalk.cyan(`bfunbot status ${result.job_id}`)}`);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
ora().fail('Token creation failed');
|
|
116
|
+
handleApiError(err);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// ── token info ────────────────────────────────────────
|
|
120
|
+
tokenCmd
|
|
121
|
+
.command('info <address>')
|
|
122
|
+
.description('Get token details')
|
|
123
|
+
.option('--chain <chain>', 'Chain hint (base, bsc, solana)')
|
|
124
|
+
.action(async (address, opts, cmd) => {
|
|
125
|
+
try {
|
|
126
|
+
const res = await agent.tokenInfo(address, opts.chain);
|
|
127
|
+
const isJson = cmd.optsWithGlobals().json;
|
|
128
|
+
if (isJson) {
|
|
129
|
+
console.log(JSON.stringify(res, null, 2));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const explorerBase = EXPLORER_URLS[res.chain] || '';
|
|
133
|
+
printCard(`${res.symbol} — ${res.name}`, [
|
|
134
|
+
['Address', res.token_address],
|
|
135
|
+
['Chain', res.chain],
|
|
136
|
+
['Platform', res.platform],
|
|
137
|
+
['Creator', res.creator_twitter_username ? `@${res.creator_twitter_username}` : undefined],
|
|
138
|
+
['Price', fmtUsd(res.price_usd)],
|
|
139
|
+
['Market Cap', fmtUsd(res.market_cap_usd)],
|
|
140
|
+
['24h Volume', fmtUsd(res.volume_24h_usd)],
|
|
141
|
+
['Creator Reward', fmtUsd(res.creator_reward_usd)],
|
|
142
|
+
['Created', fmtDate(res.created_at)],
|
|
143
|
+
['Explorer', explorerBase ? `${explorerBase}${res.token_address}` : undefined],
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
handleApiError(err);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -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: bfunbot tokens created --page ${page + 1}`));
|
|
38
|
+
console.log();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
handleApiError(err);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* BFunBot CLI — deploy tokens, check balances, and manage your AI agent.
|
|
4
|
+
*
|
|
5
|
+
* @package @bfunbot/cli
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { enableDebug } from './lib/api.js';
|
|
13
|
+
import { registerLogin } from './commands/login.js';
|
|
14
|
+
import { registerWhoami } from './commands/whoami.js';
|
|
15
|
+
import { registerTokens } from './commands/tokens.js';
|
|
16
|
+
import { registerToken } from './commands/token.js';
|
|
17
|
+
import { registerStatus } from './commands/status.js';
|
|
18
|
+
import { registerBalances } from './commands/balances.js';
|
|
19
|
+
import { registerQuota } from './commands/quota.js';
|
|
20
|
+
import { registerSkills } from './commands/skills.js';
|
|
21
|
+
import { registerLlm } from './commands/llm.js';
|
|
22
|
+
import { registerConfig } from './commands/config.js';
|
|
23
|
+
import { registerAbout } from './commands/about.js';
|
|
24
|
+
import { registerFees } from './commands/fees.js';
|
|
25
|
+
// Read version from package.json
|
|
26
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
27
|
+
const __dirname = dirname(__filename);
|
|
28
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
29
|
+
const program = new Command();
|
|
30
|
+
program
|
|
31
|
+
.name('bfunbot')
|
|
32
|
+
.description('BFunBot CLI — deploy tokens, check balances, and manage your AI agent')
|
|
33
|
+
.version(pkg.version)
|
|
34
|
+
.option('--json', 'Output raw JSON (machine-readable)')
|
|
35
|
+
.option('--debug', 'Verbose HTTP request/response logging')
|
|
36
|
+
.hook('preAction', (thisCommand) => {
|
|
37
|
+
const opts = thisCommand.optsWithGlobals();
|
|
38
|
+
if (opts.debug) {
|
|
39
|
+
enableDebug();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// Show about box on first run (skip if user is running 'bfunbot about' directly)
|
|
43
|
+
const welcomeMarker = join(homedir(), '.bfunbot', '.welcomed');
|
|
44
|
+
const isAboutCommand = process.argv[2] === 'about';
|
|
45
|
+
if (!existsSync(welcomeMarker) && !isAboutCommand) {
|
|
46
|
+
try {
|
|
47
|
+
const W = 43;
|
|
48
|
+
const pad = (t) => t + ' '.repeat(Math.max(0, W - t.length));
|
|
49
|
+
const lines = [
|
|
50
|
+
'', ' B F U N . B O T', '',
|
|
51
|
+
' Deploy tokens. Earn fees.', ' Self-fund your AI agent.', '',
|
|
52
|
+
` Version ${pkg.version}`,
|
|
53
|
+
' Website bfun.bot',
|
|
54
|
+
' Docs bfun.bot/docs/agent',
|
|
55
|
+
' GitHub github.com/BFunBot',
|
|
56
|
+
'', ' BSC', '',
|
|
57
|
+
];
|
|
58
|
+
console.log();
|
|
59
|
+
console.log(` \x1b[36m╔${'═'.repeat(W)}╗\x1b[0m`);
|
|
60
|
+
for (const l of lines)
|
|
61
|
+
console.log(` \x1b[36m║\x1b[0m${pad(l)}\x1b[36m║\x1b[0m`);
|
|
62
|
+
console.log(` \x1b[36m╚${'═'.repeat(W)}╝\x1b[0m`);
|
|
63
|
+
console.log();
|
|
64
|
+
const bfunDir = join(homedir(), '.bfunbot');
|
|
65
|
+
if (!existsSync(bfunDir))
|
|
66
|
+
mkdirSync(bfunDir, { recursive: true });
|
|
67
|
+
writeFileSync(welcomeMarker, new Date().toISOString());
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// best-effort
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Register all commands
|
|
74
|
+
registerLogin(program);
|
|
75
|
+
registerWhoami(program);
|
|
76
|
+
registerTokens(program);
|
|
77
|
+
registerToken(program);
|
|
78
|
+
registerStatus(program);
|
|
79
|
+
registerBalances(program);
|
|
80
|
+
registerQuota(program);
|
|
81
|
+
registerSkills(program);
|
|
82
|
+
registerLlm(program);
|
|
83
|
+
registerConfig(program);
|
|
84
|
+
registerAbout(program);
|
|
85
|
+
registerFees(program);
|
|
86
|
+
program.parse();
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
creditBalance: () => Promise<import("../types.js").CreditBalanceResponse>;
|
|
21
|
+
reloadCredits: () => Promise<import("../types.js").AgentReloadResponse>;
|
|
22
|
+
disableReload: () => Promise<void>;
|
|
23
|
+
quota: () => Promise<import("../types.js").QuotaResponse>;
|
|
24
|
+
skills: () => Promise<import("../types.js").SkillsResponse>;
|
|
25
|
+
feesSummary: () => Promise<import("../types.js").FeeSummaryResponse>;
|
|
26
|
+
feesEarnings: (chain: string) => Promise<import("../types.js").FeeEarningsBsc>;
|
|
27
|
+
feesToken: (chain: string, platform: string, tokenAddress: string) => Promise<import("../types.js").FeeTokenResponse>;
|
|
28
|
+
};
|
|
29
|
+
export declare const llm: {
|
|
30
|
+
models: () => Promise<import("../types.js").LlmModelsResponse>;
|
|
31
|
+
openclawConfig: () => Promise<import("../types.js").OpenclawConfigResponse>;
|
|
32
|
+
};
|