Compare commits

...

56 Commits

Author SHA1 Message Date
35C4n0r 6802df9d13 hopefully final wip 2026-01-20 17:49:05 +00:00
35C4n0r a7e0b09aa4 wip 2026-01-20 17:43:22 +00:00
35C4n0r 229056b344 wip 2026-01-20 17:35:20 +00:00
35C4n0r f257efd8e1 wip 2026-01-20 17:19:25 +00:00
35C4n0r 9258f1857f wip 2026-01-20 17:14:57 +00:00
35C4n0r 5c4480daa3 wip 2026-01-19 18:44:09 +00:00
35C4n0r 0ffd71d443 wip 2026-01-19 18:31:34 +00:00
35C4n0r 60ed61368e bun fmt 2026-01-19 18:14:41 +00:00
35C4n0r 5f4d7bf1b4 feat: merge to main changes 2026-01-19 18:13:21 +00:00
35C4n0r c411657a67 feat: merge to main changes 2026-01-19 18:10:00 +00:00
35C4n0r d8a96435c6 Merge branch 'main' into feat-conditional-agentapi
# Conflicts:
#	registry/coder/modules/claude-code/main.tf
#	registry/coder/modules/claude-code/scripts/install.sh
#	registry/coder/modules/claude-code/scripts/start.sh
2026-01-19 18:05:31 +00:00
35C4n0r d4efc09b20 bun fmt
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 18:05:17 +00:00
35C4n0r 4b03bce6f7 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 18:00:11 +00:00
35C4n0r ae48c1043b wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 17:47:20 +00:00
35C4n0r 73af151f8d wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 17:39:22 +00:00
35C4n0r c115d860f7 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 17:21:53 +00:00
35C4n0r 395f170d07 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 17:11:49 +00:00
35C4n0r 65189bc068 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 12:50:51 +00:00
35C4n0r d3b5057819 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 12:46:45 +00:00
35C4n0r dd86d3d1d8 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 12:41:21 +00:00
35C4n0r 1dee0012f5 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 12:37:52 +00:00
35C4n0r ef0f597d54 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 12:31:26 +00:00
35C4n0r aaf2c4e0dd wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 12:23:27 +00:00
35C4n0r 090fa7dd1d wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 11:04:55 +00:00
35C4n0r a630ffa42a wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 10:13:30 +00:00
35C4n0r 93f9ec3708 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 10:07:30 +00:00
35C4n0r 5870805d0f wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 09:26:01 +00:00
35C4n0r 6806985778 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 09:20:48 +00:00
35C4n0r 149e65b49f wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 09:13:33 +00:00
35C4n0r 327f05487d wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-14 09:07:52 +00:00
35C4n0r 19dc50db3e wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-13 11:33:20 +00:00
35C4n0r 48564621ad Merge branch 'main' into feat-conditional-agentapi
# Conflicts:
#	registry/coder/modules/claude-code/main.tf
#	registry/coder/modules/claude-code/scripts/start.sh
2025-12-13 11:30:22 +00:00
35C4n0r d0ef4f426b wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-13 11:28:06 +00:00
35C4n0r c2fa87aea6 wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-12 18:13:53 +00:00
35C4n0r 63eff436eb wip
Signed-off-by: 35C4n0r <work.jaykumar@gmail.com>
2025-12-12 18:02:51 +00:00
35C4n0r 250b64e44f wip 2025-12-05 19:18:01 +05:30
35C4n0r 78a0d14863 wip 2025-12-05 19:14:55 +05:30
35C4n0r 0d0bfa7131 wip 2025-12-05 19:10:01 +05:30
35C4n0r b32b2d4329 wip 2025-12-05 19:08:58 +05:30
35C4n0r 8664ded490 wip 2025-12-05 18:38:37 +05:30
35C4n0r 2ed4be2172 wip 2025-12-05 18:36:42 +05:30
35C4n0r d718c3b4e9 wip 2025-12-05 18:30:08 +05:30
35C4n0r 19f2a8f3ec wip 2025-12-04 21:42:56 +05:30
35C4n0r e12cd61e45 wip 2025-12-04 21:39:49 +05:30
35C4n0r 09386a43cd wip 2025-12-04 21:30:11 +05:30
35C4n0r 50eb191eaa wip 2025-12-04 21:29:50 +05:30
35C4n0r bdc8aea37f wip 2025-12-04 08:27:15 +05:30
35C4n0r c6a7d049bd wip 2025-12-04 08:17:07 +05:30
35C4n0r 65a73a8708 wip 2025-12-03 21:32:47 +05:30
35C4n0r 05c5724561 wip 2025-12-03 18:43:25 +05:30
35C4n0r 0d03fa4e58 wip 2025-12-03 18:34:46 +05:30
35C4n0r f3bfa9cc8d wip 2025-12-03 16:49:30 +05:30
35C4n0r a91c8845cb wip 2025-12-03 16:49:08 +05:30
35C4n0r 2c00575203 wip 2025-11-28 21:34:11 +05:30
35C4n0r c5d83570bc wip 2025-11-28 21:24:27 +05:30
35C4n0r dd96e8c74b feat: conditional agentapi 2025-11-28 21:22:06 +05:30
2 changed files with 139 additions and 82 deletions
+86 -42
View File
@@ -244,6 +244,12 @@ variable "enable_aibridge" {
}
}
variable "cli_command" {
type = string
description = "The command to run for the Claude Code CLI app when tasks are disabled."
default = ""
}
resource "coder_env" "claude_code_md_path" {
count = var.claude_md_path == "" ? 0 : 1
agent_id = var.agent_id
@@ -344,12 +350,89 @@ locals {
var.report_tasks ? format("\n%s\n", local.report_tasks_system_prompt) : "",
local.custom_system_prompt != "" ? format("\n%s\n", local.custom_system_prompt) : ""
)
# Common environment variables for install script
install_env_vars = <<-EOT
export ARG_CLAUDE_CODE_VERSION='${var.claude_code_version}'
export ARG_MCP_APP_STATUS_SLUG='${local.app_slug}'
export ARG_INSTALL_CLAUDE_CODE='${var.install_claude_code}'
export ARG_CLAUDE_BINARY_PATH='${var.claude_binary_path}'
export ARG_INSTALL_VIA_NPM='${var.install_via_npm}'
export ARG_REPORT_TASKS='${var.report_tasks}'
export ARG_WORKDIR='${local.workdir}'
export ARG_ALLOWED_TOOLS='${var.allowed_tools}'
export ARG_DISALLOWED_TOOLS='${var.disallowed_tools}'
export ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}'
export ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}'
EOT
# Common environment variables for start script
start_env_vars = <<-EOT
export ARG_RESUME_SESSION_ID='${var.resume_session_id}'
export ARG_CONTINUE='${var.continue}'
export ARG_DANGEROUSLY_SKIP_PERMISSIONS='${var.dangerously_skip_permissions}'
export ARG_PERMISSION_MODE='${var.permission_mode}'
export ARG_WORKDIR='${local.workdir}'
export ARG_AI_PROMPT='${base64encode(var.ai_prompt)}'
export ARG_REPORT_TASKS='${var.report_tasks}'
export ARG_ENABLE_BOUNDARY='${var.enable_boundary}'
export ARG_BOUNDARY_VERSION='${var.boundary_version}'
export ARG_COMPILE_FROM_SOURCE='${var.compile_boundary_from_source}'
export ARG_CODER_HOST='${local.coder_host}'
export ARG_NON_AGENTAPI_CLI='${!var.report_tasks && var.cli_app ? true : false}'
EOT
# Reusable install script command
install_command = <<-EOT
#!/bin/bash
set -o pipefail
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
chmod +x /tmp/install.sh
${local.install_env_vars}
/tmp/install.sh
EOT
# Reusable start script command for agentapi module
agentapi_start_command = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
chmod +x /tmp/start.sh
${local.start_env_vars}
/tmp/start.sh
EOT
}
resource "coder_script" "install_agent" {
count = !var.report_tasks ? 1 : 0
agent_id = var.agent_id
display_name = "Install agent"
run_on_start = true
log_path = "/home/coder/install.log"
script = local.install_command
}
resource "coder_app" "agent_cli" {
count = (!var.report_tasks && var.cli_app) ? 1 : 0
agent_id = var.agent_id
slug = local.app_slug
display_name = var.cli_app_display_name
command = length(trimprefix(var.cli_command, " ")) > 0 ? var.cli_command : local.agentapi_start_command
}
module "agentapi" {
source = "registry.coder.com/coder/agentapi/coder"
version = "2.0.0"
count = var.report_tasks ? 1 : 0
agent_id = var.agent_id
web_app_slug = local.app_slug
web_app_order = var.order
@@ -366,49 +449,10 @@ module "agentapi" {
agentapi_version = var.agentapi_version
pre_install_script = var.pre_install_script
post_install_script = var.post_install_script
start_script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail
echo -n '${base64encode(local.start_script)}' | base64 -d > /tmp/start.sh
chmod +x /tmp/start.sh
ARG_RESUME_SESSION_ID='${var.resume_session_id}' \
ARG_CONTINUE='${var.continue}' \
ARG_DANGEROUSLY_SKIP_PERMISSIONS='${var.dangerously_skip_permissions}' \
ARG_PERMISSION_MODE='${var.permission_mode}' \
ARG_WORKDIR='${local.workdir}' \
ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \
ARG_REPORT_TASKS='${var.report_tasks}' \
ARG_ENABLE_BOUNDARY='${var.enable_boundary}' \
ARG_BOUNDARY_VERSION='${var.boundary_version}' \
ARG_COMPILE_FROM_SOURCE='${var.compile_boundary_from_source}' \
ARG_CODER_HOST='${local.coder_host}' \
/tmp/start.sh
EOT
install_script = <<-EOT
#!/bin/bash
set -o errexit
set -o pipefail
echo -n '${base64encode(local.install_script)}' | base64 -d > /tmp/install.sh
chmod +x /tmp/install.sh
ARG_CLAUDE_CODE_VERSION='${var.claude_code_version}' \
ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \
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_WORKDIR='${local.workdir}' \
ARG_ALLOWED_TOOLS='${var.allowed_tools}' \
ARG_DISALLOWED_TOOLS='${var.disallowed_tools}' \
ARG_MCP='${var.mcp != null ? base64encode(replace(var.mcp, "'", "'\\''")) : ""}' \
ARG_ENABLE_AIBRIDGE='${var.enable_aibridge}' \
/tmp/install.sh
EOT
start_script = local.agentapi_start_command
install_script = local.install_command
}
output "task_app_id" {
value = module.agentapi.task_app_id
value = try(module.agentapi[0].task_app_id, null)
}
@@ -2,6 +2,8 @@
set -euo pipefail
true > "$HOME/start.log"
command_exists() {
command -v "$1" > /dev/null 2>&1
}
@@ -17,34 +19,41 @@ ARG_ENABLE_BOUNDARY=${ARG_ENABLE_BOUNDARY:-false}
ARG_BOUNDARY_VERSION=${ARG_BOUNDARY_VERSION:-"main"}
ARG_COMPILE_FROM_SOURCE=${ARG_COMPILE_FROM_SOURCE:-false}
ARG_CODER_HOST=${ARG_CODER_HOST:-}
ARG_NON_AGENTAPI_CLI=${ARG_NON_AGENTAPI_CLI:-false}
echo "--------------------------------"
log() {
if [[ "${ARG_NON_AGENTAPI_CLI}" = "true" ]]; then
printf -- "$@" >> "$HOME/start.log"
else
printf -- "$@"
fi
}
printf "ARG_RESUME: %s\n" "$ARG_RESUME_SESSION_ID"
printf "ARG_CONTINUE: %s\n" "$ARG_CONTINUE"
printf "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIONS"
printf "ARG_PERMISSION_MODE: %s\n" "$ARG_PERMISSION_MODE"
printf "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT"
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
printf "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY"
printf "ARG_BOUNDARY_VERSION: %s\n" "$ARG_BOUNDARY_VERSION"
printf "ARG_COMPILE_FROM_SOURCE: %s\n" "$ARG_COMPILE_FROM_SOURCE"
printf "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST"
log "ARG_RESUME: %s\n" "$ARG_RESUME_SESSION_ID"
log "ARG_CONTINUE: %s\n" "$ARG_CONTINUE"
log "ARG_DANGEROUSLY_SKIP_PERMISSIONS: %s\n" "$ARG_DANGEROUSLY_SKIP_PERMISSIONS"
log "ARG_PERMISSION_MODE: %s\n" "$ARG_PERMISSION_MODE"
log "ARG_AI_PROMPT: %s\n" "$ARG_AI_PROMPT"
log "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
log "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
log "ARG_ENABLE_BOUNDARY: %s\n" "$ARG_ENABLE_BOUNDARY"
log "ARG_BOUNDARY_VERSION: %s\n" "$ARG_BOUNDARY_VERSION"
log "ARG_COMPILE_FROM_SOURCE: %s\n" "$ARG_COMPILE_FROM_SOURCE"
log "ARG_CODER_HOST: %s\n" "$ARG_CODER_HOST"
echo "--------------------------------"
log "--------------------------------\n"
function install_boundary() {
if [ "${ARG_COMPILE_FROM_SOURCE:-false}" = "true" ]; then
# Install boundary by compiling from source
echo "Compiling boundary from source (version: $ARG_BOUNDARY_VERSION)"
log "Compiling boundary from source (version: $ARG_BOUNDARY_VERSION)\n"
echo "Removing existing boundary directory to allow re-running the script safely"
log "Removing existing boundary directory to allow re-running the script safely\n"
if [ -d boundary ]; then
rm -rf boundary
fi
echo "Clone boundary repository"
log "Clone boundary repository\n"
git clone https://github.com/coder/boundary.git
cd boundary
git checkout "$ARG_BOUNDARY_VERSION"
@@ -58,16 +67,16 @@ function install_boundary() {
sudo chmod +x /usr/local/bin/boundary-run
else
# Install boundary using official install script
echo "Installing boundary using official install script (version: $ARG_BOUNDARY_VERSION)"
log "Installing boundary using official install script (version: $ARG_BOUNDARY_VERSION)\n"
curl -fsSL https://raw.githubusercontent.com/coder/boundary/main/install.sh | bash -s -- --version "$ARG_BOUNDARY_VERSION"
fi
}
function validate_claude_installation() {
if command_exists claude; then
printf "Claude Code is installed\n"
log "Claude Code is installed\n"
else
printf "Error: Claude Code is not installed. Please enable install_claude_code or install it manually\n"
log "Error: Claude Code is not installed. Please enable install_claude_code or install it manually\n"
exit 1
fi
}
@@ -91,10 +100,10 @@ task_session_exists() {
session_file=$(get_task_session_file)
if [ -f "$session_file" ]; then
printf "Task session file found: %s\n" "$session_file"
log "Task session file found: %s\n" "$session_file"
return 0
else
printf "Task session file not found: %s\n" "$session_file"
log "Task session file not found: %s\n" "$session_file"
return 1
fi
}
@@ -105,12 +114,12 @@ is_valid_session() {
# Check if file exists and is not empty
# Empty files indicate the session was created but never used so they need to be removed
if [ ! -f "$session_file" ]; then
printf "Session validation failed: file does not exist\n"
log "Session validation failed: file does not exist\n"
return 1
fi
if [ ! -s "$session_file" ]; then
printf "Session validation failed: file is empty, removing stale file\n"
log "Session validation failed: file is empty, removing stale file\n"
rm -f "$session_file"
return 1
fi
@@ -120,7 +129,7 @@ is_valid_session() {
local line_count
line_count=$(wc -l < "$session_file")
if [ "$line_count" -lt 2 ]; then
printf "Session validation failed: incomplete (only %s lines), removing incomplete file\n" "$line_count"
log "Session validation failed: incomplete (only %s lines), removing incomplete file\n" "$line_count"
rm -f "$session_file"
return 1
fi
@@ -128,7 +137,7 @@ is_valid_session() {
# Validate JSONL format by checking first 3 lines
# Claude session files use JSONL (JSON Lines) format where each line is valid JSON
if ! head -3 "$session_file" | jq empty 2> /dev/null; then
printf "Session validation failed: invalid JSONL format, removing corrupt file\n"
log "Session validation failed: invalid JSONL format, removing corrupt file\n"
rm -f "$session_file"
return 1
fi
@@ -137,12 +146,12 @@ is_valid_session() {
# This ensures the file structure matches Claude's session format
if ! grep -q '"sessionId"' "$session_file" \
|| ! grep -m 1 '"sessionId"' "$session_file" | jq -e '.sessionId' > /dev/null 2>&1; then
printf "Session validation failed: no valid sessionId found, removing malformed file\n"
log "Session validation failed: no valid sessionId found, removing malformed file\n"
rm -f "$session_file"
return 1
fi
printf "Session validation passed: %s\n" "$session_file"
log "Session validation passed: %s\n" "$session_file"
return 0
}
@@ -151,16 +160,21 @@ has_any_sessions() {
project_dir=$(get_project_dir)
if [ -d "$project_dir" ] && find "$project_dir" -maxdepth 1 -name "*.jsonl" -size +0c 2> /dev/null | grep -q .; then
printf "Sessions found in: %s\n" "$project_dir"
log "Sessions found in: %s\n" "$project_dir"
return 0
else
printf "No sessions found in: %s\n" "$project_dir"
log "No sessions found in: %s\n" "$project_dir"
return 1
fi
}
ARGS=()
CORE_COMMAND=()
if [[ "${ARG_REPORT_TASKS}" == "true" ]]; then
CORE_COMMAND+=(agentapi server --type claude --term-width 67 --term-height 1190 --)
fi
function start_agentapi() {
# For Task reporting
export CODER_MCP_ALLOWED_TOOLS="coder_report_task"
@@ -173,7 +187,7 @@ function start_agentapi() {
fi
if [ -n "$ARG_RESUME_SESSION_ID" ]; then
echo "Resuming specified session: $ARG_RESUME_SESSION_ID"
log "Resuming specified session: $ARG_RESUME_SESSION_ID"
ARGS+=(--resume "$ARG_RESUME_SESSION_ID")
[ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions)
@@ -184,46 +198,45 @@ function start_agentapi() {
session_file=$(get_task_session_file)
if task_session_exists && is_valid_session "$session_file"; then
echo "Resuming task session: $TASK_SESSION_ID"
log "Resuming task session: $TASK_SESSION_ID"
ARGS+=(--resume "$TASK_SESSION_ID" --dangerously-skip-permissions)
else
echo "Starting new task session: $TASK_SESSION_ID"
log "Starting new task session: $TASK_SESSION_ID"
ARGS+=(--session-id "$TASK_SESSION_ID" --dangerously-skip-permissions)
[ -n "$ARG_AI_PROMPT" ] && ARGS+=(-- "$ARG_AI_PROMPT")
fi
else
if has_any_sessions; then
echo "Continuing most recent standalone session"
log "Continuing most recent standalone session"
ARGS+=(--continue)
[ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions)
else
echo "No sessions found, starting fresh standalone session"
log "No sessions found, starting fresh standalone session"
[ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions)
[ -n "$ARG_AI_PROMPT" ] && ARGS+=(-- "$ARG_AI_PROMPT")
fi
fi
else
echo "Continue disabled, starting fresh session"
log "Continue disabled, starting fresh session"
[ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ] && ARGS+=(--dangerously-skip-permissions)
[ -n "$ARG_AI_PROMPT" ] && ARGS+=(-- "$ARG_AI_PROMPT")
fi
printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
log "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then
install_boundary
printf "Starting with coder boundary enabled\n"
log "Starting with coder boundary enabled\n"
BOUNDARY_ARGS+=()
agentapi server --type claude --term-width 67 --term-height 1190 -- \
boundary-run "${BOUNDARY_ARGS[@]}" -- \
"${CORE_COMMAND[@]}" boundary-run "${BOUNDARY_ARGS[@]}" -- \
claude "${ARGS[@]}"
else
agentapi server --type claude --term-width 67 --term-height 1190 -- claude "${ARGS[@]}"
"${CORE_COMMAND[@]}" claude "${ARGS[@]}"
fi
}