feat: improve agent auth guidance
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
### Changes
|
### Changes
|
||||||
- Docs: clarify per-agent auth stores, sandboxed skill binaries, and elevated semantics.
|
- 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.
|
- 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`.
|
- 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`.
|
- 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.
|
- 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 Api, getEnvApiKey, type Model } from "@mariozechner/pi-ai";
|
||||||
import type { ClawdbotConfig } from "../config/config.js";
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
import type { ModelProviderConfig } from "../config/types.js";
|
import type { ModelProviderConfig } from "../config/types.js";
|
||||||
@@ -8,6 +10,7 @@ import {
|
|||||||
listProfilesForProvider,
|
listProfilesForProvider,
|
||||||
resolveApiKeyForProfile,
|
resolveApiKeyForProfile,
|
||||||
resolveAuthProfileOrder,
|
resolveAuthProfileOrder,
|
||||||
|
resolveAuthStorePathForDisplay,
|
||||||
} from "./auth-profiles.js";
|
} from "./auth-profiles.js";
|
||||||
import { normalizeProviderId } from "./model-selection.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 };
|
export type EnvApiKeyResult = { apiKey: string; source: string };
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ export async function compactEmbeddedPiSession(params: {
|
|||||||
const apiKeyInfo = await getApiKeyForModel({
|
const apiKeyInfo = await getApiKeyForModel({
|
||||||
model,
|
model,
|
||||||
cfg: params.config,
|
cfg: params.config,
|
||||||
|
agentDir,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (model.provider === "github-copilot") {
|
if (model.provider === "github-copilot") {
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ export async function runEmbeddedPiAgent(
|
|||||||
cfg: params.config,
|
cfg: params.config,
|
||||||
profileId: candidate,
|
profileId: candidate,
|
||||||
store: authStore,
|
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 { ensureAuthProfileStore } from "../agents/auth-profiles.js";
|
||||||
|
import { resolveAuthStorePath } from "../agents/auth-profiles/paths.js";
|
||||||
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../config/config.js";
|
import { CONFIG_PATH_CLAWDBOT, writeConfigFile } from "../config/config.js";
|
||||||
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
|
import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
@@ -31,6 +35,15 @@ type AgentsAddOptions = {
|
|||||||
json?: boolean;
|
json?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function fileExists(pathname: string): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await fs.stat(pathname);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function agentsAddCommand(
|
export async function agentsAddCommand(
|
||||||
opts: AgentsAddOptions,
|
opts: AgentsAddOptions,
|
||||||
runtime: RuntimeEnv = defaultRuntime,
|
runtime: RuntimeEnv = defaultRuntime,
|
||||||
@@ -205,6 +218,26 @@ export async function agentsAddCommand(
|
|||||||
agentDir,
|
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({
|
const wantsAuth = await prompter.confirm({
|
||||||
message: "Configure model/auth for this agent now?",
|
message: "Configure model/auth for this agent now?",
|
||||||
initialValue: false,
|
initialValue: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user