Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
23 changes: 19 additions & 4 deletions docker/agents/claude-code/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
ARG NODE_TAG=22-slim
FROM node:${NODE_TAG}

RUN apt-get update -qq && apt-get install -y --no-install-recommends git findutils tree jq curl && \
ARG TARGETARCH

RUN apt-get update -qq && apt-get install -y --no-install-recommends git findutils tree jq curl ca-certificates unzip && \
rm -rf /var/lib/apt/lists/*

ARG CLAUDE_CODE_VERSION=2.1.76
Expand All @@ -16,14 +18,27 @@ RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} && \

# Bun runtime for precomputation scripts (static analysis before agent runs)
ARG BUN_VERSION=1.3.11
RUN curl -fsSL https://bun.sh/install | BUN_INSTALL=/usr/local bash -s "bun-v${BUN_VERSION}"
RUN arch="${TARGETARCH:-$(dpkg --print-architecture)}" && \
case "${arch}" in \
amd64|x86_64) bun_arch="x64"; bun_sha256="8611ba935af886f05a6f38740a15160326c15e5d5d07adef966130b4493607ed" ;; \
arm64|aarch64) bun_arch="aarch64"; bun_sha256="d13944da12a53ecc74bf6a720bd1d04c4555c038dfe422365356a7be47691fdf" ;; \
*) echo "Unsupported TARGETARCH/architecture: ${arch}" >&2; exit 1 ;; \
esac && \
curl --fail --show-error --silent --location --retry 3 --retry-delay 2 \
"https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-${bun_arch}.zip" \
-o /tmp/bun.zip && \
echo "${bun_sha256} /tmp/bun.zip" | sha256sum -c - && \
unzip -o /tmp/bun.zip -d /tmp && \
mv "/tmp/bun-linux-${bun_arch}/bun" /usr/local/bin/bun && \
chmod +x /usr/local/bin/bun && \
rm -rf /tmp/bun*
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Precompute runner + shared libraries (practice scripts injected at runtime from DB)
COPY --chown=1000:1000 precompute/runner.ts /opt/precompute/runner.ts
COPY --chown=1000:1000 precompute/lib/ /opt/precompute/lib/

# Git security: neutralize hooks and external commands from mounted repos.
# System-level config cannot be overridden by repo-local .git/config.
# Git security baseline: neutralize common hooks/external-command vectors in the image.
# Runtime env-based git config in DockerSandboxAdapter provides the hard override layer.
RUN git config --system core.hooksPath /nonexistent && \
git config --system core.fsmonitor false && \
git config --system safe.directory /workspace/repo
Expand Down
19 changes: 15 additions & 4 deletions docker/agents/opencode/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
ARG NODE_TAG=22-slim
FROM node:${NODE_TAG}

ARG TARGETARCH

RUN apt-get update -qq && apt-get install -y --no-install-recommends git findutils tree jq curl ca-certificates unzip && \
rm -rf /var/lib/apt/lists/*

Expand All @@ -15,18 +17,27 @@ RUN npm install -g opencode-ai@${OPENCODE_VERSION} && \

# Bun runtime for precomputation scripts (static analysis before agent runs)
ARG BUN_VERSION=1.3.11
RUN curl -fsSL "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-x64.zip" -o /tmp/bun.zip && \
RUN arch="${TARGETARCH:-$(dpkg --print-architecture)}" && \
case "${arch}" in \
amd64|x86_64) bun_arch="x64"; bun_sha256="8611ba935af886f05a6f38740a15160326c15e5d5d07adef966130b4493607ed" ;; \
arm64|aarch64) bun_arch="aarch64"; bun_sha256="d13944da12a53ecc74bf6a720bd1d04c4555c038dfe422365356a7be47691fdf" ;; \
*) echo "Unsupported TARGETARCH/architecture: ${arch}" >&2; exit 1 ;; \
esac && \
curl --fail --show-error --silent --location --retry 3 --retry-delay 2 \
"https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-${bun_arch}.zip" \
-o /tmp/bun.zip && \
echo "${bun_sha256} /tmp/bun.zip" | sha256sum -c - && \
unzip -o /tmp/bun.zip -d /tmp && \
mv /tmp/bun-linux-x64/bun /usr/local/bin/bun && \
mv "/tmp/bun-linux-${bun_arch}/bun" /usr/local/bin/bun && \
chmod +x /usr/local/bin/bun && \
rm -rf /tmp/bun*
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Precompute runner + shared libraries (practice scripts injected at runtime from DB)
COPY --chown=1000:1000 precompute/runner.ts /opt/precompute/runner.ts
COPY --chown=1000:1000 precompute/lib/ /opt/precompute/lib/

# Git security: neutralize hooks and external commands from mounted repos.
# System-level config cannot be overridden by repo-local .git/config.
# Git security baseline: neutralize common hooks/external-command vectors in the image.
# Runtime env-based git config in DockerSandboxAdapter provides the hard override layer.
RUN git config --system core.hooksPath /nonexistent && \
git config --system core.fsmonitor false && \
git config --system safe.directory /workspace/repo
Expand Down
19 changes: 15 additions & 4 deletions docker/agents/pi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
ARG NODE_TAG=22-slim
FROM node:${NODE_TAG}

ARG TARGETARCH

RUN apt-get update -qq && apt-get install -y --no-install-recommends git findutils tree jq curl ca-certificates unzip && \
rm -rf /var/lib/apt/lists/*

Expand All @@ -15,18 +17,27 @@ RUN npm install -g @mariozechner/pi-coding-agent@${PI_VERSION} && \

# Bun runtime for precomputation scripts (static analysis before agent runs)
ARG BUN_VERSION=1.3.11
RUN curl -fsSL "https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-x64.zip" -o /tmp/bun.zip && \
RUN arch="${TARGETARCH:-$(dpkg --print-architecture)}" && \
case "${arch}" in \
amd64|x86_64) bun_arch="x64"; bun_sha256="8611ba935af886f05a6f38740a15160326c15e5d5d07adef966130b4493607ed" ;; \
arm64|aarch64) bun_arch="aarch64"; bun_sha256="d13944da12a53ecc74bf6a720bd1d04c4555c038dfe422365356a7be47691fdf" ;; \
*) echo "Unsupported TARGETARCH/architecture: ${arch}" >&2; exit 1 ;; \
esac && \
curl --fail --show-error --silent --location --retry 3 --retry-delay 2 \
"https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-${bun_arch}.zip" \
-o /tmp/bun.zip && \
echo "${bun_sha256} /tmp/bun.zip" | sha256sum -c - && \
unzip -o /tmp/bun.zip -d /tmp && \
mv /tmp/bun-linux-x64/bun /usr/local/bin/bun && \
mv "/tmp/bun-linux-${bun_arch}/bun" /usr/local/bin/bun && \
chmod +x /usr/local/bin/bun && \
rm -rf /tmp/bun*
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Precompute runner + shared libraries (practice scripts injected at runtime from DB)
COPY --chown=1000:1000 precompute/runner.ts /opt/precompute/runner.ts
COPY --chown=1000:1000 precompute/lib/ /opt/precompute/lib/

# Git security: neutralize hooks and external commands from mounted repos.
# System-level config cannot be overridden by repo-local .git/config.
# Git security baseline: neutralize common hooks/external-command vectors in the image.
# Runtime env-based git config in DockerSandboxAdapter provides the hard override layer.
RUN git config --system core.hooksPath /nonexistent && \
git config --system core.fsmonitor false && \
git config --system safe.directory /workspace/repo
Expand Down
86 changes: 86 additions & 0 deletions docker/agents/precompute/lib/grep.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { afterEach, describe, expect, it } from "bun:test";
import { mkdtemp, mkdir, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { findFiles, grep } from "./grep";

const tempDirs: string[] = [];

async function createTempDir(): Promise<string> {
const dir = await mkdtemp(join(tmpdir(), "grep helper "));
tempDirs.push(dir);
return dir;
}

afterEach(async () => {
await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
});

describe("grep", () => {
it("treats fixed-string patterns literally without shell interpretation", async () => {
const dir = await createTempDir();
const nestedDir = join(dir, "nested dir");
await mkdir(nestedDir, { recursive: true });
await Bun.write(
join(nestedDir, "example.ts"),
"const marker = \"literal $(echo nope) 'quotes'\";\n",
);

const matches = await grep("literal $(echo nope) 'quotes'", dir, {
fixedString: true,
glob: "**/*.ts",
});

