mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Fix Claude test credential setup
This commit is contained in:
20
REPORT-claude-credentials-failure.md
Normal file
20
REPORT-claude-credentials-failure.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Claude test failures: real root cause
|
||||
|
||||
## Finding
|
||||
The Claude E2E tests were not failing due to missing credentials in general; they were failing because the tests overwrite `CLAUDE_CONFIG_DIR` with a fresh temp directory and do **not** carry forward Claude's credential store. This removes the SDK's `.credentials.json` file when the SDK is running in plaintext storage mode (common in CI), so the SDK cannot load OAuth/API credentials even though the user has valid credentials in the default config directory.
|
||||
|
||||
## Evidence
|
||||
- The Claude agent SDK stores credentials in a file named `.credentials.json` under its config directory:
|
||||
- `node_modules/@anthropic-ai/claude-agent-sdk/cli.js` shows the plaintext credential store path as `join(storageDir, ".credentials.json")`.
|
||||
- The tests set `process.env.CLAUDE_CONFIG_DIR` to a temp dir in:
|
||||
- `packages/server/src/server/agent/providers/claude-agent.test.ts` (via `useTempClaudeConfigDir`)
|
||||
- `packages/server/src/server/agent/agent-mcp.e2e.test.ts`
|
||||
- The temp config dir is populated with settings only, so the SDK can't find `.credentials.json` after the override.
|
||||
|
||||
## Fix summary
|
||||
- Copy `.credentials.json` from the original config directory (explicit `CLAUDE_CONFIG_DIR` or `~/.claude`) into the temp config dir used by tests.
|
||||
- Remove the hardcoded env-var-only credential precheck so tests fail with the *real* SDK error if credentials are genuinely absent.
|
||||
|
||||
## Files updated
|
||||
- `packages/server/src/server/agent/providers/claude-agent.test.ts`
|
||||
- `packages/server/src/server/agent/agent-mcp.e2e.test.ts`
|
||||
@@ -2,7 +2,7 @@ import net from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { existsSync } from "node:fs";
|
||||
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { copyFile, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { experimental_createMCPClient } from "ai";
|
||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||
@@ -36,6 +36,14 @@ const CLAUDE_SETTINGS = {
|
||||
},
|
||||
};
|
||||
|
||||
async function copyClaudeCredentials(sourceDir: string, targetDir: string): Promise<void> {
|
||||
const sourceCredentials = path.join(sourceDir, ".credentials.json");
|
||||
if (!existsSync(sourceCredentials)) {
|
||||
return;
|
||||
}
|
||||
await copyFile(sourceCredentials, path.join(targetDir, ".credentials.json"));
|
||||
}
|
||||
|
||||
async function getAvailablePort(): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = net.createServer();
|
||||
@@ -145,10 +153,13 @@ describe("agent MCP end-to-end", () => {
|
||||
process.env.CODEX_SESSION_DIR = codexSessionDir;
|
||||
process.env.CODEX_HOME = codexHome;
|
||||
const previousClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR;
|
||||
const sourceClaudeConfigDir =
|
||||
previousClaudeConfigDir ?? path.join(os.homedir(), ".claude");
|
||||
const claudeConfigDir = await mkdtemp(path.join(os.tmpdir(), "claude-config-"));
|
||||
const claudeSettingsText = `${JSON.stringify(CLAUDE_SETTINGS, null, 2)}\n`;
|
||||
await writeFile(path.join(claudeConfigDir, "settings.json"), claudeSettingsText, "utf8");
|
||||
await writeFile(path.join(claudeConfigDir, "settings.local.json"), claudeSettingsText, "utf8");
|
||||
await copyClaudeCredentials(sourceClaudeConfigDir, claudeConfigDir);
|
||||
process.env.CLAUDE_CONFIG_DIR = claudeConfigDir;
|
||||
|
||||
const daemon = await createPaseoDaemon(daemonConfig);
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
|
||||
import { createServer as createHTTPServer } from "http";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import {
|
||||
copyFileSync,
|
||||
existsSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
@@ -34,17 +35,6 @@ import { AgentManager } from "../agent-manager.js";
|
||||
import { AgentRegistry } from "../agent-registry.js";
|
||||
import { createAgentMcpServer } from "../mcp-server.js";
|
||||
|
||||
const hasClaudeCredentials = Boolean(
|
||||
process.env.CLAUDE_CODE_OAUTH_TOKEN || process.env.ANTHROPIC_API_KEY?.trim()?.length
|
||||
);
|
||||
const requireClaudeCredentials = () => {
|
||||
if (!hasClaudeCredentials) {
|
||||
throw new Error(
|
||||
"Claude credentials missing. Set CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY to run ClaudeAgentClient integration tests."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function tmpCwd(): string {
|
||||
const dir = mkdtempSync(path.join(os.tmpdir(), "claude-agent-e2e-"));
|
||||
try {
|
||||
@@ -54,8 +44,18 @@ function tmpCwd(): string {
|
||||
}
|
||||
}
|
||||
|
||||
function copyClaudeCredentials(sourceDir: string, targetDir: string): void {
|
||||
const sourceCredentials = path.join(sourceDir, ".credentials.json");
|
||||
if (!existsSync(sourceCredentials)) {
|
||||
return;
|
||||
}
|
||||
copyFileSync(sourceCredentials, path.join(targetDir, ".credentials.json"));
|
||||
}
|
||||
|
||||
function useTempClaudeConfigDir(): () => void {
|
||||
const previousConfigDir = process.env.CLAUDE_CONFIG_DIR;
|
||||
const sourceConfigDir =
|
||||
previousConfigDir ?? path.join(os.homedir(), ".claude");
|
||||
const configDir = mkdtempSync(path.join(os.tmpdir(), "claude-config-"));
|
||||
const settings = {
|
||||
permissions: {
|
||||
@@ -72,6 +72,7 @@ function useTempClaudeConfigDir(): () => void {
|
||||
const settingsText = `${JSON.stringify(settings, null, 2)}\n`;
|
||||
writeFileSync(path.join(configDir, "settings.json"), settingsText, "utf8");
|
||||
writeFileSync(path.join(configDir, "settings.local.json"), settingsText, "utf8");
|
||||
copyClaudeCredentials(sourceConfigDir, configDir);
|
||||
process.env.CLAUDE_CONFIG_DIR = configDir;
|
||||
return () => {
|
||||
if (previousConfigDir === undefined) {
|
||||
@@ -274,9 +275,6 @@ describe("ClaudeAgentClient (SDK integration)", () => {
|
||||
},
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
requireClaudeCredentials();
|
||||
});
|
||||
beforeAll(() => {
|
||||
restoreClaudeConfigDir = useTempClaudeConfigDir();
|
||||
});
|
||||
|
||||
3
plan.md
3
plan.md
@@ -325,7 +325,7 @@ Build a new Codex MCP provider side‑by‑side with the existing Codex SDK prov
|
||||
- All tests must run unconditionally and pass
|
||||
- **Done (2025-12-24 23:21)**: WHAT: tightened Claude permission parity setup/cleanup and sandbox config in `packages/server/src/server/agent/providers/claude-agent.test.ts:259` and `packages/server/src/server/agent/providers/claude-agent.test.ts:495`, enabled Claude user+project settings in `packages/server/src/server/agent/providers/claude-agent.ts:536`, set CLAUDE test sandbox config in `packages/server/src/server/agent/agent-mcp.e2e.test.ts:26`, restored wait_for_agent completion via stream events in `packages/server/src/server/agent/agent-manager.ts:696`, and sanitized permission payloads for MCP responses in `packages/server/src/server/agent/mcp-server.ts:81` + `packages/server/src/server/agent/mcp-server.ts:263`. RESULT: Claude permission allow/deny/abort flows resolve correctly and MCP agent run completes without timeouts; create_agent no longer fails schema validation. EVIDENCE: `npm run test --workspace=@paseo/server -- claude-agent.test.ts -t "permission flow parity"` (3/3 passed) and `npm run test --workspace=@paseo/server -- agent-mcp.e2e.test.ts` (1/1 passed).
|
||||
|
||||
- [ ] **Fix**: Claude credentials work - find REAL reason Claude tests fail.
|
||||
- [x] **Fix**: Claude credentials work - find REAL reason Claude tests fail.
|
||||
|
||||
- **Context**: You (the agent reading this) ARE Codex. The test runs Codex MCP which launches Claude.
|
||||
- "Missing Claude credentials" is FALSE - verified with `scripts/codex-claude-test.ts`:
|
||||
@@ -337,6 +337,7 @@ Build a new Codex MCP provider side‑by‑side with the existing Codex SDK prov
|
||||
- So why do the E2E tests claim credentials are missing?
|
||||
- Investigate what the Claude E2E tests are ACTUALLY doing differently
|
||||
- Run the failing test with debug output and capture exact error
|
||||
- **Done (2025-12-24 23:30)**: WHAT: copied Claude SDK `.credentials.json` into temp config dirs in `packages/server/src/server/agent/providers/claude-agent.test.ts:47` and `packages/server/src/server/agent/agent-mcp.e2e.test.ts:39`, removed the env-only credential gate in `packages/server/src/server/agent/providers/claude-agent.test.ts:55`, and documented the root cause in `REPORT-claude-credentials-failure.md`. RESULT: Claude tests no longer falsely report missing credentials when auth is stored in the default config dir. EVIDENCE: `npm run test --workspace=@paseo/server -- agent-mcp.e2e.test.ts` (1/1 passed) and `npm run test --workspace=@paseo/server -- claude-agent.test.ts -t "responds with text"` (1/1 passed; remaining tests skipped by filter).
|
||||
|
||||
- [x] **Test (E2E) CRITICAL**: Interruption/abort latency for Codex MCP provider.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user