Skip to content

Commit 17a3305

Browse files
committed
feat(agents): add explicit support for bun projects
1 parent d7cb889 commit 17a3305

File tree

4 files changed

+226
-2
lines changed

4 files changed

+226
-2
lines changed

pkg/agentfs/docker.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ Please ensure your project has the appropriate dependency file, or create a Dock
8181
case ProjectTypeNodeYarnBerry:
8282
fmt.Printf("✔ Detected Node.js project with Yarn Berry package manager\n")
8383
fmt.Printf(" Using template [%s] with Yarn v2+ and PnP support\n", util.Accented("node.yarn-berry"))
84+
case ProjectTypeNodeBun:
85+
fmt.Printf("✔ Detected Node.js project with Bun runtime and package manager\n")
86+
fmt.Printf(" Using template [%s] for ultra-fast performance\n", util.Accented("node.bun"))
8487
case ProjectTypePythonUV:
8588
fmt.Printf("✔ Detected Python project with UV package manager\n")
8689
fmt.Printf(" Using template [%s] for faster builds\n", util.Accented("python.uv"))
@@ -129,6 +132,9 @@ Please ensure your project has the appropriate dependency file, or create a Dock
129132
} else if projectType == ProjectTypeNodeYarnBerry {
130133
// Validate yarn berry project setup
131134
validateYarnBerryProject(dir, silent)
135+
} else if projectType == ProjectTypeNodeBun {
136+
// Validate bun project setup
137+
validateBunProject(dir, silent)
132138
}
133139

134140
var dockerfileContent []byte
@@ -289,6 +295,18 @@ func validateYarnBerryProject(dir string, silent bool) {
289295
}
290296
}
291297

