Merge branch 'main' into jwb/claude-code-pre-start-script
This commit is contained in:
commit
c2e2964a85
2
.github/workflows/ci.yaml
vendored
2
.github/workflows/ci.yaml
vendored
@ -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
2
CODEOWNERS
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# GitHub Actions Workflow Owners
|
||||||
|
.github/ @jdomeracki-coder
|
||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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 () => {
|
||||||
|
|||||||
@ -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 model’s 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"
|
||||||
@ -218,7 +230,6 @@ 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
|
||||||
@ -238,7 +249,6 @@ 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
|
||||||
@ -246,7 +256,6 @@ resource "coder_env" "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}' \
|
||||||
|
|||||||
@ -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)')
|
||||||
|
|||||||
@ -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[@]}" -- \
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user