feat: improve agent auth guidance

This commit is contained in:
Peter Steinberger
2026-01-15 04:41:50 +00:00
parent c4402a1ce5
commit fa4670c5fe
5 changed files with 49 additions and 2 deletions

View File

@@ -5,6 +5,7 @@
### Changes
- Docs: clarify per-agent auth stores, sandboxed skill binaries, and elevated semantics.
- Docs: add FAQ entries for missing provider auth after adding agents and Gemini thinking signature errors.
- Agents: add optional auth-profile copy prompt on `agents add` and improve auth error messaging.
- Security: add `clawdbot security audit` (`--deep`, `--fix`) and surface it in `status --all` and `doctor`.
- Onboarding: add a security checkpoint prompt (docs link + sandboxing hint); require `--accept-risk` for `--non-interactive`.
- Docs: expand gateway security hardening guidance and incident response checklist.

View File

@@ -1,3 +1,5 @@
import path from "node:path";
import { type Api, getEnvApiKey, type Model } from "@mariozechner/pi-ai";
import type { ClawdbotConfig } from "../config/config.js";
import type { ModelProviderConfig } from "../config/types.js";
@@ -8,6 +10,7 @@ import {
listProfilesForProvider,
resolveApiKeyForProfile,
resolveAuthProfileOrder,
resolveAuthStorePathForDisplay,
} from "./auth-profiles.js";
import { normalizeProviderId } from "./model-selection.js";
@@ -94,7 +97,15 @@ export async function resolveApiKeyForProvider(params: {
}
}
throw new Error(`No API key found for provider "${provider}".`);
const authStorePath = resolveAuthStorePathForDisplay(params.agentDir);
const resolvedAgentDir = path.dirname(authStorePath);
throw new Error(
[
`No API key found for provider "${provider}".`,
`Auth store: ${authStorePath} (agentDir: ${resolvedAgentDir}).`,
"Configure auth for this agent (clawdbot agents add <id>) or copy auth-profiles.json from the main agentDir.",
].join(" "),
);
}
export type EnvApiKeyResult = { apiKey: string; source: string };

View File

@@ -113,6 +113,7 @@ export async function compactEmbeddedPiSession(params: {
const apiKeyInfo = await getApiKeyForModel({
model,
cfg: params.config,
agentDir,
});
if (model.provider === "github-copilot") {

View File

@@ -133,6 +133,7 @@ export async function runEmbeddedPiAgent(
cfg: params.config,
profileId: candidate,
store: authStore,
agentDir,
});
};

View File

@@ -1,5 +1,9 @@
import { resolveAgentDir, resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
import fs from "node:fs/promises";
import path from "node:path";
import { resolveAgentDir, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
import { resolveAuthStorePath } from "../agents/auth-profiles/paths.js";
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../config/config.js";
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
import type { RuntimeEnv } from "../runtime.js";
@@ -31,6 +35,15 @@ type AgentsAddOptions = {
json?: boolean;
};
async function fileExists(pathname: string): Promise<boolean> {
try {
await fs.stat(pathname);
return true;
} catch {
return false;
}
}
export async function agentsAddCommand(
opts: AgentsAddOptions,
runtime: RuntimeEnv = defaultRuntime,
@@ -205,6 +218,26 @@ export async function agentsAddCommand(
agentDir,
});
const defaultAgentId = resolveDefaultAgentId(cfg);
if (defaultAgentId !== agentId) {
const sourceAuthPath = resolveAuthStorePath(resolveAgentDir(cfg, defaultAgentId));
const destAuthPath = resolveAuthStorePath(agentDir);
const sameAuthPath =
path.resolve(sourceAuthPath).toLowerCase() ===
path.resolve(destAuthPath).toLowerCase();
if (!sameAuthPath && (await fileExists(sourceAuthPath)) && !(await fileExists(destAuthPath))) {
const shouldCopy = await prompter.confirm({
message: `Copy auth profiles from "${defaultAgentId}"?`,
initialValue: false,
});
if (shouldCopy) {
await fs.mkdir(path.dirname(destAuthPath), { recursive: true });
await fs.copyFile(sourceAuthPath, destAuthPath);
await prompter.note(`Copied auth profiles from "${defaultAgentId}".`, "Auth profiles");
}
}
}
const wantsAuth = await prompter.confirm({
message: "Configure model/auth for this agent now?",
initialValue: false,