Gcli version 0.4.3-RC1 - allow `json` output for most commands

Change started by @poka which allows using the -o/ --output-format with most commands.

This is a Release Candidate build not yet integrated into the main branch; please consider leaving some feedback if you have any issue (or everything works well :wink:) .

The idea is to merge the change into the main branch if no major issue is reported.

The changelogs (also available here):

[0.4.3-RC1] - 2025-05-17

Added / Changed

  • Added support of json output for most commands through the usage of the global -o/ --output-format argument (to be given before any command).
    • The default output remains the human format.
  • Added possibility to use vault inspect command without interactive prompts (when providing all necessary arguments)
  • To have a clean json output; those commands should be called with all necessary arguments so that no interactive prompt is required; which then allows to pipe the result to other commands like jq
    • Example usage:
      gcli -o json vault inspect -v predef-sr//Alice --no-password | jq 
      {
        "substrate_uri": "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice",
        "crypto_scheme": "sr25519",
        "secret_seed": "e5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a",
        "public_key_hex": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
        "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
        "g1v1_public_key": null
      }
      

Fixed

  • None

Deprecated

  • Two commands are still deprecated and will be removed in a future release:
    • gcli vault list-files
    • gcli vault migrate

Removed

  • None

CI/CD

  • None
3 Likes

Super! Merci

