vana-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.
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +115 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vana
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Vana CLI
|
|
2
|
+
|
|
3
|
+
> **⚠️ ALPHA SOFTWARE - EXPERIMENTAL USE ONLY**
|
|
4
|
+
>
|
|
5
|
+
> This CLI is in early alpha development and is **NOT SUITABLE FOR PRODUCTION USE**.
|
|
6
|
+
> Features may change without notice, and data loss or unexpected behavior may occur.
|
|
7
|
+
> Use at your own risk and avoid using with mainnet assets or critical operations.
|
|
8
|
+
|
|
9
|
+
A command-line interface for interacting with the Vana L1 network and related services.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Install globally via npm:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g vana-cli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or use directly with npx:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx vana-cli --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
> **Note**: This is alpha software. Only use with testnet/development environments.
|
|
28
|
+
|
|
29
|
+
1. **Initialize configuration**:
|
|
30
|
+
```bash
|
|
31
|
+
vana config init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
2. **Set your wallet private key**:
|
|
35
|
+
```bash
|
|
36
|
+
vana config set wallet_private_key 63...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
3. **Get refiner statistics**:
|
|
40
|
+
```bash
|
|
41
|
+
vana stats refiner --id 45
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
### Configuration Management
|
|
47
|
+
|
|
48
|
+
- `vana config init` - Initialize configuration with defaults
|
|
49
|
+
- `vana config get [key]` - Get configuration values
|
|
50
|
+
- `vana config set <key> <value>` - Set configuration values
|
|
51
|
+
|
|
52
|
+
### Statistics
|
|
53
|
+
|
|
54
|
+
- `vana stats refiner --id <id>` - Get ingestion statistics for a specific refiner
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
The CLI stores configuration in:
|
|
59
|
+
- **Unprotected values**: `~/.vana/cli.config.toml`
|
|
60
|
+
- **Protected values** (like private keys): OS keyring
|
|
61
|
+
|
|
62
|
+
### Available Configuration Keys
|
|
63
|
+
|
|
64
|
+
- `network` - Network to connect to (vana|moksha)
|
|
65
|
+
- `rpc_endpoint` - RPC endpoint URL
|
|
66
|
+
- `wallet_private_key` - Your wallet private key (stored securely in keyring)
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Initialize with defaults
|
|
72
|
+
vana config init
|
|
73
|
+
|
|
74
|
+
# Set network
|
|
75
|
+
vana config set network moksha
|
|
76
|
+
|
|
77
|
+
# Set custom RPC endpoint
|
|
78
|
+
vana config set rpc_endpoint https://rpc.moksha.vana.org
|
|
79
|
+
|
|
80
|
+
# Get refiner stats with verbose output
|
|
81
|
+
vana stats refiner --id 45 --verbose
|
|
82
|
+
|
|
83
|
+
# Get stats in JSON format
|
|
84
|
+
vana stats refiner --id 45 --json
|
|
85
|
+
|
|
86
|
+
# Use custom endpoint for one-off query
|
|
87
|
+
vana stats refiner --id 45 --endpoint https://custom.api.com
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
See [DEVELOPMENT.md](DEVELOPMENT.md) for development setup and contribution guidelines.
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Cli as fe,Builtins as re}from"clipanion";import{Command as G,Option as k}from"clipanion";import{ethers as ne}from"ethers";var I=class{constructor(e){this.baseUrl=e;this.baseUrl=e}async getRefinerIngestionStats(e,t){try{let r=t.startsWith("0x")?t:`0x${t}`,i=new ne.Wallet(r),o=e.toString(),s=await i.signMessage(o),c=`${this.baseUrl}/stats/refiner/${e}`,u=await fetch(c,{method:"GET",headers:{"X-Refiner-Signature":s,"Content-Type":"application/json"}});if(!u.ok){let E=await u.text(),S=`HTTP ${u.status}: ${u.statusText}`;try{let N=JSON.parse(E);S=N.detail||N.message||S}catch{S=E||S}throw new Error(S)}return await u.json()}catch(r){throw r instanceof Error?r:new Error(`Failed to get refiner ingestion stats: ${String(r)}`)}}};import{promises as _}from"fs";import{join as H,dirname as ie}from"path";import{homedir as oe}from"os";import L from"keytar";var R={network:"moksha",rpc_endpoint:"https://rpc.moksha.vana.org"},q="vana",j=H(oe(),".vana"),x=H(j,"cli.config.toml"),d=class n{static instance;unprotectedConfig=null;constructor(){}static getInstance(){return n.instance||(n.instance=new n),n.instance}async initialize(){try{await _.mkdir(j,{recursive:!0});try{await _.access(x)}catch{await this.writeUnprotectedConfig(R)}}catch(e){throw new Error(`Failed to initialize config: ${e instanceof Error?e.message:String(e)}`)}}async getConfig(){let e=await this.getUnprotectedConfig(),t=await this.getProtectedConfig();return{...e,...t}}async getUnprotectedConfig(){if(this.unprotectedConfig)return this.unprotectedConfig;try{let e=await _.readFile(x,"utf-8");return this.unprotectedConfig=this.parseToml(e),this.unprotectedConfig}catch{return this.unprotectedConfig={...R},this.unprotectedConfig}}async getProtectedConfig(){try{return{wallet_private_key:await L.getPassword(q,"wallet_private_key")||void 0}}catch{return{}}}async setConfig(e,t){this.isProtectedKey(e)?await this.setProtectedConfig(e,t):await this.setUnprotectedConfig(e,t)}async getConfigValue(e){return(await this.getConfig())[e]}async setUnprotectedConfig(e,t){let r=await this.getUnprotectedConfig();if(e==="network"&&!["vana","moksha"].includes(t))throw new Error(`Invalid network value: ${t}. Must be 'vana' or 'moksha'`);r[e]=t,await this.writeUnprotectedConfig(r),this.unprotectedConfig=r}async setProtectedConfig(e,t){try{await L.setPassword(q,e,t)}catch(r){throw new Error(`Failed to store ${e} in keyring: ${r instanceof Error?r.message:String(r)}`)}}async writeUnprotectedConfig(e){let t=this.generateToml(e);try{await _.mkdir(ie(x),{recursive:!0}),await _.writeFile(x,t,"utf-8")}catch(r){throw new Error(`Failed to write config file: ${r instanceof Error?r.message:String(r)}`)}}isProtectedKey(e){return e==="wallet_private_key"}parseToml(e){let t={},r=e.split(`
|
|
3
|
+
`);for(let i of r){let o=i.trim();if(o&&!o.startsWith("#")){let[s,...c]=o.split("=");if(s&&c.length>0){let u=s.trim(),E=c.join("=").trim().replace(/['"]/g,"");(u==="network"||u==="rpc_endpoint")&&(t[u]=E)}}}return{...R,...t}}generateToml(e){return`# Vana CLI Configuration
|
|
4
|
+
# This file stores unprotected configuration values
|
|
5
|
+
# Secrets like private keys are stored securely in the OS keyring
|
|
6
|
+
|
|
7
|
+
network = "${e.network}"
|
|
8
|
+
rpc_endpoint = "${e.rpc_endpoint}"
|
|
9
|
+
`}getConfigKeys(){return{unprotected:["network","rpc_endpoint"],protected:["wallet_private_key"]}}async reset(){await this.writeUnprotectedConfig(R),this.unprotectedConfig=null;try{await L.deletePassword(q,"wallet_private_key")}catch{}}getConfigPath(){return x}};import l from"chalk";function w(n,e){return n==="wallet_private_key"&&e?e.length>10?e.substring(0,6)+"..."+e.substring(e.length-4):e.substring(0,4)+"..."+e.substring(e.length-2):e}function b(n){return["wallet_private_key","private_key","secret","password","token"].some(t=>n.toLowerCase().includes(t))}function a(n,e,t={}){let{maskSensitive:r=!1,indent:i=2}=t,o=" ".repeat(i);if(e===void 0)return`${o}${l.cyan(n)}: ${l.gray("(not set)")}`;let s=r&&b(n)?w(n,e):e;return`${o}${l.cyan(n)}: ${s}`}function P(n,e){let t=l.blue.bold(`
|
|
10
|
+
${n}:`);return e&&(t+=l.gray(` (${e})`)),t}function f(n){return l.bold(n)+`
|
|
11
|
+
`+"\u2500".repeat(Math.min(n.length,50))}function K(n){return l.green(`\u2713 ${n}`)}function A(n){return l.red(`\u2717 ${n}`)}function B(n){return l.yellow(`\u26A0 ${n}`)}function J(n){return l.blue(`\u2139 ${n}`)}function D(n){return l.gray(n)}function se(n){return l.gray(` $ ${n}`)}var g={configSection(n,e,t,r={}){let{maskSensitive:i=!1,showEmpty:o=!0}=r,s=P(n,e)+`
|
|
12
|
+
`;for(let[c,u]of Object.entries(t))(u!==void 0||o)&&(s+=a(c,u,{maskSensitive:i})+`
|
|
13
|
+
`);return s},examples(n,e){let t=P(n)+`
|
|
14
|
+
`;for(let r of e)t+=se(r)+`
|
|
15
|
+
`;return t},nextSteps(n){let e=P("Next steps")+`
|
|
16
|
+
`;for(let t of n)e+=D(` \u2022 ${t}`)+`
|
|
17
|
+
`;return e},troubleshooting(n,e){let t=P(n)+`
|
|
18
|
+
`;for(let r of e)t+=D(` \u2022 ${r}`)+`
|
|
19
|
+
`;return t}};var F=class{constructor(e){this.context=e}success(e){this.context.stdout.write(K(e)+`
|
|
20
|
+
`)}error(e){this.context.stderr.write(A(e)+`
|
|
21
|
+
`)}warning(e){this.context.stderr.write(B(e)+`
|
|
22
|
+
`)}info(e){this.context.stdout.write(J(e)+`
|
|
23
|
+
`)}log(e){this.context.stdout.write(e+`
|
|
24
|
+
`)}write(e){this.context.stdout.write(e)}},p={configNotFound:n=>`Configuration key '${n}' not found`,invalidValue:(n,e,t)=>`Invalid value '${e}' for '${n}'${t?`. Expected: ${t}`:""}`,missingRequired:n=>`Missing required field: ${n}`,keyringAccess:n=>`Failed to ${n} keyring. Ensure your system keyring is accessible`,fileAccess:(n,e)=>`Failed to ${e} file: ${n}`,networkError:n=>`Network error connecting to: ${n}`,authenticationFailed:n=>`Authentication failed${n?`: ${n}`:""}`,permissionDenied:n=>`Permission denied accessing: ${n}`,unexpectedError:n=>`Unexpected error: ${n instanceof Error?n.message:String(n)}`},v={configSet:(n,e)=>`Set ${n} in ${e}`,configInitialized:()=>"Configuration initialized",configReset:()=>"Configuration reset to defaults",operationComplete:n=>`${n} completed successfully`},z={secretsCleared:()=>"Protected values (like private keys) have been cleared",keyringUnavailable:()=>"OS keyring unavailable, secrets cannot be stored securely",deprecatedOption:(n,e)=>`Option '${n}' is deprecated, use '${e}' instead`,experimentalFeature:n=>`${n} is experimental and may change in future versions`},y={configLocation:n=>`Config file: ${n}`,useSecretsFlag:()=>"Use --secrets to view protected values",interactiveMode:()=>"Interactive mode requires a prompt library like inquirer",nextSteps:{setPrivateKey:()=>"Set your wallet private key: vana config set wallet_private_key 63...",viewConfig:()=>"View all settings: vana config get",viewSecrets:()=>"View with secrets: vana config get --secrets"}};function m(n){return new F({stdout:n.stdout,stderr:n.stderr})}import ae from"ora";var Q=["Verify the refiner ID exists","Check your private key is correct","Ensure the API URL is accessible","Verify you have permission to view this refiner's stats"],$=class extends G{static paths=[["stats","refiner"]];static usage=G.Usage({category:"Stats",description:"Get ingestion statistics for a specific refiner",details:`
|
|
25
|
+
Retrieves comprehensive ingestion statistics for a refiner including:
|
|
26
|
+
\u2022 File contributions and data rows
|
|
27
|
+
\u2022 Query execution metrics
|
|
28
|
+
\u2022 Error breakdowns by type
|
|
29
|
+
\u2022 Table-specific row counts
|
|
30
|
+
\u2022 Contributor and timing information
|
|
31
|
+
|
|
32
|
+
Requires cryptographic authentication using your wallet private key.
|
|
33
|
+
`,examples:[["Get stats for refiner ID 45","vana stats refiner --id 45 --endpoint https://query-engine.api.com"],["Get stats with private key override","vana stats refiner --id 45 --private-key 63... --endpoint https://query-engine.api.com"],["Get stats in JSON format","vana stats refiner --id 45 --endpoint https://query-engine.api.com --json"],["Get verbose stats with raw response","vana stats refiner --id 45 --endpoint https://query-engine.api.com --verbose --include-raw"]]});refinerId=k.String("--id,-i",{description:"Refiner ID to get statistics for",required:!0});privateKey=k.String("--private-key,-pk",{description:"Private key for authentication (overrides config)"});endpoint=k.String("--endpoint,-e",{description:"Query Engine API URL (required - stats come from Query Engine, not RPC)"});json=k.Boolean("--json,-j",!1,{description:"Output in JSON format"});verbose=k.Boolean("--verbose,-v",!1,{description:"Show verbose output with additional details"});includeRaw=k.Boolean("--include-raw",!1,{description:"Include raw API response in output"});async execute(){let e=m(this.context);try{let t=this.validateRefinerId(e);if(t===null)return 1;let{privateKey:r,apiUrl:i}=await this.getConfiguration(e);if(!r||!i)return 1;this.verbose&&this.displayVerboseInfo(e,t,i,r);let o=await this.fetchRefinerStats(e,t,i,r);return o?(this.displayResults(e,o),e.success(v.operationComplete("Refiner stats retrieved")),this.verbose&&e.warning("\u26A0\uFE0F Alpha software: Verify all data independently before making decisions"),0):(e.write(g.troubleshooting("Troubleshooting",Q)),1)}catch(t){return e.error(p.unexpectedError(t)),e.write(g.troubleshooting("Troubleshooting",Q)),1}}validateRefinerId(e){let t=parseInt(this.refinerId);return isNaN(t)||t<0?(e.error("Invalid refiner ID. Must be a non-negative number."),null):t}async getConfiguration(e){let t=d.getInstance();await t.initialize();let r=this.privateKey;if(!r)try{r=await t.getConfigValue("wallet_private_key")}catch{}if(!r)return e.error("No private key provided."),e.write(g.examples("Set private key with",["vana config set wallet_private_key 63...","or use --private-key option"])),{privateKey:null,apiUrl:null};let i=this.endpoint;return i?{privateKey:r,apiUrl:i}:(e.error("Query Engine API URL is required for refiner stats."),e.info("Refiner stats are retrieved from the Query Engine API, not on-chain RPC."),e.write(g.examples("Provide Query Engine API URL with",["vana stats refiner --id 45 --endpoint https://query-engine.api.com"])),{privateKey:null,apiUrl:null})}displayVerboseInfo(e,t,r,i){e.write(f("\u{1F50D} Refiner Ingestion Stats")+`
|
|
34
|
+
`),e.write(a("Refiner ID",t.toString())+`
|
|
35
|
+
`),e.write(a("API URL",r)+`
|
|
36
|
+
`),e.write(a("Private Key",w("wallet_private_key",i))+`
|
|
37
|
+
`),e.write(`
|
|
38
|
+
`)}async fetchRefinerStats(e,t,r,i){let o=new I(r),s=ae(`Fetching ingestion stats for refiner ${t}...`).start();try{let c=await o.getRefinerIngestionStats(t,i);return s.succeed(),c}catch(c){return s.fail(),e.error(`Failed to fetch refiner stats: ${c instanceof Error?c.message:String(c)}`),null}}displayResults(e,t){this.json?e.write(JSON.stringify(t,null,2)+`
|
|
39
|
+
`):this.displayFormattedStats(e,t),this.includeRaw&&!this.json&&(e.write(`
|
|
40
|
+
`+f("\u{1F4C4} Raw API Response")+`
|
|
41
|
+
`),e.write(JSON.stringify(t,null,2)+`
|
|
42
|
+
`))}displayFormattedStats(e,t){e.write(f(`\u{1F4CA} Refiner ${t.refiner_id} - Ingestion Statistics`)+`
|
|
43
|
+
|
|
44
|
+
`),this.displayDataIngestionStats(e,t),this.displayQueryExecutionStats(e,t),this.displayQueryErrorsBreakdown(e,t),this.displayRowsPerTable(e,t),this.verbose&&this.displayTechnicalDetails(e,t)}displayDataIngestionStats(e,t){e.write(f("\u{1F4C1} Data Ingestion")+`
|
|
45
|
+
`),e.write(a("File Contributions",t.total_file_contributions.toLocaleString())+`
|
|
46
|
+
`),e.write(a("Total Data Rows",t.total_data_rows.toLocaleString())+`
|
|
47
|
+
`),e.write(a("Unique Contributors",t.unique_contributors.toLocaleString())+`
|
|
48
|
+
`),t.first_ingestion_at&&e.write(a("First Ingestion",new Date(t.first_ingestion_at).toLocaleString())+`
|
|
49
|
+
`),t.last_ingestion_at&&e.write(a("Last Ingestion",new Date(t.last_ingestion_at).toLocaleString())+`
|
|
50
|
+
`),t.ingestion_period_days&&e.write(a("Ingestion Period",`${t.ingestion_period_days.toFixed(1)} days`)+`
|
|
51
|
+
`),e.write(a("Avg Rate/Hour",t.average_ingestion_rate_per_hour.toFixed(2))+`
|
|
52
|
+
`)}displayQueryExecutionStats(e,t){if(e.write(`
|
|
53
|
+
`+f("\u{1F50D} Query Execution")+`
|
|
54
|
+
`),e.write(a("Total Queries",t.total_queries_executed.toLocaleString())+`
|
|
55
|
+
`),e.write(a("Successful",t.successful_queries.toLocaleString())+`
|
|
56
|
+
`),e.write(a("Failed",t.failed_queries.toLocaleString())+`
|
|
57
|
+
`),t.total_queries_executed>0){let r=(t.successful_queries/t.total_queries_executed*100).toFixed(1);e.write(a("Success Rate",`${r}%`)+`
|
|
58
|
+
`)}}displayQueryErrorsBreakdown(e,t){Object.keys(t.query_errors_by_type).length!==0&&(e.write(`
|
|
59
|
+
`+f("\u274C Query Errors by Type")+`
|
|
60
|
+
`),Object.entries(t.query_errors_by_type).sort(([,r],[,i])=>i-r).forEach(([r,i])=>{e.write(a(r,i.toLocaleString())+`
|
|
61
|
+
`)}))}displayRowsPerTable(e,t){Object.keys(t.rows_per_table).length!==0&&(e.write(`
|
|
62
|
+
`+f("\u{1F4CB} Rows per Table")+`
|
|
63
|
+
`),Object.entries(t.rows_per_table).sort(([,r],[,i])=>i-r).forEach(([r,i])=>{e.write(a(r,i.toLocaleString())+`
|
|
64
|
+
`)}))}displayTechnicalDetails(e,t){e.write(`
|
|
65
|
+
`+f("\u{1F527} Technical Details")+`
|
|
66
|
+
`),t.last_processed_block&&e.write(a("Last Processed Block",t.last_processed_block.toLocaleString())+`
|
|
67
|
+
`)}};import{Command as W,Option as V}from"clipanion";var M=class extends W{static paths=[["config","get"]];static usage=W.Usage({category:"Configuration",description:"Get configuration values",details:`
|
|
68
|
+
Get configuration values from the Vana CLI config.
|
|
69
|
+
|
|
70
|
+
By default, only unprotected values are shown.
|
|
71
|
+
Use --secrets to include protected values from the keyring.
|
|
72
|
+
|
|
73
|
+
Examples:
|
|
74
|
+
$ vana config get # Show all unprotected config
|
|
75
|
+
$ vana config get network # Show specific key
|
|
76
|
+
$ vana config get --secrets # Show all config including secrets
|
|
77
|
+
$ vana config get --json # Output as JSON
|
|
78
|
+
`});key=V.String({required:!1});secrets=V.Boolean("--secrets",!1,{description:"Include protected values from keyring"});json=V.Boolean("--json",!1,{description:"Output as JSON"});async execute(){let e=m(this.context);try{let t=d.getInstance();if(await t.initialize(),this.key){let r=await t.getConfigValue(this.key);if(r===void 0)return e.error(p.configNotFound(this.key)),1;if(this.json){let i=!this.secrets&&b(this.key)?w(this.key,r):r;e.write(JSON.stringify({[this.key]:i},null,2)+`
|
|
79
|
+
`)}else{let i=!this.secrets&&b(this.key)?w(this.key,r):r;e.log(`${this.key}: ${i}`)}}else{let r;if(this.secrets?r=await t.getConfig():r=await t.getUnprotectedConfig(),this.json){let i={...r};if(!this.secrets)for(let[o,s]of Object.entries(i))typeof s=="string"&&b(o)&&(i[o]=w(o,s));e.write(JSON.stringify(i,null,2)+`
|
|
80
|
+
`)}else this.displayConfig(r,e,t)}return 0}catch(t){return e.error(p.unexpectedError(t)),1}}displayConfig(e,t,r){t.write(f("Vana CLI Configuration")+`
|
|
81
|
+
`);let i=r.getConfigKeys(),o={};for(let s of i.unprotected)o[s]=e[s];if(t.write(g.configSection("Unprotected","stored in ~/.vana/cli.config.toml",o,{maskSensitive:!1,showEmpty:!1})),this.secrets){let s={};for(let c of i.protected)s[c]=e[c];t.write(g.configSection("Protected","stored in OS keyring",s,{maskSensitive:!0,showEmpty:!0}))}else t.info(y.useSecretsFlag());t.info(y.configLocation(r.getConfigPath()))}};import{Command as X,Option as U}from"clipanion";import ce from"ora";var O=class extends X{static paths=[["config","set"]];static usage=X.Usage({category:"Configuration",description:"Set configuration values",details:`
|
|
82
|
+
Set configuration values for the Vana CLI.
|
|
83
|
+
|
|
84
|
+
Unprotected values are stored in ~/.vana/cli.config.toml
|
|
85
|
+
Protected values (like private keys) are stored securely in the OS keyring.
|
|
86
|
+
|
|
87
|
+
Available keys:
|
|
88
|
+
- network (vana|moksha)
|
|
89
|
+
- rpc_endpoint (string)
|
|
90
|
+
- wallet_private_key (string, stored in keyring)
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
$ vana config set network moksha
|
|
94
|
+
$ vana config set rpc_endpoint https://rpc.moksha.vana.org
|
|
95
|
+
$ vana config set wallet_private_key 0x1234...
|
|
96
|
+
$ vana config set --interactive
|
|
97
|
+
`});key=U.String({required:!1});value=U.String({required:!1});interactive=U.Boolean("--interactive",!1,{description:"Interactive configuration setup"});quiet=U.Boolean("--quiet",!1,{description:"Suppress output messages"});async execute(){let e=m(this.context);try{let t=d.getInstance();if(await t.initialize(),this.interactive)return await this.runInteractiveSetup(t,e);if(!this.key||!this.value)return e.error(p.missingRequired("key and value")),e.info("Usage: vana config set <key> <value>"),e.info("Or use: vana config set --interactive"),1;let r=ce("Setting configuration...").start();try{if(await t.setConfig(this.key,this.value),r.succeed(),!this.quiet){let o=this.isProtectedKey(this.key)?"OS keyring":"~/.vana/cli.config.toml";e.success(v.configSet(this.key,o))}return 0}catch(i){throw r.fail(),i}}catch(t){return e.error(p.unexpectedError(t)),1}}async runInteractiveSetup(e,t){t.write(f("\u{1F527} Vana CLI Interactive Configuration")+`
|
|
98
|
+
|
|
99
|
+
`);try{let r=await e.getConfig(),i={network:r.network,rpc_endpoint:r.rpc_endpoint},o={wallet_private_key:r.wallet_private_key};return t.write(g.configSection("Current Configuration","unprotected values",i,{maskSensitive:!1,showEmpty:!0})),t.write(g.configSection("Protected Configuration","stored in keyring",o,{maskSensitive:!0,showEmpty:!0})),t.warning(y.interactiveMode()),t.info("Use: vana config set <key> <value> to set individual values"),t.write(g.examples("Example commands",["vana config set network moksha","vana config set rpc_endpoint https://rpc.moksha.vana.org","vana config set wallet_private_key 63..."])),0}catch(r){return t.error(p.unexpectedError(r)),1}}isProtectedKey(e){return e==="wallet_private_key"}};import{Command as Y,Option as Z}from"clipanion";import ee from"ora";var T=class extends Y{static paths=[["config","init"]];static usage=Y.Usage({category:"Configuration",description:"Initialize configuration with defaults",details:`
|
|
100
|
+
Initialize the Vana CLI configuration with default values.
|
|
101
|
+
|
|
102
|
+
Default configuration:
|
|
103
|
+
- network: moksha
|
|
104
|
+
- rpc_endpoint: https://rpc.moksha.vana.org
|
|
105
|
+
|
|
106
|
+
This creates ~/.vana/cli.config.toml with the default settings.
|
|
107
|
+
Protected values (like private keys) are not set by init.
|
|
108
|
+
|
|
109
|
+
Examples:
|
|
110
|
+
$ vana config init # Initialize with defaults
|
|
111
|
+
$ vana config init --reset # Reset existing config to defaults
|
|
112
|
+
`});reset=Z.Boolean("--reset",!1,{description:"Reset existing configuration to defaults"});quiet=Z.Boolean("--quiet",!1,{description:"Suppress output messages"});async execute(){let e=m(this.context);try{let t=d.getInstance();if(this.reset){let r=ee("Resetting configuration to defaults...").start();try{await t.reset(),r.succeed(),this.quiet||(e.success(v.configReset()),e.warning(z.secretsCleared()))}catch(i){throw r.fail(),i}}else{let r=ee("Initializing configuration...").start();try{await t.initialize(),r.succeed(),this.quiet||(e.success(v.configInitialized()),e.warning("\u26A0\uFE0F This is alpha software - only use for testing/development"))}catch(i){throw r.fail(),i}}if(!this.quiet){let r=await t.getUnprotectedConfig();e.write(`
|
|
113
|
+
`+f("Current Configuration")+`
|
|
114
|
+
`);let i={network:r.network,rpc_endpoint:r.rpc_endpoint};e.write(g.configSection("Configuration","unprotected values",i,{maskSensitive:!1,showEmpty:!0})),e.info(y.configLocation(t.getConfigPath())),e.write(g.nextSteps([y.nextSteps.setPrivateKey(),y.nextSteps.viewConfig(),y.nextSteps.viewSecrets()]))}return 0}catch(t){return e.error(p.unexpectedError(t)),1}}};import te from"chalk";import{readFileSync as ge}from"fs";import{fileURLToPath as ue}from"url";import{dirname as le,join as pe}from"path";var de=ue(import.meta.url),ye=le(de),me=JSON.parse(ge(pe(ye,"../package.json"),"utf-8")),[ct,ft,...C]=process.argv;C.length>0&&!C.includes("--help")&&!C.includes("-h")&&!C.includes("--version")&&!C.includes("-v")&&(console.error(te.yellow("\u26A0\uFE0F ALPHA SOFTWARE - Use only for testing/development")),console.error(te.gray(` Not suitable for production use. Features may change without notice.
|
|
115
|
+
`)));var h=new fe({binaryLabel:"Vana CLI (Alpha)",binaryName:"vana",binaryVersion:me.version});h.register(re.HelpCommand);h.register(re.VersionCommand);h.register(M);h.register(O);h.register(T);h.register($);await h.runExit(C,{stdin:process.stdin,stdout:process.stdout});
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vana-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "[ALPHA] Vana CLI for interacting with the Vana L1 network and related services - experimental software",
|
|
5
|
+
"main": "dist/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vana": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/cli.ts --format esm --dts --minify",
|
|
16
|
+
"dev": "npm run build && npm link",
|
|
17
|
+
"unlink": "npm unlink -g",
|
|
18
|
+
"lint": "biome lint src",
|
|
19
|
+
"format": "biome format src --write",
|
|
20
|
+
"check": "biome check .",
|
|
21
|
+
"precommit": "biome lint --apply",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"prepublishOnly": "npm run build && npm test"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/vana-com/vana-cli.git"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"vana",
|
|
32
|
+
"cli",
|
|
33
|
+
"blockchain",
|
|
34
|
+
"web3",
|
|
35
|
+
"datadao",
|
|
36
|
+
"data-sovereignty",
|
|
37
|
+
"alpha",
|
|
38
|
+
"experimental"
|
|
39
|
+
],
|
|
40
|
+
"author": "Vana",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"type": "module",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/vana-com/vana-cli/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/vana-com/vana-cli#readme",
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"chalk": "^5.4.1",
|
|
52
|
+
"clipanion": "^4.0.0-rc.4",
|
|
53
|
+
"ethers": "^6.14.4",
|
|
54
|
+
"keytar": "^7.9.0",
|
|
55
|
+
"ora": "^8.2.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@biomejs/biome": "^2.0.0",
|
|
59
|
+
"@semantic-release/commit-analyzer": "^13.0.0",
|
|
60
|
+
"@semantic-release/github": "^11.0.0",
|
|
61
|
+
"@semantic-release/npm": "^12.0.1",
|
|
62
|
+
"@semantic-release/release-notes-generator": "^14.0.1",
|
|
63
|
+
"@types/node": "^24.0.3",
|
|
64
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
65
|
+
"conventional-changelog-conventionalcommits": "^8.0.0",
|
|
66
|
+
"semantic-release": "^24.0.0",
|
|
67
|
+
"tsup": "^8.5.0",
|
|
68
|
+
"typescript": "^5.8.3",
|
|
69
|
+
"vitest": "^3.2.4"
|
|
70
|
+
},
|
|
71
|
+
"engines": {
|
|
72
|
+
"node": ">=20"
|
|
73
|
+
}
|
|
74
|
+
}
|