mirror of
https://github.com/coder/registry.git
synced 2026-06-03 04:58:15 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3885a5047 | |||
| de7bd01021 | |||
| 494ad9bd48 | |||
| 5ee68d04d1 | |||
| 516a934694 |
@@ -37,7 +37,7 @@ jobs:
|
||||
all:
|
||||
- '**'
|
||||
- name: Set up Terraform
|
||||
uses: coder/coder/.github/actions/setup-tf@f7650296ceb9b020c79cd525ac7bd3c7f252ae1d # v2.31.6
|
||||
uses: coder/coder/.github/actions/setup-tf@a7e9dfa7dc18625e4e0c155af3b0928a8cb3354a # v2.31.7
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
@@ -87,13 +87,13 @@ jobs:
|
||||
bun-version: latest
|
||||
# Need Terraform for its formatter
|
||||
- name: Install Terraform
|
||||
uses: coder/coder/.github/actions/setup-tf@f7650296ceb9b020c79cd525ac7bd3c7f252ae1d # v2.31.6
|
||||
uses: coder/coder/.github/actions/setup-tf@a7e9dfa7dc18625e4e0c155af3b0928a8cb3354a # v2.31.7
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- name: Validate formatting
|
||||
run: bun fmt:ci
|
||||
- name: Check for typos
|
||||
uses: crate-ci/typos@631208b7aac2daa8b707f55e7331f9112b0e062d # v1.44.0
|
||||
uses: crate-ci/typos@02ea592e44b3a53c302f697cddca7641cd051c3d # v1.45.0
|
||||
with:
|
||||
config: .github/typos.toml
|
||||
validate-readme-files:
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
bun-version: latest
|
||||
|
||||
- name: Set up Terraform
|
||||
uses: coder/coder/.github/actions/setup-tf@f7650296ceb9b020c79cd525ac7bd3c7f252ae1d # v2.31.6
|
||||
uses: coder/coder/.github/actions/setup-tf@a7e9dfa7dc18625e4e0c155af3b0928a8cb3354a # v2.31.7
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
@@ -13,7 +13,7 @@ Run [GitHub Copilot CLI](https://docs.github.com/copilot/concepts/agents/about-c
|
||||
```tf
|
||||
module "copilot" {
|
||||
source = "registry.coder.com/coder-labs/copilot/coder"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/projects"
|
||||
}
|
||||
@@ -51,7 +51,7 @@ data "coder_parameter" "ai_prompt" {
|
||||
|
||||
module "copilot" {
|
||||
source = "registry.coder.com/coder-labs/copilot/coder"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/projects"
|
||||
|
||||
@@ -71,7 +71,7 @@ Customize tool permissions, MCP servers, and Copilot settings:
|
||||
```tf
|
||||
module "copilot" {
|
||||
source = "registry.coder.com/coder-labs/copilot/coder"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/projects"
|
||||
|
||||
@@ -142,7 +142,7 @@ variable "github_token" {
|
||||
|
||||
module "copilot" {
|
||||
source = "registry.coder.com/coder-labs/copilot/coder"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/projects"
|
||||
github_token = var.github_token
|
||||
@@ -156,7 +156,7 @@ Run Copilot as a command-line tool without task reporting or web interface. This
|
||||
```tf
|
||||
module "copilot" {
|
||||
source = "registry.coder.com/coder-labs/copilot/coder"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder"
|
||||
report_tasks = false
|
||||
@@ -179,7 +179,7 @@ module "aibridge-proxy" {
|
||||
|
||||
module "copilot" {
|
||||
source = "registry.coder.com/coder-labs/copilot/coder"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/projects"
|
||||
enable_aibridge_proxy = true
|
||||
|
||||
@@ -117,18 +117,23 @@ run "copilot_model_not_created_for_default" {
|
||||
}
|
||||
}
|
||||
|
||||
run "model_validation_accepts_valid_models" {
|
||||
run "copilot_model_accepts_custom_model" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder"
|
||||
copilot_model = "gpt-5"
|
||||
copilot_model = "o3-pro"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model)
|
||||
error_message = "Model should be one of the valid options"
|
||||
condition = var.copilot_model == "o3-pro"
|
||||
error_message = "copilot_model should accept any model string"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = length(resource.coder_env.copilot_model) == 1
|
||||
error_message = "copilot_model env var should be created for non-default model"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,8 @@ variable "github_token" {
|
||||
|
||||
variable "copilot_model" {
|
||||
type = string
|
||||
description = "Model to use. Supported values: claude-sonnet-4, claude-sonnet-4.5 (default), gpt-5."
|
||||
description = "The model to use for Copilot. Any model supported by GitHub Copilot can be used."
|
||||
default = "claude-sonnet-4.5"
|
||||
validation {
|
||||
condition = contains(["claude-sonnet-4", "claude-sonnet-4.5", "gpt-5"], var.copilot_model)
|
||||
error_message = "copilot_model must be one of: claude-sonnet-4, claude-sonnet-4.5, gpt-5."
|
||||
}
|
||||
}
|
||||
|
||||
variable "copilot_config" {
|
||||
|
||||
@@ -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.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
claude_api_key = "xxxx-xxxxx-xxxx"
|
||||
@@ -60,7 +60,7 @@ By default, when `enable_boundary = true`, the module uses `coder boundary` subc
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
enable_boundary = true
|
||||
@@ -81,7 +81,7 @@ For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
enable_aibridge = true
|
||||
@@ -110,7 +110,7 @@ data "coder_task" "me" {}
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
ai_prompt = data.coder_task.me.prompt
|
||||
@@ -133,7 +133,7 @@ This example shows additional configuration options for version pinning, custom
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
|
||||
@@ -189,7 +189,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.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
install_claude_code = true
|
||||
@@ -211,7 +211,7 @@ variable "claude_code_oauth_token" {
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
claude_code_oauth_token = var.claude_code_oauth_token
|
||||
@@ -284,7 +284,7 @@ resource "coder_env" "bedrock_api_key" {
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
@@ -341,7 +341,7 @@ resource "coder_env" "google_application_credentials" {
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.9.0"
|
||||
version = "4.9.2"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
model = "claude-sonnet-4@20250514"
|
||||
|
||||
@@ -182,6 +182,24 @@ describe("claude-code", async () => {
|
||||
expect(startLog.stdout).toContain(`--permission-mode ${mode}`);
|
||||
});
|
||||
|
||||
test("claude-auto-permission-mode", async () => {
|
||||
const mode = "auto";
|
||||
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 { coderEnvVars } = await setup({
|
||||
|
||||
@@ -161,8 +161,8 @@ variable "permission_mode" {
|
||||
description = "Permission mode for the cli, check https://docs.anthropic.com/en/docs/claude-code/iam#permission-modes"
|
||||
default = ""
|
||||
validation {
|
||||
condition = contains(["", "default", "acceptEdits", "plan", "bypassPermissions"], var.permission_mode)
|
||||
error_message = "interaction_mode must be one of: default, acceptEdits, plan, bypassPermissions."
|
||||
condition = contains(["", "default", "acceptEdits", "plan", "auto", "bypassPermissions"], var.permission_mode)
|
||||
error_message = "interaction_mode must be one of: default, acceptEdits, plan, auto, bypassPermissions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,10 +368,10 @@ locals {
|
||||
|
||||
module "agentapi" {
|
||||
source = "registry.coder.com/coder/agentapi/coder"
|
||||
version = "2.3.0"
|
||||
version = "2.4.0"
|
||||
|
||||
agent_id = var.agent_id
|
||||
# TODO: pass web_app = var.web_app once agentapi module is published with web_app support
|
||||
agent_id = var.agent_id
|
||||
web_app = var.web_app
|
||||
web_app_slug = local.app_slug
|
||||
web_app_order = var.order
|
||||
web_app_group = var.group
|
||||
@@ -430,6 +430,7 @@ module "agentapi" {
|
||||
ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \
|
||||
ARG_MCP_CONFIG_REMOTE_PATH='${base64encode(jsonencode(var.mcp_config_remote_path))}' \
|
||||
ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \
|
||||
ARG_PERMISSION_MODE='${var.permission_mode}' \
|
||||
/tmp/install.sh
|
||||
EOT
|
||||
}
|
||||
|
||||
@@ -183,11 +183,26 @@ run "test_claude_code_permission_mode_validation" {
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = contains(["", "default", "acceptEdits", "plan", "bypassPermissions"], var.permission_mode)
|
||||
condition = contains(["", "default", "acceptEdits", "plan", "auto", "bypassPermissions"], var.permission_mode)
|
||||
error_message = "Permission mode should be one of the valid options"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_claude_code_auto_permission_mode" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent-auto"
|
||||
workdir = "/home/coder/test"
|
||||
permission_mode = "auto"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.permission_mode == "auto"
|
||||
error_message = "Permission mode should be set to auto"
|
||||
}
|
||||
}
|
||||
|
||||
run "test_claude_code_with_boundary" {
|
||||
command = plan
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ ARG_MCP_CONFIG_REMOTE_PATH=$(echo -n "${ARG_MCP_CONFIG_REMOTE_PATH:-}" | base64
|
||||
ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-}
|
||||
ARG_DISALLOWED_TOOLS=${ARG_DISALLOWED_TOOLS:-}
|
||||
ARG_ENABLE_AIBRIDGE=${ARG_ENABLE_AIBRIDGE:-false}
|
||||
ARG_PERMISSION_MODE=${ARG_PERMISSION_MODE:-}
|
||||
|
||||
export PATH="$ARG_CLAUDE_BINARY_PATH:$PATH"
|
||||
|
||||
@@ -195,6 +196,7 @@ function configure_standalone_mode() {
|
||||
|
||||
jq --arg workdir "$ARG_WORKDIR" --arg apikey "${CLAUDE_API_KEY:-}" \
|
||||
'.autoUpdaterStatus = "disabled" |
|
||||
.autoModeAccepted = true |
|
||||
.bypassPermissionsModeAccepted = true |
|
||||
.hasAcknowledgedCostThreshold = true |
|
||||
.hasCompletedOnboarding = true |
|
||||
@@ -207,6 +209,7 @@ function configure_standalone_mode() {
|
||||
cat > "$claude_config" << EOF
|
||||
{
|
||||
"autoUpdaterStatus": "disabled",
|
||||
"autoModeAccepted": true,
|
||||
"bypassPermissionsModeAccepted": true,
|
||||
"hasAcknowledgedCostThreshold": true,
|
||||
"hasCompletedOnboarding": true,
|
||||
@@ -235,6 +238,28 @@ function report_tasks() {
|
||||
fi
|
||||
}
|
||||
|
||||
function accept_auto_mode() {
|
||||
# Pre-accept the auto mode TOS prompt so it doesn't appear interactively.
|
||||
# Claude Code shows a confirmation dialog for auto mode that blocks
|
||||
# non-interactive/headless usage.
|
||||
# Note: bypassPermissions acceptance is already handled by
|
||||
# coder exp mcp configure (task mode) and configure_standalone_mode.
|
||||
local claude_config="$HOME/.claude.json"
|
||||
|
||||
if [ -f "$claude_config" ]; then
|
||||
jq '.autoModeAccepted = true' \
|
||||
"$claude_config" > "${claude_config}.tmp" && mv "${claude_config}.tmp" "$claude_config"
|
||||
else
|
||||
echo '{"autoModeAccepted": true}' > "$claude_config"
|
||||
fi
|
||||
|
||||
echo "Pre-accepted auto mode prompt"
|
||||
}
|
||||
|
||||
install_claude_code_cli
|
||||
setup_claude_configurations
|
||||
report_tasks
|
||||
|
||||
if [ "$ARG_PERMISSION_MODE" = "auto" ]; then
|
||||
accept_auto_mode
|
||||
fi
|
||||
|
||||
@@ -16,7 +16,7 @@ The VSCode Desktop Core module is a building block for modules that need to expo
|
||||
```tf
|
||||
module "vscode-desktop-core" {
|
||||
source = "registry.coder.com/coder/vscode-desktop-core/coder"
|
||||
version = "1.0.2"
|
||||
version = "1.1.0"
|
||||
|
||||
agent_id = var.agent_id
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@ import {
|
||||
runTerraformApply,
|
||||
runTerraformInit,
|
||||
testRequiredVariables,
|
||||
runContainer,
|
||||
execContainer,
|
||||
removeContainer,
|
||||
findResourceInstance,
|
||||
readFileContainer,
|
||||
} from "~test";
|
||||
|
||||
// hardcoded coder_app name in main.tf
|
||||
@@ -16,6 +21,7 @@ const defaultVariables = {
|
||||
coder_app_display_name: "VS Code Desktop",
|
||||
|
||||
protocol: "vscode",
|
||||
config_dir: "$HOME/.vscode",
|
||||
};
|
||||
|
||||
describe("vscode-desktop-core", async () => {
|
||||
@@ -134,4 +140,41 @@ describe("vscode-desktop-core", async () => {
|
||||
expect(coder_app?.instances[0].attributes.group).toBe("web-app-group");
|
||||
});
|
||||
});
|
||||
|
||||
it("writes mcp_config.json when mcp_config variable provided", async () => {
|
||||
const id = await runContainer("alpine");
|
||||
|
||||
try {
|
||||
const mcp_config = JSON.stringify({
|
||||
servers: { demo: { url: "http://localhost:1234" } },
|
||||
});
|
||||
|
||||
const state = await runTerraformApply(import.meta.dir, {
|
||||
...defaultVariables,
|
||||
|
||||
mcp_config,
|
||||
});
|
||||
|
||||
const script = findResourceInstance(
|
||||
state,
|
||||
"coder_script",
|
||||
"vscode-desktop-mcp",
|
||||
).script;
|
||||
|
||||
const resp = await execContainer(id, ["sh", "-c", script]);
|
||||
if (resp.exitCode !== 0) {
|
||||
console.log(resp.stdout);
|
||||
console.log(resp.stderr);
|
||||
}
|
||||
expect(resp.exitCode).toBe(0);
|
||||
|
||||
const content = await readFileContainer(
|
||||
id,
|
||||
`${defaultVariables.config_dir.replace("$HOME", "/root")}/mcp_config.json`,
|
||||
);
|
||||
expect(content).toBe(mcp_config);
|
||||
} finally {
|
||||
await removeContainer(id);
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
@@ -26,11 +26,22 @@ variable "open_recent" {
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "mcp_config" {
|
||||
type = map(any)
|
||||
description = "MCP server configuration for the IDE. When set, writes mcp_config.json in var.config_dir."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "protocol" {
|
||||
type = string
|
||||
description = "The URI protocol the IDE."
|
||||
}
|
||||
|
||||
variable "config_dir" {
|
||||
type = string
|
||||
description = "The path of the IDE's configuration folder."
|
||||
}
|
||||
|
||||
variable "coder_app_icon" {
|
||||
type = string
|
||||
description = "The icon of the coder_app."
|
||||
@@ -85,21 +96,36 @@ resource "coder_app" "vscode-desktop" {
|
||||
data.coder_workspace.me.access_url,
|
||||
"&token=$SESSION_TOKEN",
|
||||
])
|
||||
}
|
||||
|
||||
/*
|
||||
url = join("", [
|
||||
"vscode://coder.coder-remote/open",
|
||||
"?owner=${data.coder_workspace_owner.me.name}",
|
||||
"&workspace=${data.coder_workspace.me.name}",
|
||||
var.folder != "" ? join("", ["&folder=", var.folder]) : "",
|
||||
var.open_recent ? "&openRecent" : "",
|
||||
"&url=${data.coder_workspace.me.access_url}",
|
||||
"&token=$SESSION_TOKEN",
|
||||
])
|
||||
*/
|
||||
resource "coder_script" "vscode-desktop-mcp" {
|
||||
agent_id = var.agent_id
|
||||
count = var.mcp_config != null ? 1 : 0
|
||||
|
||||
icon = var.coder_app_icon
|
||||
display_name = "${var.coder_app_display_name} MCP"
|
||||
|
||||
run_on_start = true
|
||||
start_blocks_login = false
|
||||
|
||||
script = <<-EOT
|
||||
#!/bin/sh
|
||||
set -euo pipefail
|
||||
|
||||
IDE_CONFIG_FOLDER="${var.config_dir}"
|
||||
IDE_MCP_CONFIG_PATH="$IDE_CONFIG_FOLDER/mcp_config.json"
|
||||
|
||||
mkdir -p "$IDE_CONFIG_FOLDER"
|
||||
|
||||
echo -n "${base64encode(jsonencode(var.mcp_config))}" | base64 -d > "$IDE_MCP_CONFIG_PATH"
|
||||
chmod 600 "$IDE_MCP_CONFIG_PATH"
|
||||
|
||||
# Cursor/Windsurf use this config instead, no need for chmod as symlinks do not have modes
|
||||
ln -s "$IDE_MCP_CONFIG_PATH" "$IDE_CONFIG_FOLDER/mcp.json"
|
||||
EOT
|
||||
}
|
||||
|
||||
output "ide_uri" {
|
||||
value = coder_app.vscode-desktop.url
|
||||
description = "IDE URI."
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user