Voici mes essais ```-o json``

  • “vault list all” : json OK
    Le champs g1v1_public_key est à “null”, même pour une clef import v1
tmp/gcli -o json vault list all | jq
[
  {
    "address": "5DdYvvv7UAo93Am26J9xbMHN3WkRgXZaeWWw1cUKCTi5JYQt",
    "crypto_scheme": "ed25519",
    "path": "<Base>",
    "name": "COUCOU",
    "g1v1_public_key": null,
    "children": []
  },
  {
    "address": "5GFERUrAJFiCrC3GvWiQcvgDbTnj15fGHoKJV2AtEeWGirG5",
    "crypto_scheme": "ed25519",
    "path": "<Base>",
    "name": "UPlanetTest",
    "g1v1_public_key": null,
    "children": []
  }
]
  • “vault use” : json OK
    Il manque “g1v1_pub_key” en retour de commande “vault use”
tmp/gcli vault use -v  UPlanetTest
Using: Base[address:5GFERUrAJFiCrC3GvWiQcvgDbTnj15fGHoKJV2AtEeWGirG5, g1v1_pub_key:DT3bfKg2UM5XJVoMde5F17xFv6QefegD6qFWRHup2STb, name:Some("UPlanetTest"), crypto_scheme:Some(Ed25519)]
Configuration updated!

tmp/gcli -o json vault use -v  UPlanetTest
{"address":"5GFERUrAJFiCrC3GvWiQcvgDbTnj15fGHoKJV2AtEeWGirG5"}
  • “vault derive”
    no json
/tmp/gcli -o json vault derive --derivation-path "//0.00//0.00" --no-password -v TEST -n TEST_0.00_0.00
Adding derivation to: Base[address:5GcDKuUHE1mUk12Rm6YF4boZSsJQRoQFbazhHPdFBoeQ3fvK, g1v1_pub_key:EXWXnW1f6ARPd1NSfhhNaaYp9LBVaEBz3t43FTnCx1Zo, name:Some("TEST"), crypto_scheme:Some(Ed25519)]

Its parent hierarchy is this:
┌────────────────────────────────────────────────────────────────────────────────┐
│ SS58 Address/G1v1 public key                           Crypto    Path     Name │
╞════════════════════════════════════════════════════════════════════════════════╡
│ 5GcDKuUHE1mUk12Rm6YF4boZSsJQRoQFbazhHPdFBoeQ3fvK       ed25519   <Base>   TEST │
│ └ G1v1: EXWXnW1f6ARPd1NSfhhNaaYp9LBVaEBz3t43FTnCx1Zo                           │
└────────────────────────────────────────────────────────────────────────────────┘

The linked <Base> account is Base[address:5GcDKuUHE1mUk12Rm6YF4boZSsJQRoQFbazhHPdFBoeQ3fvK, g1v1_pub_key:EXWXnW1f6ARPd1NSfhhNaaYp9LBVaEBz3t43FTnCx1Zo, name:Some("TEST"), crypto_scheme:Some(Ed25519)]


Trying to create derivation with address '5G5SsfmbJWrcM1BuPNM2FMny6prNuZq4VG7VHqX3NsBKPDrX'

Creating derivation account Derivation[address:5G5SsfmbJWrcM1BuPNM2FMny6prNuZq4VG7VHqX3NsBKPDrX, name:Some("TEST_0.00_0.00"), path:Some("//0.00//0.00"), parent:Some("5GcDKuUHE1mUk12Rm6YF4boZSsJQRoQFbazhHPdFBoeQ3fvK")]
Change done
  • “vault remove”

/tmp/gcli -o json vault remove -v TEST_0.00_0.00
Deleting 1 address
Done removing address:'5G5SsfmbJWrcM1BuPNM2FMny6prNuZq4VG7VHqX3NsBKPDrX'

  • “account”
    Les erreurs ne sont pas renvoyées en json
/tmp/gcli -o json account balance
[src/data.rs:239:4] e = Rpc(
    ClientError(
        Transport(
            Io(
                Os {
                    code: 111,
                    kind: ConnectionRefused,
                    message: "Connection refused",
                },
            ),
        ),
    ),
)

  • “config show” : json OK
/tmp/gcli -o json config show | jq
{
  "duniter_endpoint": "ws://localhost:9944",
  "indexer_endpoint": "http://localhost:4350/graphql",
  "address": "5GcDKuUHE1mUk12Rm6YF4boZSsJQRoQFbazhHPdFBoeQ3fvK",
  "vault_account": "Base[address:5GcDKuUHE1mUk12Rm6YF4boZSsJQRoQFbazhHPdFBoeQ3fvK, g1v1_pub_key:EXWXnW1f6ARPd1NSfhhNaaYp9LBVaEBz3t43FTnCx1Zo, name:Some(\"TEST\"), crypto_scheme:Some(Ed25519)]"
}

Ensuite, je n’ai pas trouvé comment changer la config des adresses “endpoint” …

2 Likes

Bonne remarque, la gestion des succès/erreurs doit aussi être faite correctement en json. Ça rejoint #18.

Pour cette question, c’est possible via les arguments globaux:

Pour voir les arguments globaux:

gcli --help                                                      
A command-line interface for Duniter v2s uses

Usage: gcli [OPTIONS] <COMMAND>

Commands:
  account     Account (balance, transfer...)
  identity    Identity (get, create, confirm, revoke...)
  smith       Smith (certify, go-online, go-offline...)
  tech        Tech (list members, proposals, vote...)
  ud          Universal Dividend (claim...)
  oneshot     Oneshot account (balance, create, consume...)
  blockchain  Blockchain (current block, runtime info...)
  indexer     Indexer (check, latest block)
  config      Config (show, save...)
  vault       Key management (import, generate, list...)
  completion  Generate a completions script for a specified shell (use `completion --help` for more info)
  help        Print this message or the help of the given subcommand(s)

Options:
  -i, --indexer <INDEXER>              Overwrite indexer endpoint
      --no-indexer                     Do not use indexer
  -S, --secret-format <SECRET_FORMAT>  Secret key format (seed, substrate, g1v1)
  -s, --secret <SECRET>                Secret key or BIP39 mnemonic (only used when secret format is compatible) (eventually followed by derivation path)
  -c, --crypto-scheme <CRYPTO_SCHEME>  Crypto scheme to use (sr25519, ed25519) [default: ed25519]
  -a <ADDRESS>                         SS58 Address
  -v <NAME>                            Name of an SS58 Address in the vault
  -u, --url <URL>                      Overwrite duniter websocket RPC endpoint
  -n, --network <NETWORK>              Target network (local, gdev, gtest...)
      --no-wait                        prevent waiting for extrinsic completion
  -o, --output-format <OUTPUT_FORMAT>  Output format (human, json, ...) [default: human]
  -h, --help                           Print help
  -V, --version                        Print version

Config avant changement

gcli -o json config show | jq                                                                      
{
  "duniter_endpoint": "wss://archive-rpc.gdev.de.brussels.ovh",
  "indexer_endpoint": "https://squid-hasura.gdev.de.brussels.ovh/v1/graphql",
  "address": "5C8PhJPLE54x23RjmqBcEEnALryCDWdTJM5xLaoL9W8XEpnt",
  "vault_account": "Derivation[address:5C8PhJPLE54x23RjmqBcEEnALryCDWdTJM5xLaoL9W8XEpnt, name:None, path:Some(\"/soft\"), parent:Some(\"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\")]"
}

Tester l’impact des arguments -u et -i (ils peuvent également être utilisé pour changer les valeurs uniquement pour une commande)

gcli -o json -u wss://gdev.coinduf.eu -i https://squid.gdev.coinduf.eu/v1/graphql  config show | jq
{
  "duniter_endpoint": "wss://gdev.coinduf.eu",
  "indexer_endpoint": "https://squid.gdev.coinduf.eu/v1/graphql",
  "address": "5C8PhJPLE54x23RjmqBcEEnALryCDWdTJM5xLaoL9W8XEpnt",
  "vault_account": "Derivation[address:5C8PhJPLE54x23RjmqBcEEnALryCDWdTJM5xLaoL9W8XEpnt, name:None, path:Some(\"/soft\"), parent:Some(\"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\")]"
}

Sauvegarder le changement dans la config (cette commande n’est pas encore adaptée pour renvoyer du json - à voir ce que l’on voudrait renvoyer ?)

gcli -o json -u wss://gdev.coinduf.eu -i https://squid.gdev.coinduf.eu/v1/graphql  config save     
Configuration updated!

Vérification de la config adaptée

gcli -o json config show | jq
{
  "duniter_endpoint": "wss://gdev.coinduf.eu",
  "indexer_endpoint": "https://squid.gdev.coinduf.eu/v1/graphql",
  "address": "5C8PhJPLE54x23RjmqBcEEnALryCDWdTJM5xLaoL9W8XEpnt",
  "vault_account": "Derivation[address:5C8PhJPLE54x23RjmqBcEEnALryCDWdTJM5xLaoL9W8XEpnt, name:None, path:Some(\"/soft\"), parent:Some(\"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\")]"
}
3 Likes

J’ai fais quelques refactorings de code et quelques changements supplémentaires.

Pour répondre aux points relevés par @Frederic_Renault:

  • vault list ... il y a l’argument --show-g1v1 qu’il faut fournir si on veut voir les g1v1_public_key remplies, pour autant que ce soit bien un compte de <Base> avec le crypto scheme ed25519
  • vault use pas modifié pour le moment, juste l’adresse qui est sélectionnée
  • vault derive pas encore adapté - il faudrait voir les données structurées que l’on voudrait avoir en retour…
  • vault remove ajouté le format json et un paramètre en plus nécessaire si on veut supprimer un compte et ses dérivations sans prompt intéractif (--force)
  • account (et les autres commandes supportant json) les erreurs sont maintenant retournées sous forme de json avec 2 properties error_type & error si l’output format demandé est json

Pour plus de détails, voir sur la page de release ou ci-dessous :slight_smile:

[0.4.3-RC2] - 2025-06-13

Added / Changed

  • Added support of json output for most commands through the usage of the global -o/ --output-format argument (to be given before any command).

    • The default output remains the human format.
  • Added possibility to use vault inspect command without interactive prompts (when providing all necessary arguments)

  • Added possibility to use vault remove command without interactive prompts (added --force argument in case there were derivation accounts linked) and support for json output format

  • Added specific json serialization in case of error (for commands supporting json output)

    • example when not providing enough arguments when using json output for vault remove:
    gcli -o json vault remove -v predef-ed | jq
    
    {
      "error_type": "Input",
      "error": "Cannot delete an account with derivations without using --force"
    }
    
    • example using an incorrect vault name with vault inspect command:
    gcli -o json vault inspect -v predef-sr//Aliceaze --no-password | jq
    
    {
      "error_type": "Input",
      "error": "No account found with name:'predef-sr' and path:'//Aliceaze'"
    }
    
    • example when using an incorrect duniter websocket RPC endpoint url with identity show command:
    gcli -o json --url=badurl identity show | jq
    
    {
      "error_type": "Duniter",
      "error": "can not connect to duniter badurl"
    }
    

    Specifically for this kind of error, to get more details, you can run gcli commands with a specific log level below error (which is the default).

    RUST_LOG=warn gcli -o json --url=badurl identity show | jq
    [2025-06-13T15:50:46Z WARN  gcli::data] [src/data.rs:239] Rpc(ClientError(RelativeUrlWithoutBase))
    {
      "error_type": "Duniter",
      "error": "can not connect to duniter badurl"
    }
    

    Please note that those logs are written to stderr and not stdout; which is why jq command is not impacted in this case.

  • Added proper error json output for account balance command

  • To have a clean json output; those commands should be called with all necessary arguments so that no interactive prompt is required; which then allows to pipe the result to other commands like jq

    • Example usage:
    gcli -o json vault inspect -v predef-sr//Alice --no-password | jq 
    {
      "substrate_uri": "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice",
      "crypto_scheme": "sr25519",
      "secret_seed": "e5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a",
      "public_key_hex": "d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
      "ss58_address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
      "g1v1_public_key": null
    }
    

Fixed

  • None

Deprecated

  • Two commands are still deprecated and will be removed in a future release:
    • gcli vault list-files
    • gcli vault migrate

Removed

  • None

CI/CD

  • None
3 Likes

Super ! Tu t’es vraiment emparé sur sujet c’est chouette.

Il reste quelque chose que m’agace pas mal sur gcli depuis le début, c’est ça:

poka@macbook gcli % gcli vault import -u "truc tata titi machin" -c sr25519 --no-password -n ""
Trying to import for SS58 address :'5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn'

Creating <Base> account Base[address:5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn, name:None, crypto_scheme:Some(Sr25519)]
Change done
poka@macbook gcli % gcli account balance

thread 'main' panicked at src/data.rs:120:34:
an address is needed
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
poka@macbook gcli % gcli config show
Ğcli config
duniter endpoint wss://gdev.p2p.legal:443/ws
indexer endpoint https://squid.gdev.gyroi.de/v1/graphql
address (no address)

J’aimerais que lorsqu’on import un nouveau vault, il soit set comme default automatiquement.
Que ce soit le premier import (config vide), ou même si on a déjà un vault importé, le plus logique est que le default soit toujours le dernier vault importé.
Ca nécessite une commande en plus gcli vault use -a 5CQ8T4qpbYJq7uVsxGPQ5q2df7x3Wa4aRY6HUWMBYjfLZhnn qui fait très redondante et pénible.


Au passage ça révèle une uncaught exception ici j’ai l’impression :wink:

1 Like

Adapter le default pour le premier importé (s’il n’y avait pas encore de default) semble une bonne idée effectivement :slight_smile:

Pas sûr si c’est une bonne idée pour les imports suivants (quand on a déjà un default); à moins que l’on ajoute un paramètre de plus pour le demander lors de l’import peut-être ?

Pour moi dans mon usage quand on importe un wallet c’est pour l’utiliser directement après.

Mais pour commencer et éviter les confusions on pourrait rendre ce comportement uniquement si le défaut est vide oui :slight_smile:

1 Like