Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions apps/server/src/provider/Drivers/ClaudeExecutable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const NPM_DIR = "C:\\Users\\dev\\AppData\\Roaming\\npm";
const NPM_SHIM = `${NPM_DIR}\\claude.cmd`;
const NPM_PACKAGE_EXE = `${NPM_DIR}\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe`;
const NPM_PACKAGE_CLI = `${NPM_DIR}\\node_modules\\@anthropic-ai\\claude-code\\cli.js`;
const WINGET_DIR = "C:\\Users\\dev\\AppData\\Local\\Microsoft\\WinGet";
const WINGET_SHIM = `${WINGET_DIR}\\Links\\claude.cmd`;
const WINGET_EXE = `${WINGET_DIR}\\Packages\\Anthropic.ClaudeCode_Microsoft.Winget.Source_8wekyb3d8bbwe\\claude.exe`;

function withWindowsResolution(input: {
readonly resolvedCommand: string | undefined;
Expand Down Expand Up @@ -37,6 +40,16 @@ describe("resolveClaudeSdkExecutablePath", () => {
}),
);

it.effect("follows the WinGet launcher to the package's native binary", () =>
Effect.gen(function* () {
expect(
yield* resolveClaudeSdkExecutablePath("claude", {}).pipe(
withWindowsResolution({ resolvedCommand: WINGET_SHIM, existingFiles: [WINGET_EXE] }),
),
).toBe(WINGET_EXE);
}),
);

it.effect("follows .bat and .ps1 launcher shims the same way", () =>
Effect.gen(function* () {
for (const shim of [`${NPM_DIR}\\claude.bat`, `${NPM_DIR}\\claude.ps1`]) {
Expand Down
10 changes: 7 additions & 3 deletions apps/server/src/provider/Drivers/ClaudeExecutable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ const WINDOWS_SHIM_EXTENSIONS: ReadonlySet<string> = new Set([".cmd", ".bat", ".
export const isWindowsClaudeLauncherShimPath = (filePath: string): boolean =>
WINDOWS_SHIM_EXTENSIONS.has(NodePath.win32.extname(filePath).toLowerCase());

/** New packages provide bin/claude.exe; older packages need a JavaScript runtime for cli.js. */
const NPM_PACKAGE_ENTRY_CANDIDATES = [
/**
* npm: new packages provide bin/claude.exe; older ones need a JavaScript runtime for cli.js.
* WinGet: Links\claude.cmd forwards to the package's native executable under ..\Packages.
*/
const PACKAGE_ENTRY_CANDIDATES = [
["node_modules", "@anthropic-ai", "claude-code", "bin", "claude.exe"],
["node_modules", "@anthropic-ai", "claude-code", "cli.js"],
["..", "Packages", "Anthropic.ClaudeCode_Microsoft.Winget.Source_8wekyb3d8bbwe", "claude.exe"],
] as const;

export type ExecutableFileCheck = (filePath: string) => boolean;
Expand Down Expand Up @@ -53,7 +57,7 @@ export const resolveClaudeSdkExecutablePath = Effect.fn("resolveClaudeSdkExecuta
}

const shimDirectory = NodePath.win32.dirname(resolved);
for (const entrySegments of NPM_PACKAGE_ENTRY_CANDIDATES) {
for (const entrySegments of PACKAGE_ENTRY_CANDIDATES) {
const candidate = NodePath.win32.join(shimDirectory, ...entrySegments);
if (isFile(candidate)) {
return candidate;
Expand Down
Loading