fix(agents): stabilize cli creds cache + bash cwd
This commit is contained in:
@@ -114,6 +114,7 @@ export type BashToolDetails =
|
||||
sessionId: string;
|
||||
pid?: number;
|
||||
startedAt: number;
|
||||
cwd?: string;
|
||||
tail?: string;
|
||||
}
|
||||
| {
|
||||
@@ -121,6 +122,7 @@ export type BashToolDetails =
|
||||
exitCode: number | null;
|
||||
durationMs: number;
|
||||
aggregated: string;
|
||||
cwd?: string;
|
||||
};
|
||||
|
||||
export function createBashTool(
|
||||
@@ -316,6 +318,7 @@ export function createBashTool(
|
||||
sessionId,
|
||||
pid: session.pid ?? undefined,
|
||||
startedAt,
|
||||
cwd: session.cwd,
|
||||
tail: session.tail,
|
||||
},
|
||||
});
|
||||
@@ -356,6 +359,7 @@ export function createBashTool(
|
||||
sessionId,
|
||||
pid: session.pid ?? undefined,
|
||||
startedAt,
|
||||
cwd: session.cwd,
|
||||
tail: session.tail,
|
||||
},
|
||||
}),
|
||||
@@ -431,6 +435,7 @@ export function createBashTool(
|
||||
exitCode: code ?? 0,
|
||||
durationMs,
|
||||
aggregated,
|
||||
cwd: session.cwd,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -136,10 +136,12 @@ describe("cli credentials", () => {
|
||||
const first = readClaudeCliCredentialsCached({
|
||||
allowKeychainPrompt: true,
|
||||
ttlMs: 15 * 60 * 1000,
|
||||
platform: "darwin",
|
||||
});
|
||||
const second = readClaudeCliCredentialsCached({
|
||||
allowKeychainPrompt: false,
|
||||
ttlMs: 15 * 60 * 1000,
|
||||
platform: "darwin",
|
||||
});
|
||||
|
||||
expect(first).toBeTruthy();
|
||||
@@ -167,6 +169,7 @@ describe("cli credentials", () => {
|
||||
const first = readClaudeCliCredentialsCached({
|
||||
allowKeychainPrompt: true,
|
||||
ttlMs: 15 * 60 * 1000,
|
||||
platform: "darwin",
|
||||
});
|
||||
|
||||
vi.advanceTimersByTime(15 * 60 * 1000 + 1);
|
||||
@@ -174,6 +177,7 @@ describe("cli credentials", () => {
|
||||
const second = readClaudeCliCredentialsCached({
|
||||
allowKeychainPrompt: true,
|
||||
ttlMs: 15 * 60 * 1000,
|
||||
platform: "darwin",
|
||||
});
|
||||
|
||||
expect(first).toBeTruthy();
|
||||
|
||||
@@ -111,8 +111,11 @@ function readClaudeCliKeychainCredentials(): ClaudeCliCredential | null {
|
||||
|
||||
export function readClaudeCliCredentials(options?: {
|
||||
allowKeychainPrompt?: boolean;
|
||||
platform?: NodeJS.Platform;
|
||||
homeDir?: string;
|
||||
}): ClaudeCliCredential | null {
|
||||
if (process.platform === "darwin" && options?.allowKeychainPrompt !== false) {
|
||||
const platform = options?.platform ?? process.platform;
|
||||
if (platform === "darwin" && options?.allowKeychainPrompt !== false) {
|
||||
const keychainCreds = readClaudeCliKeychainCredentials();
|
||||
if (keychainCreds) {
|
||||
log.info("read anthropic credentials from claude cli keychain", {
|
||||
@@ -122,7 +125,7 @@ export function readClaudeCliCredentials(options?: {
|
||||
}
|
||||
}
|
||||
|
||||
const credPath = resolveClaudeCliCredentialsPath();
|
||||
const credPath = resolveClaudeCliCredentialsPath(options?.homeDir);
|
||||
const raw = loadJsonFile(credPath);
|
||||
if (!raw || typeof raw !== "object") return null;
|
||||
|
||||
@@ -158,10 +161,12 @@ export function readClaudeCliCredentials(options?: {
|
||||
export function readClaudeCliCredentialsCached(options?: {
|
||||
allowKeychainPrompt?: boolean;
|
||||
ttlMs?: number;
|
||||
platform?: NodeJS.Platform;
|
||||
homeDir?: string;
|
||||
}): ClaudeCliCredential | null {
|
||||
const ttlMs = options?.ttlMs ?? 0;
|
||||
const now = Date.now();
|
||||
const cacheKey = resolveClaudeCliCredentialsPath();
|
||||
const cacheKey = resolveClaudeCliCredentialsPath(options?.homeDir);
|
||||
if (
|
||||
ttlMs > 0 &&
|
||||
claudeCliCache &&
|
||||
@@ -172,6 +177,8 @@ export function readClaudeCliCredentialsCached(options?: {
|
||||
}
|
||||
const value = readClaudeCliCredentials({
|
||||
allowKeychainPrompt: options?.allowKeychainPrompt,
|
||||
platform: options?.platform,
|
||||
homeDir: options?.homeDir,
|
||||
});
|
||||
if (ttlMs > 0) {
|
||||
claudeCliCache = { value, readAt: now, cacheKey };
|
||||
|
||||
@@ -5,9 +5,6 @@ import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createClawdbotCodingTools } from "./pi-tools.js";
|
||||
|
||||
const normalizeText = (value?: string) =>
|
||||
(value ?? "").replace(/\r\n/g, "\n").trim();
|
||||
|
||||
async function withTempDir<T>(prefix: string, fn: (dir: string) => Promise<T>) {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
||||
try {
|
||||
@@ -120,11 +117,17 @@ describe("workspace path resolution", () => {
|
||||
expect(bashTool).toBeDefined();
|
||||
|
||||
const result = await bashTool?.execute("ws-bash", {
|
||||
command: 'node -e "console.log(process.cwd())"',
|
||||
command: "echo ok",
|
||||
});
|
||||
const output = normalizeText(getTextContent(result));
|
||||
const cwd =
|
||||
result?.details &&
|
||||
typeof result.details === "object" &&
|
||||
"cwd" in result.details
|
||||
? (result.details as { cwd?: string }).cwd
|
||||
: undefined;
|
||||
expect(cwd).toBeTruthy();
|
||||
const [resolvedOutput, resolvedWorkspace] = await Promise.all([
|
||||
fs.realpath(output),
|
||||
fs.realpath(String(cwd)),
|
||||
fs.realpath(workspaceDir),
|
||||
]);
|
||||
expect(resolvedOutput).toBe(resolvedWorkspace);
|
||||
@@ -139,12 +142,18 @@ describe("workspace path resolution", () => {
|
||||
expect(bashTool).toBeDefined();
|
||||
|
||||
const result = await bashTool?.execute("ws-bash-override", {
|
||||
command: 'node -e "console.log(process.cwd())"',
|
||||
command: "echo ok",
|
||||
workdir: overrideDir,
|
||||
});
|
||||
const output = normalizeText(getTextContent(result));
|
||||
const cwd =
|
||||
result?.details &&
|
||||
typeof result.details === "object" &&
|
||||
"cwd" in result.details
|
||||
? (result.details as { cwd?: string }).cwd
|
||||
: undefined;
|
||||
expect(cwd).toBeTruthy();
|
||||
const [resolvedOutput, resolvedOverride] = await Promise.all([
|
||||
fs.realpath(output),
|
||||
fs.realpath(String(cwd)),
|
||||
fs.realpath(overrideDir),
|
||||
]);
|
||||
expect(resolvedOutput).toBe(resolvedOverride);
|
||||
|
||||
Reference in New Issue
Block a user