Sitelet https://github.com/1jehuang/jcode/issues/1052
Skip to content

macOS: TUI login reports keys saved to ~/.config/jcode/, but writes them to ~/Library/Application Support/jcode/ #1052

Description

@uncle9x9

Summary

On macOS, the TUI API-key login flow reports that it saved the key to
~/.config/jcode/<provider>.env, but the file is actually written to
~/Library/Application Support/jcode/<provider>.env. The directory in that message is a
hardcoded string literal, not the resolved config directory.

The save itself is correct and the key works. Only the message is wrong — but it is wrong
in the one place a user looks to confirm setup succeeded, so it reads as "jcode failed to
create its config files."

Environment

  • jcode v0.80.0 (c3ccfa0), macOS 15.5, aarch64
  • Reproduced with OpenRouter; affects every provider that goes through the TUI API-key flow

Steps to reproduce

  1. On macOS, run jcode and use the TUI login flow to add an OpenRouter API key.

  2. Observe the success message:

    OpenRouter API key saved.
    
    Stored at ~/.config/jcode/openrouter.env.
    You can now use /model to switch to OpenRouter models. If the model list looks stale,
    run /refresh-model-list.
    
  3. ls ~/.config/jcode/ → no such directory.

  4. ls ~/Library/Application\ Support/jcode/openrouter.env → the key is here, mode 0600.

Root cause

storage::app_config_dir() (crates/jcode-storage/src/lib.rs:189) resolves the config
directory properly:

pub fn app_config_dir() -> Result<PathBuf> {
    if let Ok(path) = std::env::var("JCODE_HOME") {
        return Ok(PathBuf::from(path).join("config").join("jcode"));
    }
    let config_dir = dirs::config_dir()
        .ok_or_else(|| anyhow::anyhow!("No config directory found"))?;
    Ok(config_dir.join("jcode"))
}

On macOS dirs::config_dir() is ~/Library/Application Support, not ~/.config.

But the TUI success message hardcodes the directory and interpolates only the filename —
crates/jcode-tui/src/tui/app/auth.rs:2450:

message: format!(
    "{}.\n\n\
     Stored at ~/.config/jcode/{}.\n\
     {}{}",
    saved_label, env_file, guidance, model_hint
),

The CLI login path already does this correctly — src/cli/login.rs:625 formats the
resolved directory:

eprintln!(
    "Stored at {}",
    crate::storage::app_config_dir()?.join("openrouter.env").display()
);

So jcode login --provider openrouter prints the truth while the TUI flow does not.

Other affected sites

Same hardcoded literal, non-test:

  • crates/jcode-tui/src/tui/app/auth.rs:2450 — generic API-key login success (OpenRouter, OpenAI, Anthropic, Bedrock, custom profiles)
  • crates/jcode-tui/src/tui/app/auth.rs:2646 — Cursor
  • crates/jcode-tui/src/tui/app/auth.rs:3441 — Azure OpenAI
  • crates/jcode-base/src/auth/mod.rs:751, 766, 788, 807, 828, 869, 880config_source(...) display labels in the auth inventory
  • crates/jcode-base/src/auth/azure.rs:72 — "not found in environment or ~/.config/jcode/{}" error text

The runtime "key not found" error carries the same string; see the log line quoted in #849
(OPENROUTER_API_KEY not found in environment or /home/xxx/.config/jcode/openrouter.env).
On Linux that path happens to be correct, which is probably why this has gone unreported.

Also wrong when JCODE_HOME is set

Independent of platform: with JCODE_HOME set, keys go to
$JCODE_HOME/config/jcode/<provider>.env, but the message still claims ~/.config/jcode/.

Expected

The message names the directory the key was actually written to, on every platform and
under JCODE_HOME.

Suggested fix

Format these messages from app_config_dir() the way src/cli/login.rs:625 already does,
rather than embedding the Linux path as a literal. The config_source call sites in
auth/mod.rs take a path_label argument that could be derived the same way.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions