Skip to content

feat: support multiple accounts and instances - #44

Open
scmmishra wants to merge 12 commits into
mainfrom
feat/multi-account
Open

scmmishra wants to merge 12 commits into
mainfrom
feat/multi-account

Conversation

@scmmishra

@scmmishra scmmishra commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Adds support for multiple accounts and multiple Chatwoot instances (production, staging, self-hosted). Log in once per instance and every account you belong to is registered under a short name.

chatwoot auth login staging.chatwoot.com       # registers every account on that instance
chatwoot accounts                              # list them (* = default)
chatwoot @acme convs                           # run a command in another account
chatwoot use acme                              # change the saved default
chatwoot https://app.chatwoot.com/app/accounts/1/conversations/4521 reply "on it"

What's new

  • Login registers every account. auth login [url] accepts a bare host, a base URL, or any dashboard link, asks only for the token, and registers each account from the profile's accounts list. The account ID prompt remains only for Chatwoot versions whose profile doesn't list accounts.
  • Accounts have names. They're named after the account (globex-inc). When a name is taken, the host is added for another instance (chatwoot-staging) and the user's name for a second user on the same instance (acme-test-agent). Names never change once given.
  • Picking the account: a pasted link, then @name or -a name (unique prefixes work), then CHATWOOT_ACCOUNT, then the default. -a <number> still means that account ID on the default's instance.
  • Pasted links: conversation links (including inbox, label, team, mention and custom-view scoped ones), contact links and inbox links map to conv, contact and inbox. A link to an instance you aren't logged in to offers to log in when run in a terminal.
  • New commands: accounts (with --refresh and rename), use, and auth logout <url>. config view now shows every account.
  • Safety:
    • Writes to a non-default account print → <name> to stderr first.
    • A 401 explains which login was rejected and how to log in again.
    • Conversation locks are now per account.
    • An unknown @name refreshes accounts once before failing.

Upgrading needs no action

  • Old single-account configs are converted in memory and saved atomically, with the original kept as config.yaml.bak. If that save fails, the command still runs and the next run retries.
  • The old keyring entry is copied to the new per-login entry, never moved or deleted.
  • On the first successful run, the migrated account gets its real name and the user sees a one-time list of their other accounts. It goes to stderr, only in a terminal, and never in --json or --quiet output.

Downgrading: v0.7.0 can't read the new config format. Restoring config.yaml.bak goes back.

Also fixed

  • Global flags with values before the noun (-a acme conv 123 reply …) no longer break the id-first grammar.
  • -a <id> works with keyring logins. Previously the saved token was tied to one account ID, so it only worked with CHATWOOT_API_KEY.
  • Detail views align values when a key is as long as the longest key.

Testing

  • Each phase was written test-first. go test ./... passes, as do the config tests with -tags dev.
  • I also ran main() end to end against local fake Chatwoot servers with a mock keyring: login to two instances, accounts, use, @name, upgrading from an old config, the → name line, the 401 hint and link routing.
  • Not yet tested: a real Chatwoot instance, the real macOS keychain, and the offered login for links in a real terminal.

Docs: chatwoot/docs#584 (new "Multiple accounts" page plus updates across the CLI pages).

Config moves to a versioned schema holding every registered account plus
the default. Version 1 files convert in memory, save atomically with a
.bak copy, and never block a command if the save fails. Tokens are stored
per login (base URL + user) and the pre-upgrade keyring entry is copied,
not moved.
A leading @name selects an account by name or unique prefix. -a takes a
name too, and a number still means that account ID on the default login.
Global flags with values no longer break the id-first rewrite.
auth login takes an optional URL (bare host or any dashboard link), asks
only for the token, and registers every account the user can see. New
commands: accounts (list, --refresh, rename), use, and auth logout <url>.
An unknown @name refreshes saved logins once before failing.
A dashboard link can replace the noun and id: conversation links (plain or
inbox/label/team/mention/custom-view scoped), contact links, and inbox
links map to conv, contact, and inbox. The link's account overrides any
other selector. A link to an instance without a login offers to log in
when run in a terminal and explains how otherwise.
… writes

The first run after upgrading names the migrated account and lists the
user's other accounts once (stderr, terminals only). Writes to a
non-default account print the target to stderr, a 401 explains how to
log in again, and conversation locks are scoped per account.
config view only showed the default account. It now shows the config
path, the default, and each account with its instance, user, credential
status, and help center default, as text or JSON. With accounts but no
default it lists them instead of saying no configuration exists.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T11:03:23.398331Z 29349b3 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 64956571f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/cmd/auth.go
Comment thread internal/config/config.go
Covers every SDK service endpoint (method, path, body, decoding) and
error path, all output formats and escape-sequence branches, and the
lock's config-dir path and filesystem failures.
On Chatwoot versions whose profile omits the accounts list, login merged
the typed account ID as if it were the full membership list and removed
the user's other accounts. It now adds it without removing anything.