298+
func validateBunProject(dir string, silent bool) {
299+
bunLockPath := filepath.Join(dir, "bun.lockb")
300+
if _, err := os.Stat(bunLockPath); err != nil {
301+
if !silent {
302+
fmt.Printf("! Warning: Bun project detected but %s file not found\n", util.Accented("bun.lockb"))
303+
fmt.Printf(" Consider running %s to generate %s for reproducible builds\n", util.Accented("bun install"), util.Accented("bun.lockb"))
304+
fmt.Printf(" This ensures consistent dependency versions across environments\n")
305+
fmt.Printf(" Note: bun.lockb is a binary file, ensure it's committed to version control\n\n")
306+
}
307+
}
308+
}
309+
292310
func validateEntrypoint(dir string, dockerfileContent []byte, dockerignoreContent []byte, projectType ProjectType, settingsMap map[string]string, silent bool) ([]byte, error) {
293311
valFile := func(fileName string) (string, error) {
294312
// Parse dockerignore patterns to filter out files that won't be in build context
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# This Dockerfile creates a production-ready container for a LiveKit Node.js agent using Bun
2+
# It uses a multi-stage build to minimize the final image size
3+
# syntax=docker/dockerfile:1
4+
5+
# === MULTI-STAGE BUILD STRUCTURE ===
6+
# Stage 1 (base): Sets up Bun runtime environment
7+
# Stage 2 (build): Installs dependencies and builds the application
8+
# Stage 3 (final): Copies only necessary files for runtime
9+
#
10+
# Benefits: Smaller final image without build tools and source files
11+
# Final image contains only: compiled JS, node_modules, and runtime dependencies
12+
13+
# Use official Bun image as base
14+
FROM oven/bun:1 AS base
15+
16+
# Define the program entrypoint file where your agent is started.
17+
ARG PROGRAM_MAIN="{{.ProgramMain}}"
18+
19+
# Set the working directory where our application will live
20+
WORKDIR /app
21+
22+
# === BUILD STAGE ===
23+
# This stage is discarded after building, keeping the final image small
24+
FROM base AS build
25+
26+
# Copy package.json and bun.lockb first for better layer caching
27+
# This allows Docker to cache the dependency installation step
28+
COPY package.json bun.lockb* ./
29+
30+
# Install dependencies using bun
31+
# Bun automatically uses the lock file if it exists
32+
RUN bun install --frozen-lockfile
33+
34+
# Copy all application files into the build container
35+
COPY . .
36+
37+
# Build the TypeScript application (if needed)
38+
# Bun can run TypeScript directly, but building may still be needed for bundling
39+
RUN bun run build
40+
41+
# === FINAL PRODUCTION STAGE ===
42+
# Start from the base image without build tools
43+
FROM base
44+
45+
# Copy the built application from the build stage
46+
# This includes node_modules and compiled JavaScript files
47+
COPY --from=build /app /app
48+
49+
# Expose the healthcheck port
50+
# This allows Docker and orchestration systems to check if the container is healthy
51+
EXPOSE 8081
52+
53+
# Run the application using Bun
54+
# The "start" command tells the agent to connect to LiveKit and begin waiting for jobs
55+
# Bun can run TypeScript directly, but we use the built JS for production
56+
CMD [ "bun", "run", "{{.ProgramMain}}", "start" ]
57+
58+
# === COMMON CUSTOMIZATIONS ===
59+
#
60+
# 1. Production-only dependencies:
61+
# To install only production dependencies (exclude devDependencies):
62+
# RUN bun install --frozen-lockfile --production
63+
#
64+
# 2. Direct TypeScript execution:
65+
# Bun can run TypeScript files directly without compilation:
66+
# CMD ["bun", "run", "./src/agent.ts", "start"]
67+
#
68+
# 3. Different entry point locations:
69+
# - If using src/index.ts: CMD ["bun", "run", "./src/index.ts", "start"]
70+
# - If using dist/main.js: CMD ["bun", "run", "./dist/main.js", "start"]
71+
# - For development: CMD ["bun", "run", "dev"]
72+
#
73+
# 4. Environment variables:
74+
# Set Node.js environment for production:
75+
# ENV NODE_ENV=production
76+
#
77+
# 5. Running as non-root user (recommended for security):
78+
# Add before the final CMD:
79+
# RUN adduser --disabled-password --gecos "" --uid 10001 appuser
80+
# USER appuser
81+
#
82+
# 6. Using Node.js compatibility mode:
83+
# Some packages may need Node.js APIs:
84+
# CMD ["bun", "--bun", "run", "{{.ProgramMain}}", "start"]
85+
#
86+
# === TROUBLESHOOTING BUN-SPECIFIC ISSUES ===
87+
#
88+
# 1. "bun.lockb not found":
89+
# - Run `bun install` locally to generate bun.lockb
90+
# - This is a binary file, ensure it's committed to version control
91+
# - Use --frozen-lockfile for reproducible builds
92+
#
93+
# 2. "Module not found" errors:
94+
# - Ensure all dependencies are in package.json
95+
# - Bun uses a different module resolution than Node.js
96+
# - Try using --bun flag for better compatibility
97+
# - Check that node_modules are copied correctly
98+
#
99+
# 3. Node.js API compatibility:
100+
# - Some Node.js APIs may not be fully implemented
101+
# - Use --bun flag to enable Bun's runtime
102+
# - Consider fallback to Node.js for incompatible packages
103+
#
104+
# 4. TypeScript issues:
105+
# - Bun runs TypeScript natively, no compilation needed
106+
# - However, some TypeScript features may differ
107+
# - Check tsconfig.json compatibility with Bun
108+
#
109+
# 5. Large image sizes:
110+
# - Use oven/bun:alpine for smaller base image
111+
# - Ensure .dockerignore excludes unnecessary files
112+
# - Use bun install --production for production builds
113+
#
114+
# 6. Performance differences:
115+
# - Bun is generally faster for startup and execution
116+
# - However, some optimizations may differ from Node.js
117+
# - Profile your application if performance issues arise
118+
#
119+
# 7. Native module issues:
120+
# - Bun has different native module support than Node.js
121+
# - Some Node.js native modules may not work
122+
# - Check Bun's compatibility list for your dependencies
123+
#
124+
# 8. Runtime connection issues:
125+
# - Verify the agent can reach the LiveKit server
126+
# - Check that required environment variables are set
127+
# - Ensure the healthcheck endpoint (8081) is accessible
128+
#
129+
# 9. Binary lock file issues:
130+
# - bun.lockb is binary, which can cause Git issues
131+
# - Ensure Git LFS is not treating it as a large file
132+
# - Consider using text-based lockfile with --save-text-lockfile
133+
#
134+
# For more help: https://bun.sh/docs
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Node.js/Bun artifacts
2+
node_modules/
3+
.bun/
4+
bun-debug.log*
5+
npm-debug.log*
6+
yarn-debug.log*
7+
yarn-error.log*
8+
pnpm-debug.log*
9+
lerna-debug.log*
10+
11+
# Build artifacts
12+
dist/
13+
build/
14+
*.tsbuildinfo
15+
16+
# Testing
17+
coverage/
18+
.nyc_output/
19+
20+
# IDE & Editor files
21+
.vscode/
22+
.idea/
23+
*.swp
24+
*.swo
25+
*~
26+
.DS_Store
27+
28+
# Environment files
29+
.env
30+
.env.local
31+
.env.*.local
32+
33+
# Docker artifacts
34+
Dockerfile*
35+
.dockerignore
36+
37+
# Git files
38+
.git/
39+
.gitignore
40+
41+
# Documentation
42+
README.md
43+
docs/
44+
45+
# CI/CD
46+
.github/
47+
.gitlab-ci.yml
48+
.travis.yml
49+
50+
# Logs
51+
logs/
52+
*.log
53+
54+
# Temporary files
55+
.tmp/
56+
.temp/
57+
tmp/
58+
temp/
59+
60+
# Bun specific
61+
.bun-cache/

pkg/agentfs/utils.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const (
3939
ProjectTypeNodePNPM ProjectType = "node.pnpm"
4040
ProjectTypeNodeYarn ProjectType = "node.yarn"
4141
ProjectTypeNodeYarnBerry ProjectType = "node.yarn-berry"
42+
ProjectTypeNodeBun ProjectType = "node.bun"
4243
ProjectTypeUnknown ProjectType = "unknown"
4344
)
4445

@@ -47,7 +48,7 @@ func (p ProjectType) IsPython() bool {
4748
}
4849

4950
func (p ProjectType) IsNode() bool {
50-
return p == ProjectTypeNodeNPM || p == ProjectTypeNodePNPM || p == ProjectTypeNodeYarn || p == ProjectTypeNodeYarnBerry
51+
return p == ProjectTypeNodeNPM || p == ProjectTypeNodePNPM || p == ProjectTypeNodeYarn || p == ProjectTypeNodeYarnBerry || p == ProjectTypeNodeBun
5152
}
5253

5354
func (p ProjectType) Lang() string {
@@ -132,6 +133,11 @@ func LocateLockfile(dir string, p ProjectType) (bool, string) {
132133
".yarnrc.yml", // Yarn Berry configuration
133134
"package.json", // Package manifest (fallback)
134135
}
136+
case ProjectTypeNodeBun:
137+
filesToCheck = []string{
138+
"bun.lockb", // Bun lock file (highest priority, binary format)
139+
"package.json", // Package manifest (fallback)
140+
}
135141
default:
136142
return false, ""
137143
}
@@ -149,7 +155,12 @@ func LocateLockfile(dir string, p ProjectType) (bool, string) {
149155
// DetectProjectType determines the project type by checking for specific configuration/lock files and their content
150156
func DetectProjectType(dir string) (ProjectType, error) {
151157
// Node.js detection with specific package manager detection
152-
// Check for pnpm first (most definitive pnpm indicator)
158+
// Check for Bun first (most definitive Bun indicator)
159+
if util.FileExists(dir, "bun.lockb") {
160+
return ProjectTypeNodeBun, nil
161+
}
162+
163+
// Check for pnpm (most definitive pnpm indicator)
153164
if util.FileExists(dir, "pnpm-lock.yaml") {
154165
return ProjectTypeNodePNPM, nil
155166
}

0 commit comments

Comments
 (0)