From 28b559b7a0bf4ac0e08abd4ab9dbbebfd47f3b10 Mon Sep 17 00:00:00 2001 From: Mantra <87142457+mantrakp04@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:53:37 -0700 Subject: [PATCH] fix: resolve MODULE_NOT_FOUND for claude-agent-sdk cli.js in RDE config updates (#1639) --- apps/dashboard/next.config.mjs | 18 ++----- packages/cli/scripts/copy-runtime-assets.mjs | 51 +++++++++++++++++++- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/apps/dashboard/next.config.mjs b/apps/dashboard/next.config.mjs index 47fe847bb..549da815d 100644 --- a/apps/dashboard/next.config.mjs +++ b/apps/dashboard/next.config.mjs @@ -1,12 +1,8 @@ import { withSentryConfig } from "@sentry/nextjs"; -import { createRequire } from "module"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const sharedBackendRequire = createRequire(path.join(__dirname, "../../packages/shared-backend/package.json")); -const claudeAgentSdkDir = path.dirname(sharedBackendRequire.resolve("@anthropic-ai/claude-agent-sdk")); -const claudeAgentSdkTraceDir = path.relative(__dirname, claudeAgentSdkDir); const withConfiguredSentryConfig = (nextConfig) => withSentryConfig( @@ -67,15 +63,11 @@ const nextConfig = { output: process.env.NEXT_CONFIG_OUTPUT, distDir: process.env.HEXCLAVE_DASHBOARD_NEXT_DIST_DIR, outputFileTracingRoot: path.join(__dirname, "../.."), - outputFileTracingIncludes: { - "/api/remote-development-environment/config/apply-update": [ - path.join(claudeAgentSdkTraceDir, "cli.js"), - path.join(claudeAgentSdkTraceDir, "manifest.json"), - path.join(claudeAgentSdkTraceDir, "manifest.zst.json"), - path.join(claudeAgentSdkTraceDir, "resvg.wasm"), - path.join(claudeAgentSdkTraceDir, "vendor/**/*"), - ], - }, + // The claude-agent-sdk spawns cli.js as a child process (resolved via + // import.meta.url). Keeping it external ensures the entire package directory + // is included in the standalone trace and hoisted to top-level node_modules/ + // by copy-runtime-assets — so cli.js, vendor/, etc. survive .pnpm removal. + serverExternalPackages: ["@anthropic-ai/claude-agent-sdk"], pageExtensions: ["js", "jsx", "mdx", "ts", "tsx"], diff --git a/packages/cli/scripts/copy-runtime-assets.mjs b/packages/cli/scripts/copy-runtime-assets.mjs index d3646858c..8ae804e59 100644 --- a/packages/cli/scripts/copy-runtime-assets.mjs +++ b/packages/cli/scripts/copy-runtime-assets.mjs @@ -1,5 +1,5 @@ #!/usr/bin/env node -import { cpSync, existsSync, readlinkSync, readdirSync, rmSync } from "fs"; +import { cpSync, existsSync, mkdirSync, readlinkSync, readdirSync, rmSync } from "fs"; import { dirname, join, relative, resolve } from "path"; import { fileURLToPath } from "url"; @@ -123,12 +123,61 @@ function hoistPnpmNodeModules(pnpmDir) { } } +function getPackageNameFromPnpmSpecifier(specifier) { + // Extract the package name from a pnpm store specifier directory name. + // e.g. "@anthropic-ai+claude-agent-sdk@0.2.73_zod@4.3.6" → "@anthropic-ai/claude-agent-sdk" + // e.g. "react@19.2.3" → "react" + const atVersionIndex = specifier.indexOf("@", specifier.startsWith("@") ? 1 : 0); + if (atVersionIndex < 0) { + return undefined; + } + const nameWithPlus = specifier.slice(0, atVersionIndex); + if (nameWithPlus.startsWith("@")) { + const plusIndex = nameWithPlus.indexOf("+"); + if (plusIndex >= 0) { + return nameWithPlus.slice(0, plusIndex) + "/" + nameWithPlus.slice(plusIndex + 1); + } + } + return nameWithPlus; +} + +function hoistPnpmStorePackages(pnpmDir) { + // Each .pnpm//node_modules// contains the primary package + // for that specifier. When serverExternalPackages are used, these directories + // hold the actual module files (including non-imported assets like cli.js) + // that Node.js needs to resolve at runtime. Hoist them to the top-level + // node_modules/ so they survive .pnpm removal. + const destNodeModules = dirname(pnpmDir); + for (const entry of readdirSync(pnpmDir, { withFileTypes: true })) { + if (!entry.isDirectory() || entry.name === "node_modules" || entry.name.startsWith(".")) { + continue; + } + const packageName = getPackageNameFromPnpmSpecifier(entry.name); + if (packageName == null || EXCLUDED_RUNTIME_PACKAGES.has(packageName)) { + continue; + } + const packageDir = join(pnpmDir, entry.name, "node_modules", ...packageName.split("/")); + if (!existsSync(packageDir) || !existsSync(join(packageDir, "package.json"))) { + continue; + } + const dest = join(destNodeModules, packageName); + if (!existsSync(dest)) { + const scopeDir = packageName.includes("/") ? join(destNodeModules, packageName.split("/")[0]) : null; + if (scopeDir && !existsSync(scopeDir)) { + mkdirSync(scopeDir, { recursive: true }); + } + cpSync(packageDir, dest, { recursive: true, dereference: true }); + } + } +} + function removePnpmStore(nodeModulesDir) { const pnpmDir = join(nodeModulesDir, ".pnpm"); if (!existsSync(pnpmDir)) { return; } hoistPnpmNodeModules(pnpmDir); + hoistPnpmStorePackages(pnpmDir); rmSync(pnpmDir, { recursive: true, force: true }); }