expect(matches).toHaveLength(1);
expect(matches[0]?.file).toBe("nested dir/example.ts");
});

it("enforces maxResults globally across files", async () => {
const dir = await createTempDir();
await Bun.write(join(dir, "one.txt"), "needle\nneedle\n");
await Bun.write(join(dir, "two.txt"), "needle\nneedle\n");
await Bun.write(join(dir, "three.txt"), "needle\nneedle\n");

const matches = await grep("needle", dir, {
fixedString: true,
maxResults: 2,
});

expect(matches).toHaveLength(2);
expect(matches.every((match) => match.content === "needle")).toBe(true);
});

it("applies path-aware glob filters instead of basename-only includes", async () => {
const dir = await createTempDir();
const nestedDir = join(dir, "src", "nested");
await mkdir(nestedDir, { recursive: true });
await Bun.write(join(nestedDir, "match.ts"), "needle\n");
await Bun.write(join(nestedDir, "skip.js"), "needle\n");

const matches = await grep("needle", dir, {
fixedString: true,
glob: "src/**/*.ts",
});

expect(matches).toHaveLength(1);
expect(matches[0]?.file).toBe("src/nested/match.ts");
});

it("finds extension matches without shelling out and skips ignored paths", async () => {
const dir = await createTempDir();
await mkdir(join(dir, "src", "nested"), { recursive: true });
await mkdir(join(dir, ".hidden"), { recursive: true });
await mkdir(join(dir, "node_modules", "pkg"), { recursive: true });
await mkdir(join(dir, ".build"), { recursive: true });

await Bun.write(join(dir, "src", "nested", "match.swift"), "struct Match {}\n");
await Bun.write(join(dir, ".hidden", "hidden.swift"), "struct Hidden {}\n");
await Bun.write(join(dir, "node_modules", "pkg", "dep.swift"), "struct Dep {}\n");
await Bun.write(join(dir, ".build", "generated.swift"), "struct Generated {}\n");

const files = await findFiles(dir, "swift");

expect(files).toHaveLength(1);
expect(files[0]).toBe(join(dir, "src", "nested", "match.swift"));
});
});
Loading
Loading