Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
26b8410
test charm lib impelentation for cre init
ejacquier Jan 30, 2026
4a7699d
Add shared UI package with Charm ecosystem for styled CLI output
ejacquier Jan 30, 2026
c37b25c
Add spinner to cre init and fix layout issues
ejacquier Jan 30, 2026
26ec16a
Style help page with Lipgloss
ejacquier Jan 30, 2026
46d61de
Apply Chainlink Blocks color palette to CLI
ejacquier Jan 30, 2026
fbfda62
Modernize cre login command with Charm UI components
ejacquier Jan 30, 2026
6ebd121
Add styled error display for CLI using ui.Error()
ejacquier Jan 30, 2026
3d819a4
Improve CLI error display with styled output and smart usage handling
ejacquier Jan 30, 2026
744e5de
Add login prompt when authentication is required
ejacquier Jan 30, 2026
ebe0091
Fix tests broken by Charm UI changes
ejacquier Jan 30, 2026
eb5a70e
Fix cre logout to skip credential check and handle nil credentials
ejacquier Jan 30, 2026
3b0f685
Modernize cre version command with Charm UI styling
ejacquier Jan 30, 2026
137594c
Modernize cre update command with progress bar and Charm UI
ejacquier Jan 30, 2026
f56168f
Fix creinit tests to provide all inputs via flags
ejacquier Jan 30, 2026
214ed37
Update the list-key command to use the shared Charm UI components for
ejacquier Jan 30, 2026
f2ba1ae
Update test assertions for Charm UI output changes
ejacquier Jan 30, 2026
dc83bff
Update the generate-bindings command to use the shared Charm UI
ejacquier Jan 30, 2026
4dd218d
Update the secrets list command to use the shared Charm UI component
ejacquier Jan 30, 2026
a6dde07
Update the secrets delete command to use the shared Charm UI components
ejacquier Jan 30, 2026
ac70c74
Modernize workflow pause, activate, delete with Charm UI
ejacquier Jan 30, 2026
277e84b
updated styling for workflow deploy, pause, delete and transaction
ejacquier Jan 31, 2026
d76bdc3
mondernized link and unlink command using charm lib
ejacquier Jan 31, 2026
30ecbde
updated missing charm console output in creinit.go
ejacquier Jan 31, 2026
c32a002
fixed build output ui
ejacquier Jan 31, 2026
932908c
updated fmt.print with new ui that were not replaced
ejacquier Jan 31, 2026
182ae41
mondernized simulate command with charm ui output
ejacquier Jan 31, 2026
620e035
1. Updated internal/settings/settings_generate.go:
ejacquier Jan 31, 2026
6daa25a
improved prompt behavior with autocomplete and default value
ejacquier Jan 31, 2026
81bbfc9
added error helpers
ejacquier Jan 31, 2026
bee55e8
improved login command with cancellation / escape
ejacquier Jan 31, 2026
1856e4e
fixed logout issue that was not flushing credentials
ejacquier Jan 31, 2026
2b6a6f6
updated login to remove the esc logic
ejacquier Jan 31, 2026
e8c8cb8
updated secret tests that were failing
ejacquier Jan 31, 2026
2d89b8d
fixed lint error: unused functions and returns
ejacquier Feb 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 44 additions & 35 deletions cmd/account/link_key/link_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (
"fmt"
"io"
"math/big"
"os"
"strconv"
"strings"
"sync"
"time"

"github.com/charmbracelet/huh"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
"github.com/machinebox/graphql"
Expand All @@ -26,10 +26,10 @@ import (
"github.com/smartcontractkit/cre-cli/internal/constants"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/prompt"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/settings"
"github.com/smartcontractkit/cre-cli/internal/types"
"github.com/smartcontractkit/cre-cli/internal/ui"
"github.com/smartcontractkit/cre-cli/internal/validation"
)

Expand Down Expand Up @@ -59,7 +59,7 @@ type initiateLinkingResponse struct {
}

func Exec(ctx *runtime.Context, in Inputs) error {
h := newHandler(ctx, os.Stdin)
h := newHandler(ctx, nil)

if err := h.ValidateInputs(in); err != nil {
return err
Expand Down Expand Up @@ -161,10 +161,14 @@ func (h *handler) Execute(in Inputs) error {
h.displayDetails()

if in.WorkflowOwnerLabel == "" {
if err := prompt.SimplePrompt(h.stdin, "Provide a label for your owner address", func(inputLabel string) error {
in.WorkflowOwnerLabel = inputLabel
return nil
}); err != nil {
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().
Title("Provide a label for your owner address").
Value(&in.WorkflowOwnerLabel),
),
).WithTheme(ui.ChainlinkTheme())
if err := form.Run(); err != nil {
return err
}
}
Expand All @@ -182,7 +186,7 @@ func (h *handler) Execute(in Inputs) error {
return nil
}

fmt.Printf("Starting linking: owner=%s, label=%s\n", in.WorkflowOwner, in.WorkflowOwnerLabel)
ui.Dim(fmt.Sprintf("Starting linking: owner=%s, label=%s", in.WorkflowOwner, in.WorkflowOwnerLabel))

resp, err := h.callInitiateLinking(context.Background(), in)
if err != nil {
Expand All @@ -198,7 +202,7 @@ func (h *handler) Execute(in Inputs) error {
h.log.Debug().Msg("\nRaw linking response payload:\n\n" + string(prettyResp))

if in.WorkflowRegistryContractAddress == resp.ContractAddress {
fmt.Println("Contract address validation passed")
ui.Success("Contract address validation passed")
} else {
h.log.Warn().Msg("The workflowRegistryContractAddress in your settings does not match the one returned by the server")
return fmt.Errorf("contract address validation failed")
Expand Down Expand Up @@ -299,11 +303,14 @@ func (h *handler) linkOwner(resp initiateLinkingResponse) error {

switch txOut.Type {
case client.Regular:
fmt.Println("Transaction confirmed")
fmt.Printf("View on explorer: \033]8;;%s/tx/%s\033\\%s/tx/%s\033]8;;\033\\\n", h.environmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash, h.environmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash)
fmt.Println("\n[OK] web3 address linked to your CRE organization successfully")
fmt.Println("\nNote: Linking verification may take up to 60 seconds.")
fmt.Println("\n→ You can now deploy workflows using this address")
ui.Success("Transaction confirmed")
ui.URL(fmt.Sprintf("%s/tx/%s", h.environmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash))
ui.Line()
ui.Success("web3 address linked to your CRE organization successfully")
ui.Line()
ui.Dim("Note: Linking verification may take up to 60 seconds.")
ui.Line()
ui.Bold("You can now deploy workflows using this address")

case client.Raw:
selector, err := strconv.ParseUint(resp.ChainSelector, 10, 64)
Expand All @@ -317,19 +324,19 @@ func (h *handler) linkOwner(resp initiateLinkingResponse) error {
return err
}

fmt.Println("")
fmt.Println("Ownership linking initialized successfully!")
fmt.Println("")
fmt.Println("Next steps:")
fmt.Println("")
fmt.Println(" 1. Submit the following transaction on the target chain:")
fmt.Printf(" Chain: %s\n", ChainName)
fmt.Printf(" Contract Address: %s\n", txOut.RawTx.To)
fmt.Println("")
fmt.Println(" 2. Use the following transaction data:")
fmt.Println("")
fmt.Printf(" %x\n", txOut.RawTx.Data)
fmt.Println("")
ui.Line()
ui.Success("Ownership linking initialized successfully!")
ui.Line()
ui.Bold("Next steps:")
ui.Line()
ui.Print(" 1. Submit the following transaction on the target chain:")
ui.Dim(fmt.Sprintf(" Chain: %s", ChainName))
ui.Dim(fmt.Sprintf(" Contract Address: %s", txOut.RawTx.To))
ui.Line()
ui.Print(" 2. Use the following transaction data:")
ui.Line()
ui.Code(fmt.Sprintf(" %x", txOut.RawTx.Data))
ui.Line()

case client.Changeset:
chainSelector, err := settings.GetChainSelectorByChainName(h.environmentSet.WorkflowRegistryChainName)
Expand All @@ -338,7 +345,7 @@ func (h *handler) linkOwner(resp initiateLinkingResponse) error {
}
mcmsConfig, err := settings.GetMCMSConfig(h.settings, chainSelector)
if err != nil {
fmt.Println("\nMCMS config not found or is incorrect, skipping MCMS config in changeset")
ui.Warning("MCMS config not found or is incorrect, skipping MCMS config in changeset")
}
cldSettings := h.settings.CLDSettings
changesets := []types.Changeset{
Expand Down Expand Up @@ -370,30 +377,32 @@ func (h *handler) linkOwner(resp initiateLinkingResponse) error {
h.log.Warn().Msgf("Unsupported transaction type: %s", txOut.Type)
}

fmt.Println("Linked successfully")
ui.Success("Linked successfully")
return nil
}

func (h *handler) checkIfAlreadyLinked() (bool, error) {
ownerAddr := common.HexToAddress(h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress)
fmt.Println("\nChecking existing registrations...")
ui.Dim("Checking existing registrations...")

linked, err := h.wrc.IsOwnerLinked(ownerAddr)
if err != nil {
return false, fmt.Errorf("failed to check owner link status: %w", err)
}

if linked {
fmt.Println("web3 address already linked")
ui.Success("web3 address already linked")
return true, nil
}

fmt.Println("✓ No existing link found for this address")
ui.Success("No existing link found for this address")
return false, nil
}

func (h *handler) displayDetails() {
fmt.Println("Linking web3 key to your CRE organization")
fmt.Printf("Target : \t\t %s\n", h.settings.User.TargetName)
fmt.Printf("✔ Using Address : \t %s\n\n", h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress)
ui.Line()
ui.Title("Linking web3 key to your CRE organization")
ui.Dim(fmt.Sprintf("Target: %s", h.settings.User.TargetName))
ui.Dim(fmt.Sprintf("Owner Address: %s", h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress))
ui.Line()
}
30 changes: 18 additions & 12 deletions cmd/account/list_key/list_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/ui"
)

const queryListWorkflowOwners = `
Expand Down Expand Up @@ -88,6 +89,9 @@ type WorkflowOwner struct {
}

func (h *Handler) Execute(ctx context.Context) error {
spinner := ui.NewSpinner()
spinner.Start("Fetching workflow owners...")

req := graphql.NewRequest(queryListWorkflowOwners)

var respEnvelope struct {
Expand All @@ -97,32 +101,34 @@ func (h *Handler) Execute(ctx context.Context) error {
}

if err := h.client.Execute(ctx, req, &respEnvelope); err != nil {
spinner.Stop()
return fmt.Errorf("fetch workflow owners failed: %w", err)
}

fmt.Println("\nWorkflow owners retrieved successfully:")
spinner.Stop()
ui.Success("Workflow owners retrieved successfully")
h.logOwners("Linked Owners", respEnvelope.ListWorkflowOwners.LinkedOwners)

return nil
}

func (h *Handler) logOwners(label string, owners []WorkflowOwner) {
fmt.Println("")
ui.Line()
if len(owners) == 0 {
fmt.Printf(" No %s found\n", strings.ToLower(label))
ui.Warning(fmt.Sprintf("No %s found", strings.ToLower(label)))
return
}

fmt.Printf("%s:\n", label)
fmt.Println("")
ui.Title(label)
ui.Line()

for i, o := range owners {
fmt.Printf(" %d. %s\n", i+1, o.WorkflowOwnerLabel)
fmt.Printf(" Owner Address: \t%s\n", o.WorkflowOwnerAddress)
fmt.Printf(" Status: \t%s\n", o.VerificationStatus)
fmt.Printf(" Verified At: \t%s\n", o.VerifiedAt)
fmt.Printf(" Chain Selector: \t%s\n", o.ChainSelector)
fmt.Printf(" Contract Address:\t%s\n", o.ContractAddress)
fmt.Println("")
ui.Bold(fmt.Sprintf("%d. %s", i+1, o.WorkflowOwnerLabel))
ui.Dim(fmt.Sprintf(" Owner Address: %s", o.WorkflowOwnerAddress))
ui.Dim(fmt.Sprintf(" Status: %s", o.VerificationStatus))
ui.Dim(fmt.Sprintf(" Verified At: %s", o.VerifiedAt))
ui.Dim(fmt.Sprintf(" Chain Selector: %s", o.ChainSelector))
ui.Dim(fmt.Sprintf(" Contract Address: %s", o.ContractAddress))
ui.Line()
}
}
81 changes: 46 additions & 35 deletions cmd/account/unlink_key/unlink_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"sync"
"time"

"github.com/charmbracelet/huh"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
"github.com/machinebox/graphql"
Expand All @@ -24,10 +25,10 @@ import (
"github.com/smartcontractkit/cre-cli/internal/client/graphqlclient"
"github.com/smartcontractkit/cre-cli/internal/credentials"
"github.com/smartcontractkit/cre-cli/internal/environments"
"github.com/smartcontractkit/cre-cli/internal/prompt"
"github.com/smartcontractkit/cre-cli/internal/runtime"
"github.com/smartcontractkit/cre-cli/internal/settings"
"github.com/smartcontractkit/cre-cli/internal/types"
"github.com/smartcontractkit/cre-cli/internal/ui"
"github.com/smartcontractkit/cre-cli/internal/validation"
)

Expand Down Expand Up @@ -142,7 +143,7 @@ func (h *handler) Execute(in Inputs) error {

h.displayDetails()

fmt.Printf("Starting unlinking: owner=%s\n", in.WorkflowOwner)
ui.Dim(fmt.Sprintf("Starting unlinking: owner=%s", in.WorkflowOwner))

h.wg.Wait()
if h.wrcErr != nil {
Expand All @@ -154,20 +155,26 @@ func (h *handler) Execute(in Inputs) error {
return err
}
if !linked {
fmt.Println("Your web3 address is not linked, nothing to do")
ui.Warning("Your web3 address is not linked, nothing to do")
return nil
}

// Check if confirmation should be skipped
if !in.SkipConfirmation {
deleteWorkflows, err := prompt.YesNoPrompt(
h.stdin,
"! Warning: Unlink is a destructive action that will wipe out all workflows registered under your owner address. Do you wish to proceed?",
)
if err != nil {
ui.Warning("Unlink is a destructive action that will wipe out all workflows registered under your owner address.")
ui.Line()
var confirm bool
confirmForm := huh.NewForm(
huh.NewGroup(
huh.NewConfirm().
Title("Do you wish to proceed?").
Value(&confirm),
),
).WithTheme(ui.ChainlinkTheme())
if err := confirmForm.Run(); err != nil {
return err
}
if !deleteWorkflows {
if !confirm {
return fmt.Errorf("unlinking aborted by user")
}
}
Expand All @@ -186,7 +193,7 @@ func (h *handler) Execute(in Inputs) error {
h.log.Debug().Msg("\nRaw linking response payload:\n\n" + string(prettyResp))

if in.WorkflowRegistryContractAddress == resp.ContractAddress {
fmt.Println("Contract address validation passed")
ui.Success("Contract address validation passed")
} else {
return fmt.Errorf("contract address validation failed")
}
Expand Down Expand Up @@ -256,12 +263,15 @@ func (h *handler) unlinkOwner(owner string, resp initiateUnlinkingResponse) erro

switch txOut.Type {
case client.Regular:
fmt.Println("Transaction confirmed")
fmt.Printf("View on explorer: \033]8;;%s/tx/%s\033\\%s/tx/%s\033]8;;\033\\\n", h.environmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash, h.environmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash)
fmt.Println("\n[OK] web3 address unlinked from your CRE organization successfully")
fmt.Println("\nNote: Unlinking verification may take up to 60 seconds.")
fmt.Println(" You must wait for verification to complete before linking this address again.")
fmt.Println("\n→ This address can no longer deploy workflows on behalf of your organization")
ui.Success("Transaction confirmed")
ui.URL(fmt.Sprintf("%s/tx/%s", h.environmentSet.WorkflowRegistryChainExplorerURL, txOut.Hash))
ui.Line()
ui.Success("web3 address unlinked from your CRE organization successfully")
ui.Line()
ui.Dim("Note: Unlinking verification may take up to 60 seconds.")
ui.Dim(" You must wait for verification to complete before linking this address again.")
ui.Line()
ui.Bold("This address can no longer deploy workflows on behalf of your organization")

case client.Raw:
selector, err := strconv.ParseUint(resp.ChainSelector, 10, 64)
Expand All @@ -275,20 +285,19 @@ func (h *handler) unlinkOwner(owner string, resp initiateUnlinkingResponse) erro
return err
}

fmt.Println("")
fmt.Println("Ownership unlinking initialized successfully!")
fmt.Println("")
fmt.Println("Next steps:")
fmt.Println("")
fmt.Println(" 1. Submit the following transaction on the target chain:")
fmt.Println("")
fmt.Printf(" Chain: %s\n", ChainName)
fmt.Printf(" Contract Address: %s\n", resp.ContractAddress)
fmt.Println("")
fmt.Println(" 2. Use the following transaction data:")
fmt.Println("")
fmt.Printf(" %s\n", resp.TransactionData)
fmt.Println("")
ui.Line()
ui.Success("Ownership unlinking initialized successfully!")
ui.Line()
ui.Bold("Next steps:")
ui.Line()
ui.Print(" 1. Submit the following transaction on the target chain:")
ui.Dim(fmt.Sprintf(" Chain: %s", ChainName))
ui.Dim(fmt.Sprintf(" Contract Address: %s", resp.ContractAddress))
ui.Line()
ui.Print(" 2. Use the following transaction data:")
ui.Line()
ui.Code(fmt.Sprintf(" %s", resp.TransactionData))
ui.Line()

case client.Changeset:
chainSelector, err := settings.GetChainSelectorByChainName(h.environmentSet.WorkflowRegistryChainName)
Expand All @@ -297,7 +306,7 @@ func (h *handler) unlinkOwner(owner string, resp initiateUnlinkingResponse) erro
}
mcmsConfig, err := settings.GetMCMSConfig(h.settings, chainSelector)
if err != nil {
fmt.Println("\nMCMS config not found or is incorrect, skipping MCMS config in changeset")
ui.Warning("MCMS config not found or is incorrect, skipping MCMS config in changeset")
}
cldSettings := h.settings.CLDSettings
changesets := []types.Changeset{
Expand Down Expand Up @@ -328,7 +337,7 @@ func (h *handler) unlinkOwner(owner string, resp initiateUnlinkingResponse) erro
h.log.Warn().Msgf("Unsupported transaction type: %s", txOut.Type)
}

fmt.Println("Unlinked successfully")
ui.Success("Unlinked successfully")
return nil
}

Expand All @@ -344,7 +353,9 @@ func (h *handler) checkIfAlreadyLinked() (bool, error) {
}

func (h *handler) displayDetails() {
fmt.Println("Unlinking web3 key from your CRE organization")
fmt.Printf("Target : \t\t %s\n", h.settings.User.TargetName)
fmt.Printf("✔ Using Address : \t %s\n\n", h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress)
ui.Line()
ui.Title("Unlinking web3 key from your CRE organization")
ui.Dim(fmt.Sprintf("Target: %s", h.settings.User.TargetName))
ui.Dim(fmt.Sprintf("Owner Address: %s", h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress))
ui.Line()
}
Loading
Loading