fix: show /model auth source

This commit is contained in:
Peter Steinberger
2026-01-05 14:14:26 +00:00
parent cffbe79077
commit 0c37f27a4a
2 changed files with 36 additions and 9 deletions

View File

@@ -14,6 +14,7 @@
- Auth: add OpenAI Codex OAuth support and migrate legacy oauth.json into auth.json. - Auth: add OpenAI Codex OAuth support and migrate legacy oauth.json into auth.json.
- Model: `/model` list shows auth source (masked key or OAuth email) per provider. - Model: `/model` list shows auth source (masked key or OAuth email) per provider.
- Model: `/model list` is an alias for `/model`. - Model: `/model list` is an alias for `/model`.
- Model: `/model` output now includes auth source location (env/auth.json/models.json).
- Docs: clarify auth storage, migration, and OpenAI Codex OAuth onboarding. - Docs: clarify auth storage, migration, and OpenAI Codex OAuth onboarding.
- Sandbox: copy inbound media into sandbox workspaces so agent tools can read attachments. - Sandbox: copy inbound media into sandbox workspaces so agent tools can read attachments.
- Status: show runtime (docker/direct) and move shortcuts to `/help`. - Status: show runtime (docker/direct) and move shortcuts to `/help`.

View File

@@ -18,6 +18,7 @@ import {
import type { ClawdbotConfig } from "../../config/config.js"; import type { ClawdbotConfig } from "../../config/config.js";
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js"; import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
import { enqueueSystemEvent } from "../../infra/system-events.js"; import { enqueueSystemEvent } from "../../infra/system-events.js";
import { shortenHomePath } from "../../utils.js";
import { extractModelDirective } from "../model.js"; import { extractModelDirective } from "../model.js";
import type { MsgContext } from "../templating.js"; import type { MsgContext } from "../templating.js";
import type { ReplyPayload } from "../types.js"; import type { ReplyPayload } from "../types.js";
@@ -53,28 +54,43 @@ const maskApiKey = (value: string): string => {
const resolveAuthLabel = async ( const resolveAuthLabel = async (
provider: string, provider: string,
authStorage: ReturnType<typeof discoverAuthStorage>, authStorage: ReturnType<typeof discoverAuthStorage>,
): Promise<string> => { authPaths: { authPath: string; modelsPath: string },
): Promise<{ label: string; source: string }> => {
const formatPath = (value: string) => shortenHomePath(value);
const stored = authStorage.get(provider); const stored = authStorage.get(provider);
if (stored?.type === "oauth") { if (stored?.type === "oauth") {
const email = stored.email?.trim(); const email = stored.email?.trim();
return email ? `OAuth ${email}` : "OAuth (unknown)"; return {
label: email ? `OAuth ${email}` : "OAuth (unknown)",
source: `auth.json: ${formatPath(authPaths.authPath)}`,
};
} }
if (stored?.type === "api_key") { if (stored?.type === "api_key") {
return maskApiKey(stored.key); return {
label: maskApiKey(stored.key),
source: `auth.json: ${formatPath(authPaths.authPath)}`,
};
} }
const envKey = getEnvApiKey(provider); const envKey = getEnvApiKey(provider);
if (envKey) return maskApiKey(envKey); if (envKey) return { label: maskApiKey(envKey), source: "env" };
if (provider === "anthropic") { if (provider === "anthropic") {
const oauthEnv = process.env.ANTHROPIC_OAUTH_TOKEN?.trim(); const oauthEnv = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
if (oauthEnv) return "OAuth (env)"; if (oauthEnv) {
return { label: "OAuth (env)", source: "env: ANTHROPIC_OAUTH_TOKEN" };
}
} }
try { try {
const key = await authStorage.getApiKey(provider); const key = await authStorage.getApiKey(provider);
if (key) return maskApiKey(key); if (key) {
return {
label: maskApiKey(key),
source: `models.json: ${formatPath(authPaths.modelsPath)}`,
};
}
} catch { } catch {
// ignore missing auth // ignore missing auth
} }
return "missing"; return { label: "missing", source: "missing" };
}; };
export type InlineDirectives = { export type InlineDirectives = {
@@ -241,14 +257,24 @@ export async function handleDirectiveOnly(params: {
if (allowedModelCatalog.length === 0) { if (allowedModelCatalog.length === 0) {
return { text: "No models available." }; return { text: "No models available." };
} }
const authStorage = discoverAuthStorage(resolveClawdbotAgentDir()); const agentDir = resolveClawdbotAgentDir();
const authStorage = discoverAuthStorage(agentDir);
const authPaths = {
authPath: `${agentDir}/auth.json`,
modelsPath: `${agentDir}/models.json`,
};
hydrateAuthStorage(authStorage); hydrateAuthStorage(authStorage);
const authByProvider = new Map<string, string>(); const authByProvider = new Map<string, string>();
for (const entry of allowedModelCatalog) { for (const entry of allowedModelCatalog) {
if (authByProvider.has(entry.provider)) continue; if (authByProvider.has(entry.provider)) continue;
const auth = await resolveAuthLabel(
entry.provider,
authStorage,
authPaths,
);
authByProvider.set( authByProvider.set(
entry.provider, entry.provider,
await resolveAuthLabel(entry.provider, authStorage), `${auth.label} (${auth.source})`,
); );
} }
const current = `${params.provider}/${params.model}`; const current = `${params.provider}/${params.model}`;