blofin-cli 0.1.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.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # blofin-cli
2
+
3
+ Agent-first CLI for BloFin exchange. Exposes all 30 tools from `blofin-core` as grouped shell commands.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g blofin-cli
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Configure API credentials (interactive)
15
+ blofin setup
16
+
17
+ # Get ticker prices (table output by default)
18
+ blofin market tickers --instId=BTC-USDT
19
+
20
+ # JSON output
21
+ blofin market tickers --instId=BTC-USDT -o json
22
+
23
+ # Use demo trading environment
24
+ blofin --demo market tickers
25
+ ```
26
+
27
+ ## Setup
28
+
29
+ Run the interactive setup wizard to save API credentials to `~/.config/blofin/config.json`:
30
+
31
+ ```bash
32
+ blofin setup
33
+ ```
34
+
35
+ The wizard prompts for API key, secret key, passphrase, and demo mode preference. Existing values are shown masked (`...xxxx`); press Enter to keep them.
36
+
37
+ Alternatively, use environment variables (they take priority over the config file):
38
+
39
+ ```bash
40
+ export BLOFIN_API_KEY="your-key"
41
+ export BLOFIN_API_SECRET="your-secret"
42
+ export BLOFIN_PASSPHRASE="your-passphrase"
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```bash
48
+ blofin [flags] <group> <subcommand> [--param=value ...]
49
+ ```
50
+
51
+ ### Market Data (no auth required)
52
+
53
+ ```bash
54
+ blofin market tickers --instId=BTC-USDT
55
+ blofin market instruments
56
+ blofin market orderbook --instId=BTC-USDT --size=20
57
+ blofin market candles --instId=BTC-USDT --bar=1H --limit=24
58
+ blofin market mark-price --instId=BTC-USDT
59
+ blofin market trades --instId=BTC-USDT
60
+ blofin market funding-rate --instId=BTC-USDT
61
+ ```
62
+
63
+ ### Account
64
+
65
+ ```bash
66
+ blofin account balance --productType=USDT-FUTURES
67
+ blofin account positions
68
+ blofin account config
69
+ blofin account leverage --instId=BTC-USDT --marginMode=cross # query
70
+ blofin account leverage --instId=BTC-USDT --marginMode=cross --leverage=10 # set
71
+ blofin account margin-mode # query
72
+ blofin account margin-mode --marginMode=cross # set
73
+ blofin account position-mode
74
+ ```
75
+
76
+ ### Trading
77
+
78
+ ```bash
79
+ # Dangerous operations require --confirm
80
+ blofin trade place --instId=BTC-USDT --side=buy --orderType=market \
81
+ --size=0.01 --marginMode=cross --positionSide=net --confirm
82
+
83
+ blofin trade cancel --orderId=123456 --confirm
84
+
85
+ blofin trade close --instId=BTC-USDT --marginMode=cross \
86
+ --positionSide=net --confirm
87
+
88
+ # Read operations
89
+ blofin trade orders --instId=BTC-USDT # pending orders
90
+ blofin trade orders --orderId=123456 # order detail
91
+ blofin trade orders --status=filled # order history
92
+ blofin trade fills --instId=BTC-USDT
93
+
94
+ # TP/SL and algo orders
95
+ blofin trade tpsl --instId=BTC-USDT --side=sell --marginMode=cross \
96
+ --positionSide=net --tpTriggerPrice=55000 --tpOrderPrice=-1 --size=-1
97
+ blofin trade tpsl-orders --instId=BTC-USDT
98
+ blofin trade cancel-tpsl --tpslId=123456
99
+ blofin trade algo --instId=BTC-USDT --orderType=trigger \
100
+ --side=buy --size=0.01 --triggerPrice=50000 --marginMode=cross --positionSide=net
101
+ blofin trade algo-orders --orderType=trigger
102
+ blofin trade cancel-algo --algoId=123456
103
+ ```
104
+
105
+ ### Assets
106
+
107
+ ```bash
108
+ blofin asset balances --accountType=futures
109
+ blofin asset transfer --currency=USDT --fromAccount=funding \
110
+ --toAccount=futures --amount=100 --confirm
111
+ blofin asset bills
112
+ blofin asset deposits
113
+ blofin asset withdrawals
114
+ blofin asset apikey-info
115
+ ```
116
+
117
+ ## Global Flags
118
+
119
+ | Flag | Description |
120
+ |------|-------------|
121
+ | `--help` | Show help (global or per-group) |
122
+ | `--version` | Show version |
123
+ | `-o, --output <format>` | Output format: `table` (default) or `json` |
124
+ | `--demo` | Use demo trading environment |
125
+ | `--confirm` | Required for dangerous operations |
126
+ | `--read-only` | Only expose read-level tools |
127
+ | `--modules=<list>` | Comma-separated module filter (e.g. `public,account`) |
128
+
129
+ ## Output Formats
130
+
131
+ ### Table (default)
132
+
133
+ ```
134
+ ┌────────────┬──────────┬──────────┐
135
+ │ instId │ last │ vol24h │
136
+ ├────────────┼──────────┼──────────┤
137
+ │ BTC-USDT │ 67000.5 │ 12345.67 │
138
+ └────────────┴──────────┴──────────┘
139
+ ```
140
+
141
+ ### JSON (`-o json`)
142
+
143
+ ```json
144
+ {
145
+ "tool": "get_tickers",
146
+ "ok": true,
147
+ "data": { "code": "0", "data": [...] },
148
+ "timestamp": 1700000000000
149
+ }
150
+ ```
151
+
152
+ ## Credential Resolution
153
+
154
+ Priority (highest to lowest):
155
+
156
+ 1. **Environment variables** — `BLOFIN_API_KEY`, `BLOFIN_API_SECRET`, `BLOFIN_PASSPHRASE`
157
+ 2. **Config file** — `~/.config/blofin/config.json` (created by `blofin setup`)
158
+ 3. **Defaults** — empty strings (public endpoints only)
159
+
160
+ The `--demo` flag and `BLOFIN_BASE_URL` env var control which environment is used:
161
+
162
+ | Condition | Base URL |
163
+ |-----------|----------|
164
+ | `--demo` flag | `https://demo-trading-openapi.blofin.com` |
165
+ | `demo: true` in config | `https://demo-trading-openapi.blofin.com` |
166
+ | `BLOFIN_BASE_URL` set | Uses that URL (overrides all) |
167
+ | Otherwise | `https://openapi.blofin.com` (production) |
168
+
169
+ ## Command Groups
170
+
171
+ | Group | Description | Subcommands |
172
+ |-------|-------------|-------------|
173
+ | `market` | Market data (public) | `instruments`, `tickers`, `orderbook`, `trades`, `candles`, `mark-price`, `funding-rate` |
174
+ | `account` | Account information | `balance`, `positions`, `config`, `leverage`, `margin-mode`, `position-mode` |
175
+ | `trade` | Trading operations | `place`, `cancel`, `close`, `orders`, `tpsl`, `cancel-tpsl`, `tpsl-orders`, `algo`, `cancel-algo`, `algo-orders`, `fills` |
176
+ | `asset` | Asset management | `balances`, `transfer`, `bills`, `deposits`, `withdrawals`, `apikey-info` |
@@ -0,0 +1,53 @@
1
+ // src/config.ts
2
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ import { DEMO_BASE_URL, PROD_BASE_URL } from "blofin-core";
6
+ function configDir() {
7
+ return join(homedir(), ".config", "blofin");
8
+ }
9
+ function configFile() {
10
+ return join(configDir(), "config.json");
11
+ }
12
+ function getConfigPath() {
13
+ return configFile();
14
+ }
15
+ function defaultConfig() {
16
+ return { apiKey: "", secretKey: "", passphrase: "", demo: false };
17
+ }
18
+ function loadCliConfig() {
19
+ try {
20
+ const raw = readFileSync(configFile(), "utf-8");
21
+ const parsed = JSON.parse(raw);
22
+ return {
23
+ apiKey: typeof parsed.apiKey === "string" ? parsed.apiKey : "",
24
+ secretKey: typeof parsed.secretKey === "string" ? parsed.secretKey : "",
25
+ passphrase: typeof parsed.passphrase === "string" ? parsed.passphrase : "",
26
+ demo: typeof parsed.demo === "boolean" ? parsed.demo : false
27
+ };
28
+ } catch {
29
+ return defaultConfig();
30
+ }
31
+ }
32
+ function saveCliConfig(config) {
33
+ mkdirSync(configDir(), { recursive: true, mode: 448 });
34
+ writeFileSync(configFile(), JSON.stringify(config, null, 2) + "\n", {
35
+ mode: 384
36
+ });
37
+ }
38
+ function resolveCredentials(flags) {
39
+ const fileConfig = loadCliConfig();
40
+ const apiKey = process.env.BLOFIN_API_KEY ?? fileConfig.apiKey ?? "";
41
+ const secretKey = process.env.BLOFIN_API_SECRET ?? fileConfig.secretKey ?? "";
42
+ const passphrase = process.env.BLOFIN_PASSPHRASE ?? fileConfig.passphrase ?? "";
43
+ const demo = flags.demo ?? fileConfig.demo;
44
+ const baseUrl = process.env.BLOFIN_BASE_URL ?? (demo ? DEMO_BASE_URL : PROD_BASE_URL);
45
+ return { apiKey, secretKey, passphrase, baseUrl };
46
+ }
47
+
48
+ export {
49
+ getConfigPath,
50
+ loadCliConfig,
51
+ saveCliConfig,
52
+ resolveCredentials
53
+ };
package/dist/index.js ADDED
@@ -0,0 +1,359 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveCredentials
4
+ } from "./chunk-ZOIYPWGU.js";
5
+
6
+ // src/index.ts
7
+ import { fileURLToPath } from "url";
8
+ import { resolve } from "path";
9
+ import { readFileSync } from "fs";
10
+ import { parseArgs } from "util";
11
+ import {
12
+ ALL_MODULES,
13
+ buildTools,
14
+ loadConfig,
15
+ BlofinClient
16
+ } from "blofin-core";
17
+
18
+ // src/commands.ts
19
+ function group(description, commands) {
20
+ const map = /* @__PURE__ */ new Map();
21
+ for (const [sub, toolName, desc, risk] of commands) {
22
+ map.set(sub, { toolName, description: desc, riskLevel: risk });
23
+ }
24
+ return { description, commands: map };
25
+ }
26
+ var COMMAND_GROUPS = /* @__PURE__ */ new Map([
27
+ [
28
+ "market",
29
+ group("Market data (public)", [
30
+ ["instruments", "get_instruments", "List available instruments", "read"],
31
+ ["tickers", "get_tickers", "Get ticker prices", "read"],
32
+ ["orderbook", "get_order_book", "Get order book depth", "read"],
33
+ ["trades", "get_market_trades", "Get recent trades", "read"],
34
+ ["candles", "get_candlesticks", "Get candlestick/kline data", "read"],
35
+ ["mark-price", "get_mark_price", "Get mark price", "read"],
36
+ ["funding-rate", "get_funding_rate", "Get funding rate", "read"]
37
+ ])
38
+ ],
39
+ [
40
+ "account",
41
+ group("Account information", [
42
+ ["balance", "get_balance", "Get account balance", "read"],
43
+ ["positions", "get_positions", "Get open positions", "read"],
44
+ ["config", "get_account_config", "Get account configuration", "read"],
45
+ ["leverage", "manage_leverage", "Get or set leverage", "write"],
46
+ ["margin-mode", "manage_margin_mode", "Get or set margin mode", "write"],
47
+ ["position-mode", "manage_position_mode", "Get or set position mode", "write"]
48
+ ])
49
+ ],
50
+ [
51
+ "trade",
52
+ group("Trading operations", [
53
+ ["place", "place_order", "Place a new order", "dangerous"],
54
+ ["cancel", "cancel_order", "Cancel an order", "dangerous"],
55
+ ["close", "close_position", "Close a position", "dangerous"],
56
+ ["orders", "get_orders", "Get order list", "read"],
57
+ ["tpsl", "place_tpsl", "Place take-profit/stop-loss", "write"],
58
+ ["cancel-tpsl", "cancel_tpsl", "Cancel TP/SL order", "write"],
59
+ ["tpsl-orders", "get_tpsl_orders", "Get TP/SL orders", "read"],
60
+ ["algo", "place_algo_order", "Place an algo order", "write"],
61
+ ["cancel-algo", "cancel_algo_order", "Cancel an algo order", "write"],
62
+ ["algo-orders", "get_algo_orders", "Get algo orders", "read"],
63
+ ["fills", "get_fills_history", "Get fill history", "read"]
64
+ ])
65
+ ],
66
+ [
67
+ "asset",
68
+ group("Asset management", [
69
+ ["balances", "get_asset_balances", "Get asset balances", "read"],
70
+ ["transfer", "fund_transfer", "Transfer between accounts", "dangerous"],
71
+ ["bills", "get_transfer_history", "Get transfer history", "read"],
72
+ ["deposits", "get_deposit_history", "Get deposit history", "read"],
73
+ ["withdrawals", "get_withdrawal_history", "Get withdrawal history", "read"],
74
+ ["apikey-info", "get_apikey_info", "Get API key info", "read"]
75
+ ])
76
+ ]
77
+ ]);
78
+ function resolveCommand(groupName, subcommand) {
79
+ return COMMAND_GROUPS.get(groupName)?.commands.get(subcommand);
80
+ }
81
+
82
+ // src/output.ts
83
+ import Table from "cli-table3";
84
+ import { toToolResponse, toToolError } from "blofin-core";
85
+ function outputTable(rows, columns, title) {
86
+ if (rows.length === 0) return title ? `${title}
87
+ (no data)` : "(no data)";
88
+ const cols = columns ?? Object.keys(rows[0]);
89
+ const table = new Table({ head: [...cols] });
90
+ for (const row of rows) {
91
+ table.push(cols.map((c) => String(row[c] ?? "")));
92
+ }
93
+ const rendered = table.toString();
94
+ return title ? `${title}
95
+ ${rendered}` : rendered;
96
+ }
97
+ function outputResult(toolName, data, format) {
98
+ if (format === "json") {
99
+ return JSON.stringify(toToolResponse(toolName, data), null, 2);
100
+ }
101
+ const rows = extractRows(data);
102
+ if (rows.length > 0) {
103
+ return outputTable(rows);
104
+ }
105
+ return JSON.stringify(data, null, 2);
106
+ }
107
+ function outputError(toolName, error, format) {
108
+ if (format === "json") {
109
+ return JSON.stringify(toToolError(toolName, error), null, 2);
110
+ }
111
+ const msg = error instanceof Error ? error.message : String(error);
112
+ return `Error: ${msg}`;
113
+ }
114
+ function extractRows(data) {
115
+ if (data == null || typeof data !== "object") return [];
116
+ const envelope = data;
117
+ const inner = envelope.data;
118
+ if (Array.isArray(inner) && inner.length > 0 && isRecord(inner[0])) {
119
+ return inner;
120
+ }
121
+ if (isRecord(inner)) {
122
+ return [inner];
123
+ }
124
+ if (Array.isArray(data) && data.length > 0 && isRecord(data[0])) {
125
+ return data;
126
+ }
127
+ return [];
128
+ }
129
+ function isRecord(v) {
130
+ return v != null && typeof v === "object" && !Array.isArray(v);
131
+ }
132
+
133
+ // src/index.ts
134
+ var CLI_VERSION = (() => {
135
+ const pkgPath = new URL("../package.json", import.meta.url);
136
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
137
+ return pkg.version;
138
+ })();
139
+ var VALID_MODULES = new Set(ALL_MODULES);
140
+ var CLI_FLAGS = /* @__PURE__ */ new Set([
141
+ "help",
142
+ "version",
143
+ "read-only",
144
+ "confirm",
145
+ "modules",
146
+ "output",
147
+ "demo"
148
+ ]);
149
+ function formatGlobalHelp() {
150
+ const lines = [
151
+ `blofin-cli v${CLI_VERSION}`,
152
+ "",
153
+ "Usage: blofin [flags] <group> <subcommand> [--param=value ...]",
154
+ "",
155
+ "Groups:"
156
+ ];
157
+ for (const [name, group2] of COMMAND_GROUPS) {
158
+ lines.push(` ${name.padEnd(12)} ${group2.description}`);
159
+ }
160
+ lines.push(
161
+ "",
162
+ "Commands:",
163
+ " setup Configure API credentials",
164
+ "",
165
+ "Global flags:",
166
+ " --help Show help",
167
+ " --version Show version",
168
+ " -o, --output Output format: table (default) | json",
169
+ " --demo Use demo trading environment",
170
+ " --read-only Exclude write/dangerous tools",
171
+ " --confirm Allow dangerous operations",
172
+ " --modules Comma-separated module filter",
173
+ "",
174
+ "Run 'blofin <group> --help' for subcommand details."
175
+ );
176
+ return lines.join("\n");
177
+ }
178
+ function formatGroupHelp(groupName) {
179
+ const group2 = COMMAND_GROUPS.get(groupName);
180
+ if (!group2) return `Unknown group: ${groupName}`;
181
+ const lines = [
182
+ `blofin ${groupName} \u2014 ${group2.description}`,
183
+ "",
184
+ "Subcommands:"
185
+ ];
186
+ const maxSub = Math.max(
187
+ ...[...group2.commands.keys()].map((k) => k.length)
188
+ );
189
+ for (const [sub, cmd] of group2.commands) {
190
+ const tag = `[${cmd.riskLevel}]`;
191
+ lines.push(` ${sub.padEnd(maxSub + 2)} ${tag.padEnd(14)} ${cmd.description}`);
192
+ }
193
+ return lines.join("\n");
194
+ }
195
+ function parseModules(raw) {
196
+ const ids = raw.split(",");
197
+ const invalid = ids.filter((id) => !VALID_MODULES.has(id));
198
+ if (invalid.length > 0) {
199
+ return `Invalid module(s): ${invalid.join(", ")}. Valid modules: ${ALL_MODULES.join(", ")}`;
200
+ }
201
+ return ids;
202
+ }
203
+ var SHORT_VALUE_FLAGS = /* @__PURE__ */ new Map([
204
+ ["-o", "output"]
205
+ ]);
206
+ function expandShortFlags(args) {
207
+ const result = [];
208
+ for (let i = 0; i < args.length; i++) {
209
+ const longName = SHORT_VALUE_FLAGS.get(args[i]);
210
+ if (longName && i + 1 < args.length) {
211
+ result.push(`--${longName}=${args[i + 1]}`);
212
+ i++;
213
+ } else {
214
+ result.push(args[i]);
215
+ }
216
+ }
217
+ return result;
218
+ }
219
+ async function runCli(args) {
220
+ const normalizedArgs = expandShortFlags(args);
221
+ const { values, positionals } = parseArgs({
222
+ args: normalizedArgs,
223
+ strict: false,
224
+ allowPositionals: true
225
+ });
226
+ const help = values["help"] === true;
227
+ const version = values["version"] === true;
228
+ const readOnly = values["read-only"] === true;
229
+ const confirm = values["confirm"] === true;
230
+ const demo = values["demo"] === true ? true : void 0;
231
+ const modulesRaw = typeof values["modules"] === "string" ? values["modules"] : void 0;
232
+ const outputFlag = typeof values["output"] === "string" ? values["output"] : void 0;
233
+ const format = outputFlag === "json" ? "json" : "table";
234
+ if (version) {
235
+ return { output: CLI_VERSION, ok: true };
236
+ }
237
+ let modules;
238
+ if (modulesRaw) {
239
+ const parsed = parseModules(modulesRaw);
240
+ if (typeof parsed === "string") {
241
+ return {
242
+ output: outputError("cli", new Error(parsed), format),
243
+ ok: false
244
+ };
245
+ }
246
+ modules = parsed;
247
+ }
248
+ if (positionals.length === 0 || help && positionals.length === 0) {
249
+ return { output: formatGlobalHelp(), ok: true };
250
+ }
251
+ const first = positionals[0];
252
+ if (first === "setup") {
253
+ const { runSetup } = await import("./setup-2N757DQH.js");
254
+ return runSetup();
255
+ }
256
+ const groupName = first;
257
+ const group2 = COMMAND_GROUPS.get(groupName);
258
+ if (!group2) {
259
+ return {
260
+ output: outputError(
261
+ "cli",
262
+ new Error(`Unknown command: ${first}. Run 'blofin --help' for usage.`),
263
+ format
264
+ ),
265
+ ok: false
266
+ };
267
+ }
268
+ if (help || positionals.length < 2) {
269
+ return { output: formatGroupHelp(groupName), ok: true };
270
+ }
271
+ const subcommand = positionals[1];
272
+ const cmdDef = resolveCommand(groupName, subcommand);
273
+ if (!cmdDef) {
274
+ return {
275
+ output: outputError(
276
+ "cli",
277
+ new Error(
278
+ `Unknown subcommand: ${groupName} ${subcommand}. Run 'blofin ${groupName} --help' for available subcommands.`
279
+ ),
280
+ format
281
+ ),
282
+ ok: false
283
+ };
284
+ }
285
+ const creds = resolveCredentials({ demo });
286
+ const config = loadConfig({
287
+ modules,
288
+ readOnly,
289
+ apiKey: creds.apiKey,
290
+ secretKey: creds.secretKey,
291
+ passphrase: creds.passphrase,
292
+ baseUrl: creds.baseUrl
293
+ });
294
+ const tools = buildTools(config);
295
+ const tool = tools.find((t) => t.name === cmdDef.toolName);
296
+ if (!tool) {
297
+ return {
298
+ output: outputError(
299
+ cmdDef.toolName,
300
+ new Error(
301
+ `Tool '${cmdDef.toolName}' not available (may be excluded by --read-only or --modules).`
302
+ ),
303
+ format
304
+ ),
305
+ ok: false
306
+ };
307
+ }
308
+ if (tool.riskLevel === "dangerous" && !confirm) {
309
+ return {
310
+ output: outputError(
311
+ tool.name,
312
+ new Error(
313
+ `Command '${groupName} ${subcommand}' is dangerous. Use --confirm to execute.`
314
+ ),
315
+ format
316
+ ),
317
+ ok: false
318
+ };
319
+ }
320
+ const params = {};
321
+ for (const [key, value] of Object.entries(values)) {
322
+ if (CLI_FLAGS.has(key)) continue;
323
+ params[key] = value;
324
+ }
325
+ const client = new BlofinClient(config);
326
+ const ctx = { client, config };
327
+ try {
328
+ const result = await tool.handler(params, ctx);
329
+ return {
330
+ output: outputResult(tool.name, result, format),
331
+ ok: true
332
+ };
333
+ } catch (error) {
334
+ return {
335
+ output: outputError(tool.name, error, format),
336
+ ok: false
337
+ };
338
+ }
339
+ }
340
+ function isMainModule() {
341
+ if (typeof process === "undefined" || !process.argv[1]) {
342
+ return false;
343
+ }
344
+ const self = fileURLToPath(import.meta.url);
345
+ const invoked = resolve(process.argv[1]);
346
+ return self === invoked;
347
+ }
348
+ if (isMainModule()) {
349
+ runCli(process.argv.slice(2)).then(({ output, ok }) => {
350
+ if (output) console.log(output);
351
+ if (!ok) process.exit(1);
352
+ }).catch((err) => {
353
+ console.error(err);
354
+ process.exit(1);
355
+ });
356
+ }
357
+ export {
358
+ runCli
359
+ };
@@ -0,0 +1,50 @@
1
+ import {
2
+ getConfigPath,
3
+ loadCliConfig,
4
+ saveCliConfig
5
+ } from "./chunk-ZOIYPWGU.js";
6
+
7
+ // src/setup.ts
8
+ import { createInterface } from "readline/promises";
9
+ function mask(value) {
10
+ if (value.length <= 4) return value ? "****" : "(not set)";
11
+ return `...${value.slice(-4)}`;
12
+ }
13
+ async function runSetup() {
14
+ const rl = createInterface({
15
+ input: process.stdin,
16
+ output: process.stderr
17
+ });
18
+ try {
19
+ const current = loadCliConfig();
20
+ process.stderr.write("\nBloFin CLI Setup\n");
21
+ process.stderr.write(`Config file: ${getConfigPath()}
22
+
23
+ `);
24
+ const apiKey = await promptField(rl, "API Key", current.apiKey);
25
+ const secretKey = await promptField(rl, "Secret Key", current.secretKey);
26
+ const passphrase = await promptField(rl, "Passphrase", current.passphrase);
27
+ const demoAnswer = await rl.question(
28
+ `Use demo trading? [${current.demo ? "Y/n" : "y/N"}]: `
29
+ );
30
+ const demo = demoAnswer.trim() === "" ? current.demo : demoAnswer.trim().toLowerCase().startsWith("y");
31
+ const config = { apiKey, secretKey, passphrase, demo };
32
+ saveCliConfig(config);
33
+ const msg = `
34
+ Config saved to ${getConfigPath()}
35
+ `;
36
+ process.stderr.write(msg);
37
+ return { output: "", ok: true };
38
+ } finally {
39
+ rl.close();
40
+ }
41
+ }
42
+ async function promptField(rl, label, current) {
43
+ const answer = await rl.question(
44
+ `${label} [${mask(current)}]: `
45
+ );
46
+ return answer.trim() || current;
47
+ }
48
+ export {
49
+ runSetup
50
+ };
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "blofin-cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "blofin": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsup src/index.ts --format esm",
11
+ "typecheck": "tsc --noEmit",
12
+ "test": "vitest run"
13
+ },
14
+ "dependencies": {
15
+ "blofin-core": "^0.2.2",
16
+ "cli-table3": "^0.6.5"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^22.0.0",
20
+ "tsup": "^8.0.0",
21
+ "typescript": "^5.9.3",
22
+ "vitest": "^4.0.18"
23
+ },
24
+ "engines": {
25
+ "node": ">=20"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "license": "MIT"
31
+ }