hufi-cli 0.3.1 → 0.5.2

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 (3) hide show
  1. package/README.md +28 -12
  2. package/dist/cli.js +90 -133
  3. package/package.json +3 -2
package/README.md CHANGED
@@ -12,31 +12,31 @@ bun install
12
12
 
13
13
  ```bash
14
14
  # Show help
15
- bun src/cli.ts --help
15
+ bunx hufi-cli --help
16
16
 
17
- # Authenticate
18
- bun src/cli.ts auth login --private-key 0x...
17
+ # Generate a new wallet (key saved to ~/.hufi-cli/key.json)
18
+ bunx hufi-cli auth generate
19
19
 
20
- # Generate a new wallet
21
- bun src/cli.ts auth generate
20
+ # Authenticate (uses saved key by default)
21
+ bunx hufi-cli auth login
22
22
 
23
23
  # Check auth status
24
- bun src/cli.ts auth status
24
+ bunx hufi-cli auth status
25
25
 
26
26
  # Register exchange API key
27
- bun src/cli.ts exchange register --name mexc --api-key xxx --secret-key yyy
27
+ bunx hufi-cli exchange register --name mexc --api-key xxx --secret-key yyy
28
28
 
29
29
  # List exchange API keys
30
- bun src/cli.ts exchange list
30
+ bunx hufi-cli exchange list
31
31
 
32
32
  # Check campaign join status
33
- bun src/cli.ts campaign status --chain-id 137 --address 0x...
33
+ bunx hufi-cli campaign status --chain-id 137 --address 0x...
34
34
 
35
35
  # Join a campaign
36
- bun src/cli.ts campaign join --chain-id 137 --address 0x...
36
+ bunx hufi-cli campaign join --chain-id 137 --address 0x...
37
37
 
38
38
  # List joined campaigns
39
- bun src/cli.ts campaign list --limit 20
39
+ bunx hufi-cli campaign list --limit 20
40
40
  ```
41
41
 
42
42
  ## Global Options
@@ -47,6 +47,22 @@ bun src/cli.ts campaign list --limit 20
47
47
 
48
48
  Configuration is stored at `~/.hufi-cli/config.json`.
49
49
 
50
+ ## Capabilities
51
+
52
+ ### What This CLI Can Do
53
+
54
+ | Module | Commands | Description |
55
+ |--------|----------|-------------|
56
+ | **auth** | login, generate, status | Authentication & wallet management |
57
+ | **exchange** | register, list | Exchange API key management |
58
+ | **campaign** | list, get, joined, join, status, progress, leaderboard | Campaign browsing, joining, and monitoring |
59
+
60
+ ### What This CLI Cannot Do
61
+
62
+ - ❌ Execute on-chain transactions
63
+ - ❌ Configure trading strategies
64
+ - ❌ Multi-signature or voting operations
65
+
50
66
  ## API Endpoints
51
67
 
52
68
  - Recording Oracle: `https://ro.hu.finance`
@@ -56,7 +72,7 @@ Configuration is stored at `~/.hufi-cli/config.json`.
56
72
 
57
73
  ```bash
58
74
  # Run directly
59
- bun src/cli.ts --help
75
+ bunx hufi-cli --help
60
76
 
61
77
  # Run tests
62
78
  bun test
package/dist/cli.js CHANGED
@@ -9339,7 +9339,22 @@ import { homedir } from "node:os";
9339
9339
  import { join } from "node:path";
9340
9340
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9341
9341
  var CONFIG_DIR = join(homedir(), ".hufi-cli");
9342
- var CONFIG_FILE = join(CONFIG_DIR, "config.json");
9342
+ var DEFAULT_CONFIG_FILE = join(CONFIG_DIR, "config.json");
9343
+ var DEFAULT_KEY_FILE = join(CONFIG_DIR, "key.json");
9344
+ var customConfigFile = null;
9345
+ var customKeyFile = null;
9346
+ function setConfigFile(path) {
9347
+ customConfigFile = path;
9348
+ }
9349
+ function setKeyFile(path) {
9350
+ customKeyFile = path;
9351
+ }
9352
+ function configFile() {
9353
+ return customConfigFile ?? DEFAULT_CONFIG_FILE;
9354
+ }
9355
+ function keyFile() {
9356
+ return customKeyFile ?? DEFAULT_KEY_FILE;
9357
+ }
9343
9358
  var DEFAULT_CONFIG = {
9344
9359
  recordingApiUrl: "https://ro.hu.finance",
9345
9360
  launcherApiUrl: "https://cl.hu.finance"
@@ -9351,11 +9366,11 @@ function ensureConfigDir() {
9351
9366
  }
9352
9367
  function loadConfig() {
9353
9368
  ensureConfigDir();
9354
- if (!existsSync(CONFIG_FILE)) {
9369
+ if (!existsSync(configFile())) {
9355
9370
  return { ...DEFAULT_CONFIG };
9356
9371
  }
9357
9372
  try {
9358
- const raw = readFileSync(CONFIG_FILE, "utf-8");
9373
+ const raw = readFileSync(configFile(), "utf-8");
9359
9374
  const parsed = JSON.parse(raw);
9360
9375
  return { ...DEFAULT_CONFIG, ...parsed };
9361
9376
  } catch {
@@ -9364,7 +9379,7 @@ function loadConfig() {
9364
9379
  }
9365
9380
  function saveConfig(config) {
9366
9381
  ensureConfigDir();
9367
- writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + `
9382
+ writeFileSync(configFile(), JSON.stringify(config, null, 2) + `
9368
9383
  `);
