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
35 changes: 16 additions & 19 deletions pkg/agentfs/examples/node.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,12 @@ ENV PATH="$PNPM_HOME:$PATH"
RUN apt-get update -qq && apt-get install --no-install-recommends -y ca-certificates && rm -rf /var/lib/apt/lists/*

# Pin pnpm version for reproducible builds
RUN npm install -g pnpm@9.15.9
RUN npm install -g pnpm@10

# Create a new directory for our application code
# And set it as the working directory
WORKDIR /app

# Build stage
# We use a multi-stage build to keep the runtime image minimal
FROM base AS build

# Copy just the dependency files first, for more efficient layer caching
COPY package.json pnpm-lock.yaml ./

Expand All @@ -40,13 +36,8 @@ RUN pnpm install --frozen-lockfile
COPY . .

# Build the project
RUN pnpm run build

# Remove development-only dependencies to reduce the runtime image size
RUN pnpm prune --prod

# Create the runtime image
FROM base AS runtime
# Your package.json must contain a "build" script, such as `"build": "tsc"`
RUN pnpm build

# Create a non-privileged user that the app will run under
# See https://docs.docker.com/develop/develop-images/dockerfile_best_practices/#user
Expand All @@ -59,19 +50,25 @@ RUN adduser \
--uid "${UID}" \
appuser

# Copy built application and production dependencies from the build stage
COPY --from=build /app /app
# Set proper permissions
RUN chown -R appuser:appuser /app
USER appuser

# Copy system CA certificates to ensure HTTPS works correctly at runtime
COPY --from=build /etc/ssl/certs /etc/ssl/certs
# Pre-download any ML models or files the agent needs
# This ensures the container is ready to run immediately without downloading
# dependencies at runtime, which improves startup time and reliability
# Your package.json must contain a "download-files" script, such as `"download-files": "pnpm run build && node dist/agent.js download-files"`
RUN pnpm download-files

# Ensure ownership of app files and drop privileges for better security
RUN chown -R appuser:appuser /app
# Switch back to root to remove dev dependencies and finalize setup
USER root
RUN pnpm prune --prod && chown -R appuser:appuser /app
USER appuser

# Set Node.js to production mode
ENV NODE_ENV=production

# Run the application
# The "start" command tells the worker to connect to LiveKit and begin waiting for jobs.
CMD [ "node", "./dist/agent.js", "start" ]
# Your package.json must contain a "start" script, such as `"start": "node dist/agent.js start"`
CMD [ "pnpm", "start" ]
2 changes: 1 addition & 1 deletion pkg/agentfs/sdk_version_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ func checkPipfileLock(filePath, minVersion string) VersionCheckResult {
// isVersionSatisfied checks if a version satisfies the minimum requirement
func isVersionSatisfied(version, minVersion string) (bool, error) {
// Handle special cases
if version == "latest" || version == "*" || version == "" {
if version == "latest" || version == "*" || version == "" || version == "next" {
return true, nil // Latest version always satisfies
}

Expand Down
27 changes: 20 additions & 7 deletions pkg/agentfs/tar.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"io"
"net/http"
"os"
pathpkg "path"
"path/filepath"
"strings"

Expand All @@ -43,7 +44,6 @@ var (
}

ignoreFilePatterns = []string{
".gitignore",
".dockerignore",
}
)
Expand All @@ -62,8 +62,17 @@ func UploadTarball(directory string, presignedUrl string, excludeFiles []string)
}
}

for i, exclude := range excludeFiles {
excludeFiles[i] = strings.TrimSpace(exclude)
// Normalize and filter ignore patterns
{
filtered := make([]string, 0, len(excludeFiles))
for _, exclude := range excludeFiles {
exclude = strings.TrimSpace(exclude)
if exclude == "" || strings.HasPrefix(exclude, "#") {
continue
}
filtered = append(filtered, filepath.ToSlash(exclude))
}
excludeFiles = filtered
}

var totalSize int64
Expand All @@ -76,17 +85,19 @@ func UploadTarball(directory string, presignedUrl string, excludeFiles []string)
if err != nil {
return nil
}
// Use forward slashes inside tar archives regardless of OS
relPath = filepath.ToSlash(relPath)

for _, exclude := range excludeFiles {
if exclude == "" || strings.Contains(exclude, "Dockerfile") {
continue
}
if info.IsDir() {
if strings.HasPrefix(relPath, exclude+"/") || strings.HasPrefix(relPath, exclude) {
if strings.HasPrefix(relPath, exclude+"/") || relPath == exclude {
return filepath.SkipDir
}
}
matched, err := filepath.Match(exclude, relPath)
matched, err := pathpkg.Match(exclude, relPath)
if err != nil {
return nil
}
Expand Down Expand Up @@ -133,20 +144,22 @@ func UploadTarball(directory string, presignedUrl string, excludeFiles []string)
if err != nil {
return fmt.Errorf("failed to calculate relative path for %s: %w", path, err)
}
// Normalize to forward slashes for tar header names and matching
relPath = filepath.ToSlash(relPath)

for _, exclude := range excludeFiles {
if exclude == "" || strings.Contains(exclude, "Dockerfile") {
continue
}

if info.IsDir() {
if strings.HasPrefix(relPath, exclude+"/") || strings.HasPrefix(relPath, exclude) {
if strings.HasPrefix(relPath, exclude+"/") || relPath == exclude {
logger.Debugw("excluding directory from tarball", "path", path)
return filepath.SkipDir
}
}

matched, err := filepath.Match(exclude, relPath)
matched, err := pathpkg.Match(exclude, relPath)
if err != nil {
return nil
}
Expand Down
Loading