Merge branch 'main' into jwb/claude-code-pre-start-script

This commit is contained in:
DevCats 2026-01-16 15:12:48 -06:00 committed by GitHub
commit c2e2964a85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 185 additions and 73 deletions

View File

@ -93,7 +93,7 @@ jobs:
- name: Validate formatting - name: Validate formatting
run: bun fmt:ci run: bun fmt:ci
- name: Check for typos - name: Check for typos
uses: crate-ci/typos@v1.41.0 uses: crate-ci/typos@v1.42.0
with: with:
config: .github/typos.toml config: .github/typos.toml
validate-readme-files: validate-readme-files:

2
CODEOWNERS Normal file
View File

@ -0,0 +1,2 @@
# GitHub Actions Workflow Owners
.github/ @jdomeracki-coder

View File

@ -13,7 +13,7 @@ Run Codex CLI in your workspace to access OpenAI's models through the Codex inte
```tf ```tf
module "codex" { module "codex" {
source = "registry.coder.com/coder-labs/codex/coder" source = "registry.coder.com/coder-labs/codex/coder"
version = "3.1.1" version = "4.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
openai_api_key = var.openai_api_key openai_api_key = var.openai_api_key
workdir = "/home/coder/project" workdir = "/home/coder/project"
@ -22,7 +22,6 @@ module "codex" {
## Prerequisites ## Prerequisites
- You must add the [Coder Login](https://registry.coder.com/modules/coder/coder-login) module to your template
- OpenAI API key for Codex access - OpenAI API key for Codex access
## Examples ## Examples
@ -33,7 +32,7 @@ module "codex" {
module "codex" { module "codex" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder-labs/codex/coder" source = "registry.coder.com/coder-labs/codex/coder"
version = "3.1.1" version = "4.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
openai_api_key = "..." openai_api_key = "..."
workdir = "/home/coder/project" workdir = "/home/coder/project"
@ -44,27 +43,19 @@ module "codex" {
### Tasks integration ### Tasks integration
```tf ```tf
data "coder_parameter" "ai_prompt" { resource "coder_ai_task" "task" {
type = "string" count = data.coder_workspace.me.start_count
name = "AI Prompt" app_id = module.codex.task_app_id
default = ""
description = "Initial prompt for the Codex CLI"
mutable = true
} }
module "coder-login" { data "coder_task" "me" {}
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/coder-login/coder"
version = "3.1.1"
agent_id = coder_agent.example.id
}
module "codex" { module "codex" {
source = "registry.coder.com/coder-labs/codex/coder" source = "registry.coder.com/coder-labs/codex/coder"
version = "3.1.1" version = "4.0.0"
agent_id = coder_agent.example.id agent_id = coder_agent.example.id
openai_api_key = "..." openai_api_key = "..."
ai_prompt = data.coder_parameter.ai_prompt.value ai_prompt = data.coder_task.me.prompt
workdir = "/home/coder/project" workdir = "/home/coder/project"
# Custom configuration for full auto mode # Custom configuration for full auto mode
@ -108,7 +99,7 @@ For custom Codex configuration, use `base_config_toml` and/or `additional_mcp_se
```tf ```tf
module "codex" { module "codex" {
source = "registry.coder.com/coder-labs/codex/coder" source = "registry.coder.com/coder-labs/codex/coder"
version = "3.1.1" version = "4.0.0"
# ... other variables ... # ... other variables ...
# Override default configuration # Override default configuration
@ -137,7 +128,7 @@ module "codex" {
- Ensure your OpenAI API key has access to the specified model - Ensure your OpenAI API key has access to the specified model
> [!IMPORTANT] > [!IMPORTANT]
> To use tasks with Codex CLI, ensure you have the `openai_api_key` variable set, and **you create a `coder_parameter` named `"AI Prompt"` and pass its value to the codex module's `ai_prompt` variable**. [Tasks Template Example](https://registry.coder.com/templates/coder-labs/tasks-docker). > To use tasks with Codex CLI, ensure you have the `openai_api_key` variable set. [Tasks Template Example](https://registry.coder.com/templates/coder-labs/tasks-docker).
> The module automatically configures Codex with your API key and model preferences. > The module automatically configures Codex with your API key and model preferences.
> workdir is a required variable for the module to function correctly. > workdir is a required variable for the module to function correctly.

View File

@ -4,7 +4,7 @@ terraform {
required_providers { required_providers {
coder = { coder = {
source = "coder/coder" source = "coder/coder"
version = ">= 2.7" version = ">= 2.12"
} }
} }
} }
@ -110,12 +110,12 @@ variable "install_agentapi" {
variable "agentapi_version" { variable "agentapi_version" {
type = string type = string
description = "The version of AgentAPI to install." description = "The version of AgentAPI to install."
default = "v0.10.0" default = "v0.11.6"
} }
variable "codex_model" { variable "codex_model" {
type = string type = string
description = "The model for Codex to use. Defaults to gpt-5." description = "The model for Codex to use. Defaults to gpt-5.1-codex-max."
default = "" default = ""
} }
@ -165,7 +165,7 @@ locals {
module "agentapi" { module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder" source = "registry.coder.com/coder/agentapi/coder"
version = "1.2.0" version = "2.0.0"
agent_id = var.agent_id agent_id = var.agent_id
folder = local.workdir folder = local.workdir
@ -218,3 +218,7 @@ module "agentapi" {
/tmp/install.sh /tmp/install.sh
EOT EOT
} }
output "task_app_id" {
value = module.agentapi.task_app_id
}

View File

@ -115,7 +115,7 @@ append_mcp_servers_section() {
[mcp_servers.Coder] [mcp_servers.Coder]
command = "coder" command = "coder"
args = ["exp", "mcp", "server"] args = ["exp", "mcp", "server"]
env = { "CODER_MCP_APP_STATUS_SLUG" = "${ARG_CODER_MCP_APP_STATUS_SLUG}", "CODER_MCP_AI_AGENTAPI_URL" = "${CODER_MCP_AI_AGENTAPI_URL}" , "CODER_AGENT_URL" = "${CODER_AGENT_URL}", "CODER_AGENT_TOKEN" = "${CODER_AGENT_TOKEN}" } env = { "CODER_MCP_APP_STATUS_SLUG" = "${ARG_CODER_MCP_APP_STATUS_SLUG}", "CODER_MCP_AI_AGENTAPI_URL" = "${CODER_MCP_AI_AGENTAPI_URL}" , "CODER_AGENT_URL" = "${CODER_AGENT_URL}", "CODER_AGENT_TOKEN" = "${CODER_AGENT_TOKEN}", "CODER_MCP_ALLOWED_TOOLS" = "coder_report_task" }
description = "Report ALL tasks and statuses (in progress, done, failed) you are working on." description = "Report ALL tasks and statuses (in progress, done, failed) you are working on."
type = "stdio" type = "stdio"

View File

@ -182,7 +182,7 @@ build_codex_args() {
if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then
if [ "${ARG_REPORT_TASKS}" == "true" ]; then if [ "${ARG_REPORT_TASKS}" == "true" ]; then
PROMPT="Complete the task at hand in one go. Every step of the way, report your progress using coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_CODEX_TASK_PROMPT" PROMPT="Complete the task at hand in one go. Every step of the way, report your progress using Coder.coder_report_task tool with proper summary and statuses. Your task at hand: $ARG_CODEX_TASK_PROMPT"
else else
PROMPT="Your task at hand: $ARG_CODEX_TASK_PROMPT" PROMPT="Your task at hand: $ARG_CODEX_TASK_PROMPT"
fi fi

View File

@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
claude_api_key = "xxxx-xxxxx-xxxx" claude_api_key = "xxxx-xxxxx-xxxx"
@ -44,8 +44,8 @@ This example shows how to configure the Claude Code module to run the agent behi
```tf ```tf
module "claude-code" { module "claude-code" {
source = "dev.registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
enable_boundary = true enable_boundary = true
@ -57,6 +57,9 @@ module "claude-code" {
This example shows how to configure the Claude Code module with an AI prompt, API key shared by all users of the template, and other custom settings. This example shows how to configure the Claude Code module with an AI prompt, API key shared by all users of the template, and other custom settings.
> [!NOTE]
> When a specific `claude_code_version` (other than "latest") is provided, the module will install Claude Code via npm instead of the official installer. This allows for version pinning. The `claude_binary_path` variable can be used to specify where a pre-installed Claude binary is located.
```tf ```tf
data "coder_parameter" "ai_prompt" { data "coder_parameter" "ai_prompt" {
type = "string" type = "string"
@ -68,7 +71,7 @@ data "coder_parameter" "ai_prompt" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
@ -76,7 +79,8 @@ module "claude-code" {
# OR # OR
claude_code_oauth_token = "xxxxx-xxxx-xxxx" claude_code_oauth_token = "xxxxx-xxxx-xxxx"
claude_code_version = "2.0.62" # Pin to a specific version claude_code_version = "2.0.62" # Pin to a specific version (uses npm)
claude_binary_path = "/opt/claude/bin" # Path to pre-installed Claude binary
agentapi_version = "0.11.4" agentapi_version = "0.11.4"
ai_prompt = data.coder_parameter.ai_prompt.value ai_prompt = data.coder_parameter.ai_prompt.value
@ -104,7 +108,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
```tf ```tf
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
install_claude_code = true install_claude_code = true
@ -126,7 +130,7 @@ variable "claude_code_oauth_token" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
claude_code_oauth_token = var.claude_code_oauth_token claude_code_oauth_token = var.claude_code_oauth_token
@ -199,7 +203,7 @@ resource "coder_env" "bedrock_api_key" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0" model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
@ -256,7 +260,7 @@ resource "coder_env" "google_application_credentials" {
module "claude-code" { module "claude-code" {
source = "registry.coder.com/coder/claude-code/coder" source = "registry.coder.com/coder/claude-code/coder"
version = "4.3.0" version = "4.4.2"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
workdir = "/home/coder/project" workdir = "/home/coder/project"
model = "claude-sonnet-4@20250514" model = "claude-sonnet-4@20250514"

View File

@ -184,20 +184,15 @@ describe("claude-code", async () => {
test("claude-model", async () => { test("claude-model", async () => {
const model = "opus"; const model = "opus";
const { id } = await setup({ const { coderEnvVars } = await setup({
moduleVariables: { moduleVariables: {
model: model, model: model,
ai_prompt: "test prompt", ai_prompt: "test prompt",
}, },
}); });
await execModuleScript(id);
const startLog = await execContainer(id, [ // Verify ANTHROPIC_MODEL env var is set via coder_env
"bash", expect(coderEnvVars["ANTHROPIC_MODEL"]).toBe(model);
"-c",
"cat /home/coder/.claude-module/agentapi-start.log",
]);
expect(startLog.stdout).toContain(`--model ${model}`);
}); });
test("claude-continue-resume-task-session", async () => { test("claude-continue-resume-task-session", async () => {

View File

@ -86,7 +86,7 @@ variable "install_agentapi" {
variable "agentapi_version" { variable "agentapi_version" {
type = string type = string
description = "The version of AgentAPI to install." description = "The version of AgentAPI to install."
default = "v0.11.6" default = "v0.11.8"
} }
variable "ai_prompt" { variable "ai_prompt" {
@ -128,7 +128,7 @@ variable "claude_api_key" {
variable "model" { variable "model" {
type = string type = string
description = "Sets the model for the current session with an alias for the latest model (sonnet or opus) or a models full name." description = "Sets the default model for Claude Code via ANTHROPIC_MODEL env var. If empty, Claude Code uses its default. Supports aliases (sonnet, opus) or full model names."
default = "" default = ""
} }
@ -198,6 +198,18 @@ variable "claude_md_path" {
default = "$HOME/.claude/CLAUDE.md" default = "$HOME/.claude/CLAUDE.md"
} }
variable "claude_binary_path" {
type = string
description = "Directory where the Claude Code binary is located. Use this if Claude is pre-installed or installed outside the module to a non-default location."
default = "$HOME/.local/bin"
}
variable "install_via_npm" {
type = bool
description = "Install Claude Code via npm instead of the official installer. Useful if npm is preferred or the official installer fails."
default = false
}
variable "enable_boundary" { variable "enable_boundary" {
type = bool type = bool
description = "Whether to enable coder boundary for network filtering" description = "Whether to enable coder boundary for network filtering"
@ -217,8 +229,7 @@ variable "compile_boundary_from_source" {
} }
resource "coder_env" "claude_code_md_path" { resource "coder_env" "claude_code_md_path" {
count = var.claude_md_path == "" ? 0 : 1 count = var.claude_md_path == "" ? 0 : 1
agent_id = var.agent_id agent_id = var.agent_id
name = "CODER_MCP_CLAUDE_MD_PATH" name = "CODER_MCP_CLAUDE_MD_PATH"
value = var.claude_md_path value = var.claude_md_path
@ -237,16 +248,14 @@ resource "coder_env" "claude_code_oauth_token" {
} }
resource "coder_env" "claude_api_key" { resource "coder_env" "claude_api_key" {
count = length(var.claude_api_key) > 0 ? 1 : 0 count = length(var.claude_api_key) > 0 ? 1 : 0
agent_id = var.agent_id agent_id = var.agent_id
name = "CLAUDE_API_KEY" name = "CLAUDE_API_KEY"
value = var.claude_api_key value = var.claude_api_key
} }
resource "coder_env" "disable_autoupdater" { resource "coder_env" "disable_autoupdater" {
count = var.disable_autoupdater ? 1 : 0 count = var.disable_autoupdater ? 1 : 0
agent_id = var.agent_id agent_id = var.agent_id
name = "DISABLE_AUTOUPDATER" name = "DISABLE_AUTOUPDATER"
value = "1" value = "1"
@ -255,7 +264,21 @@ resource "coder_env" "disable_autoupdater" {
resource "coder_env" "claude_binary_path" { resource "coder_env" "claude_binary_path" {
agent_id = var.agent_id agent_id = var.agent_id
name = "PATH" name = "PATH"
value = "$HOME/.local/bin:$PATH" value = "${var.claude_binary_path}:$PATH"
lifecycle {
precondition {
condition = var.claude_binary_path == "$HOME/.local/bin" || !var.install_claude_code
error_message = "Custom claude_binary_path can only be used when install_claude_code is false. The official installer and npm both install to fixed locations."
}
}
}
resource "coder_env" "anthropic_model" {
count = var.model != "" ? 1 : 0
agent_id = var.agent_id
name = "ANTHROPIC_MODEL"
value = var.model
} }
locals { locals {
@ -328,7 +351,6 @@ module "agentapi" {
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
chmod +x /tmp/start.sh chmod +x /tmp/start.sh
ARG_MODEL='${var.model}' \
ARG_RESUME_SESSION_ID='${var.resume_session_id}' \ ARG_RESUME_SESSION_ID='${var.resume_session_id}' \
ARG_CONTINUE='${var.continue}' \ ARG_CONTINUE='${var.continue}' \
ARG_DANGEROUSLY_SKIP_PERMISSIONS='${var.dangerously_skip_permissions}' \ ARG_DANGEROUSLY_SKIP_PERMISSIONS='${var.dangerously_skip_permissions}' \
@ -353,6 +375,8 @@ module "agentapi" {
ARG_CLAUDE_CODE_VERSION='${var.claude_code_version}' \ ARG_CLAUDE_CODE_VERSION='${var.claude_code_version}' \
ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \ ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \
ARG_INSTALL_CLAUDE_CODE='${var.install_claude_code}' \ ARG_INSTALL_CLAUDE_CODE='${var.install_claude_code}' \
ARG_CLAUDE_BINARY_PATH='${var.claude_binary_path}' \
ARG_INSTALL_VIA_NPM='${var.install_via_npm}' \
ARG_REPORT_TASKS='${var.report_tasks}' \ ARG_REPORT_TASKS='${var.report_tasks}' \
ARG_WORKDIR='${local.workdir}' \ ARG_WORKDIR='${local.workdir}' \
ARG_ALLOWED_TOOLS='${var.allowed_tools}' \ ARG_ALLOWED_TOOLS='${var.allowed_tools}' \

View File

@ -11,6 +11,8 @@ command_exists() {
ARG_CLAUDE_CODE_VERSION=${ARG_CLAUDE_CODE_VERSION:-} ARG_CLAUDE_CODE_VERSION=${ARG_CLAUDE_CODE_VERSION:-}
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"} ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
ARG_INSTALL_CLAUDE_CODE=${ARG_INSTALL_CLAUDE_CODE:-} ARG_INSTALL_CLAUDE_CODE=${ARG_INSTALL_CLAUDE_CODE:-}
ARG_CLAUDE_BINARY_PATH=${ARG_CLAUDE_BINARY_PATH:-"$HOME/.local/bin"}
ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false}
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true} ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-} ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
ARG_MCP=$(echo -n "${ARG_MCP:-}" | base64 -d) ARG_MCP=$(echo -n "${ARG_MCP:-}" | base64 -d)
@ -22,6 +24,8 @@ echo "--------------------------------"
printf "ARG_CLAUDE_CODE_VERSION: %s\n" "$ARG_CLAUDE_CODE_VERSION" printf "ARG_CLAUDE_CODE_VERSION: %s\n" "$ARG_CLAUDE_CODE_VERSION"
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR" printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
printf "ARG_INSTALL_CLAUDE_CODE: %s\n" "$ARG_INSTALL_CLAUDE_CODE" printf "ARG_INSTALL_CLAUDE_CODE: %s\n" "$ARG_INSTALL_CLAUDE_CODE"
printf "ARG_CLAUDE_BINARY_PATH: %s\n" "$ARG_CLAUDE_BINARY_PATH"
printf "ARG_INSTALL_VIA_NPM: %s\n" "$ARG_INSTALL_VIA_NPM"
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS" printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG" printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG"
printf "ARG_MCP: %s\n" "$ARG_MCP" printf "ARG_MCP: %s\n" "$ARG_MCP"
@ -30,20 +34,66 @@ printf "ARG_DISALLOWED_TOOLS: %s\n" "$ARG_DISALLOWED_TOOLS"
echo "--------------------------------" echo "--------------------------------"
function ensure_claude_in_path() {
if [ -z "${CODER_SCRIPT_BIN_DIR:-}" ]; then
echo "CODER_SCRIPT_BIN_DIR not set, skipping PATH setup"
return
fi
if [ ! -e "$CODER_SCRIPT_BIN_DIR/claude" ]; then
local CLAUDE_BIN=""
if command -v claude > /dev/null 2>&1; then
CLAUDE_BIN=$(command -v claude)
elif [ -x "$ARG_CLAUDE_BINARY_PATH/claude" ]; then
CLAUDE_BIN="$ARG_CLAUDE_BINARY_PATH/claude"
elif [ -x "$HOME/.local/bin/claude" ]; then
CLAUDE_BIN="$HOME/.local/bin/claude"
fi
if [ -n "$CLAUDE_BIN" ] && [ -x "$CLAUDE_BIN" ]; then
ln -s "$CLAUDE_BIN" "$CODER_SCRIPT_BIN_DIR/claude"
echo "Created symlink: $CODER_SCRIPT_BIN_DIR/claude -> $CLAUDE_BIN"
else
echo "Warning: Could not find claude binary to symlink"
fi
else
echo "Claude already available in CODER_SCRIPT_BIN_DIR"
fi
local marker="# Added by claude-code module"
for profile in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.profile"; do
if [ -f "$profile" ] && ! grep -q "$marker" "$profile" 2> /dev/null; then
printf "\n%s\nexport PATH=\"%s:\$PATH\"\n" "$marker" "$CODER_SCRIPT_BIN_DIR" >> "$profile"
echo "Added $CODER_SCRIPT_BIN_DIR to PATH in $profile"
fi
done
}
function install_claude_code_cli() { function install_claude_code_cli() {
if [ "$ARG_INSTALL_CLAUDE_CODE" = "true" ]; then if [ "$ARG_INSTALL_CLAUDE_CODE" != "true" ]; then
echo "Skipping Claude Code installation as per configuration."
ensure_claude_in_path
return
fi
# Use npm when install_via_npm is true or for specific version pinning
if [ "$ARG_INSTALL_VIA_NPM" = "true" ] || { [ -n "$ARG_CLAUDE_CODE_VERSION" ] && [ "$ARG_CLAUDE_CODE_VERSION" != "latest" ]; }; then
echo "Installing Claude Code via npm (version: $ARG_CLAUDE_CODE_VERSION)"
npm install -g "@anthropic-ai/claude-code@$ARG_CLAUDE_CODE_VERSION"
echo "Installed Claude Code via npm. Version: $(claude --version || echo 'unknown')"
else
echo "Installing Claude Code via official installer" echo "Installing Claude Code via official installer"
set +e set +e
curl -fsSL claude.ai/install.sh | bash -s -- "$ARG_CLAUDE_CODE_VERSION" 2>&1 curl -fsSL claude.ai/install.sh | bash -s -- "$ARG_CLAUDE_CODE_VERSION" 2>&1
CURL_EXIT=${PIPESTATUS[0]} CURL_EXIT=${PIPESTATUS[0]}
set -e set -e
if [ $CURL_EXIT -ne 0 ]; then if [ $CURL_EXIT -ne 0 ]; then
echo "Claude Code installer failed with exit code $$CURL_EXIT" echo "Claude Code installer failed with exit code $CURL_EXIT"
fi fi
echo "Installed Claude Code successfully. Version: $(claude --version || echo 'unknown')" echo "Installed Claude Code successfully. Version: $(claude --version || echo 'unknown')"
else
echo "Skipping Claude Code installation as per configuration."
fi fi
ensure_claude_in_path
} }
function setup_claude_configurations() { function setup_claude_configurations() {
@ -63,7 +113,7 @@ function setup_claude_configurations() {
while IFS= read -r server_name && IFS= read -r server_json; do while IFS= read -r server_name && IFS= read -r server_json; do
echo "------------------------" echo "------------------------"
echo "Executing: claude mcp add-json \"$server_name\" '$server_json' (in $ARG_WORKDIR)" echo "Executing: claude mcp add-json \"$server_name\" '$server_json' (in $ARG_WORKDIR)"
claude mcp add-json "$server_name" "$server_json" claude mcp add-json "$server_name" "$server_json" || echo "Warning: Failed to add MCP server '$server_name', continuing..."
echo "------------------------" echo "------------------------"
echo "" echo ""
done < <(echo "$ARG_MCP" | jq -r '.mcpServers | to_entries[] | .key, (.value | @json)') done < <(echo "$ARG_MCP" | jq -r '.mcpServers | to_entries[] | .key, (.value | @json)')

View File

@ -6,7 +6,6 @@ command_exists() {
command -v "$1" > /dev/null 2>&1 command -v "$1" > /dev/null 2>&1
} }
ARG_MODEL=${ARG_MODEL:-}
ARG_RESUME_SESSION_ID=${ARG_RESUME_SESSION_ID:-} ARG_RESUME_SESSION_ID=${ARG_RESUME_SESSION_ID:-}
ARG_CONTINUE=${ARG_CONTINUE:-false} ARG_CONTINUE=${ARG_CONTINUE:-false}
ARG_DANGEROUSLY_SKIP_PERMISSIONS=${ARG_DANGEROUSLY_SKIP_PERMISSIONS:-} ARG_DANGEROUSLY_SKIP_PERMISSIONS=${ARG_DANGEROUSLY_SKIP_PERMISSIONS:-}
@ -21,7 +20,6 @@ ARG_CODER_HOST=${ARG_CODER_HOST:-}
echo "--------------------------------" echo "--------------------------------"
printf "ARG_MODEL: %s\n" "$ARG_MODEL"
printf "ARG_RESUME: %s\n" "$ARG_RESUME_SESSION_ID" printf "ARG_RESUME: %s\n" "$ARG_RESUME_SESSION_ID"
printf "ARG_CONTINUE: %s\n" "$ARG_CONTINUE" printf "ARG_CONTINUE: %s\n" "$ARG_CONTINUE"
printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIONS"
@ -170,10 +168,6 @@ function start_agentapi() {
mkdir -p "$ARG_WORKDIR" mkdir -p "$ARG_WORKDIR"
cd "$ARG_WORKDIR" cd "$ARG_WORKDIR"
if [ -n "$ARG_MODEL" ]; then
ARGS+=(--model "$ARG_MODEL")
fi
if [ -n "$ARG_PERMISSION_MODE" ]; then if [ -n "$ARG_PERMISSION_MODE" ]; then
ARGS+=(--permission-mode "$ARG_PERMISSION_MODE") ARGS+=(--permission-mode "$ARG_PERMISSION_MODE")
fi fi
@ -223,8 +217,7 @@ function start_agentapi() {
printf "Starting with coder boundary enabled\n" printf "Starting with coder boundary enabled\n"
# Add default allowed URLs BOUNDARY_ARGS+=()
BOUNDARY_ARGS+=(--allow "domain=anthropic.com" --allow "domain=registry.npmjs.org" --allow "domain=sentry.io" --allow "domain=claude.ai" --allow "domain=$ARG_CODER_HOST")
agentapi server --type claude --term-width 67 --term-height 1190 -- \ agentapi server --type claude --term-width 67 --term-height 1190 -- \
boundary-run "${BOUNDARY_ARGS[@]}" -- \ boundary-run "${BOUNDARY_ARGS[@]}" -- \

View File

@ -1,5 +1,9 @@
import { describe, expect, it } from "bun:test"; import { describe, expect, it } from "bun:test";
import { import {
execContainer,
findResourceInstance,
removeContainer,
runContainer,
runTerraformApply, runTerraformApply,
runTerraformInit, runTerraformInit,
testRequiredVariables, testRequiredVariables,
@ -34,5 +38,47 @@ describe("code-server", async () => {
expect(t).toThrow("Offline mode does not allow extensions to be installed"); expect(t).toThrow("Offline mode does not allow extensions to be installed");
}); });
// More tests depend on shebang refactors it("installs and runs code-server", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
});
const id = await runContainer("ubuntu:latest");
try {
await execContainer(id, [
"bash",
"-c",
"apt-get update && apt-get install -y curl",
]);
const script = findResourceInstance(state, "coder_script").script;
const result = await execContainer(id, ["bash", "-c", script]);
if (result.exitCode !== 0) {
console.log(result.stdout);
console.log(result.stderr);
}
expect(result.exitCode).toBe(0);
const version = await execContainer(id, [
"/tmp/code-server/bin/code-server",
"--version",
]);
expect(version.exitCode).toBe(0);
expect(version.stdout).toMatch(/\d+\.\d+\.\d+/);
const health = await execContainer(id, [
"curl",
"--retry",
"10",
"--retry-delay",
"1",
"--retry-all-errors",
"-sf",
"http://localhost:13337/healthz",
]);
expect(health.exitCode).toBe(0);
} finally {
await removeContainer(id);
}
}, 60000);
}); });

View File

@ -8,6 +8,9 @@ tags: [ide, jetbrains, fleet]
# Jetbrains Fleet # Jetbrains Fleet
> [!WARNING]
> **Deprecation Notice:** JetBrains has announced that Fleet will be discontinued. For more information, see [The Future of Fleet](https://blog.jetbrains.com/fleet/2025/12/the-future-of-fleet). Consider migrating to other JetBrains IDEs such as IntelliJ IDEA, PyCharm, or GoLand with the [JetBrains](https://registry.coder.com/modules/jetbrains) module.
This module adds a Jetbrains Fleet button to your Coder workspace that opens the workspace in JetBrains Fleet using SSH remote development. This module adds a Jetbrains Fleet button to your Coder workspace that opens the workspace in JetBrains Fleet using SSH remote development.
JetBrains Fleet is a next-generation IDE that supports collaborative development and distributed architectures. It connects to your Coder workspace via SSH, providing a seamless remote development experience. JetBrains Fleet is a next-generation IDE that supports collaborative development and distributed architectures. It connects to your Coder workspace via SSH, providing a seamless remote development experience.
@ -16,7 +19,7 @@ JetBrains Fleet is a next-generation IDE that supports collaborative development
module "jetbrains_fleet" { module "jetbrains_fleet" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-fleet/coder" source = "registry.coder.com/coder/jetbrains-fleet/coder"
version = "1.0.2" version = "1.0.3"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
} }
``` ```
@ -37,7 +40,7 @@ module "jetbrains_fleet" {
module "jetbrains_fleet" { module "jetbrains_fleet" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-fleet/coder" source = "registry.coder.com/coder/jetbrains-fleet/coder"
version = "1.0.2" version = "1.0.3"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
} }
``` ```
@ -48,7 +51,7 @@ module "jetbrains_fleet" {
module "jetbrains_fleet" { module "jetbrains_fleet" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-fleet/coder" source = "registry.coder.com/coder/jetbrains-fleet/coder"
version = "1.0.2" version = "1.0.3"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
folder = "/home/coder/project" folder = "/home/coder/project"
} }
@ -60,7 +63,7 @@ module "jetbrains_fleet" {
module "jetbrains_fleet" { module "jetbrains_fleet" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-fleet/coder" source = "registry.coder.com/coder/jetbrains-fleet/coder"
version = "1.0.2" version = "1.0.3"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
display_name = "Fleet" display_name = "Fleet"
group = "JetBrains IDEs" group = "JetBrains IDEs"
@ -74,7 +77,7 @@ module "jetbrains_fleet" {
module "jetbrains_fleet" { module "jetbrains_fleet" {
count = data.coder_workspace.me.start_count count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/jetbrains-fleet/coder" source = "registry.coder.com/coder/jetbrains-fleet/coder"
version = "1.0.2" version = "1.0.3"
agent_id = coder_agent.main.id agent_id = coder_agent.main.id
agent_name = coder_agent.example.name agent_name = coder_agent.example.name
} }