Base URLs are stored and compared with a lowercase scheme and host, so a
v1 config or keyring entry written with uppercase host characters still
matches pasted links and resolves its token.
Runs every list, view, and write command end to end against a fake
Chatwoot API in text, JSON, and quiet modes, including empty results and
API failures. Also covers version --check, api request bodies, logout,
the default-account prompt, and help center defaults. latestReleaseURL
becomes a var so version --check can be pointed at a test server.
@scmmishra

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29349b33bc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/config/accounts.go Outdated
Comment thread internal/config/accounts.go
Comment thread internal/config/config.go
On Chatwoot versions without an accounts list, logging in after an
upgrade added a duplicate beside the migrated account (saved without a
user ID) and left the unusable one as the default. MergeAccounts now
adopts it, like SyncAccounts does.

A config written by a newer CLI version is now refused with an upgrade
hint instead of being read as version 2, which dropped unknown fields
that the next save would then lose.
@github-actions

Copy link
Copy Markdown

Merging this branch will increase overall coverage

Impacted Packages Coverage Δ 🤖
github.com/chatwoot/cli/cmd/chatwoot 81.25% (+14.58%) 🎉
github.com/chatwoot/cli/internal/cmd 94.11% (+45.61%) 🌟
github.com/chatwoot/cli/internal/config 86.00% (+12.44%) 🎉
github.com/chatwoot/cli/internal/lock 96.55% (+19.63%) 🎉
github.com/chatwoot/cli/internal/output 100.00% (+21.84%) 🌟
github.com/chatwoot/cli/internal/sdk 97.05% (+40.80%) 🌟

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/chatwoot/cli/cmd/chatwoot/main.go 81.25% (+14.58%) 80 (+38) 65 (+37) 15 (+1) 🎉
github.com/chatwoot/cli/internal/cmd/accounts.go 91.45% (+91.45%) 117 (+117) 107 (+107) 10 (+10) 🌟
github.com/chatwoot/cli/internal/cmd/app.go 86.27% (+11.27%) 51 (+35) 44 (+32) 7 (+3) 🎉
github.com/chatwoot/cli/internal/cmd/auth.go 86.15% (+14.44%) 195 (+96) 168 (+97) 27 (-1) 🎉
github.com/chatwoot/cli/internal/cmd/cli.go 0.00% (ø) 0 0 0
github.com/chatwoot/cli/internal/cmd/config.go 89.23% (+41.40%) 65 (+42) 58 (+47) 7 (-5) 🌟
github.com/chatwoot/cli/internal/cmd/conversation.go 99.57% (+52.41%) 231 (+2) 230 (+122) 1 (-120) 🌟
github.com/chatwoot/cli/internal/cmd/help_center.go 96.52% (+21.74%) 115 111 (+25) 4 (-25) 🌟
github.com/chatwoot/cli/internal/cmd/notices.go 91.11% (+91.11%) 45 (+45) 41 (+41) 4 (+4) 🌟
github.com/chatwoot/cli/internal/cmd/version.go 100.00% (+100.00%) 43 43 (+43) 0 (-43) 🌟
github.com/chatwoot/cli/internal/config/accounts.go 90.48% (+90.48%) 147 (+147) 133 (+133) 14 (+14) 🌟
github.com/chatwoot/cli/internal/config/config.go 74.31% (+10.21%) 109 (+70) 81 (+56) 28 (+14) 🎉
github.com/chatwoot/cli/internal/config/credentials.go 81.25% (ø) 80 (+32) 65 (+26) 15 (+6)
github.com/chatwoot/cli/internal/config/resolve.go 95.08% (+95.08%) 61 (+61) 58 (+58) 3 (+3) 🌟
github.com/chatwoot/cli/internal/config/url.go 94.34% (+94.34%) 53 (+53) 50 (+50) 3 (+3) 🌟
github.com/chatwoot/cli/internal/lock/lock.go 95.83% (+24.40%) 24 (+3) 23 (+8) 1 (-5) 🌟
github.com/chatwoot/cli/internal/output/output.go 100.00% (+21.84%) 87 87 (+19) 0 (-19) 🌟
github.com/chatwoot/cli/internal/sdk/client.go 97.69% (+37.23%) 130 (+1) 127 (+49) 3 (-48) 🌟

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

Changed unit test files

  • github.com/chatwoot/cli/cmd/chatwoot/main_test.go
  • github.com/chatwoot/cli/internal/cmd/accounts_test.go
  • github.com/chatwoot/cli/internal/cmd/api_test.go
  • github.com/chatwoot/cli/internal/cmd/app_test.go
  • github.com/chatwoot/cli/internal/cmd/auth_test.go
  • github.com/chatwoot/cli/internal/cmd/commands_test.go
  • github.com/chatwoot/cli/internal/cmd/config_test.go
  • github.com/chatwoot/cli/internal/cmd/conversation_lock_test.go
  • github.com/chatwoot/cli/internal/cmd/conversation_test.go
  • github.com/chatwoot/cli/internal/cmd/help_center_test.go
  • github.com/chatwoot/cli/internal/config/accounts_test.go
  • github.com/chatwoot/cli/internal/config/config_test.go
  • github.com/chatwoot/cli/internal/config/credentials_test.go
  • github.com/chatwoot/cli/internal/config/resolve_test.go
  • github.com/chatwoot/cli/internal/config/url_test.go
  • github.com/chatwoot/cli/internal/lock/lock_test.go
  • github.com/chatwoot/cli/internal/output/output_test.go
  • github.com/chatwoot/cli/internal/sdk/errors_test.go
  • github.com/chatwoot/cli/internal/sdk/services_test.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant