fix(config): make project .env override global config as documented

src/config.ts loaded ~/.config/gmgn/.env with override:true and then the
cwd .env without override, so the global file always won — the opposite
of what Readme, Readme.zh, CLAUDE.md and the gmgn-cooking skill promise.
A user who updated GMGN_PRIVATE_KEY in a project .env kept signing with
the stale global key and got AUTH_SIGNATURE_INVALID.

Load the cwd .env first and the global file second, both without
override, so precedence is shell env > project .env > global .env.
`config --check` reuses the same loader instead of its own copy.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zhongsongzhi-cmyk
2026-10-25 23:04:53 +08:00
co-authored by Claude
parent b0f4ac7135
commit 227011b834
5 changed files with 15 additions and 8 deletions
+2
View File
@@ -97,6 +97,8 @@ Config lookup order (project overrides global):
1. `~/.config/gmgn/.env` — global config, set once for all projects
2. `.env` in the current working directory — project-level override (takes precedence)
Variables already exported in the shell environment override both files.
Required variables:
- `GMGN_API_KEY` — apply at https://gmgn.ai/ai
- `GMGN_PRIVATE_KEY` — PEM content, required for swap/order commands only
+1 -1
View File
@@ -329,7 +329,7 @@ cp .env.example .env
# Edit .env and fill in your values
```
Config lookup order: `~/.config/gmgn/.env` → project `.env` (project takes precedence).
Config lookup order: `~/.config/gmgn/.env` → project `.env` (project takes precedence). Variables already exported in your shell override both files.
## 5. Try in AI Clients
+1 -1
View File
@@ -333,7 +333,7 @@ cp .env.example .env
# 编辑 .env,填入实际值
```
配置加载顺序:`~/.config/gmgn/.env` → 项目 `.env`(项目级优先)。
配置加载顺序:`~/.config/gmgn/.env` → 项目 `.env`(项目级优先)。Shell 中已 export 的同名环境变量优先级最高,会覆盖两个文件。
## 5. 在 AI 客户端中使用
+2 -3
View File
@@ -1,10 +1,10 @@
import { config as loadDotenv } from "dotenv";
import { Command } from "commander";
import * as crypto from "crypto";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { execSync } from "child_process";
import { loadEnvFiles } from "../config.js";
const GMGN_CONFIG_DIR = path.join(os.homedir(), ".config", "gmgn");
const KEYPAIR_FILE = path.join(GMGN_CONFIG_DIR, "keypair.pem");
@@ -116,8 +116,7 @@ export function registerConfigCommands(program: Command): void {
const lang = detectLang();
if (opts.check) {
loadDotenv({ path: ENV_FILE, override: true });
loadDotenv();
loadEnvFiles();
process.exit(process.env.GMGN_API_KEY ? 0 : 1);
}
+9 -3
View File
@@ -28,9 +28,15 @@ function enforceCredentialFilePermissions(path: string): void {
enforceCredentialFilePermissions(GLOBAL_ENV_PATH);
// Load global config first (~/.config/gmgn/.env, takes precedence), then project .env (supplements only)
loadDotenv({ path: GLOBAL_ENV_PATH, override: true });
loadDotenv();
// Precedence: shell environment > project .env (cwd) > global ~/.config/gmgn/.env.
// dotenv never overwrites a variable that is already set, so the higher-priority
// source must be loaded first and the global file only fills in what is missing.
export function loadEnvFiles(): void {
loadDotenv();
loadDotenv({ path: GLOBAL_ENV_PATH });
}
loadEnvFiles();
export interface Config {
apiKey: string;