9369
9384
  }
9370
9385
  function updateConfig(partial) {
@@ -9373,6 +9388,30 @@ function updateConfig(partial) {
9373
9388
  saveConfig(merged);
9374
9389
  return merged;
9375
9390
  }
9391
+ function getConfigPath() {
9392
+ return configFile();
9393
+ }
9394
+ function getKeyPath() {
9395
+ return keyFile();
9396
+ }
9397
+ function keyExists() {
9398
+ return existsSync(keyFile());
9399
+ }
9400
+ function saveKey(key, address) {
9401
+ ensureConfigDir();
9402
+ writeFileSync(keyFile(), JSON.stringify({ address, privateKey: key }, null, 2) + `
9403
+ `);
9404
+ }
9405
+ function loadKey() {
9406
+ if (!existsSync(keyFile()))
9407
+ return null;
9408
+ try {
9409
+ const raw = readFileSync(keyFile(), "utf-8");
9410
+ return JSON.parse(raw).privateKey ?? null;
9411
+ } catch {
9412
+ return null;
9413
+ }
9414
+ }
9376
9415
 
9377
9416
  // src/lib/output.ts
9378
9417
  function printJson(data) {
@@ -9392,11 +9431,16 @@ function maskSecret(value) {
9392
9431
  // src/commands/auth.ts
9393
9432
  function createAuthCommand() {
9394
9433
  const auth = new Command("auth").description("Authentication commands");
9395
- auth.command("login").description("Authenticate with Recording Oracle using a private key").requiredOption("-k, --private-key <key>", "EVM private key").option("-u, --api-url <url>", "Recording Oracle API URL").option("--json", "Output as JSON").action(async (opts) => {
9434
+ auth.command("login").description("Authenticate with Recording Oracle using a private key").option("-k, --private-key <key>", "EVM private key (uses saved key if not provided)").option("-u, --api-url <url>", "Recording Oracle API URL").option("--json", "Output as JSON").action(async (opts) => {
9435
+ const privateKey = opts.privateKey ?? loadKey();
9436
+ if (!privateKey) {
9437
+ printText("No private key provided. Run: hufi auth login -k <key> or hufi auth generate");
9438
+ process.exit(1);
9439
+ }
9396
9440
  const config = loadConfig();
9397
9441
  const baseUrl = (opts.apiUrl ?? config.recordingApiUrl).replace(/\/+$/, "");
9398
9442
  try {
9399
- const result = await authenticate(baseUrl, opts.privateKey);
9443
+ const result = await authenticate(baseUrl, privateKey);
9400
9444
  updateConfig({
9401
9445
  recordingApiUrl: baseUrl,
9402
9446
  address: result.address,
@@ -9407,7 +9451,7 @@ function createAuthCommand() {
9407
9451
  printJson({ address: result.address, accessToken: result.accessToken });
9408
9452
  } else {
9409
9453
  printText(`Authenticated as ${result.address}`);
9410
- printText(`Token saved to ~/.hufi-cli/config.json`);
9454
+ printText(`Token saved to ${getConfigPath()}`);
9411
9455
  }
9412
9456
  } catch (err) {
9413
9457
  const message = err instanceof Error ? err.message : String(err);
@@ -9415,13 +9459,31 @@ function createAuthCommand() {
9415
9459
  process.exitCode = 1;
9416
9460
  }
9417
9461
  });
9418
- auth.command("generate").description("Generate a new EVM wallet").option("--json", "Output as JSON").action((opts) => {
9462
+ auth.command("generate").description("Generate a new EVM wallet").option("--json", "Output as JSON").action(async (opts) => {
9463
+ if (keyExists()) {
9464
+ if (!opts.json) {
9465
+ printText(`Key already exists at ${getKeyPath()}. Overwrite? (y/N)`);
9466
+ const answer = await new Promise((resolve) => {
9467
+ process.stdin.resume();
9468
+ process.stdin.once("data", (data) => {
9469
+ process.stdin.pause();
9470
+ resolve(data.toString().trim().toLowerCase());
9471
+ });
9472
+ });
9473
+ if (answer !== "y") {
9474
+ printText("Cancelled.");
9475
+ return;
9476
+ }
9477
+ }
9478
+ }
9419
9479
  const wallet = createWallet();
9480
+ saveKey(wallet.privateKey, wallet.address);
9481
+ const keyPath = getKeyPath();
9420
9482
  if (opts.json) {
9421
- printJson(wallet);
9483
+ printJson({ address: wallet.address, keyPath });
9422
9484
  } else {
9423
9485
  printText(`Address: ${wallet.address}`);
9424
- printText(`Private key: ${wallet.privateKey}`);
9486
+ printText(`Private key saved to ${keyPath}`);
9425
9487
  }
9426
9488
  });
9427
9489
  auth.command("status").description("Show current authentication status").option("--json", "Output as JSON").action((opts) => {
@@ -9614,10 +9676,18 @@ function createCampaignCommand() {
9614
9676
  const key = `${c.chain_id}:${c.address}`;
9615
9677
  const joined = joinedKeys.has(key);
9616
9678
  const tag = joined ? " [JOINED]" : "";
9679
+ const decimals = c.fund_token_decimals ?? 0;
9680
+ const div = Math.pow(10, decimals);
9681
+ const fmt = (v) => (Number(v) / div).toLocaleString(undefined, { maximumFractionDigits: decimals });
9682
+ const fundAmount = Number(c.fund_amount);
9683
+ const balanceNum = Number(c.balance);
9684
+ const pct = fundAmount > 0 ? (balanceNum / fundAmount * 100).toFixed(1) : "0.0";
9617
9685
  printText(` ${c.exchange_name} ${c.symbol} (${c.type})${tag}`);
9618
- printText(` address: ${c.address}`);
9619
- printText(` status: ${c.status}`);
9620
- printText(` funded: ${c.fund_amount} ${c.fund_token_symbol} balance: ${c.balance}`);
9686
+ printText(` chain: ${c.chain_id}`);
9687
+ printText(` address: ${c.address}`);
9688
+ printText(` status: ${c.status}`);
9689
+ printText(` duration: ${c.start_date?.split("T")[0] ?? "-"} ~ ${c.end_date?.split("T")[0] ?? "-"}`);
9690
+ printText(` funded: ${fmt(c.fund_amount)} ${c.fund_token_symbol} paid: ${fmt(c.amount_paid)} balance: ${fmt(c.balance)} (${pct}%)`);
9621
9691
  printText("");
9622
9692
  }
9623
9693
  if (opts.status === "active") {
@@ -9773,127 +9843,14 @@ function createCampaignCommand() {
9773
9843
  }
9774
9844
 
9775
9845
  // src/cli.ts
9776
- var BASH_COMPLETION = [
9777
- "_hufi_completions() {",
9778
- " local cur prev commands",
9779
- " COMPREPLY=()",
9780
- ' cur="${COMP_WORDS[COMP_CWORD]}"',
9781
- ' prev="${COMP_WORDS[COMP_CWORD-1]}"',
9782
- "",
9783
- ' commands="auth exchange campaign completion help"',
9784
- "",
9785
- ' case "$prev" in',
9786
- " hufi)",
9787
- ' COMPREPLY=( $(compgen -W "$commands" -- "$cur") )',
9788
- " return 0",
9789
- " ;;",
9790
- " auth)",
9791
- ' COMPREPLY=( $(compgen -W "login generate status help" -- "$cur") )',
9792
- " return 0",
9793
- " ;;",
9794
- " exchange)",
9795
- ' COMPREPLY=( $(compgen -W "register list help" -- "$cur") )',
9796
- " return 0",
9797
- " ;;",
9798
- " campaign)",
9799
- ' COMPREPLY=( $(compgen -W "list get joined status join progress leaderboard help" -- "$cur") )',
9800
- " return 0",
9801
- " ;;",
9802
- " esac",
9803
- "",
9804
- ' COMPREPLY=( $(compgen -W "--help --version --json" -- "$cur") )',
9805
- " return 0",
9806
- "}",
9807
- "",
9808
- "complete -F _hufi_completions hufi"
9809
- ].join(`
9810
- `);
9811
- var ZSH_COMPLETION = [
9812
- "#compdef hufi",
9813
- "",
9814
- "_hufi() {",
9815
- " local -a commands",
9816
- " commands=(",
9817
- " 'auth:Authentication commands'",
9818
- " 'exchange:Exchange API key management'",
9819
- " 'campaign:Campaign management'",
9820
- " 'completion:Generate shell completion script'",
9821
- " 'help:Display help'",
9822
- " )",
9823
- "",
9824
- " _arguments -C \\",
9825
- " '1:command:->command' \\",
9826
- " '*::arg:->args'",
9827
- "",
9828
- " case $state in",
9829
- " command)",
9830
- " _describe 'command' commands",
9831
- " ;;",
9832
- " args)",
9833
- " case ${words[1]} in",
9834
- " auth)",
9835
- " _arguments \\",
9836
- " '1:subcommand:(login generate status help)'",
9837
- " ;;",
9838
- " exchange)",
9839
- " _arguments \\",
9840
- " '1:subcommand:(register list help)'",
9841
- " ;;",
9842
- " campaign)",
9843
- " _arguments \\",
9844
- " '1:subcommand:(list get joined status join progress leaderboard help)'",
9845
- " ;;",
9846
- " esac",
9847
- " ;;",
9848
- " esac",
9849
- "}",
9850
- "",
9851
- '_hufi "$@"'
9852
- ].join(`
9853
- `);
9854
- var FISH_COMPLETION = [
9855
- "# hufi completions for fish",
9856
- "complete -c hufi -f",
9857
- "",
9858
- "# Top-level commands",
9859
- "complete -c hufi -n '__fish_use_subcommand' -a auth -d 'Authentication commands'",
9860
- "complete -c hufi -n '__fish_use_subcommand' -a exchange -d 'Exchange API key management'",
9861
- "complete -c hufi -n '__fish_use_subcommand' -a campaign -d 'Campaign management'",
9862
- "complete -c hufi -n '__fish_use_subcommand' -a completion -d 'Generate shell completion script'",
9863
- "",
9864
- "# auth subcommands",
9865
- "complete -c hufi -n '__fish_seen_subcommand_from auth' -a login -d 'Authenticate with private key'",
9866
- "complete -c hufi -n '__fish_seen_subcommand_from auth' -a generate -d 'Generate a new wallet'",
9867
- "complete -c hufi -n '__fish_seen_subcommand_from auth' -a status -d 'Show auth status'",
9868
- "",
9869
- "# exchange subcommands",
9870
- "complete -c hufi -n '__fish_seen_subcommand_from exchange' -a register -d 'Register exchange API key'",
9871
- "complete -c hufi -n '__fish_seen_subcommand_from exchange' -a list -d 'List exchange API keys'",
9872
- "",
9873
- "# campaign subcommands",
9874
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a list -d 'List available campaigns'",
9875
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a get -d 'Get campaign details'",
9876
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a joined -d 'List joined campaigns'",
9877
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a status -d 'Check join status'",
9878
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a join -d 'Join a campaign'",
9879
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a progress -d 'Check your progress'",
9880
- "complete -c hufi -n '__fish_seen_subcommand_from campaign' -a leaderboard -d 'View leaderboard'",
9881
- "",
9882
- "# Global options",
9883
- "complete -c hufi -l help -d 'Show help'",
9884
- "complete -c hufi -l version -d 'Show version'",
9885
- "complete -c hufi -l json -d 'Output as JSON'"
9886
- ].join(`
9887
- `);
9888
9846
  var program2 = new Command;
9889
- program2.name("hufi").description("CLI tool for hu.fi DeFi platform").version("0.3.1");
9890
- program2.command("completion").description("Generate shell completion script").option("--bash", "Bash completion (default)").option("--zsh", "Zsh completion").option("--fish", "Fish completion").action((opts) => {
9891
- if (opts.zsh) {
9892
- console.log(ZSH_COMPLETION);
9893
- } else if (opts.fish) {
9894
- console.log(FISH_COMPLETION);
9895
- } else {
9896
- console.log(BASH_COMPLETION);
9847
+ program2.name("hufi").description("CLI tool for hu.fi DeFi platform").version("0.5.2").option("--config-file <path>", "Custom config file path (default: ~/.hufi-cli/config.json)").option("--key-file <path>", "Custom key file path (default: ~/.hufi-cli/key.json)").hook("preAction", (thisCommand) => {
9848
+ const opts = thisCommand.opts();
9849
+ if (opts.configFile) {
9850
+ setConfigFile(opts.configFile);
9851
+ }
9852
+ if (opts.keyFile) {
9853
+ setKeyFile(opts.keyFile);
9897
9854
  }
9898
9855
  });
9899
9856
  program2.addCommand(createAuthCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hufi-cli",
3
- "version": "0.3.1",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "hufi": "./dist/cli.js"
@@ -10,7 +10,8 @@
10
10
  "build": "bun build ./src/cli.ts --outfile ./dist/cli.js --target node",
11
11
  "prepublishOnly": "bun run build",
12
12
  "test": "bun test",
13
- "typecheck": "bunx tsc --noEmit"
13
+ "typecheck": "bunx tsc --noEmit",
14
+ "test:cli": "./test-cli.sh"
14
15
  },
15
16
  "dependencies": {
16
17
  "commander": "^12.0.0",