feat: improve onboarding auth prompts
This commit is contained in:
@@ -17,6 +17,9 @@
|
|||||||
- Onboarding/CLI: group model/auth choice by provider and label Z.AI as GLM 4.7.
|
- Onboarding/CLI: group model/auth choice by provider and label Z.AI as GLM 4.7.
|
||||||
- Onboarding/Docs: add Moonshot AI (Kimi K2) auth choice + config example.
|
- Onboarding/Docs: add Moonshot AI (Kimi K2) auth choice + config example.
|
||||||
- CLI/Onboarding: prompt to reuse detected API keys for Moonshot/MiniMax/Z.AI/Gemini/Anthropic/OpenCode.
|
- CLI/Onboarding: prompt to reuse detected API keys for Moonshot/MiniMax/Z.AI/Gemini/Anthropic/OpenCode.
|
||||||
|
- CLI/Onboarding: move MiniMax to the top of the provider list.
|
||||||
|
- CLI/Onboarding: add MiniMax M2.1 Lightning auth choice.
|
||||||
|
- CLI/Onboarding: show key previews when reusing detected API keys.
|
||||||
- Auto-reply: add compact `/model` picker (models + available providers) and show provider endpoints in `/model status`.
|
- Auto-reply: add compact `/model` picker (models + available providers) and show provider endpoints in `/model status`.
|
||||||
- Control UI: add Config tab model presets (MiniMax M2.1, GLM 4.7, Kimi) for one-click setup.
|
- Control UI: add Config tab model presets (MiniMax M2.1, GLM 4.7, Kimi) for one-click setup.
|
||||||
- Plugins: add extension loader (tools/RPC/CLI/services), discovery paths, and config schema + Control UI labels (uiHints).
|
- Plugins: add extension loader (tools/RPC/CLI/services), discovery paths, and config schema + Control UI labels (uiHints).
|
||||||
@@ -73,6 +76,7 @@
|
|||||||
- Auto-reply: allow inline `/status` for allowlisted senders (stripped before the model); unauthorized senders see it as plain text.
|
- Auto-reply: allow inline `/status` for allowlisted senders (stripped before the model); unauthorized senders see it as plain text.
|
||||||
- Auto-reply: include config-only allowlisted models in `/model` even when the catalog is partial.
|
- Auto-reply: include config-only allowlisted models in `/model` even when the catalog is partial.
|
||||||
- Auto-reply: ignore inline `/status` directives unless the message is directive-only.
|
- Auto-reply: ignore inline `/status` directives unless the message is directive-only.
|
||||||
|
- CLI/Configure: enter the selected section immediately, then return to the section picker.
|
||||||
- Auto-reply: align `/think` default display with model reasoning defaults. (#751) — thanks @gabriel-trigo.
|
- Auto-reply: align `/think` default display with model reasoning defaults. (#751) — thanks @gabriel-trigo.
|
||||||
- Auto-reply: flush block reply buffers on tool boundaries. (#750) — thanks @sebslight.
|
- Auto-reply: flush block reply buffers on tool boundaries. (#750) — thanks @sebslight.
|
||||||
- Auto-reply: allow sender fallback for command authorization when `SenderId` is empty (WhatsApp self-chat). (#755) — thanks @juanpablodlc.
|
- Auto-reply: allow sender fallback for command authorization when `SenderId` is empty (WhatsApp self-chat). (#755) — thanks @juanpablodlc.
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ export function buildProgram() {
|
|||||||
.option("--mode <mode>", "Wizard mode: local|remote")
|
.option("--mode <mode>", "Wizard mode: local|remote")
|
||||||
.option(
|
.option(
|
||||||
"--auth-choice <choice>",
|
"--auth-choice <choice>",
|
||||||
"Auth: setup-token|claude-cli|token|openai-codex|openai-api-key|openrouter-api-key|moonshot-api-key|codex-cli|antigravity|gemini-api-key|zai-api-key|apiKey|minimax-api|opencode-zen|skip",
|
"Auth: setup-token|claude-cli|token|openai-codex|openai-api-key|openrouter-api-key|moonshot-api-key|codex-cli|antigravity|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip",
|
||||||
)
|
)
|
||||||
.option(
|
.option(
|
||||||
"--token-provider <id>",
|
"--token-provider <id>",
|
||||||
@@ -339,6 +339,7 @@ export function buildProgram() {
|
|||||||
| "apiKey"
|
| "apiKey"
|
||||||
| "minimax-cloud"
|
| "minimax-cloud"
|
||||||
| "minimax-api"
|
| "minimax-api"
|
||||||
|
| "minimax-api-lightning"
|
||||||
| "minimax"
|
| "minimax"
|
||||||
| "opencode-zen"
|
| "opencode-zen"
|
||||||
| "skip"
|
| "skip"
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ describe("buildAuthChoiceOptions", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(options.some((opt) => opt.value === "minimax-api")).toBe(true);
|
expect(options.some((opt) => opt.value === "minimax-api")).toBe(true);
|
||||||
|
expect(options.some((opt) => opt.value === "minimax-api-lightning")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes Moonshot auth choice", () => {
|
it("includes Moonshot auth choice", () => {
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ const AUTH_CHOICE_GROUP_DEFS: {
|
|||||||
hint: "Claude CLI + API key",
|
hint: "Claude CLI + API key",
|
||||||
choices: ["claude-cli", "setup-token", "token", "apiKey"],
|
choices: ["claude-cli", "setup-token", "token", "apiKey"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "minimax",
|
||||||
|
label: "MiniMax",
|
||||||
|
hint: "M2.1 (recommended)",
|
||||||
|
choices: ["minimax-api", "minimax-api-lightning"],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: "google",
|
value: "google",
|
||||||
label: "Google",
|
label: "Google",
|
||||||
@@ -77,12 +83,6 @@ const AUTH_CHOICE_GROUP_DEFS: {
|
|||||||
hint: "API key",
|
hint: "API key",
|
||||||
choices: ["opencode-zen"],
|
choices: ["opencode-zen"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
value: "minimax",
|
|
||||||
label: "MiniMax",
|
|
||||||
hint: "M2.1 (recommended)",
|
|
||||||
choices: ["minimax-api"],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function formatOAuthHint(
|
function formatOAuthHint(
|
||||||
@@ -181,6 +181,11 @@ export function buildAuthChoiceOptions(params: {
|
|||||||
hint: "Claude, GPT, Gemini via opencode.ai/zen",
|
hint: "Claude, GPT, Gemini via opencode.ai/zen",
|
||||||
});
|
});
|
||||||
options.push({ value: "minimax-api", label: "MiniMax M2.1" });
|
options.push({ value: "minimax-api", label: "MiniMax M2.1" });
|
||||||
|
options.push({
|
||||||
|
value: "minimax-api-lightning",
|
||||||
|
label: "MiniMax M2.1 Lightning",
|
||||||
|
hint: "Faster, lower cost",
|
||||||
|
});
|
||||||
if (params.includeSkip) {
|
if (params.includeSkip) {
|
||||||
options.push({ value: "skip", label: "Skip for now" });
|
options.push({ value: "skip", label: "Skip for now" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,27 @@ import {
|
|||||||
} from "./openai-codex-model-default.js";
|
} from "./openai-codex-model-default.js";
|
||||||
import { OPENCODE_ZEN_DEFAULT_MODEL } from "./opencode-zen-model-default.js";
|
import { OPENCODE_ZEN_DEFAULT_MODEL } from "./opencode-zen-model-default.js";
|
||||||
|
|
||||||
|
const DEFAULT_KEY_PREVIEW = { head: 4, tail: 4 };
|
||||||
|
|
||||||
|
function formatApiKeyPreview(
|
||||||
|
raw: string,
|
||||||
|
opts: { head?: number; tail?: number } = {},
|
||||||
|
): string {
|
||||||
|
const trimmed = raw.trim();
|
||||||
|
if (!trimmed) return "…";
|
||||||
|
const head = opts.head ?? DEFAULT_KEY_PREVIEW.head;
|
||||||
|
const tail = opts.tail ?? DEFAULT_KEY_PREVIEW.tail;
|
||||||
|
if (trimmed.length <= head + tail) {
|
||||||
|
const shortHead = Math.min(2, trimmed.length);
|
||||||
|
const shortTail = Math.min(2, trimmed.length - shortHead);
|
||||||
|
if (shortTail <= 0) {
|
||||||
|
return `${trimmed.slice(0, shortHead)}…`;
|
||||||
|
}
|
||||||
|
return `${trimmed.slice(0, shortHead)}…${trimmed.slice(-shortTail)}`;
|
||||||
|
}
|
||||||
|
return `${trimmed.slice(0, head)}…${trimmed.slice(-tail)}`;
|
||||||
|
}
|
||||||
|
|
||||||
export async function warnIfModelConfigLooksOff(
|
export async function warnIfModelConfigLooksOff(
|
||||||
config: ClawdbotConfig,
|
config: ClawdbotConfig,
|
||||||
prompter: WizardPrompter,
|
prompter: WizardPrompter,
|
||||||
@@ -339,7 +360,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = resolveEnvApiKey("openai");
|
const envKey = resolveEnvApiKey("openai");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing OPENAI_API_KEY (${envKey.source})?`,
|
message: `Use existing OPENAI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -406,7 +427,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = resolveEnvApiKey("openrouter");
|
const envKey = resolveEnvApiKey("openrouter");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing OPENROUTER_API_KEY (${envKey.source})?`,
|
message: `Use existing OPENROUTER_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -448,7 +469,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = resolveEnvApiKey("moonshot");
|
const envKey = resolveEnvApiKey("moonshot");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing MOONSHOT_API_KEY (${envKey.source})?`,
|
message: `Use existing MOONSHOT_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -691,7 +712,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = resolveEnvApiKey("google");
|
const envKey = resolveEnvApiKey("google");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing GEMINI_API_KEY (${envKey.source})?`,
|
message: `Use existing GEMINI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -729,7 +750,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = resolveEnvApiKey("zai");
|
const envKey = resolveEnvApiKey("zai");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing ZAI_API_KEY (${envKey.source})?`,
|
message: `Use existing ZAI_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -782,7 +803,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = process.env.ANTHROPIC_API_KEY?.trim();
|
const envKey = process.env.ANTHROPIC_API_KEY?.trim();
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: "Use existing ANTHROPIC_API_KEY (env)?",
|
message: `Use existing ANTHROPIC_API_KEY (env, ${formatApiKeyPreview(envKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -804,14 +825,18 @@ export async function applyAuthChoice(params: {
|
|||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
params.authChoice === "minimax-cloud" ||
|
params.authChoice === "minimax-cloud" ||
|
||||||
params.authChoice === "minimax-api"
|
params.authChoice === "minimax-api" ||
|
||||||
|
params.authChoice === "minimax-api-lightning"
|
||||||
) {
|
) {
|
||||||
const modelId = "MiniMax-M2.1";
|
const modelId =
|
||||||
|
params.authChoice === "minimax-api-lightning"
|
||||||
|
? "MiniMax-M2.1-lightning"
|
||||||
|
: "MiniMax-M2.1";
|
||||||
let hasCredential = false;
|
let hasCredential = false;
|
||||||
const envKey = resolveEnvApiKey("minimax");
|
const envKey = resolveEnvApiKey("minimax");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing MINIMAX_API_KEY (${envKey.source})?`,
|
message: `Use existing MINIMAX_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -860,7 +885,7 @@ export async function applyAuthChoice(params: {
|
|||||||
const envKey = resolveEnvApiKey("opencode");
|
const envKey = resolveEnvApiKey("opencode");
|
||||||
if (envKey) {
|
if (envKey) {
|
||||||
const useExisting = await params.prompter.confirm({
|
const useExisting = await params.prompter.confirm({
|
||||||
message: `Use existing OPENCODE_API_KEY (${envKey.source})?`,
|
message: `Use existing OPENCODE_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||||
initialValue: true,
|
initialValue: true,
|
||||||
});
|
});
|
||||||
if (useExisting) {
|
if (useExisting) {
|
||||||
@@ -923,6 +948,7 @@ export function resolvePreferredProviderForAuthChoice(
|
|||||||
return "google-antigravity";
|
return "google-antigravity";
|
||||||
case "minimax-cloud":
|
case "minimax-cloud":
|
||||||
case "minimax-api":
|
case "minimax-api":
|
||||||
|
case "minimax-api-lightning":
|
||||||
return "minimax";
|
return "minimax";
|
||||||
case "minimax":
|
case "minimax":
|
||||||
return "lmstudio";
|
return "lmstudio";
|
||||||
|
|||||||
@@ -305,7 +305,11 @@ export async function runNonInteractiveOnboarding(
|
|||||||
mode: "api_key",
|
mode: "api_key",
|
||||||
});
|
});
|
||||||
nextConfig = applyMoonshotConfig(nextConfig);
|
nextConfig = applyMoonshotConfig(nextConfig);
|
||||||
} else if (authChoice === "minimax-cloud" || authChoice === "minimax-api") {
|
} else if (
|
||||||
|
authChoice === "minimax-cloud" ||
|
||||||
|
authChoice === "minimax-api" ||
|
||||||
|
authChoice === "minimax-api-lightning"
|
||||||
|
) {
|
||||||
const resolved = await resolveNonInteractiveApiKey({
|
const resolved = await resolveNonInteractiveApiKey({
|
||||||
provider: "minimax",
|
provider: "minimax",
|
||||||
cfg: baseConfig,
|
cfg: baseConfig,
|
||||||
@@ -323,7 +327,10 @@ export async function runNonInteractiveOnboarding(
|
|||||||
provider: "minimax",
|
provider: "minimax",
|
||||||
mode: "api_key",
|
mode: "api_key",
|
||||||
});
|
});
|
||||||
const modelId = "MiniMax-M2.1";
|
const modelId =
|
||||||
|
authChoice === "minimax-api-lightning"
|
||||||
|
? "MiniMax-M2.1-lightning"
|
||||||
|
: "MiniMax-M2.1";
|
||||||
nextConfig = applyMinimaxApiConfig(nextConfig, modelId);
|
nextConfig = applyMinimaxApiConfig(nextConfig, modelId);
|
||||||
} else if (authChoice === "claude-cli") {
|
} else if (authChoice === "claude-cli") {
|
||||||
const store = ensureAuthProfileStore(undefined, {
|
const store = ensureAuthProfileStore(undefined, {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type AuthChoice =
|
|||||||
| "minimax-cloud"
|
| "minimax-cloud"
|
||||||
| "minimax"
|
| "minimax"
|
||||||
| "minimax-api"
|
| "minimax-api"
|
||||||
|
| "minimax-api-lightning"
|
||||||
| "opencode-zen"
|
| "opencode-zen"
|
||||||
| "skip";
|
| "skip";
|
||||||
export type GatewayAuthChoice = "off" | "token" | "password";
|
export type GatewayAuthChoice = "off" | "token" | "password";
|
||||||
|
|||||||
Reference in New Issue
Block a user