feat: improve agent auth guidance
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -113,6 +113,7 @@ export async function compactEmbeddedPiSession(params: {
|
||||
const apiKeyInfo = await getApiKeyForModel({
|
||||
model,
|
||||
cfg: params.config,
|
||||
agentDir,
|
||||
});
|
||||
|
||||
if (model.provider === "github-copilot") {
|
||||
|
||||
@@ -133,6 +133,7 @@ export async function runEmbeddedPiAgent(
|
||||
cfg: params.config,
|
||||
profileId: candidate,
|
||||
store: authStore,
|
||||
agentDir,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user