From e8af4b88b5a2b2f7c98d01a56f014178d85652be Mon Sep 17 00:00:00 2001 From: gina888666 Date: Wed, 24 Jun 2026 13:53:48 +0800 Subject: [PATCH 01/14] feat(config): add gmgn-cli config command for API Key onboarding Add a new `gmgn-cli config` command that automates Ed25519 key pair generation for new users. The command reuses an existing keypair.pem if present, or generates a new one, then outputs a pre-filled API Key creation link with the public key embedded. Guidance text is output in the user's system locale (zh-CN, zh-TW, or English). All 6 SKILL.md files are updated with a BEFORE RUNNING ANY COMMAND block that detects missing GMGN_API_KEY, triggers gmgn-cli config, and instructs the Agent to write both GMGN_API_KEY and GMGN_PRIVATE_KEY into ~/.config/gmgn/.env once the user provides their API Key. Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 11 +++++++ skills/gmgn-market/SKILL.md | 11 +++++++ skills/gmgn-portfolio/SKILL.md | 11 +++++++ skills/gmgn-swap/SKILL.md | 11 +++++++ skills/gmgn-token/SKILL.md | 11 +++++++ skills/gmgn-track/SKILL.md | 11 +++++++ src/commands/config.ts | 56 ++++++++++++++++++++++++++++++++++ src/index.ts | 2 ++ 8 files changed, 124 insertions(+) create mode 100644 src/commands/config.ts diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index 1e5eb31..ac133fd 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -6,6 +6,17 @@ metadata: cliHelp: "gmgn-cli cooking --help" --- +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** + +```bash +PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp +echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env +chmod 600 ~/.config/gmgn/.env +``` + **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.** **IMPORTANT: Do NOT guess field names or values. When a field's meaning is unclear, look it up in the Response Fields sections below before using it.** diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index 89b04cb..abcf4f5 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -6,6 +6,17 @@ metadata: cliHelp: "gmgn-cli market --help" --- +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** + +```bash +PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp +echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env +chmod 600 ~/.config/gmgn/.env +``` + **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** **IMPORTANT: Do NOT guess field names or values. When a field's meaning is unclear, look it up in the Response Fields sections below before using it.** diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index a9e2cef..a2fa8fd 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -6,6 +6,17 @@ metadata: cliHelp: "gmgn-cli portfolio --help" --- +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** + +```bash +PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp +echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env +chmod 600 ~/.config/gmgn/.env +``` + **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** **⚠️ IPv6 NOT SUPPORTED: If you get a `401` or `403` error and credentials look correct, check for IPv6 immediately: (1) list all network interfaces and their IPv6 addresses — run `ifconfig | grep inet6` (macOS) or `ip addr show | grep inet6` (Linux); (2) send a test request to `https://ipv6.icanhazip.com` — if the response is an IPv6 address, outbound traffic is going via IPv6. Tell the user immediately: "Please disable IPv6 on your network interface — gmgn-cli commands only work over IPv4."** diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index eef9832..53ebb03 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -6,6 +6,17 @@ metadata: cliHelp: "gmgn-cli swap --help" --- +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** + +```bash +PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp +echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env +chmod 600 ~/.config/gmgn/.env +``` + **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all swap operations must go through the CLI. The CLI handles signing and submission automatically.** **IMPORTANT: Do NOT guess field names or values. When a field's meaning is unclear, look it up in the Response Fields sections below before using it.** diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index 3ab13e0..57c61f5 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -6,6 +6,17 @@ metadata: cliHelp: "gmgn-cli token --help" --- +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** + +```bash +PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp +echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env +chmod 600 ~/.config/gmgn/.env +``` + **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** **⚠️ IPv6 NOT SUPPORTED: If you get a `401` or `403` error and credentials look correct, check for IPv6 immediately: (1) list all network interfaces and their IPv6 addresses — run `ifconfig | grep inet6` (macOS) or `ip addr show | grep inet6` (Linux); (2) send a test request to `https://ipv6.icanhazip.com` — if the response is an IPv6 address, outbound traffic is going via IPv6. Tell the user immediately: "Please disable IPv6 on your network interface — gmgn-cli commands only work over IPv4."** diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index 2c18c99..5f32903 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -6,6 +6,17 @@ metadata: cliHelp: "gmgn-cli track --help" --- +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** + +```bash +PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp +echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env +chmod 600 ~/.config/gmgn/.env +``` + **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** **IMPORTANT: Do NOT guess field names or values. When a field's meaning is unclear, look it up in the Response Fields sections below before using it.** diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..f9acbd4 --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,56 @@ +import { Command } from "commander"; +import * as crypto from "crypto"; +import * as fs from "fs"; +import * as path from "path"; +import * as os from "os"; + +const GMGN_CONFIG_DIR = path.join(os.homedir(), ".config", "gmgn"); +const KEYPAIR_FILE = path.join(GMGN_CONFIG_DIR, "keypair.pem"); +const GMGN_API_URL = "https://gmgn.ai/ai/generateapi"; + +export function registerConfigCommands(program: Command): void { + program + .command("config") + .description("Generate an Ed25519 key pair and output a pre-filled GMGN API Key creation link") + .action(async () => { + fs.mkdirSync(GMGN_CONFIG_DIR, { recursive: true }); + + let publicPem: string; + + if (fs.existsSync(KEYPAIR_FILE)) { + // Reuse existing key pair + const content = fs.readFileSync(KEYPAIR_FILE, "utf-8"); + const match = content.match(/(-----BEGIN PUBLIC KEY-----[\s\S]+?-----END PUBLIC KEY-----)/); + if (!match) { + console.error("Error: keypair.pem exists but public key could not be parsed. Delete ~/.config/gmgn/keypair.pem and try again."); + process.exit(1); + } + publicPem = match[1] + "\n"; + } else { + // Generate new Ed25519 key pair + const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519"); + const privatePem = privateKey.export({ type: "pkcs8", format: "pem" }) as string; + publicPem = publicKey.export({ type: "spki", format: "pem" }) as string; + + const entry = `# Private Key\n${privatePem}\n# Public Key\n${publicPem}\n`; + fs.writeFileSync(KEYPAIR_FILE, entry, { mode: 0o600 }); + } + + const link = `${GMGN_API_URL}?pbk=${encodeURIComponent(publicPem)}`; + + const locale = (process.env.LANG ?? process.env.LC_ALL ?? process.env.LC_MESSAGES ?? "").toLowerCase(); + let message: string; + if (locale.startsWith("zh_tw") || locale.startsWith("zh_hk")) { + message = "請點擊連結建立你的 GMGN API Key,完成後將 Key 發給我,我來幫你完成配置:"; + } else if (locale.startsWith("zh")) { + message = "请点击链接创建你的 GMGN API Key,完成后将 Key 发给我,我来帮你完成配置:"; + } else { + message = "Please click the link below to create your GMGN API Key. Once created, send me the API Key and I will finish the configuration:"; + } + + console.log(""); + console.log(message); + console.log(link); + console.log(""); + }); +} diff --git a/src/index.ts b/src/index.ts index b3897f2..9e7d736 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ import { registerPortfolioCommands } from "./commands/portfolio.js"; import { registerTrackCommands } from "./commands/track.js"; import { registerSwapCommands } from "./commands/swap.js"; import { registerCookingCommands } from "./commands/cooking.js"; +import { registerConfigCommands } from "./commands/config.js"; const proxy = process.env.HTTPS_PROXY ?? process.env.https_proxy ?? process.env.HTTP_PROXY ?? process.env.http_proxy; @@ -60,6 +61,7 @@ registerPortfolioCommands(program); registerTrackCommands(program); registerSwapCommands(program); registerCookingCommands(program); +registerConfigCommands(program); program.parseAsync().catch((err) => { console.error(`[gmgn-cli] ${err.message}`); From 5453373ffabe806d41454c6717f607e7a1805ef5 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Wed, 24 Jun 2026 14:36:03 +0800 Subject: [PATCH 02/14] fix(skills): fix private key extraction command in BEFORE RUNNING ANY COMMAND Replace awk+echo with python3+printf to correctly serialize the PEM private key as a single-line \n-escaped value when writing to .env. The awk approach produced multi-line output that broke .env parsing. Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 6 +++--- skills/gmgn-market/SKILL.md | 6 +++--- skills/gmgn-portfolio/SKILL.md | 6 +++--- skills/gmgn-swap/SKILL.md | 6 +++--- skills/gmgn-token/SKILL.md | 6 +++--- skills/gmgn-track/SKILL.md | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index ac133fd..d22a18d 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -9,10 +9,10 @@ metadata: **BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** ```bash -PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index abcf4f5..442ccf4 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -9,10 +9,10 @@ metadata: **BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** ```bash -PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index a2fa8fd..921bd98 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -9,10 +9,10 @@ metadata: **BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** ```bash -PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 53ebb03..6aecba6 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -9,10 +9,10 @@ metadata: **BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** ```bash -PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index 57c61f5..c08c707 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -9,10 +9,10 @@ metadata: **BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** ```bash -PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index 5f32903..b16f469 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -9,10 +9,10 @@ metadata: **BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** ```bash -PRIVATE_KEY=$(awk '/-----BEGIN PRIVATE KEY-----/{p=1} p{printf "%s\\n", $0} /-----END PRIVATE KEY-----/{p=0}' ~/.config/gmgn/keypair.pem) +PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -echo "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -echo "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp +printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env chmod 600 ~/.config/gmgn/.env ``` From cc2fc0ed00957a3051205a31c63c531424a4f6ce Mon Sep 17 00:00:00 2001 From: gina888666 Date: Wed, 24 Jun 2026 19:08:27 +0800 Subject: [PATCH 03/14] fix(skills): replace python3/awk with node for .env config in all SKILL.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use node -e to write GMGN_API_KEY and GMGN_PRIVATE_KEY into .env — node is guaranteed to be present since gmgn-cli requires Node.js. Also clarify that the same config step applies when the user explicitly runs gmgn-cli config and sends back an API Key. Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 9 ++------- skills/gmgn-market/SKILL.md | 9 ++------- skills/gmgn-portfolio/SKILL.md | 9 ++------- skills/gmgn-swap/SKILL.md | 9 ++------- skills/gmgn-token/SKILL.md | 9 ++------- skills/gmgn-track/SKILL.md | 9 ++------- 6 files changed, 12 insertions(+), 42 deletions(-) diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index d22a18d..680f2df 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -6,15 +6,10 @@ metadata: cliHelp: "gmgn-cli cooking --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** ```bash -PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") -grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp -mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env -chmod 600 ~/.config/gmgn/.env +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" ``` **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.** diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index 442ccf4..64a68c4 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -6,15 +6,10 @@ metadata: cliHelp: "gmgn-cli market --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** ```bash -PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") -grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp -mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env -chmod 600 ~/.config/gmgn/.env +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" ``` **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index 921bd98..b103600 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -6,15 +6,10 @@ metadata: cliHelp: "gmgn-cli portfolio --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** ```bash -PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") -grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp -mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env -chmod 600 ~/.config/gmgn/.env +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" ``` **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 6aecba6..8c08c32 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -6,15 +6,10 @@ metadata: cliHelp: "gmgn-cli swap --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** ```bash -PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") -grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp -mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env -chmod 600 ~/.config/gmgn/.env +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" ``` **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all swap operations must go through the CLI. The CLI handles signing and submission automatically.** diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index c08c707..2c633ad 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -6,15 +6,10 @@ metadata: cliHelp: "gmgn-cli token --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** ```bash -PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") -grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp -mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env -chmod 600 ~/.config/gmgn/.env +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" ``` **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index b16f469..065ad74 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -6,15 +6,10 @@ metadata: cliHelp: "gmgn-cli track --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following commands, then proceed:** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** ```bash -PRIVATE_KEY=$(python3 -c "import re; c=open('$HOME/.config/gmgn/keypair.pem').read(); m=re.search(r'(-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----)', c, re.DOTALL); print(m.group(1).replace('\n', '\\\\n') if m else '', end='')") -grep -v "^GMGN_API_KEY=\|^GMGN_PRIVATE_KEY=" ~/.config/gmgn/.env 2>/dev/null > /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_API_KEY=" >> /tmp/gmgn_env_tmp -printf '%s\n' "GMGN_PRIVATE_KEY=$PRIVATE_KEY" >> /tmp/gmgn_env_tmp -mv /tmp/gmgn_env_tmp ~/.config/gmgn/.env -chmod 600 ~/.config/gmgn/.env +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" ``` **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** From ec35707d1522c1aa0c3ee781cfdf6f8ccb63b5b3 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Wed, 24 Jun 2026 20:20:29 +0800 Subject: [PATCH 04/14] feat(config): extract setup flow into gmgn-config skill; simplify CLI output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add skills/gmgn-config/SKILL.md with complete setup flow: key pair generation, credential writing, and verification via track follow-wallet. Verification result is output in the user's conversation language. - Simplify all 6 SKILL.md BEFORE RUNNING ANY COMMAND to one line that delegates to gmgn-config skill, removing duplicate logic. - Remove locale detection from CLI — output only the link; Agent handles all user-facing guidance in the conversation language. Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-config/SKILL.md | 56 ++++++++++++++++++++++++++++++++++ skills/gmgn-cooking/SKILL.md | 6 +--- skills/gmgn-market/SKILL.md | 6 +--- skills/gmgn-portfolio/SKILL.md | 6 +--- skills/gmgn-swap/SKILL.md | 6 +--- skills/gmgn-token/SKILL.md | 6 +--- skills/gmgn-track/SKILL.md | 6 +--- src/commands/config.ts | 13 -------- 8 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 skills/gmgn-config/SKILL.md diff --git a/skills/gmgn-config/SKILL.md b/skills/gmgn-config/SKILL.md new file mode 100644 index 0000000..73c28e3 --- /dev/null +++ b/skills/gmgn-config/SKILL.md @@ -0,0 +1,56 @@ +--- +name: gmgn-config +description: GMGN API Key and key pair setup. Use this skill when GMGN_API_KEY is not configured, or when the user explicitly runs gmgn-cli config. Handles key pair generation, API Key configuration, and verification. +argument-hint: "" +metadata: + cliHelp: "gmgn-cli config --help" +--- + +**All output to the user must be in the same language as the current conversation.** + +## When to use this skill + +- Triggered automatically by other GMGN skills when `GMGN_API_KEY` is not found in `/.env` or `~/.config/gmgn/.env` +- Also applies when the user explicitly runs `gmgn-cli config` + +## Setup flow + +### Step 1 — Generate key pair and show the link + +Run: + +```bash +gmgn-cli config +``` + +The command outputs a pre-filled link. Show the link to the user and ask them to click it to create their GMGN API Key. Tell the user to send back the API Key once it is created. + +### Step 2 — Write credentials to .env + +Once the user sends back the API Key, run the following command to write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env`: + +```bash +node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" +``` + +### Step 3 — Verify configuration + +Run the following command to verify that the API Key and local key pair match: + +```bash +gmgn-cli track follow-wallet --chain sol +``` + +- **Success** — Tell the user (in the conversation language): + - 中文:配置验证成功,可以开始使用了。 + - 繁體:配置驗證成功,可以開始使用了。 + - English: Configuration verified successfully. You are ready to use GMGN. + +- **Failure** — Tell the user (in the conversation language): + - 中文:配置验证失败:API Key 与本地密钥不匹配。\n请确认:1. API Key 是否填写正确;2. 创建 API Key 时,是否使用的是页面自动填入的公钥。 + - 繁體:配置驗證失敗:API Key 與本地密鑰不匹配。\n請確認:1. API Key 是否填寫正確;2. 創建 API Key 時,是否使用的是頁面自動填入的公鑰。 + - English: Configuration verification failed: API Key does not match the local key.\nPlease confirm: 1. Whether the API Key was entered correctly; 2. Whether the public key auto-filled on the page was used when creating the API Key. + +### Step 4 — Continue + +After successful verification, proceed with the user's original request. diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index 680f2df..faca6c7 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -6,11 +6,7 @@ metadata: cliHelp: "gmgn-cli cooking --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.** diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index 64a68c4..3752841 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -6,11 +6,7 @@ metadata: cliHelp: "gmgn-cli market --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index b103600..b2a098f 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -6,11 +6,7 @@ metadata: cliHelp: "gmgn-cli portfolio --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 8c08c32..8621a49 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -6,11 +6,7 @@ metadata: cliHelp: "gmgn-cli swap --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all swap operations must go through the CLI. The CLI handles signing and submission automatically.** diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index 2c633ad..bedd1b0 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -6,11 +6,7 @@ metadata: cliHelp: "gmgn-cli token --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index 065ad74..67d21a5 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -6,11 +6,7 @@ metadata: cliHelp: "gmgn-cli track --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and run `gmgn-cli config` first — this generates an Ed25519 key pair and outputs a pre-filled link. Show the link to the user and ask them to create their API Key. Once the user sends back the API Key, write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env` using the following command, then proceed. Also apply this same configuration step whenever the user explicitly runs `gmgn-cli config` and sends back an API Key.** - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/src/commands/config.ts b/src/commands/config.ts index f9acbd4..18705a9 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -38,19 +38,6 @@ export function registerConfigCommands(program: Command): void { const link = `${GMGN_API_URL}?pbk=${encodeURIComponent(publicPem)}`; - const locale = (process.env.LANG ?? process.env.LC_ALL ?? process.env.LC_MESSAGES ?? "").toLowerCase(); - let message: string; - if (locale.startsWith("zh_tw") || locale.startsWith("zh_hk")) { - message = "請點擊連結建立你的 GMGN API Key,完成後將 Key 發給我,我來幫你完成配置:"; - } else if (locale.startsWith("zh")) { - message = "请点击链接创建你的 GMGN API Key,完成后将 Key 发给我,我来帮你完成配置:"; - } else { - message = "Please click the link below to create your GMGN API Key. Once created, send me the API Key and I will finish the configuration:"; - } - - console.log(""); - console.log(message); console.log(link); - console.log(""); }); } From 48a94a46a5f3c39c2f7dae1852c57440ed6e1c3b Mon Sep 17 00:00:00 2001 From: gina888666 Date: Wed, 24 Jun 2026 20:25:49 +0800 Subject: [PATCH 05/14] fix(gmgn-config): fix fixed guidance and verification messages in all languages Add exact fixed messages for link guidance and verification results in zh-CN, zh-TW, and English. Instruct Agent to output them verbatim in the user's conversation language without paraphrasing. Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-config/SKILL.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/skills/gmgn-config/SKILL.md b/skills/gmgn-config/SKILL.md index 73c28e3..5117d19 100644 --- a/skills/gmgn-config/SKILL.md +++ b/skills/gmgn-config/SKILL.md @@ -6,8 +6,6 @@ metadata: cliHelp: "gmgn-cli config --help" --- -**All output to the user must be in the same language as the current conversation.** - ## When to use this skill - Triggered automatically by other GMGN skills when `GMGN_API_KEY` is not found in `/.env` or `~/.config/gmgn/.env` @@ -23,7 +21,13 @@ Run: gmgn-cli config ``` -The command outputs a pre-filled link. Show the link to the user and ask them to click it to create their GMGN API Key. Tell the user to send back the API Key once it is created. +The command outputs a pre-filled link. Show the link to the user using **exactly** the following message in the user's conversation language — do not paraphrase: + +- 中文:请点击下方链接创建你的 GMGN API Key,完成后将 Key 发给我,我来帮你完成配置: +- 繁體:請點擊下方連結建立你的 GMGN API Key,完成後將 Key 發給我,我來幫你完成配置: +- English: Please click the link below to create your GMGN API Key. Once created, send me the API Key and I will finish the configuration: + +Then output the link on the next line. ### Step 2 — Write credentials to .env @@ -41,15 +45,17 @@ Run the following command to verify that the API Key and local key pair match: gmgn-cli track follow-wallet --chain sol ``` -- **Success** — Tell the user (in the conversation language): +Output the result to the user using **exactly** the following message in the user's conversation language — do not paraphrase: + +- **Success**: - 中文:配置验证成功,可以开始使用了。 - 繁體:配置驗證成功,可以開始使用了。 - English: Configuration verified successfully. You are ready to use GMGN. -- **Failure** — Tell the user (in the conversation language): - - 中文:配置验证失败:API Key 与本地密钥不匹配。\n请确认:1. API Key 是否填写正确;2. 创建 API Key 时,是否使用的是页面自动填入的公钥。 - - 繁體:配置驗證失敗:API Key 與本地密鑰不匹配。\n請確認:1. API Key 是否填寫正確;2. 創建 API Key 時,是否使用的是頁面自動填入的公鑰。 - - English: Configuration verification failed: API Key does not match the local key.\nPlease confirm: 1. Whether the API Key was entered correctly; 2. Whether the public key auto-filled on the page was used when creating the API Key. +- **Failure**: + - 中文:配置验证失败:API Key 与本地密钥不匹配。\n请确认:\n1. API Key 是否填写正确;\n2. 创建 API Key 时,是否使用的是页面自动填入的公钥。 + - 繁體:配置驗證失敗:API Key 與本地密鑰不匹配。\n請確認:\n1. API Key 是否填寫正確;\n2. 創建 API Key 時,是否使用的是頁面自動填入的公鑰。 + - English: Configuration verification failed: API Key does not match your local key pair.\nPlease confirm:\n1. Whether the API Key was entered correctly.\n2. Whether you used the public key that was pre-filled on the page when creating the API Key. ### Step 4 — Continue From d701420705d37f6de08b51150e862c7980c1bb6c Mon Sep 17 00:00:00 2001 From: gina888666 Date: Wed, 24 Jun 2026 21:13:11 +0800 Subject: [PATCH 06/14] refactor(config): move setup flow into CLI, remove gmgn-config skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - gmgn-cli config: add multi-language guidance (zh-CN/zh-TW/en) based on system locale - gmgn-cli config --apply : write GMGN_API_KEY + GMGN_PRIVATE_KEY to ~/.config/gmgn/.env and verify via track follow-wallet - Delete skills/gmgn-config/SKILL.md — all logic now lives in CLI source - Simplify BEFORE RUNNING ANY COMMAND in all 6 SKILL.md files to two CLI commands, no duplicated text - Remove stale First-time setup sections (openssl-based) from all 6 SKILL.md files Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-config/SKILL.md | 62 ---------------- skills/gmgn-cooking/SKILL.md | 25 +------ skills/gmgn-market/SKILL.md | 16 +--- skills/gmgn-portfolio/SKILL.md | 18 +---- skills/gmgn-swap/SKILL.md | 28 +------ skills/gmgn-token/SKILL.md | 18 +---- skills/gmgn-track/SKILL.md | 20 +---- src/commands/config.ts | 132 +++++++++++++++++++++++++++------ 8 files changed, 117 insertions(+), 202 deletions(-) delete mode 100644 skills/gmgn-config/SKILL.md diff --git a/skills/gmgn-config/SKILL.md b/skills/gmgn-config/SKILL.md deleted file mode 100644 index 5117d19..0000000 --- a/skills/gmgn-config/SKILL.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: gmgn-config -description: GMGN API Key and key pair setup. Use this skill when GMGN_API_KEY is not configured, or when the user explicitly runs gmgn-cli config. Handles key pair generation, API Key configuration, and verification. -argument-hint: "" -metadata: - cliHelp: "gmgn-cli config --help" ---- - -## When to use this skill - -- Triggered automatically by other GMGN skills when `GMGN_API_KEY` is not found in `/.env` or `~/.config/gmgn/.env` -- Also applies when the user explicitly runs `gmgn-cli config` - -## Setup flow - -### Step 1 — Generate key pair and show the link - -Run: - -```bash -gmgn-cli config -``` - -The command outputs a pre-filled link. Show the link to the user using **exactly** the following message in the user's conversation language — do not paraphrase: - -- 中文:请点击下方链接创建你的 GMGN API Key,完成后将 Key 发给我,我来帮你完成配置: -- 繁體:請點擊下方連結建立你的 GMGN API Key,完成後將 Key 發給我,我來幫你完成配置: -- English: Please click the link below to create your GMGN API Key. Once created, send me the API Key and I will finish the configuration: - -Then output the link on the next line. - -### Step 2 — Write credentials to .env - -Once the user sends back the API Key, run the following command to write `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` together into `~/.config/gmgn/.env`: - -```bash -node -e "const fs=require('fs'),os=require('os');const envPath=os.homedir()+'/.config/gmgn/.env';const pemPath=os.homedir()+'/.config/gmgn/keypair.pem';const pem=fs.readFileSync(pemPath,'utf8');const m=pem.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/);const pk=m?m[1].replace(/\r?\n/g,'\\\\n'):'';let env=fs.existsSync(envPath)?fs.readFileSync(envPath,'utf8'):'';env=env.split('\n').filter(function(l){return!/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)}).join('\n').trim();const content=(env?env+'\n':'')+'GMGN_API_KEY=\n'+'GMGN_PRIVATE_KEY='+pk+'\n';fs.writeFileSync(envPath,content,{mode:0o600});" -``` - -### Step 3 — Verify configuration - -Run the following command to verify that the API Key and local key pair match: - -```bash -gmgn-cli track follow-wallet --chain sol -``` - -Output the result to the user using **exactly** the following message in the user's conversation language — do not paraphrase: - -- **Success**: - - 中文:配置验证成功,可以开始使用了。 - - 繁體:配置驗證成功,可以開始使用了。 - - English: Configuration verified successfully. You are ready to use GMGN. - -- **Failure**: - - 中文:配置验证失败:API Key 与本地密钥不匹配。\n请确认:\n1. API Key 是否填写正确;\n2. 创建 API Key 时,是否使用的是页面自动填入的公钥。 - - 繁體:配置驗證失敗:API Key 與本地密鑰不匹配。\n請確認:\n1. API Key 是否填寫正確;\n2. 創建 API Key 時,是否使用的是頁面自動填入的公鑰。 - - English: Configuration verification failed: API Key does not match your local key pair.\nPlease confirm:\n1. Whether the API Key was entered correctly.\n2. Whether you used the public key that was pre-filled on the page when creating the API Key. - -### Step 4 — Continue - -After successful verification, proceed with the user's original request. diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index faca6c7..e8d8622 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli cooking --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.** @@ -102,29 +102,6 @@ When a request returns `429`: - `cooking create` is a real transaction: **never loop or auto-resubmit** after a `429`. Wait until the reset time, then ask for confirmation again before retrying. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during cooldown extend the ban by 5 seconds each time, up to 5 minutes. -**First-time setup** (if credentials are not configured): - -1. Generate key pair and show the public key to the user: - ```bash - openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ - openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null - ``` - Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form (enable swap/cooking capability), then send me the API Key value shown on the page."* - -2. Wait for the user's API key, then configure both credentials: - ```bash - mkdir -p ~/.config/gmgn - echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env - echo 'GMGN_PRIVATE_KEY=""' >> ~/.config/gmgn/.env - chmod 600 ~/.config/gmgn/.env - ``` - -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## `cooking stats` Usage ```bash diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index 3752841..c2b4087 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli market --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** @@ -79,20 +79,6 @@ When a request returns `429`: - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. -**First-time setup** (if `GMGN_API_KEY` is not configured): - -1. Generate key pair and show the public key to the user: - ```bash - openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ - openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null - ``` - Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."* - -2. Wait for the user's API key, then save it with `gmgn-cli config` (creates `~/.config/gmgn/.env` and sets `chmod 600` automatically — do not hand-edit the file): - ```bash - gmgn-cli config set-key - ``` - ## `market kline` Parameters | Parameter | Required | Description | diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index b2a098f..b077651 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli portfolio --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** @@ -79,22 +79,6 @@ When a request returns `429`: - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. -**First-time setup** (if `GMGN_API_KEY` is not configured): - -1. Generate key pair and show the public key to the user: - ```bash - openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ - openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null - ``` - Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."* - -2. Wait for the user's API key, then configure: - ```bash - mkdir -p ~/.config/gmgn - echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env - chmod 600 ~/.config/gmgn/.env - ``` - ## Usage Examples ```bash diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 8621a49..ae150e6 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli swap --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all swap operations must go through the CLI. The CLI handles signing and submission automatically.** @@ -73,7 +73,6 @@ Currency tokens are the base/native assets of each chain. They are used to buy o | `base` | ETH (native, `0x0000000000000000000000000000000000000000`), USDC (`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`) | | `eth` | ETH (native, `0x0000000000000000000000000000000000000000`) | - ## Prerequisites `GMGN_API_KEY` must be configured in `~/.config/gmgn/.env`. `GMGN_PRIVATE_KEY` is additionally required for `swap` and `order` subcommands other than `order quote`. The private key must correspond to the wallet bound to the API Key. @@ -104,31 +103,6 @@ When a request returns `429`: - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. - `POST /v1/trade/swap` also has an error-count limiter. Repeatedly triggering the same business error, especially `40003701` (insufficient token balance), can return `ERROR_RATE_LIMIT_BLOCKED`. When this happens, do not retry until the reset time and fix the underlying request first. -**First-time setup** (if credentials are not configured): - -1. Generate key pair and show the public key to the user: - ```bash - openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ - openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null - ``` - Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form (enable swap capability), then send me the API Key value shown on the page."* - -2. Wait for the user's API key, then configure both credentials: - ```bash - mkdir -p ~/.config/gmgn - echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env - echo 'GMGN_PRIVATE_KEY=""' >> ~/.config/gmgn/.env - chmod 600 ~/.config/gmgn/.env - ``` - -### Credential Model - -- Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` are read from the `.env` file by the CLI at startup. They are **never passed as command-line arguments** and never appear in shell command strings. -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 or RSA-SHA256 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` request header to GMGN's servers over HTTPS. - ---- - ## `swap` Usage ```bash diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index bedd1b0..38e2bf7 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli token --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** @@ -70,22 +70,6 @@ When a request returns `429`: - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. -**First-time setup** (if `GMGN_API_KEY` is not configured): - -1. Generate key pair and show the public key to the user: - ```bash - openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ - openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null - ``` - Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."* - -2. Wait for the user's API key, then configure: - ```bash - mkdir -p ~/.config/gmgn - echo 'GMGN_API_KEY=' > ~/.config/gmgn/.env - chmod 600 ~/.config/gmgn/.env - ``` - ## Parameters — `token info` / `token security` / `token pool` | Parameter | Required | Description | diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index 67d21a5..93c05ab 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli track --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, stop and follow the `gmgn-config` skill to complete setup first.** +**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** @@ -86,24 +86,6 @@ When a request returns `429`: - The CLI may wait and retry once automatically when the remaining cooldown is short. If it still fails, stop and tell the user the exact retry time instead of sending more requests. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during the cooldown can extend the ban by 5 seconds each time, up to 5 minutes. Do not spam retries. -**First-time setup** (if `GMGN_API_KEY` is not configured): - -1. Generate key pair and show the public key to the user: - ```bash - openssl genpkey -algorithm ed25519 -out /tmp/gmgn_private.pem 2>/dev/null && \ - openssl pkey -in /tmp/gmgn_private.pem -pubout 2>/dev/null - ``` - Tell the user: *"This is your Ed25519 public key. Go to **https://gmgn.ai/ai**, paste it into the API key creation form, then send me the API Key value shown on the page."* - -2. Wait for the user's API key, then configure (saves both API key and private key — private key is required for `track follow-wallet`): - ```bash - mkdir -p ~/.config/gmgn - echo "GMGN_API_KEY=" > ~/.config/gmgn/.env - echo "GMGN_PRIVATE_KEY=$(awk '{printf "%s\\n", $0}' /tmp/gmgn_private.pem)" >> ~/.config/gmgn/.env - chmod 600 ~/.config/gmgn/.env - rm /tmp/gmgn_private.pem - ``` - ## Usage Examples ```bash diff --git a/src/commands/config.ts b/src/commands/config.ts index 18705a9..0a28ea9 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -3,41 +3,131 @@ 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"; const GMGN_CONFIG_DIR = path.join(os.homedir(), ".config", "gmgn"); const KEYPAIR_FILE = path.join(GMGN_CONFIG_DIR, "keypair.pem"); +const ENV_FILE = path.join(GMGN_CONFIG_DIR, ".env"); const GMGN_API_URL = "https://gmgn.ai/ai/generateapi"; +type Lang = "zh-CN" | "zh-TW" | "en"; + +function detectLang(): Lang { + const locale = + process.env.LANG || + process.env.LC_ALL || + process.env.LC_MESSAGES || + Intl.DateTimeFormat().resolvedOptions().locale || + "en"; + const l = locale.toLowerCase(); + if (l.startsWith("zh_tw") || l.startsWith("zh-tw") || l.startsWith("zh_hk") || l.startsWith("zh-hk")) return "zh-TW"; + if (l.startsWith("zh")) return "zh-CN"; + return "en"; +} + +const MESSAGES = { + linkGuide: { + "zh-CN": (link: string) => + `请点击下方链接创建你的 GMGN API Key,完成后将 Key 发给我,我来帮你完成配置:\n${link}`, + "zh-TW": (link: string) => + `請點擊下方連結建立你的 GMGN API Key,完成後將 Key 發給我,我來幫你完成配置:\n${link}`, + en: (link: string) => + `Please click the link below to create your GMGN API Key. Once created, send me the API Key and I will finish the configuration:\n${link}`, + }, + verifySuccess: { + "zh-CN": "配置验证成功,可以开始使用了。", + "zh-TW": "配置驗證成功,可以開始使用了。", + en: "Configuration verified successfully. You are ready to use GMGN.", + }, + verifyFail: { + "zh-CN": + "配置验证失败:API Key 与本地密钥不匹配。\n请确认:\n1. API Key 是否填写正确;\n2. 创建 API Key 时,是否使用的是页面自动填入的公钥。", + "zh-TW": + "配置驗證失敗:API Key 與本地密鑰不匹配。\n請確認:\n1. API Key 是否填寫正確;\n2. 創建 API Key 時,是否使用的是頁面自動填入的公鑰。", + en: "Configuration verification failed: API Key does not match your local key pair.\nPlease confirm:\n1. Whether the API Key was entered correctly.\n2. Whether you used the public key that was pre-filled on the page when creating the API Key.", + }, +}; + +function getOrCreateKeypair(): string { + fs.mkdirSync(GMGN_CONFIG_DIR, { recursive: true }); + + if (fs.existsSync(KEYPAIR_FILE)) { + const content = fs.readFileSync(KEYPAIR_FILE, "utf-8"); + const match = content.match(/(-----BEGIN PUBLIC KEY-----[\s\S]+?-----END PUBLIC KEY-----)/); + if (!match) { + console.error("Error: keypair.pem exists but public key could not be parsed. Delete ~/.config/gmgn/keypair.pem and try again."); + process.exit(1); + } + return match[1] + "\n"; + } + + const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519"); + const privatePem = privateKey.export({ type: "pkcs8", format: "pem" }) as string; + const publicPem = publicKey.export({ type: "spki", format: "pem" }) as string; + const entry = `# Private Key\n${privatePem}\n# Public Key\n${publicPem}\n`; + fs.writeFileSync(KEYPAIR_FILE, entry, { mode: 0o600 }); + return publicPem; +} + +function writeEnv(apiKey: string, privatePem: string): void { + const pkOneLine = privatePem.replace(/\r?\n/g, "\\n"); + let existing = fs.existsSync(ENV_FILE) ? fs.readFileSync(ENV_FILE, "utf-8") : ""; + existing = existing + .split("\n") + .filter((l) => !/^GMGN_API_KEY=|^GMGN_PRIVATE_KEY=/.test(l)) + .join("\n") + .trim(); + const content = (existing ? existing + "\n" : "") + `GMGN_API_KEY=${apiKey}\nGMGN_PRIVATE_KEY=${pkOneLine}\n`; + fs.mkdirSync(GMGN_CONFIG_DIR, { recursive: true }); + fs.writeFileSync(ENV_FILE, content, { mode: 0o600 }); +} + +function verify(): boolean { + try { + execSync("gmgn-cli track follow-wallet --chain sol --limit 1", { stdio: "pipe" }); + return true; + } catch { + return false; + } +} + export function registerConfigCommands(program: Command): void { - program + const cmd = program .command("config") - .description("Generate an Ed25519 key pair and output a pre-filled GMGN API Key creation link") - .action(async () => { - fs.mkdirSync(GMGN_CONFIG_DIR, { recursive: true }); + .description("Generate an Ed25519 key pair and output a pre-filled GMGN API Key creation link, or apply an API Key"); - let publicPem: string; + cmd + .option("--apply ", "Write API Key + private key to ~/.config/gmgn/.env and verify") + .action(async (opts) => { + const lang = detectLang(); - if (fs.existsSync(KEYPAIR_FILE)) { - // Reuse existing key pair - const content = fs.readFileSync(KEYPAIR_FILE, "utf-8"); - const match = content.match(/(-----BEGIN PUBLIC KEY-----[\s\S]+?-----END PUBLIC KEY-----)/); - if (!match) { - console.error("Error: keypair.pem exists but public key could not be parsed. Delete ~/.config/gmgn/keypair.pem and try again."); + if (opts.apply) { + // --apply: read private key from keypair.pem, write .env, verify + if (!fs.existsSync(KEYPAIR_FILE)) { + console.error("Error: ~/.config/gmgn/keypair.pem not found. Run `gmgn-cli config` first to generate a key pair."); process.exit(1); } - publicPem = match[1] + "\n"; - } else { - // Generate new Ed25519 key pair - const { privateKey, publicKey } = crypto.generateKeyPairSync("ed25519"); - const privatePem = privateKey.export({ type: "pkcs8", format: "pem" }) as string; - publicPem = publicKey.export({ type: "spki", format: "pem" }) as string; + const content = fs.readFileSync(KEYPAIR_FILE, "utf-8"); + const match = content.match(/(-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----)/); + if (!match) { + console.error("Error: keypair.pem exists but private key could not be parsed. Delete ~/.config/gmgn/keypair.pem and run `gmgn-cli config` again."); + process.exit(1); + } + writeEnv(opts.apply, match[1]); - const entry = `# Private Key\n${privatePem}\n# Public Key\n${publicPem}\n`; - fs.writeFileSync(KEYPAIR_FILE, entry, { mode: 0o600 }); + const ok = verify(); + if (ok) { + console.log(MESSAGES.verifySuccess[lang]); + } else { + console.error(MESSAGES.verifyFail[lang]); + process.exit(1); + } + return; } + // Default: generate/reuse keypair, output link with guidance + const publicPem = getOrCreateKeypair(); const link = `${GMGN_API_URL}?pbk=${encodeURIComponent(publicPem)}`; - - console.log(link); + console.log(MESSAGES.linkGuide[lang](link)); }); } From 88643ca05c815e691d4e3aa1615cc5728de5e2f3 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Fri, 26 Jun 2026 13:52:02 +0800 Subject: [PATCH 07/14] feat(config): add --check flag and improve BEFORE RUNNING ANY COMMAND detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add gmgn-cli config --check: pure local check for GMGN_API_KEY, exit 0 = found, exit 1 = not found - Update BEFORE RUNNING ANY COMMAND in all 6 SKILL.md files to use --check instead of file path detection - Add fallback instruction for outdated gmgn-cli versions (unknown option → prompt user to npm install -g gmgn-cli) Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 2 +- skills/gmgn-market/SKILL.md | 2 +- skills/gmgn-portfolio/SKILL.md | 2 +- skills/gmgn-swap/SKILL.md | 2 +- skills/gmgn-token/SKILL.md | 2 +- skills/gmgn-track/SKILL.md | 2 +- src/commands/config.ts | 5 +++++ 7 files changed, 11 insertions(+), 6 deletions(-) diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index e8d8622..df238f8 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli cooking --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** +**BEFORE RUNNING ANY COMMAND: Run `gmgn-cli config --check`. If exit code is 0, proceed normally. If exit code is 1, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user. If `--check` returns an error (unknown option or command not found), tell the user to run `npm install -g gmgn-cli` to update, then retry.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all token creation operations must go through the CLI. The CLI handles signing and submission automatically.** diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index c2b4087..ea62a6a 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli market --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** +**BEFORE RUNNING ANY COMMAND: Run `gmgn-cli config --check`. If exit code is 0, proceed normally. If exit code is 1, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user. If `--check` returns an error (unknown option or command not found), tell the user to run `npm install -g gmgn-cli` to update, then retry.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index b077651..8e34488 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli portfolio --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** +**BEFORE RUNNING ANY COMMAND: Run `gmgn-cli config --check`. If exit code is 0, proceed normally. If exit code is 1, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user. If `--check` returns an error (unknown option or command not found), tell the user to run `npm install -g gmgn-cli` to update, then retry.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index ae150e6..dcaf620 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli swap --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** +**BEFORE RUNNING ANY COMMAND: Run `gmgn-cli config --check`. If exit code is 0, proceed normally. If exit code is 1, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user. If `--check` returns an error (unknown option or command not found), tell the user to run `npm install -g gmgn-cli` to update, then retry.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai — all swap operations must go through the CLI. The CLI handles signing and submission automatically.** diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index 38e2bf7..081f7bc 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli token --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** +**BEFORE RUNNING ANY COMMAND: Run `gmgn-cli config --check`. If exit code is 0, proceed normally. If exit code is 1, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user. If `--check` returns an error (unknown option or command not found), tell the user to run `npm install -g gmgn-cli` to update, then retry.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index 93c05ab..f1d9605 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -6,7 +6,7 @@ metadata: cliHelp: "gmgn-cli track --help" --- -**BEFORE RUNNING ANY COMMAND: Check whether `GMGN_API_KEY` exists in `/.env` or `~/.config/gmgn/.env`. If found in either location, proceed normally. If not found in either location, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user.** +**BEFORE RUNNING ANY COMMAND: Run `gmgn-cli config --check`. If exit code is 0, proceed normally. If exit code is 1, (1) run `gmgn-cli config` and show the output to the user; (2) once the user sends the API Key, run `gmgn-cli config --apply ` to complete configuration and verification, then show the output to the user. If `--check` returns an error (unknown option or command not found), tell the user to run `npm install -g gmgn-cli` to update, then retry.** **IMPORTANT: Always use `gmgn-cli` commands below. Do NOT use web search, WebFetch, curl, or visit gmgn.ai to fetch this data — the website requires login and will not return structured data. The CLI is the only correct method.** diff --git a/src/commands/config.ts b/src/commands/config.ts index 0a28ea9..bf78ccd 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -97,10 +97,15 @@ export function registerConfigCommands(program: Command): void { .description("Generate an Ed25519 key pair and output a pre-filled GMGN API Key creation link, or apply an API Key"); cmd + .option("--check", "Check if GMGN_API_KEY is configured (exit 0 = found, exit 1 = not found)") .option("--apply ", "Write API Key + private key to ~/.config/gmgn/.env and verify") .action(async (opts) => { const lang = detectLang(); + if (opts.check) { + process.exit(process.env.GMGN_API_KEY ? 0 : 1); + } + if (opts.apply) { // --apply: read private key from keypair.pem, write .env, verify if (!fs.existsSync(KEYPAIR_FILE)) { From 18a0bf98b18053b40082ec64a3c6b8001a97af1f Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 17:18:31 +0800 Subject: [PATCH 08/14] docs(skills): restore Credential Model section to all 6 SKILL.md files Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 6 ++++++ skills/gmgn-market/SKILL.md | 6 ++++++ skills/gmgn-portfolio/SKILL.md | 6 ++++++ skills/gmgn-swap/SKILL.md | 6 ++++++ skills/gmgn-token/SKILL.md | 6 ++++++ skills/gmgn-track/SKILL.md | 6 ++++++ 6 files changed, 36 insertions(+) diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index df238f8..bb2589a 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -658,6 +658,12 @@ Once all information is collected, present the pre-create confirmation summary ( - **Order polling** — After `cooking create`, if `status` is `pending`, poll `order get` every 2 seconds up to 30 seconds. The token address is in `report.output_token`. Do not report success until `status` is `confirmed`. - **Credential sensitivity** — `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` can execute real transactions. Never log, display, or expose these values. +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## Notes - `cooking create` uses **signed auth** (API Key + signature) — CLI handles signing automatically. diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index ea62a6a..5ebf970 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -1031,6 +1031,12 @@ gmgn-cli market signal --chain sol \ --- +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## Notes - `market kline`: `--from` and `--to` are Unix timestamps in **seconds** — CLI converts to milliseconds automatically diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index 8e34488..c0e117b 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -352,6 +352,12 @@ PnL Ratio: {pnl}x Show the `[Identity: ...]` line only if `common` is present in the response. For batch queries (multiple wallets), present one summary block per wallet. +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## Notes - `portfolio holdings` uses **critical auth** (`GMGN_API_KEY` + `GMGN_PRIVATE_KEY` required — CLI signs the request automatically). All other portfolio commands use exist auth (API Key only, no signature required). diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index dcaf620..67ffe79 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -734,6 +734,12 @@ gmgn-cli order strategy cancel \ --- +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## Notes - Swap uses **signed auth** (API Key + signature) — CLI handles signing automatically, no manual processing needed diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index 081f7bc..b52cc8b 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -707,6 +707,12 @@ SM holders: {smart_wallets} KOL holders: {renowned_wallets} Show top rows only. Highlight wallets tagged `kol`, `smart_degen`, or flagged `bundler` / `rat_trader` in `maker_token_tags`. +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## Notes - **Market cap is not returned directly** — calculate it as `price.price × circulating_supply` (`price` is now a nested object; use `price.price` for the current USD price string, and `circulating_supply` is a top-level field already in human-readable token units). Example: `price.price="3.11"` × `circulating_supply=999999151` ≈ $3.11B market cap. diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index f1d9605..a1f3762 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -369,6 +369,12 @@ To research any token surfaced by smart money activity, follow [`docs/workflow-t - **`follow-wallet` reveals your following list** — results expose which wallets you have followed on GMGN. Do not share raw output in public channels. - **`track kol` / `track smartmoney` expose no personal data** — these use API Key auth only and return platform-tagged public wallet activity. Safe to share raw output. +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## Notes - `track follow-tokens` uses exist auth (API Key only); `--wallet` is required From d0e9e42f178bcafd326f6e20c616009b6090384d Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 17:25:48 +0800 Subject: [PATCH 09/14] docs(skills): remove Credential Model from token/market/portfolio/track (only cooking/swap had it on main) Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-market/SKILL.md | 6 ------ skills/gmgn-portfolio/SKILL.md | 6 ------ skills/gmgn-token/SKILL.md | 6 ------ skills/gmgn-track/SKILL.md | 6 ------ 4 files changed, 24 deletions(-) diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index 5ebf970..ea62a6a 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -1031,12 +1031,6 @@ gmgn-cli market signal --chain sol \ --- -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## Notes - `market kline`: `--from` and `--to` are Unix timestamps in **seconds** — CLI converts to milliseconds automatically diff --git a/skills/gmgn-portfolio/SKILL.md b/skills/gmgn-portfolio/SKILL.md index c0e117b..8e34488 100644 --- a/skills/gmgn-portfolio/SKILL.md +++ b/skills/gmgn-portfolio/SKILL.md @@ -352,12 +352,6 @@ PnL Ratio: {pnl}x Show the `[Identity: ...]` line only if `common` is present in the response. For batch queries (multiple wallets), present one summary block per wallet. -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## Notes - `portfolio holdings` uses **critical auth** (`GMGN_API_KEY` + `GMGN_PRIVATE_KEY` required — CLI signs the request automatically). All other portfolio commands use exist auth (API Key only, no signature required). diff --git a/skills/gmgn-token/SKILL.md b/skills/gmgn-token/SKILL.md index b52cc8b..081f7bc 100644 --- a/skills/gmgn-token/SKILL.md +++ b/skills/gmgn-token/SKILL.md @@ -707,12 +707,6 @@ SM holders: {smart_wallets} KOL holders: {renowned_wallets} Show top rows only. Highlight wallets tagged `kol`, `smart_degen`, or flagged `bundler` / `rat_trader` in `maker_token_tags`. -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## Notes - **Market cap is not returned directly** — calculate it as `price.price × circulating_supply` (`price` is now a nested object; use `price.price` for the current USD price string, and `circulating_supply` is a top-level field already in human-readable token units). Example: `price.price="3.11"` × `circulating_supply=999999151` ≈ $3.11B market cap. diff --git a/skills/gmgn-track/SKILL.md b/skills/gmgn-track/SKILL.md index a1f3762..f1d9605 100644 --- a/skills/gmgn-track/SKILL.md +++ b/skills/gmgn-track/SKILL.md @@ -369,12 +369,6 @@ To research any token surfaced by smart money activity, follow [`docs/workflow-t - **`follow-wallet` reveals your following list** — results expose which wallets you have followed on GMGN. Do not share raw output in public channels. - **`track kol` / `track smartmoney` expose no personal data** — these use API Key auth only and return platform-tagged public wallet activity. Safe to share raw output. -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## Notes - `track follow-tokens` uses exist auth (API Key only); `--wallet` is required From 3ecd76c572221efcee12c845bf73c2b2803fcbcd Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 17:30:05 +0800 Subject: [PATCH 10/14] docs(skills): restore Credential Model to original position in cooking and swap Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-cooking/SKILL.md | 12 ++++++------ skills/gmgn-swap/SKILL.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/skills/gmgn-cooking/SKILL.md b/skills/gmgn-cooking/SKILL.md index bb2589a..733949d 100644 --- a/skills/gmgn-cooking/SKILL.md +++ b/skills/gmgn-cooking/SKILL.md @@ -102,6 +102,12 @@ When a request returns `429`: - `cooking create` is a real transaction: **never loop or auto-resubmit** after a `429`. Wait until the reset time, then ask for confirmation again before retrying. - For `RATE_LIMIT_EXCEEDED` or `RATE_LIMIT_BANNED`, repeated requests during cooldown extend the ban by 5 seconds each time, up to 5 minutes. +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## `cooking stats` Usage ```bash @@ -658,12 +664,6 @@ Once all information is collected, present the pre-create confirmation summary ( - **Order polling** — After `cooking create`, if `status` is `pending`, poll `order get` every 2 seconds up to 30 seconds. The token address is in `report.output_token`. Do not report success until `status` is `confirmed`. - **Credential sensitivity** — `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` can execute real transactions. Never log, display, or expose these values. -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## Notes - `cooking create` uses **signed auth** (API Key + signature) — CLI handles signing automatically. diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index 67ffe79..b71ee03 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -407,6 +407,12 @@ The response `data` is an array — one element per wallet: --- +### Credential Model + +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. +- Neither credential is ever passed as a command-line argument. + ## `order quote` Usage Get an estimated output amount before submitting a swap. Uses normal auth — only `GMGN_API_KEY` required, no `GMGN_PRIVATE_KEY` needed. @@ -734,12 +740,6 @@ gmgn-cli order strategy cancel \ --- -### Credential Model - -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. - ## Notes - Swap uses **signed auth** (API Key + signature) — CLI handles signing automatically, no manual processing needed From d56c904b26a4b07632e73de4cabdc548e9116395 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 17:37:38 +0800 Subject: [PATCH 11/14] docs(skills): fix Credential Model content in swap to match main verbatim Co-Authored-By: Claude Sonnet 4.6 --- skills/gmgn-swap/SKILL.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/skills/gmgn-swap/SKILL.md b/skills/gmgn-swap/SKILL.md index b71ee03..10f5877 100644 --- a/skills/gmgn-swap/SKILL.md +++ b/skills/gmgn-swap/SKILL.md @@ -409,9 +409,11 @@ The response `data` is an array — one element per wallet: ### Credential Model -- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. -- `GMGN_API_KEY` is transmitted in the `X-APIKEY` header over HTTPS. -- Neither credential is ever passed as a command-line argument. +- Both `GMGN_API_KEY` and `GMGN_PRIVATE_KEY` are read from the `.env` file by the CLI at startup. They are **never passed as command-line arguments** and never appear in shell command strings. +- `GMGN_PRIVATE_KEY` is used exclusively for **local message signing** — the private key never leaves the machine. The CLI computes an Ed25519 or RSA-SHA256 signature in-process and transmits only the base64-encoded result in the `X-Signature` request header. +- `GMGN_API_KEY` is transmitted in the `X-APIKEY` request header to GMGN's servers over HTTPS. + +--- ## `order quote` Usage From b782743c8cdc01c659125c11830ce0557a27d983 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 20:12:11 +0800 Subject: [PATCH 12/14] fix(config): load dotenv before --check so ~/.config/gmgn/.env is read Co-Authored-By: Claude Sonnet 4.6 --- src/commands/config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/commands/config.ts b/src/commands/config.ts index bf78ccd..ccdec27 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,3 +1,4 @@ +import { config as loadDotenv } from "dotenv"; import { Command } from "commander"; import * as crypto from "crypto"; import * as fs from "fs"; @@ -103,6 +104,8 @@ export function registerConfigCommands(program: Command): void { const lang = detectLang(); if (opts.check) { + loadDotenv({ path: ENV_FILE, override: true }); + loadDotenv(); process.exit(process.env.GMGN_API_KEY ? 0 : 1); } From ac1ea2e3367618ab0d723bd9db071e2c40f54877 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 21:08:55 +0800 Subject: [PATCH 13/14] fix(config): distinguish auth vs network failures in verify step Co-Authored-By: Claude Sonnet 4.6 --- src/commands/config.ts | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/commands/config.ts b/src/commands/config.ts index ccdec27..b3ed5ef 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -47,6 +47,11 @@ const MESSAGES = { "配置驗證失敗:API Key 與本地密鑰不匹配。\n請確認:\n1. API Key 是否填寫正確;\n2. 創建 API Key 時,是否使用的是頁面自動填入的公鑰。", en: "Configuration verification failed: API Key does not match your local key pair.\nPlease confirm:\n1. Whether the API Key was entered correctly.\n2. Whether you used the public key that was pre-filled on the page when creating the API Key.", }, + verifyNetworkFail: { + "zh-CN": "配置已写入,但验证请求失败(可能是网络问题)。你可以先尝试使用,如遇接口报错再重新配置。", + "zh-TW": "配置已寫入,但驗證請求失敗(可能是網路問題)。你可以先嘗試使用,如遇介面報錯再重新配置。", + en: "Configuration saved, but the verification request failed (possibly a network issue). You can try using it now and reconfigure if you encounter API errors.", + }, }; function getOrCreateKeypair(): string { @@ -83,12 +88,20 @@ function writeEnv(apiKey: string, privatePem: string): void { fs.writeFileSync(ENV_FILE, content, { mode: 0o600 }); } -function verify(): boolean { +function verify(): "ok" | "auth_fail" | "network_fail" { try { execSync("gmgn-cli track follow-wallet --chain sol --limit 1", { stdio: "pipe" }); - return true; - } catch { - return false; + return "ok"; + } catch (e: unknown) { + const output = (() => { + if (e && typeof e === "object" && "stderr" in e) return String((e as { stderr: unknown }).stderr); + if (e && typeof e === "object" && "stdout" in e) return String((e as { stdout: unknown }).stdout); + return String(e); + })(); + if (/401|403|unauthorized|forbidden|invalid.*key|key.*invalid|signature/i.test(output)) { + return "auth_fail"; + } + return "network_fail"; } } @@ -123,12 +136,14 @@ export function registerConfigCommands(program: Command): void { } writeEnv(opts.apply, match[1]); - const ok = verify(); - if (ok) { + const result = verify(); + if (result === "ok") { console.log(MESSAGES.verifySuccess[lang]); - } else { + } else if (result === "auth_fail") { console.error(MESSAGES.verifyFail[lang]); process.exit(1); + } else { + console.log(MESSAGES.verifyNetworkFail[lang]); } return; } From e3b713b1505308b5b981e2c6d71fa3815b61b9d1 Mon Sep 17 00:00:00 2001 From: gina888666 Date: Tue, 30 Jun 2026 21:12:27 +0800 Subject: [PATCH 14/14] fix(config): combine stderr+stdout when detecting auth errors in verify Co-Authored-By: Claude Sonnet 4.6 --- src/commands/config.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/commands/config.ts b/src/commands/config.ts index b3ed5ef..9119f24 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -93,11 +93,10 @@ function verify(): "ok" | "auth_fail" | "network_fail" { execSync("gmgn-cli track follow-wallet --chain sol --limit 1", { stdio: "pipe" }); return "ok"; } catch (e: unknown) { - const output = (() => { - if (e && typeof e === "object" && "stderr" in e) return String((e as { stderr: unknown }).stderr); - if (e && typeof e === "object" && "stdout" in e) return String((e as { stdout: unknown }).stdout); - return String(e); - })(); + const output = [ + (e as any).stderr?.toString() ?? "", + (e as any).stdout?.toString() ?? "", + ].join(" "); if (/401|403|unauthorized|forbidden|invalid.*key|key.*invalid|signature/i.test(output)) { return "auth_fail"; }