Compare commits

...

3 Commits

Author SHA1 Message Date
35C4n0r 1ec4ef9cf6 chore: bun fmt 2026-01-12 09:57:43 +00:00
35C4n0r 545c6fcf80 chore: bump version 2026-01-12 09:56:42 +00:00
35C4n0r 6fc075c818 fix: update boundary version to default to 'latest' and adjust related tests 2026-01-12 09:51:20 +00:00
4 changed files with 15 additions and 381 deletions
+7 -7
View File
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx"
@@ -45,7 +45,7 @@ This example shows how to configure the Claude Code module to run the agent behi
```tf
module "claude-code" {
source = "dev.registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
enable_boundary = true
@@ -68,7 +68,7 @@ data "coder_parameter" "ai_prompt" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
@@ -104,7 +104,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
install_claude_code = true
@@ -126,7 +126,7 @@ variable "claude_code_oauth_token" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token
@@ -199,7 +199,7 @@ resource "coder_env" "bedrock_api_key" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
@@ -256,7 +256,7 @@ resource "coder_env" "google_application_credentials" {
module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0"
version = "4.3.1"
agent_id = coder_agent.main.id
workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514"
+5 -371
View File
@@ -79,391 +79,25 @@ describe("claude-code", async () => {
await expectAgentAPIStarted(id);
});
test("install-claude-code-version", async () => {
const version_to_install = "1.0.40";
test("run-with-boundary", async () => {
const { id, coderEnvVars } = await setup({
skipClaudeMock: true,
moduleVariables: {
install_claude_code: "true",
claude_code_version: version_to_install,
},
});
await execModuleScript(id, coderEnvVars);
const resp = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/install.log",
]);
expect(resp.stdout).toContain(version_to_install);
});
test("check-latest-claude-code-version-works", async () => {
const { id, coderEnvVars } = await setup({
skipClaudeMock: true,
skipAgentAPIMock: true,
skipClaudeMock: true,
moduleVariables: {
install_claude_code: "true",
enable_boundary: "true",
},
});
await execModuleScript(id, coderEnvVars);
await expectAgentAPIStarted(id);
});
test("claude-api-key", async () => {
const apiKey = "test-api-key-123";
const { id } = await setup({
moduleVariables: {
claude_api_key: apiKey,
},
});
await execModuleScript(id);
const envCheck = await execContainer(id, [
"bash",
"-c",
'env | grep CLAUDE_API_KEY || echo "CLAUDE_API_KEY not found"',
]);
expect(envCheck.stdout).toContain("CLAUDE_API_KEY");
});
test("claude-mcp-config", async () => {
const mcpConfig = JSON.stringify({
mcpServers: {
test: {
command: "test-cmd",
type: "stdio",
},
},
});
const { id, coderEnvVars } = await setup({
skipClaudeMock: true,
moduleVariables: {
mcp: mcpConfig,
},
});
await execModuleScript(id, coderEnvVars);
const resp = await readFileContainer(id, "/home/coder/.claude.json");
expect(resp).toContain("test-cmd");
});
test("claude-task-prompt", async () => {
const prompt = "This is a task prompt for Claude.";
const { id } = await setup({
moduleVariables: {
ai_prompt: prompt,
},
});
await execModuleScript(id);
const resp = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(resp.stdout).toContain(prompt);
});
test("claude-permission-mode", async () => {
const mode = "plan";
const { id } = await setup({
moduleVariables: {
permission_mode: mode,
ai_prompt: "test prompt",
},
});
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain(`--permission-mode ${mode}`);
});
test("claude-model", async () => {
const model = "opus";
const { id } = await setup({
moduleVariables: {
model: model,
ai_prompt: "test prompt",
},
});
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain(`--model ${model}`);
});
test("claude-continue-resume-task-session", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "true",
ai_prompt: "test prompt",
},
});
// Create a mock task session file with the hardcoded task session ID
// Note: Claude CLI creates files without "session-" prefix when using --session-id
const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2";
const sessionDir = `/home/coder/.claude/projects/-home-coder-project`;
await execContainer(id, ["mkdir", "-p", sessionDir]);
await execContainer(id, [
"bash",
"-c",
`cat > ${sessionDir}/${taskSessionId}.jsonl << 'SESSIONEOF'
{"sessionId":"${taskSessionId}","message":{"content":"Task"},"timestamp":"2020-01-01T10:00:00.000Z"}
{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-01T10:00:05.000Z"}
SESSIONEOF`,
]);
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain("--resume");
expect(startLog.stdout).toContain(taskSessionId);
expect(startLog.stdout).toContain("Resuming task session");
expect(startLog.stdout).toContain("--dangerously-skip-permissions");
});
test("pre-post-install-scripts", async () => {
const { id } = await setup({
moduleVariables: {
pre_install_script: "#!/bin/bash\necho 'claude-pre-install-script'",
post_install_script: "#!/bin/bash\necho 'claude-post-install-script'",
},
});
await execModuleScript(id);
const preInstallLog = await readFileContainer(
id,
"/home/coder/.claude-module/pre_install.log",
);
expect(preInstallLog).toContain("claude-pre-install-script");
const postInstallLog = await readFileContainer(
id,
"/home/coder/.claude-module/post_install.log",
);
expect(postInstallLog).toContain("claude-post-install-script");
});
test("workdir-variable", async () => {
const workdir = "/home/coder/claude-test-folder";
const { id } = await setup({
skipClaudeMock: false,
moduleVariables: {
workdir,
},
});
await execModuleScript(id);
const resp = await readFileContainer(
id,
"/home/coder/.claude-module/agentapi-start.log",
);
expect(resp).toContain(workdir);
});
test("coder-mcp-config-created", async () => {
const { id } = await setup({
moduleVariables: {
install_claude_code: "false",
},
});
await execModuleScript(id);
const installLog = await readFileContainer(
id,
"/home/coder/.claude-module/install.log",
);
expect(installLog).toContain(
"Configuring Claude Code to report tasks via Coder MCP",
);
});
test("dangerously-skip-permissions", async () => {
const { id } = await setup({
moduleVariables: {
dangerously_skip_permissions: "true",
},
});
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain(`--dangerously-skip-permissions`);
});
test("subdomain-false", async () => {
const { id } = await setup({
skipAgentAPIMock: true,
moduleVariables: {
subdomain: "false",
post_install_script: dedent`
#!/bin/bash
env | grep AGENTAPI_CHAT_BASE_PATH || echo "AGENTAPI_CHAT_BASE_PATH not found"
`,
},
});
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/post_install.log",
]);
expect(startLog.stdout).toContain(
"ARG_AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/ccw/chat",
`boundary-run wrapper is available in PATH`,
);
});
test("partial-initialization-detection", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "true",
ai_prompt: "test prompt",
},
});
const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2";
const sessionDir = `/home/coder/.claude/projects/-home-coder-project`;
await execContainer(id, ["mkdir", "-p", sessionDir]);
await execContainer(id, [
"bash",
"-c",
`echo '{"sessionId":"${taskSessionId}"}' > ${sessionDir}/${taskSessionId}.jsonl`,
]);
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
// Should start new session, not try to resume invalid one
expect(startLog.stdout).toContain("Starting new task session");
expect(startLog.stdout).toContain("--session-id");
});
test("standalone-first-build-no-sessions", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "false",
},
});
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
// Should start fresh, not try to continue
expect(startLog.stdout).toContain("No sessions found");
expect(startLog.stdout).toContain("starting fresh standalone session");
expect(startLog.stdout).not.toContain("--continue");
});
test("standalone-with-sessions-continues", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "false",
},
});
const sessionDir = `/home/coder/.claude/projects/-home-coder-project`;
await execContainer(id, ["mkdir", "-p", sessionDir]);
await execContainer(id, [
"bash",
"-c",
`cat > ${sessionDir}/generic-123.jsonl << 'EOF'
{"sessionId":"generic-123","message":{"content":"User session"},"timestamp":"2020-01-01T10:00:00.000Z"}
{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-01T10:00:05.000Z"}
EOF`,
]);
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
// Should continue existing session
expect(startLog.stdout).toContain("Sessions found");
expect(startLog.stdout).toContain(
"Continuing most recent standalone session",
);
expect(startLog.stdout).toContain("--continue");
});
test("task-mode-ignores-manual-sessions", async () => {
const { id } = await setup({
moduleVariables: {
continue: "true",
report_tasks: "true",
ai_prompt: "test prompt",
},
});
const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2";
const sessionDir = `/home/coder/.claude/projects/-home-coder-project`;
await execContainer(id, ["mkdir", "-p", sessionDir]);
// Create task session (without "session-" prefix, as CLI does)
await execContainer(id, [
"bash",
"-c",
`cat > ${sessionDir}/${taskSessionId}.jsonl << 'EOF'
{"sessionId":"${taskSessionId}","message":{"content":"Task"},"timestamp":"2020-01-01T10:00:00.000Z"}
{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-01T10:00:05.000Z"}
EOF`,
]);
// Create manual session (newer)
await execContainer(id, [
"bash",
"-c",
`cat > ${sessionDir}/manual-456.jsonl << 'EOF'
{"sessionId":"manual-456","message":{"content":"Manual"},"timestamp":"2020-01-02T10:00:00.000Z"}
{"type":"assistant","message":{"content":"Response"},"timestamp":"2020-01-02T10:00:05.000Z"}
EOF`,
]);
await execModuleScript(id);
const startLog = await execContainer(id, [
"bash",
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
// Should resume task session, not manual session
expect(startLog.stdout).toContain("Resuming task session");
expect(startLog.stdout).toContain(taskSessionId);
expect(startLog.stdout).not.toContain("manual-456");
});
});
+2 -2
View File
@@ -206,8 +206,8 @@ variable "enable_boundary" {
variable "boundary_version" {
type = string
description = "Boundary version, valid git reference should be provided (tag, commit, branch)"
default = "main"
description = "Boundary version, defaults to latest."
default = "latest"
}
variable "compile_boundary_from_source" {
@@ -15,7 +15,7 @@ ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d)
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false}
ARG_BOUNDARY_VERSION=${ARG_BOUNDARY_VERSION:-"main"}
ARG_BOUNDARY_VERSION=${ARG_BOUNDARY_VERSION:-"latest"}
ARG_COMPILE_FROM_SOURCE=${ARG_COMPILE_FROM_SOURCE:-false}
ARG_CODER_HOST=${ARG_CODER_HOST:-}