diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b993358..dfc2bf4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,10 +26,10 @@ jobs: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: - version: 10.5.1 + version: 11.5.2 - uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "22.13.0" cache: "pnpm" - name: Install dependencies run: pnpm install && cd frontend && pnpm install @@ -43,10 +43,10 @@ jobs: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 with: - version: 10.5.1 + version: 11.5.2 - uses: actions/setup-node@v4 with: - node-version: "18.x" + node-version: "22.13.0" cache: "pnpm" - name: Install dependencies run: pnpm install && cd frontend && pnpm install diff --git a/Dockerfile b/Dockerfile index ce5fde7..b6ce79b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,16 +6,16 @@ # ============================================================================ # ---------- Stage 1: base ---------- -FROM node:20-alpine AS base +FROM node:22.13-alpine AS base RUN apk add --no-cache libc6-compat openssl RUN corepack enable pnpm # ---------- Stage 2: deps ---------- FROM base AS deps WORKDIR /app -COPY package.json pnpm-lock.yaml ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ RUN pnpm install --frozen-lockfile -COPY frontend/package.json frontend/pnpm-lock.yaml ./frontend/ +COPY frontend/package.json frontend/pnpm-lock.yaml frontend/pnpm-workspace.yaml ./frontend/ RUN cd frontend && pnpm install --frozen-lockfile --ignore-scripts # ---------- Stage 3: builder ---------- diff --git a/README.ko.md b/README.ko.md index 0c3a58a..9e819f8 100644 --- a/README.ko.md +++ b/README.ko.md @@ -54,8 +54,8 @@ ### 요구사항 -- **Node.js** 18+ -- **pnpm** 8+ +- **Node.js** 22.13+ +- **pnpm** 10+ (프로젝트는 pnpm 11.5.2를 고정 사용) - 실행 중인 **LangGraph 서버** (`langgraph dev`) ### 설치 및 실행 @@ -69,6 +69,8 @@ pnpm launch `pnpm launch`를 실행하면 대화형 설정 마법사가 시작됩니다: 실행 모드, 인증 모드, LangGraph 서버 URL, LangSmith API 키, 데이터베이스 마이그레이션, 서버 자동 시작. +pnpm을 사용할 수 없거나 고정 버전이 적용되지 않으면 `corepack enable` 실행 후 다시 시도하세요. + > 인증 모드별 상세 설정은 `examples/` 폴더의 예제를 참고하세요. ### 인증 모드 diff --git a/README.md b/README.md index e35b989..ee10375 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ A Next.js web app for interacting with [LangGraph](https://github.com/langchain- ### Prerequisites -- **Node.js** 18+ -- **pnpm** 8+ +- **Node.js** 22.13+ +- **pnpm** 10+ (the project pins pnpm 11.5.2) - A running **LangGraph server** (`langgraph dev`) ### Installation @@ -69,6 +69,8 @@ pnpm launch `pnpm launch` runs an interactive setup wizard: run mode, auth mode, LangGraph server URL, LangSmith API key, database migration, and auto-start. +If pnpm is unavailable or does not use the pinned version, run `corepack enable` and retry. + > See `examples/` for per-mode configuration examples. ### Auth Modes diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md index 313bf92..c8f6086 100644 --- a/docs/QUICK_START.md +++ b/docs/QUICK_START.md @@ -4,7 +4,7 @@ The fastest way to get LangGraph Chat UI running locally with no authentication. ## Prerequisites -- Node.js 18+, pnpm 8+ +- Node.js 22.13+, pnpm 10+ (the project pins pnpm 11.5.2) - A LangGraph server running (e.g. `langgraph dev`) ## Steps @@ -16,6 +16,8 @@ pnpm install pnpm launch # interactive setup — select "standalone" mode ``` +If pnpm is unavailable or does not use the pinned version, run `corepack enable` and retry. + Or manually: ```bash diff --git a/frontend/.npmrc b/frontend/.npmrc deleted file mode 100644 index 73e93ed..0000000 --- a/frontend/.npmrc +++ /dev/null @@ -1 +0,0 @@ -hoist-pattern=** diff --git a/frontend/package.json b/frontend/package.json index c08a247..961586f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -103,5 +103,8 @@ "overrides": { "react-is": "^19.0.0-rc-69d4b800-20241021" }, - "packageManager": "pnpm@10.5.1" + "packageManager": "pnpm@11.5.2", + "engines": { + "node": ">=22.13.0" + } } diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml new file mode 100644 index 0000000..28102dd --- /dev/null +++ b/frontend/pnpm-workspace.yaml @@ -0,0 +1,14 @@ +confirmModulesPurge: false + +hoistPattern: + - "**" + +allowBuilds: + "@parcel/watcher": true + "@prisma/client": true + "@prisma/engines": true + "@swc/core": true + esbuild: true + prisma: true + sharp: true + unrs-resolver: true diff --git a/frontend/scripts/prisma-generate.mjs b/frontend/scripts/prisma-generate.mjs index 816a26c..4685dfd 100644 --- a/frontend/scripts/prisma-generate.mjs +++ b/frontend/scripts/prisma-generate.mjs @@ -38,7 +38,7 @@ if (schema !== originalSchema) { // Run prisma generate try { - execSync("npx prisma generate", { + execSync("pnpm exec prisma generate", { stdio: "inherit", cwd: resolve(__dirname, ".."), }); @@ -46,7 +46,7 @@ try { console.warn( "[prisma-generate] Failed to generate Prisma client. " + "This is OK if you are using AUTH_MODE=none (no database required). " + - "If you need authentication, check your network/SSL settings and run: npx prisma generate", + "If you need authentication, check your network/SSL settings and run: pnpm exec prisma generate", ); // Create stub .prisma/client so the bundler can resolve the module. diff --git a/package.json b/package.json index 2437bd5..f94bee1 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "description": "Chat UI for LangGraph agents with multiple authentication modes", "scripts": { "prelaunch": "pnpm install", - "launch": "npx tsx scripts/setup.ts", + "launch": "tsx scripts/setup.ts", "dev": "cd frontend && pnpm dev", "build": "cd frontend && pnpm build", "start": "cd frontend && pnpm start", @@ -20,8 +20,9 @@ "picocolors": "^1.0.0", "tsx": "^4.0.0" }, + "packageManager": "pnpm@11.5.2", "engines": { - "node": ">=18.0.0" + "node": ">=22.13.0" }, "license": "Apache-2.0" } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..5baf7c8 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +confirmModulesPurge: false + +allowBuilds: + esbuild: true diff --git a/scripts/setup.ts b/scripts/setup.ts index a2cd002..9e4bb57 100644 --- a/scripts/setup.ts +++ b/scripts/setup.ts @@ -1068,8 +1068,8 @@ async function runSetup(config: SetupConfig) { if (needsDatabase) { s.start(t("settingUpDb")); try { - await runAsync("npx", ["prisma", "generate"], { cwd: FRONTEND_DIR }); - await runAsync("npx", ["prisma", "db", "push", "--skip-generate"], { cwd: FRONTEND_DIR }); + await runAsync("pnpm", ["exec", "prisma", "generate"], { cwd: FRONTEND_DIR }); + await runAsync("pnpm", ["exec", "prisma", "db", "push", "--skip-generate"], { cwd: FRONTEND_DIR }); s.stop(t("dbReady")); } catch (error) { s.stop(LANG === "ko" ? "데이터베이스 설정 실패" : "Failed to setup database"); @@ -1162,10 +1162,10 @@ async function runDevelopment(config: SetupConfig) { p.log.info(pc.dim(t("pressCtrlC"))); - // Run dev server with minimal output + // Run dev server and surface its logs so startup failures are actionable. const devProcess = crossSpawn("pnpm", ["dev"], { cwd: FRONTEND_DIR, - stdio: ["ignore", "ignore", "ignore"], + stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, NODE_NO_WARNINGS: "1" }, }); @@ -1174,6 +1174,14 @@ async function runDevelopment(config: SetupConfig) { process.exit(1); }); + devProcess.on("close", (code, signal) => { + if (signal) { + p.log.error(`Development server exited with signal ${signal}`); + process.exit(1); + } + process.exit(code ?? 1); + }); + // Handle exit process.on("SIGINT", () => { devProcess.kill(); @@ -1359,7 +1367,7 @@ ${pc.cyan(`tail -f ${logFile}`)}`, const startProcess = crossSpawn("pnpm", ["start"], { cwd: FRONTEND_DIR, - stdio: ["ignore", "ignore", "ignore"], + stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, NODE_NO_WARNINGS: "1" }, }); @@ -1368,6 +1376,14 @@ ${pc.cyan(`tail -f ${logFile}`)}`, process.exit(1); }); + startProcess.on("close", (code, signal) => { + if (signal) { + p.log.error(`Production server exited with signal ${signal}`); + process.exit(1); + } + process.exit(code ?? 1); + }); + process.on("SIGINT", () => { startProcess.kill(); p.outro(pc.green(LANG === "ko" ? "서버가 중지되었습니다." : "Server stopped.")); @@ -1520,14 +1536,14 @@ function getEnvVarsForProduction(config: SetupConfig): string { } function createDockerfile() { - const dockerfile = `FROM node:20-alpine AS base + const dockerfile = `FROM node:22.13-alpine AS base # Install dependencies only when needed FROM base AS deps RUN apk add --no-cache libc6-compat WORKDIR /app -COPY package.json pnpm-lock.yaml* ./ +COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml* ./ RUN corepack enable pnpm && pnpm i --frozen-lockfile # Rebuild the source code only when needed