chore: improve Prettier configuration (#392)

This commit is contained in:
Atif Ali
2025-08-27 01:57:43 +05:00
committed by GitHub
parent 6bebc02122
commit 62951f1fca
48 changed files with 627 additions and 485 deletions
+2 -2
View File
@@ -192,8 +192,8 @@ main() {
# Always run formatter to ensure consistent formatting # Always run formatter to ensure consistent formatting
echo "🔧 Running formatter to ensure consistent formatting..." echo "🔧 Running formatter to ensure consistent formatting..."
if command -v bun >/dev/null 2>&1; then if command -v bun > /dev/null 2>&1; then
bun fmt >/dev/null 2>&1 || echo "⚠️ Warning: bun fmt failed, but continuing..." bun fmt > /dev/null 2>&1 || echo "⚠️ Warning: bun fmt failed, but continuing..."
else else
echo "⚠️ Warning: bun not found, skipping formatting" echo "⚠️ Warning: bun not found, skipping formatting"
fi fi
+2 -2
View File
@@ -163,8 +163,8 @@ linters:
staticcheck: staticcheck:
checks: checks:
- all - all
- SA4006 # Detects redundant assignments - SA4006 # Detects redundant assignments
- SA4009 # Detects redundant variable declarations - SA4009 # Detects redundant variable declarations
- SA1019 - SA1019
exclusions: exclusions:
generated: lax generated: lax
+22
View File
@@ -0,0 +1,22 @@
# Ignore symlinks to avoid Prettier errors
CLAUDE.md
.github/copilot-instructions.md
# Ignore node_modules and dependencies
node_modules/
# Ignore Terraform files (formatted by terraform fmt)
*.tf
*.hcl
*.tfvars
# Ignore generated and temporary files
.terraform/
*.tfstate
*.tfstate.backup
*.tfstate.lock.info
# Ignore other files that shouldn't be formatted
bun.lock
go.sum
go.mod
+6 -6
View File
@@ -4,11 +4,11 @@
"": { "": {
"name": "registry", "name": "registry",
"devDependencies": { "devDependencies": {
"@types/bun": "^1.2.18", "@types/bun": "^1.2.21",
"bun-types": "^1.2.18", "bun-types": "^1.2.21",
"dedent": "^1.6.0", "dedent": "^1.6.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"marked": "^16.0.0", "marked": "^16.2.0",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-plugin-sh": "^0.18.0", "prettier-plugin-sh": "^0.18.0",
"prettier-plugin-terraform-formatter": "^1.2.1", "prettier-plugin-terraform-formatter": "^1.2.1",
@@ -21,7 +21,7 @@
"packages": { "packages": {
"@reteps/dockerfmt": ["@reteps/dockerfmt@0.3.6", "", {}, "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg=="], "@reteps/dockerfmt": ["@reteps/dockerfmt@0.3.6", "", {}, "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg=="],
"@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], "@types/bun": ["@types/bun@1.2.21", "", { "dependencies": { "bun-types": "1.2.21" } }, "sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A=="],
"@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="], "@types/node": ["@types/node@24.0.14", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw=="],
@@ -29,7 +29,7 @@
"argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
"bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], "bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
@@ -47,7 +47,7 @@
"kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
"marked": ["marked@16.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-MUKMXDjsD/eptB7GPzxo4xcnLS6oo7/RHimUMHEDRhUooPwmN9BEpMl7AEOJv3bmso169wHI2wUF9VQgL7zfmA=="], "marked": ["marked@16.2.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg=="],
"prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
+5 -5
View File
@@ -1,18 +1,18 @@
{ {
"name": "registry", "name": "registry",
"scripts": { "scripts": {
"fmt": "bun x prettier --write **/*.sh **/*.ts **/*.md *.md && terraform fmt -recursive -diff", "fmt": "bun x prettier --write . && terraform fmt -recursive -diff",
"fmt:ci": "bun x prettier --check **/*.sh **/*.ts **/*.md *.md && terraform fmt -check -recursive -diff", "fmt:ci": "bun x prettier --check . && terraform fmt -check -recursive -diff",
"terraform-validate": "./scripts/terraform_validate.sh", "terraform-validate": "./scripts/terraform_validate.sh",
"test": "./scripts/terraform_test_all.sh", "test": "./scripts/terraform_test_all.sh",
"update-version": "./update-version.sh" "update-version": "./update-version.sh"
}, },
"devDependencies": { "devDependencies": {
"@types/bun": "^1.2.18", "@types/bun": "^1.2.21",
"bun-types": "^1.2.18", "bun-types": "^1.2.21",
"dedent": "^1.6.0", "dedent": "^1.6.0",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"marked": "^16.0.0", "marked": "^16.2.0",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"prettier-plugin-sh": "^0.18.0", "prettier-plugin-sh": "^0.18.0",
"prettier-plugin-terraform-formatter": "^1.2.1" "prettier-plugin-terraform-formatter": "^1.2.1"
+3 -1
View File
@@ -28,7 +28,9 @@ describe("tmux module", async () => {
// check that the script contains expected lines // check that the script contains expected lines
expect(scriptResource.script).toContain("Installing tmux"); expect(scriptResource.script).toContain("Installing tmux");
expect(scriptResource.script).toContain("Installing Tmux Plugin Manager (TPM)"); expect(scriptResource.script).toContain(
"Installing Tmux Plugin Manager (TPM)",
);
expect(scriptResource.script).toContain("tmux configuration created at"); expect(scriptResource.script).toContain("tmux configuration created at");
expect(scriptResource.script).toContain("✅ tmux setup complete!"); expect(scriptResource.script).toContain("✅ tmux setup complete!");
}); });
+74 -74
View File
@@ -8,75 +8,75 @@ TMUX_CONFIG="${TMUX_CONFIG}"
# Function to install tmux # Function to install tmux
install_tmux() { install_tmux() {
printf "Checking for tmux installation\n" printf "Checking for tmux installation\n"
if command -v tmux &> /dev/null; then if command -v tmux &> /dev/null; then
printf "tmux is already installed \n\n" printf "tmux is already installed \n\n"
return 0 return 0
fi fi
printf "Installing tmux \n\n" printf "Installing tmux \n\n"
# Detect package manager and install tmux # Detect package manager and install tmux
if command -v apt-get &> /dev/null; then if command -v apt-get &> /dev/null; then
sudo apt-get update sudo apt-get update
sudo apt-get install -y tmux sudo apt-get install -y tmux
elif command -v yum &> /dev/null; then elif command -v yum &> /dev/null; then
sudo yum install -y tmux sudo yum install -y tmux
elif command -v dnf &> /dev/null; then elif command -v dnf &> /dev/null; then
sudo dnf install -y tmux sudo dnf install -y tmux
elif command -v zypper &> /dev/null; then elif command -v zypper &> /dev/null; then
sudo zypper install -y tmux sudo zypper install -y tmux
elif command -v apk &> /dev/null; then elif command -v apk &> /dev/null; then
sudo apk add tmux sudo apk add tmux
elif command -v brew &> /dev/null; then elif command -v brew &> /dev/null; then
brew install tmux brew install tmux
else else
printf "No supported package manager found. Please install tmux manually. \n" printf "No supported package manager found. Please install tmux manually. \n"
exit 1 exit 1
fi fi
printf "tmux installed successfully \n" printf "tmux installed successfully \n"
} }
# Function to install Tmux Plugin Manager (TPM) # Function to install Tmux Plugin Manager (TPM)
install_tpm() { install_tpm() {
local tpm_dir="$HOME/.tmux/plugins/tpm" local tpm_dir="$HOME/.tmux/plugins/tpm"
if [ -d "$tpm_dir" ]; then if [ -d "$tpm_dir" ]; then
printf "TPM is already installed" printf "TPM is already installed"
return 0 return 0
fi fi
printf "Installing Tmux Plugin Manager (TPM) \n" printf "Installing Tmux Plugin Manager (TPM) \n"
# Create plugins directory # Create plugins directory
mkdir -p "$HOME/.tmux/plugins" mkdir -p "$HOME/.tmux/plugins"
# Clone TPM repository # Clone TPM repository
if command -v git &> /dev/null; then if command -v git &> /dev/null; then
git clone https://github.com/tmux-plugins/tpm "$tpm_dir" git clone https://github.com/tmux-plugins/tpm "$tpm_dir"
printf "TPM installed successfully" printf "TPM installed successfully"
else else
printf "Git is not installed. Please install git to use tmux plugins. \n" printf "Git is not installed. Please install git to use tmux plugins. \n"
exit 1 exit 1
fi fi
} }
# Function to create tmux configuration # Function to create tmux configuration
setup_tmux_config() { setup_tmux_config() {
printf "Setting up tmux configuration \n" printf "Setting up tmux configuration \n"
local config_dir="$HOME/.tmux" local config_dir="$HOME/.tmux"
local config_file="$HOME/.tmux.conf" local config_file="$HOME/.tmux.conf"
mkdir -p "$config_dir" mkdir -p "$config_dir"
if [ -n "$TMUX_CONFIG" ]; then if [ -n "$TMUX_CONFIG" ]; then
printf "$TMUX_CONFIG" > "$config_file" printf "$TMUX_CONFIG" > "$config_file"
printf "$${BOLD}Custom tmux configuration applied at {$config_file} \n\n" printf "$${BOLD}Custom tmux configuration applied at {$config_file} \n\n"
else else
cat > "$config_file" << EOF cat > "$config_file" << EOF
# Tmux Configuration File # Tmux Configuration File
# ============================================================================= # =============================================================================
@@ -106,46 +106,46 @@ bind C-r run-shell "~/.tmux/plugins/tmux-resurrect/scripts/restore.sh"
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf) # Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm' run '~/.tmux/plugins/tpm/tpm'
EOF EOF
printf "tmux configuration created at {$config_file} \n\n" printf "tmux configuration created at {$config_file} \n\n"
fi fi
} }
# Function to install tmux plugins # Function to install tmux plugins
install_plugins() { install_plugins() {
printf "Installing tmux plugins" printf "Installing tmux plugins"
# Check if TPM is installed # Check if TPM is installed
if [ ! -d "$HOME/.tmux/plugins/tpm" ]; then if [ ! -d "$HOME/.tmux/plugins/tpm" ]; then
printf "TPM is not installed. Cannot install plugins. \n" printf "TPM is not installed. Cannot install plugins. \n"
return 1 return 1
fi fi
# Install plugins using TPM # Install plugins using TPM
"$HOME/.tmux/plugins/tpm/bin/install_plugins" "$HOME/.tmux/plugins/tpm/bin/install_plugins"
printf "tmux plugins installed successfully \n" printf "tmux plugins installed successfully \n"
} }
# Main execution # Main execution
main() { main() {
printf "$${BOLD} 🛠️Setting up tmux with session persistence! \n\n" printf "$${BOLD} 🛠️Setting up tmux with session persistence! \n\n"
printf "" printf ""
# Install dependencies # Install dependencies
install_tmux install_tmux
install_tpm install_tpm
# Setup tmux configuration # Setup tmux configuration
setup_tmux_config setup_tmux_config
# Install plugins # Install plugins
install_plugins install_plugins
printf "$${BOLD}✅ tmux setup complete! \n\n" printf "$${BOLD}✅ tmux setup complete! \n\n"
printf "$${BOLD} Attempting to restore sessions\n" printf "$${BOLD} Attempting to restore sessions\n"
tmux new-session -d \; source-file ~/.tmux.conf \; run-shell '~/.tmux/plugins/tmux-resurrect/scripts/restore.sh' tmux new-session -d \; source-file ~/.tmux.conf \; run-shell '~/.tmux/plugins/tmux-resurrect/scripts/restore.sh'
printf "$${BOLD} Sessions restored: -> %s\n" "$(tmux ls)" printf "$${BOLD} Sessions restored: -> %s\n" "$(tmux ls)"
} }
@@ -16,7 +16,7 @@ handle_session() {
local session_name="$1" local session_name="$1"
# Check if the session exists # Check if the session exists
if tmux has-session -t "$session_name" 2>/dev/null; then if tmux has-session -t "$session_name" 2> /dev/null; then
echo "Session '$session_name' exists, attaching to it..." echo "Session '$session_name' exists, attaching to it..."
tmux attach-session -t "$session_name" tmux attach-session -t "$session_name"
else else
@@ -165,9 +165,9 @@ describe("auggie", async () => {
mcpServers: { mcpServers: {
test: { test: {
command: "test-cmd", command: "test-cmd",
type: "stdio" type: "stdio",
} },
} },
}); });
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
@@ -187,13 +187,16 @@ describe("auggie", async () => {
const rules = "Always use TypeScript for new files"; const rules = "Always use TypeScript for new files";
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
install_auggie: "false", // Don't need to install auggie to test rules file creation install_auggie: "false", // Don't need to install auggie to test rules file creation
rules: rules, rules: rules,
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const rulesFile = await readFileContainer(id, "/home/coder/.augment/rules.md"); const rulesFile = await readFileContainer(
id,
"/home/coder/.augment/rules.md",
);
expect(rulesFile).toContain(rules); expect(rulesFile).toContain(rules);
}); });
@@ -309,12 +312,15 @@ describe("auggie", async () => {
test("coder-mcp-config-created", async () => { test("coder-mcp-config-created", async () => {
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
install_auggie: "false", // Don't need to install auggie to test MCP config creation install_auggie: "false", // Don't need to install auggie to test MCP config creation
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const mcpConfig = await readFileContainer(id, "/home/coder/.augment/coder_mcp.json"); const mcpConfig = await readFileContainer(
id,
"/home/coder/.augment/coder_mcp.json",
);
expect(mcpConfig).toContain("mcpServers"); expect(mcpConfig).toContain("mcpServers");
expect(mcpConfig).toContain("coder"); expect(mcpConfig).toContain("coder");
expect(mcpConfig).toContain("CODER_MCP_APP_STATUS_SLUG"); expect(mcpConfig).toContain("CODER_MCP_APP_STATUS_SLUG");
@@ -25,7 +25,6 @@ printf "rules: %s\n" "$ARG_AUGGIE_RULES"
echo "--------------------------------" echo "--------------------------------"
function check_dependencies() { function check_dependencies() {
if ! command_exists node; then if ! command_exists node; then
printf "Error: Node.js is not installed. Please install Node.js manually or use the pre_install_script to install it.\n" printf "Error: Node.js is not installed. Please install Node.js manually or use the pre_install_script to install it.\n"
@@ -72,7 +71,6 @@ function install_auggie() {
fi fi
} }
function create_coder_mcp() { function create_coder_mcp() {
AUGGIE_CODER_MCP_FILE="$HOME/.augment/coder_mcp.json" AUGGIE_CODER_MCP_FILE="$HOME/.augment/coder_mcp.json"
CODER_MCP=$( CODER_MCP=$(
@@ -39,7 +39,6 @@ printf "report_tasks: %s\n" "$ARG_REPORT_TASKS"
echo "--------------------------------" echo "--------------------------------"
function validate_auggie_installation() { function validate_auggie_installation() {
if command_exists auggie; then if command_exists auggie; then
printf "Auggie is installed\n" printf "Auggie is installed\n"
+12 -9
View File
@@ -124,8 +124,8 @@ describe("codex", async () => {
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); const resp = await readFileContainer(id, "/home/coder/.codex/config.toml");
expect(resp).toContain("sandbox_mode = \"danger-full-access\""); expect(resp).toContain('sandbox_mode = "danger-full-access"');
expect(resp).toContain("preferred_auth_method = \"apikey\""); expect(resp).toContain('preferred_auth_method = "apikey"');
expect(resp).toContain("[custom_section]"); expect(resp).toContain("[custom_section]");
expect(resp).toContain("[mcp_servers.Coder]"); expect(resp).toContain("[mcp_servers.Coder]");
}); });
@@ -246,11 +246,11 @@ describe("codex", async () => {
const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); const resp = await readFileContainer(id, "/home/coder/.codex/config.toml");
// Check base config // Check base config
expect(resp).toContain("sandbox_mode = \"read-only\""); expect(resp).toContain('sandbox_mode = "read-only"');
expect(resp).toContain("preferred_auth_method = \"chatgpt\""); expect(resp).toContain('preferred_auth_method = "chatgpt"');
expect(resp).toContain("custom_setting = \"test-value\""); expect(resp).toContain('custom_setting = "test-value"');
expect(resp).toContain("[advanced_settings]"); expect(resp).toContain("[advanced_settings]");
expect(resp).toContain("logging_level = \"verbose\""); expect(resp).toContain('logging_level = "verbose"');
// Check MCP servers // Check MCP servers
expect(resp).toContain("[mcp_servers.Coder]"); expect(resp).toContain("[mcp_servers.Coder]");
@@ -270,8 +270,8 @@ describe("codex", async () => {
const resp = await readFileContainer(id, "/home/coder/.codex/config.toml"); const resp = await readFileContainer(id, "/home/coder/.codex/config.toml");
// Check default base config // Check default base config
expect(resp).toContain("sandbox_mode = \"workspace-write\""); expect(resp).toContain('sandbox_mode = "workspace-write"');
expect(resp).toContain("approval_policy = \"never\""); expect(resp).toContain('approval_policy = "never"');
expect(resp).toContain("[sandbox_workspace_write]"); expect(resp).toContain("[sandbox_workspace_write]");
expect(resp).toContain("network_access = true"); expect(resp).toContain("network_access = true");
@@ -328,7 +328,10 @@ describe("codex", async () => {
}, },
}); });
await execModuleScript(id_2); await execModuleScript(id_2);
const resp_2 = await readFileContainer(id_2, "/home/coder/.codex/AGENTS.md"); const resp_2 = await readFileContainer(
id_2,
"/home/coder/.codex/AGENTS.md",
);
expect(resp_2).toContain(prompt_1); expect(resp_2).toContain(prompt_1);
const count = (resp_2.match(new RegExp(prompt_1, "g")) || []).length; const count = (resp_2.match(new RegExp(prompt_1, "g")) || []).length;
expect(count).toBe(1); expect(count).toBe(1);
@@ -84,8 +84,8 @@ function install_codex() {
} }
write_minimal_default_config() { write_minimal_default_config() {
local config_path="$1" local config_path="$1"
cat << EOF > "$config_path" cat << EOF > "$config_path"
# Minimal Default Codex Configuration # Minimal Default Codex Configuration
sandbox_mode = "workspace-write" sandbox_mode = "workspace-write"
approval_policy = "never" approval_policy = "never"
@@ -98,9 +98,9 @@ EOF
} }
append_mcp_servers_section() { append_mcp_servers_section() {
local config_path="$1" local config_path="$1"
cat << EOF >> "$config_path" cat << EOF >> "$config_path"
# MCP Servers Configuration # MCP Servers Configuration
[mcp_servers.Coder] [mcp_servers.Coder]
@@ -112,25 +112,25 @@ type = "stdio"
EOF EOF
if [ -n "$ARG_ADDITIONAL_MCP_SERVERS" ]; then if [ -n "$ARG_ADDITIONAL_MCP_SERVERS" ]; then
printf "Adding additional MCP servers\n" printf "Adding additional MCP servers\n"
echo "$ARG_ADDITIONAL_MCP_SERVERS" >> "$config_path" echo "$ARG_ADDITIONAL_MCP_SERVERS" >> "$config_path"
fi fi
} }
function populate_config_toml() { function populate_config_toml() {
CONFIG_PATH="$HOME/.codex/config.toml" CONFIG_PATH="$HOME/.codex/config.toml"
mkdir -p "$(dirname "$CONFIG_PATH")" mkdir -p "$(dirname "$CONFIG_PATH")"
if [ -n "$ARG_BASE_CONFIG_TOML" ]; then if [ -n "$ARG_BASE_CONFIG_TOML" ]; then
printf "Using provided base configuration\n" printf "Using provided base configuration\n"
echo "$ARG_BASE_CONFIG_TOML" > "$CONFIG_PATH" echo "$ARG_BASE_CONFIG_TOML" > "$CONFIG_PATH"
else else
printf "Using minimal default configuration\n" printf "Using minimal default configuration\n"
write_minimal_default_config "$CONFIG_PATH" write_minimal_default_config "$CONFIG_PATH"
fi fi
append_mcp_servers_section "$CONFIG_PATH" append_mcp_servers_section "$CONFIG_PATH"
} }
function add_instruction_prompt_if_exists() { function add_instruction_prompt_if_exists() {
@@ -55,8 +55,6 @@ if [ -n "$ARG_CODEX_MODEL" ]; then
CODEX_ARGS+=("--model" "$ARG_CODEX_MODEL") CODEX_ARGS+=("--model" "$ARG_CODEX_MODEL")
fi fi
if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then if [ -n "$ARG_CODEX_TASK_PROMPT" ]; then
printf "Running the task prompt %s\n" "$ARG_CODEX_TASK_PROMPT" printf "Running the task prompt %s\n" "$ARG_CODEX_TASK_PROMPT"
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_report_task tool with proper summary and statuses. Your task at hand: $ARG_CODEX_TASK_PROMPT"
@@ -65,7 +63,6 @@ else
printf "No task prompt given.\n" printf "No task prompt given.\n"
fi fi
# Terminal dimensions optimized for Coder Tasks UI sidebar: # Terminal dimensions optimized for Coder Tasks UI sidebar:
# - Width 67: fits comfortably in sidebar # - Width 67: fits comfortably in sidebar
# - Height 1190: adjusted due to Codex terminal height bug # - Height 1190: adjusted due to Codex terminal height bug
@@ -1,12 +1,22 @@
import { afterEach, beforeAll, describe, expect, setDefaultTimeout, test } from "bun:test"; import {
afterEach,
beforeAll,
describe,
expect,
setDefaultTimeout,
test,
} from "bun:test";
import { execContainer, runTerraformInit, writeFileContainer } from "~test"; import { execContainer, runTerraformInit, writeFileContainer } from "~test";
import { import {
execModuleScript, execModuleScript,
expectAgentAPIStarted, expectAgentAPIStarted,
loadTestFile, loadTestFile,
setup as setupUtil setup as setupUtil,
} from "../../../coder/modules/agentapi/test-util";
import {
setupContainer,
writeExecutable,
} from "../../../coder/modules/agentapi/test-util"; } from "../../../coder/modules/agentapi/test-util";
import { setupContainer, writeExecutable } from "../../../coder/modules/agentapi/test-util";
let cleanupFns: (() => Promise<void>)[] = []; let cleanupFns: (() => Promise<void>)[] = [];
const registerCleanup = (fn: () => Promise<void>) => cleanupFns.push(fn); const registerCleanup = (fn: () => Promise<void>) => cleanupFns.push(fn);
@@ -72,11 +82,12 @@ describe("cursor-cli", async () => {
}); });
test("agentapi-mcp-json", async () => { test("agentapi-mcp-json", async () => {
const mcpJson = '{"mcpServers": {"test": {"command": "test-cmd", "type": "stdio"}}}'; const mcpJson =
'{"mcpServers": {"test": {"command": "test-cmd", "type": "stdio"}}}';
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
mcp: mcpJson, mcp: mcpJson,
} },
}); });
const resp = await execModuleScript(id); const resp = await execModuleScript(id);
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
@@ -99,7 +110,7 @@ describe("cursor-cli", async () => {
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
rules_files: JSON.stringify({ "typescript.md": rulesContent }), rules_files: JSON.stringify({ "typescript.md": rulesContent }),
} },
}); });
const resp = await execModuleScript(id); const resp = await execModuleScript(id);
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
@@ -118,7 +129,7 @@ describe("cursor-cli", async () => {
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
api_key: apiKey, api_key: apiKey,
} },
}); });
const resp = await execModuleScript(id); const resp = await execModuleScript(id);
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
@@ -138,7 +149,7 @@ describe("cursor-cli", async () => {
model: model, model: model,
force: "true", force: "true",
ai_prompt: "test prompt", ai_prompt: "test prompt",
} },
}); });
const resp = await execModuleScript(id); const resp = await execModuleScript(id);
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
@@ -158,7 +169,7 @@ describe("cursor-cli", async () => {
moduleVariables: { moduleVariables: {
pre_install_script: "#!/bin/bash\necho 'cursor-pre-install-script'", pre_install_script: "#!/bin/bash\necho 'cursor-pre-install-script'",
post_install_script: "#!/bin/bash\necho 'cursor-post-install-script'", post_install_script: "#!/bin/bash\necho 'cursor-post-install-script'",
} },
}); });
const resp = await execModuleScript(id); const resp = await execModuleScript(id);
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
@@ -183,7 +194,7 @@ describe("cursor-cli", async () => {
const { id } = await setup({ const { id } = await setup({
moduleVariables: { moduleVariables: {
folder: folder, folder: folder,
} },
}); });
const resp = await execModuleScript(id); const resp = await execModuleScript(id);
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
@@ -205,8 +216,5 @@ describe("cursor-cli", async () => {
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
await expectAgentAPIStarted(id); await expectAgentAPIStarted(id);
}) });
}); });
@@ -58,7 +58,7 @@ fi
if [ -n "$ARG_AI_PROMPT" ]; then if [ -n "$ARG_AI_PROMPT" ]; then
printf "AI prompt provided\n" printf "AI prompt provided\n"
ARGS+=("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_AI_PROMPT") ARGS+=("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_AI_PROMPT")
fi fi
# Log and run in background, redirecting all output to the log file # Log and run in background, redirecting all output to the log file
@@ -9,6 +9,6 @@ fi
set -e set -e
while true; do while true; do
echo "$(date) - cursor-agent-mock" echo "$(date) - cursor-agent-mock"
sleep 15 sleep 15
done done
+42 -11
View File
@@ -127,7 +127,10 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini/settings.json"); const resp = await readFileContainer(
id,
"/home/coder/.gemini/settings.json",
);
expect(resp).toContain("foo"); expect(resp).toContain("foo");
expect(resp).toContain("bar"); expect(resp).toContain("bar");
}); });
@@ -141,7 +144,10 @@ describe("gemini", async () => {
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log"); const resp = await readFileContainer(
id,
"/home/coder/.gemini-module/agentapi-start.log",
);
expect(resp).toContain("Using direct Gemini API with API key"); expect(resp).toContain("Using direct Gemini API with API key");
}); });
@@ -153,8 +159,11 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log"); const resp = await readFileContainer(
expect(resp).toContain('GOOGLE_GENAI_USE_VERTEXAI=\'true\''); id,
"/home/coder/.gemini-module/agentapi-start.log",
);
expect(resp).toContain("GOOGLE_GENAI_USE_VERTEXAI='true'");
}); });
test("gemini-model", async () => { test("gemini-model", async () => {
@@ -166,7 +175,10 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log"); const resp = await readFileContainer(
id,
"/home/coder/.gemini-module/agentapi-start.log",
);
expect(resp).toContain(model); expect(resp).toContain(model);
}); });
@@ -178,9 +190,15 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const preInstallLog = await readFileContainer(id, "/home/coder/.gemini-module/pre_install.log"); const preInstallLog = await readFileContainer(
id,
"/home/coder/.gemini-module/pre_install.log",
);
expect(preInstallLog).toContain("pre-install-script"); expect(preInstallLog).toContain("pre-install-script");
const postInstallLog = await readFileContainer(id, "/home/coder/.gemini-module/post_install.log"); const postInstallLog = await readFileContainer(
id,
"/home/coder/.gemini-module/post_install.log",
);
expect(postInstallLog).toContain("post-install-script"); expect(postInstallLog).toContain("post-install-script");
}); });
@@ -193,7 +211,10 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log"); const resp = await readFileContainer(
id,
"/home/coder/.gemini-module/agentapi-start.log",
);
expect(resp).toContain(folder); expect(resp).toContain(folder);
}); });
@@ -205,7 +226,10 @@ describe("gemini", async () => {
}, },
}); });
await execModuleScript(id); await execModuleScript(id);
const resp = await readFileContainer(id, "/home/coder/.gemini/settings.json"); const resp = await readFileContainer(
id,
"/home/coder/.gemini/settings.json",
);
expect(resp).toContain("custom"); expect(resp).toContain("custom");
expect(resp).toContain("enabled"); expect(resp).toContain("enabled");
}); });
@@ -232,14 +256,21 @@ describe("gemini", async () => {
await execModuleScript(id, { await execModuleScript(id, {
GEMINI_TASK_PROMPT: taskPrompt, GEMINI_TASK_PROMPT: taskPrompt,
}); });
const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log"); const resp = await readFileContainer(
id,
"/home/coder/.gemini-module/agentapi-start.log",
);
expect(resp).toContain("Running automated task:"); expect(resp).toContain("Running automated task:");
}); });
test("start-without-prompt", async () => { test("start-without-prompt", async () => {
const { id } = await setup(); const { id } = await setup();
await execModuleScript(id); await execModuleScript(id);
const prompt = await execContainer(id, ["ls", "-l", "/home/coder/GEMINI.md"]); const prompt = await execContainer(id, [
"ls",
"-l",
"/home/coder/GEMINI.md",
]);
expect(prompt.exitCode).not.toBe(0); expect(prompt.exitCode).not.toBe(0);
expect(prompt.stderr).toContain("No such file or directory"); expect(prompt.stderr).toContain("No such file or directory");
}); });
@@ -1,18 +1,18 @@
FROM ubuntu FROM ubuntu
RUN apt-get update \ RUN apt-get update \
&& apt-get install -y \ && apt-get install -y \
curl \ curl \
git \ git \
golang \ golang \
sudo \ sudo \
vim \ vim \
wget \ wget \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
ARG USER=coder ARG USER=coder
RUN useradd --groups sudo --no-create-home --shell /bin/bash ${USER} \ RUN useradd --groups sudo --no-create-home --shell /bin/bash ${USER} \
&& echo "${USER} ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/${USER} \ && echo "${USER} ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/${USER} \
&& chmod 0440 /etc/sudoers.d/${USER} && chmod 0440 /etc/sudoers.d/${USER}
USER ${USER} USER ${USER}
WORKDIR /home/${USER} WORKDIR /home/${USER}
+14 -6
View File
@@ -164,7 +164,9 @@ describe("agentapi", async () => {
id, id,
"/home/coder/test-agentapi-start.log", "/home/coder/test-agentapi-start.log",
); );
expect(agentApiStartLog).toContain("Using AGENTAPI_CHAT_BASE_PATH: /@default/default.foo/apps/agentapi-web/chat"); expect(agentApiStartLog).toContain(
"Using AGENTAPI_CHAT_BASE_PATH: /@default/default.foo/apps/agentapi-web/chat",
);
}); });
test("validate-agentapi-version", async () => { test("validate-agentapi-version", async () => {
@@ -186,14 +188,16 @@ describe("agentapi", async () => {
agentapi_version: "v0.0.1", agentapi_version: "v0.0.1",
agentapi_subdomain: "false", agentapi_subdomain: "false",
}, },
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.", shouldThrow:
"Running with subdomain = false is only supported by agentapi >= v0.3.3.",
}, },
{ {
moduleVariables: { moduleVariables: {
agentapi_version: "v0.3.2", agentapi_version: "v0.3.2",
agentapi_subdomain: "false", agentapi_subdomain: "false",
}, },
shouldThrow: "Running with subdomain = false is only supported by agentapi >= v0.3.3.", shouldThrow:
"Running with subdomain = false is only supported by agentapi >= v0.3.3.",
}, },
{ {
moduleVariables: { moduleVariables: {
@@ -226,13 +230,17 @@ describe("agentapi", async () => {
agentapi_version: "arbitrary-string-bypasses-validation", agentapi_version: "arbitrary-string-bypasses-validation",
}, },
shouldThrow: "", shouldThrow: "",
} },
]; ];
for (const { moduleVariables, shouldThrow } of cases) { for (const { moduleVariables, shouldThrow } of cases) {
if (shouldThrow) { if (shouldThrow) {
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).rejects.toThrow(shouldThrow); expect(
setup({ moduleVariables: moduleVariables as Record<string, string> }),
).rejects.toThrow(shouldThrow);
} else { } else {
expect(setup({ moduleVariables: moduleVariables as Record<string, string> })).resolves.toBeDefined(); expect(
setup({ moduleVariables: moduleVariables as Record<string, string> }),
).resolves.toBeDefined();
} }
} }
}); });
@@ -11,22 +11,22 @@ agentapi_started=false
echo "Waiting for agentapi server to start on port $port..." echo "Waiting for agentapi server to start on port $port..."
for i in $(seq 1 150); do for i in $(seq 1 150); do
for j in $(seq 1 3); do for j in $(seq 1 3); do
sleep 0.1 sleep 0.1
if curl -fs -o /dev/null "http://localhost:$port/status"; then if curl -fs -o /dev/null "http://localhost:$port/status"; then
echo "agentapi response received ($j/3)" echo "agentapi response received ($j/3)"
else else
echo "agentapi server not responding ($i/15)" echo "agentapi server not responding ($i/15)"
continue 2 continue 2
fi fi
done done
agentapi_started=true agentapi_started=true
break break
done done
if [ "$agentapi_started" != "true" ]; then if [ "$agentapi_started" != "true" ]; then
echo "Error: agentapi server did not start on port $port after 15 seconds." echo "Error: agentapi server did not start on port $port after 15 seconds."
exit 1 exit 1
fi fi
echo "agentapi server started on port $port." echo "agentapi server started on port $port."
+47 -47
View File
@@ -17,76 +17,76 @@ AGENTAPI_CHAT_BASE_PATH="${ARG_AGENTAPI_CHAT_BASE_PATH:-}"
set +o nounset set +o nounset
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" > /dev/null 2>&1
} }
module_path="$HOME/${MODULE_DIR_NAME}" module_path="$HOME/${MODULE_DIR_NAME}"
mkdir -p "$module_path/scripts" mkdir -p "$module_path/scripts"
if [ ! -d "${WORKDIR}" ]; then if [ ! -d "${WORKDIR}" ]; then
echo "Warning: The specified folder '${WORKDIR}' does not exist." echo "Warning: The specified folder '${WORKDIR}' does not exist."
echo "Creating the folder..." echo "Creating the folder..."
mkdir -p "${WORKDIR}" mkdir -p "${WORKDIR}"
echo "Folder created successfully." echo "Folder created successfully."
fi fi
if [ -n "${PRE_INSTALL_SCRIPT}" ]; then if [ -n "${PRE_INSTALL_SCRIPT}" ]; then
echo "Running pre-install script..." echo "Running pre-install script..."
echo -n "${PRE_INSTALL_SCRIPT}" >"$module_path/pre_install.sh" echo -n "${PRE_INSTALL_SCRIPT}" > "$module_path/pre_install.sh"
chmod +x "$module_path/pre_install.sh" chmod +x "$module_path/pre_install.sh"
"$module_path/pre_install.sh" 2>&1 | tee "$module_path/pre_install.log" "$module_path/pre_install.sh" 2>&1 | tee "$module_path/pre_install.log"
fi fi
echo "Running install script..." echo "Running install script..."
echo -n "${INSTALL_SCRIPT}" >"$module_path/install.sh" echo -n "${INSTALL_SCRIPT}" > "$module_path/install.sh"
chmod +x "$module_path/install.sh" chmod +x "$module_path/install.sh"
"$module_path/install.sh" 2>&1 | tee "$module_path/install.log" "$module_path/install.sh" 2>&1 | tee "$module_path/install.log"
# Install AgentAPI if enabled # Install AgentAPI if enabled
if [ "${INSTALL_AGENTAPI}" = "true" ]; then if [ "${INSTALL_AGENTAPI}" = "true" ]; then
echo "Installing AgentAPI..." echo "Installing AgentAPI..."
arch=$(uname -m) arch=$(uname -m)
if [ "$arch" = "x86_64" ]; then if [ "$arch" = "x86_64" ]; then
binary_name="agentapi-linux-amd64" binary_name="agentapi-linux-amd64"
elif [ "$arch" = "aarch64" ]; then elif [ "$arch" = "aarch64" ]; then
binary_name="agentapi-linux-arm64" binary_name="agentapi-linux-arm64"
else else
echo "Error: Unsupported architecture: $arch" echo "Error: Unsupported architecture: $arch"
exit 1 exit 1
fi fi
if [ "${AGENTAPI_VERSION}" = "latest" ]; then if [ "${AGENTAPI_VERSION}" = "latest" ]; then
# for the latest release the download URL pattern is different than for tagged releases # for the latest release the download URL pattern is different than for tagged releases
# https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases # https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases
download_url="https://github.com/coder/agentapi/releases/latest/download/$binary_name" download_url="https://github.com/coder/agentapi/releases/latest/download/$binary_name"
else else
download_url="https://github.com/coder/agentapi/releases/download/${AGENTAPI_VERSION}/$binary_name" download_url="https://github.com/coder/agentapi/releases/download/${AGENTAPI_VERSION}/$binary_name"
fi fi
curl \ curl \
--retry 5 \ --retry 5 \
--retry-delay 5 \ --retry-delay 5 \
--fail \ --fail \
--retry-all-errors \ --retry-all-errors \
-L \ -L \
-C - \ -C - \
-o agentapi \ -o agentapi \
"$download_url" "$download_url"
chmod +x agentapi chmod +x agentapi
sudo mv agentapi /usr/local/bin/agentapi sudo mv agentapi /usr/local/bin/agentapi
fi fi
if ! command_exists agentapi; then if ! command_exists agentapi; then
echo "Error: AgentAPI is not installed. Please enable install_agentapi or install it manually." echo "Error: AgentAPI is not installed. Please enable install_agentapi or install it manually."
exit 1 exit 1
fi fi
echo -n "${START_SCRIPT}" >"$module_path/scripts/agentapi-start.sh" echo -n "${START_SCRIPT}" > "$module_path/scripts/agentapi-start.sh"
echo -n "${WAIT_FOR_START_SCRIPT}" >"$module_path/scripts/agentapi-wait-for-start.sh" echo -n "${WAIT_FOR_START_SCRIPT}" > "$module_path/scripts/agentapi-wait-for-start.sh"
chmod +x "$module_path/scripts/agentapi-start.sh" chmod +x "$module_path/scripts/agentapi-start.sh"
chmod +x "$module_path/scripts/agentapi-wait-for-start.sh" chmod +x "$module_path/scripts/agentapi-wait-for-start.sh"
if [ -n "${POST_INSTALL_SCRIPT}" ]; then if [ -n "${POST_INSTALL_SCRIPT}" ]; then
echo "Running post-install script..." echo "Running post-install script..."
echo -n "${POST_INSTALL_SCRIPT}" >"$module_path/post_install.sh" echo -n "${POST_INSTALL_SCRIPT}" > "$module_path/post_install.sh"
chmod +x "$module_path/post_install.sh" chmod +x "$module_path/post_install.sh"
"$module_path/post_install.sh" 2>&1 | tee "$module_path/post_install.log" "$module_path/post_install.sh" 2>&1 | tee "$module_path/post_install.log"
fi fi
export LANG=en_US.UTF-8 export LANG=en_US.UTF-8
@@ -97,5 +97,5 @@ cd "${WORKDIR}"
export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}" export AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
# Disable host header check since AgentAPI is proxied by Coder (which does its own validation) # Disable host header check since AgentAPI is proxied by Coder (which does its own validation)
export AGENTAPI_ALLOWED_HOSTS="*" export AGENTAPI_ALLOWED_HOSTS="*"
nohup "$module_path/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &>"$module_path/agentapi-start.log" & nohup "$module_path/scripts/agentapi-start.sh" true "${AGENTAPI_PORT}" &> "$module_path/agentapi-start.log" &
"$module_path/scripts/agentapi-wait-for-start.sh" "${AGENTAPI_PORT}" "$module_path/scripts/agentapi-wait-for-start.sh" "${AGENTAPI_PORT}"
+9 -3
View File
@@ -25,14 +25,20 @@ export const setupContainer = async ({
const coderScript = findResourceInstance(state, "coder_script"); const coderScript = findResourceInstance(state, "coder_script");
const id = await runContainer(image ?? "codercom/enterprise-node:latest"); const id = await runContainer(image ?? "codercom/enterprise-node:latest");
return { return {
id, coderScript, cleanup: async () => { id,
if (process.env["DEBUG"] === "true" || process.env["DEBUG"] === "1" || process.env["DEBUG"] === "yes") { coderScript,
cleanup: async () => {
if (
process.env["DEBUG"] === "true" ||
process.env["DEBUG"] === "1" ||
process.env["DEBUG"] === "yes"
) {
console.log(`Not removing container ${id} in debug mode`); console.log(`Not removing container ${id} in debug mode`);
console.log(`Run "docker rm -f ${id}" to remove it manually.`); console.log(`Run "docker rm -f ${id}" to remove it manually.`);
} else { } else {
await removeContainer(id); await removeContainer(id);
} }
} },
}; };
}; };
+4 -1
View File
@@ -7,7 +7,10 @@ const portIdx = args.findIndex((arg) => arg === "--port") + 1;
const port = portIdx ? args[portIdx] : 3284; const port = portIdx ? args[portIdx] : 3284;
console.log(`starting server on port ${port}`); console.log(`starting server on port ${port}`);
fs.writeFileSync("/home/coder/agentapi-mock.log", `AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`); fs.writeFileSync(
"/home/coder/agentapi-mock.log",
`AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`,
);
http http
.createServer(function (_request, response) { .createServer(function (_request, response) {
+6 -6
View File
@@ -8,15 +8,15 @@ port=${2:-3284}
module_path="$HOME/.agentapi-module" module_path="$HOME/.agentapi-module"
log_file_path="$module_path/agentapi.log" log_file_path="$module_path/agentapi.log"
echo "using prompt: $use_prompt" >>/home/coder/test-agentapi-start.log echo "using prompt: $use_prompt" >> /home/coder/test-agentapi-start.log
echo "using port: $port" >>/home/coder/test-agentapi-start.log echo "using port: $port" >> /home/coder/test-agentapi-start.log
AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}" AGENTAPI_CHAT_BASE_PATH="${AGENTAPI_CHAT_BASE_PATH:-}"
if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then if [ -n "$AGENTAPI_CHAT_BASE_PATH" ]; then
echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" >>/home/coder/test-agentapi-start.log echo "Using AGENTAPI_CHAT_BASE_PATH: $AGENTAPI_CHAT_BASE_PATH" >> /home/coder/test-agentapi-start.log
export AGENTAPI_CHAT_BASE_PATH export AGENTAPI_CHAT_BASE_PATH
fi fi
agentapi server --port "$port" --term-width 67 --term-height 1190 -- \ agentapi server --port "$port" --term-width 67 --term-height 1190 -- \
bash -c aiagent \ bash -c aiagent \
>"$log_file_path" 2>&1 > "$log_file_path" 2>&1
@@ -9,33 +9,33 @@ log_file_path="$module_path/agentapi.log"
# if the first argument is not empty, start claude with the prompt # if the first argument is not empty, start claude with the prompt
if [ -n "$1" ]; then if [ -n "$1" ]; then
cp "$module_path/prompt.txt" /tmp/claude-code-prompt cp "$module_path/prompt.txt" /tmp/claude-code-prompt
else else
rm -f /tmp/claude-code-prompt rm -f /tmp/claude-code-prompt
fi fi
# if the log file already exists, archive it # if the log file already exists, archive it
if [ -f "$log_file_path" ]; then if [ -f "$log_file_path" ]; then
mv "$log_file_path" "$log_file_path"".$(date +%s)" mv "$log_file_path" "$log_file_path"".$(date +%s)"
fi fi
# see the remove-last-session-id.sh script for details # see the remove-last-session-id.sh script for details
# about why we need it # about why we need it
# avoid exiting if the script fails # avoid exiting if the script fails
bash "$scripts_dir/remove-last-session-id.sh" "$(pwd)" 2>/dev/null || true bash "$scripts_dir/remove-last-session-id.sh" "$(pwd)" 2> /dev/null || true
# we'll be manually handling errors from this point on # we'll be manually handling errors from this point on
set +o errexit set +o errexit
function start_agentapi() { function start_agentapi() {
local continue_flag="$1" local continue_flag="$1"
local prompt_subshell='"$(cat /tmp/claude-code-prompt)"' local prompt_subshell='"$(cat /tmp/claude-code-prompt)"'
# use low width to fit in the tasks UI sidebar. height is adjusted so that width x height ~= 80x1000 characters # use low width to fit in the tasks UI sidebar. height is adjusted so that width x height ~= 80x1000 characters
# visible in the terminal screen by default. # visible in the terminal screen by default.
agentapi server --term-width 67 --term-height 1190 -- \ agentapi server --term-width 67 --term-height 1190 -- \
bash -c "claude $continue_flag --dangerously-skip-permissions $prompt_subshell" \ bash -c "claude $continue_flag --dangerously-skip-permissions $prompt_subshell" \
> "$log_file_path" 2>&1 > "$log_file_path" 2>&1
} }
echo "Starting AgentAPI..." echo "Starting AgentAPI..."
@@ -47,15 +47,15 @@ exit_code=$?
echo "First AgentAPI exit code: $exit_code" echo "First AgentAPI exit code: $exit_code"
if [ $exit_code -eq 0 ]; then if [ $exit_code -eq 0 ]; then
exit 0 exit 0
fi fi
# if there was no conversation to continue, claude exited with an error. # if there was no conversation to continue, claude exited with an error.
# start claude without the --continue flag. # start claude without the --continue flag.
if grep -q "No conversation found to continue" "$log_file_path"; then if grep -q "No conversation found to continue" "$log_file_path"; then
echo "AgentAPI with --continue flag failed, starting claude without it." echo "AgentAPI with --continue flag failed, starting claude without it."
start_agentapi start_agentapi
exit_code=$? exit_code=$?
fi fi
echo "Second AgentAPI exit code: $exit_code" echo "Second AgentAPI exit code: $exit_code"
@@ -9,22 +9,22 @@ agentapi_started=false
echo "Waiting for agentapi server to start on port 3284..." echo "Waiting for agentapi server to start on port 3284..."
for i in $(seq 1 150); do for i in $(seq 1 150); do
for j in $(seq 1 3); do for j in $(seq 1 3); do
sleep 0.1 sleep 0.1
if curl -fs -o /dev/null "http://localhost:3284/status"; then if curl -fs -o /dev/null "http://localhost:3284/status"; then
echo "agentapi response received ($j/3)" echo "agentapi response received ($j/3)"
else else
echo "agentapi server not responding ($i/15)" echo "agentapi server not responding ($i/15)"
continue 2 continue 2
fi fi
done done
agentapi_started=true agentapi_started=true
break break
done done
if [ "$agentapi_started" != "true" ]; then if [ "$agentapi_started" != "true" ]; then
echo "Error: agentapi server did not start on port 3284 after 15 seconds." echo "Error: agentapi server did not start on port 3284 after 15 seconds."
exit 1 exit 1
fi fi
echo "agentapi server started on port 3284." echo "agentapi server started on port 3284."
@@ -20,7 +20,10 @@ if (
process.exit(1); process.exit(1);
} }
fs.writeFileSync("/home/coder/agentapi-mock.log", `AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`); fs.writeFileSync(
"/home/coder/agentapi-mock.log",
`AGENTAPI_ALLOWED_HOSTS: ${process.env.AGENTAPI_ALLOWED_HOSTS}`,
);
console.log(`starting server on port ${port}`); console.log(`starting server on port ${port}`);
+8 -2
View File
@@ -94,12 +94,18 @@ describe("cursor", async () => {
it("writes ~/.cursor/mcp.json when mcp provided", async () => { it("writes ~/.cursor/mcp.json when mcp provided", async () => {
const id = await runContainer("alpine"); const id = await runContainer("alpine");
try { try {
const mcp = JSON.stringify({ servers: { demo: { url: "http://localhost:1234" } } }); const mcp = JSON.stringify({
servers: { demo: { url: "http://localhost:1234" } },
});
const state = await runTerraformApply(import.meta.dir, { const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo", agent_id: "foo",
mcp, mcp,
}); });
const script = findResourceInstance(state, "coder_script", "cursor_mcp").script; const script = findResourceInstance(
state,
"coder_script",
"cursor_mcp",
).script;
const resp = await execContainer(id, ["sh", "-c", script]); const resp = await execContainer(id, ["sh", "-c", script]);
if (resp.exitCode !== 0) { if (resp.exitCode !== 0) {
console.log(resp.stdout); console.log(resp.stdout);
+33 -33
View File
@@ -7,55 +7,55 @@
cd "$CODER_SCRIPT_DATA_DIR" cd "$CODER_SCRIPT_DATA_DIR"
# If @devcontainers/cli is already installed, we can skip # If @devcontainers/cli is already installed, we can skip
if command -v devcontainer >/dev/null 2>&1; then if command -v devcontainer > /dev/null 2>&1; then
echo "🥳 @devcontainers/cli is already installed into $(which devcontainer)!" echo "🥳 @devcontainers/cli is already installed into $(which devcontainer)!"
exit 0 exit 0
fi fi
# Check if docker is installed # Check if docker is installed
if ! command -v docker >/dev/null 2>&1; then if ! command -v docker > /dev/null 2>&1; then
echo "WARNING: Docker was not found but is required to use @devcontainers/cli, please make sure it is available." echo "WARNING: Docker was not found but is required to use @devcontainers/cli, please make sure it is available."
fi fi
# Determine the package manager to use: npm, pnpm, or yarn # Determine the package manager to use: npm, pnpm, or yarn
if command -v yarn >/dev/null 2>&1; then if command -v yarn > /dev/null 2>&1; then
PACKAGE_MANAGER="yarn" PACKAGE_MANAGER="yarn"
elif command -v npm >/dev/null 2>&1; then elif command -v npm > /dev/null 2>&1; then
PACKAGE_MANAGER="npm" PACKAGE_MANAGER="npm"
elif command -v pnpm >/dev/null 2>&1; then elif command -v pnpm > /dev/null 2>&1; then
PACKAGE_MANAGER="pnpm" PACKAGE_MANAGER="pnpm"
else else
echo "ERROR: No supported package manager (npm, pnpm, yarn) is installed. Please install one first." 1>&2 echo "ERROR: No supported package manager (npm, pnpm, yarn) is installed. Please install one first." 1>&2
exit 1 exit 1
fi fi
install() { install() {
echo "Installing @devcontainers/cli using $PACKAGE_MANAGER..." echo "Installing @devcontainers/cli using $PACKAGE_MANAGER..."
if [ "$PACKAGE_MANAGER" = "npm" ]; then if [ "$PACKAGE_MANAGER" = "npm" ]; then
npm install -g @devcontainers/cli npm install -g @devcontainers/cli
elif [ "$PACKAGE_MANAGER" = "pnpm" ]; then elif [ "$PACKAGE_MANAGER" = "pnpm" ]; then
# Check if PNPM_HOME is set, if not, set it to the script's bin directory # Check if PNPM_HOME is set, if not, set it to the script's bin directory
# pnpm needs this to be set to install binaries # pnpm needs this to be set to install binaries
# coder agent ensures this part is part of the PATH # coder agent ensures this part is part of the PATH
# so that the devcontainer command is available # so that the devcontainer command is available
if [ -z "$PNPM_HOME" ]; then if [ -z "$PNPM_HOME" ]; then
PNPM_HOME="$CODER_SCRIPT_BIN_DIR" PNPM_HOME="$CODER_SCRIPT_BIN_DIR"
export PNPM_HOME export PNPM_HOME
fi
pnpm add -g @devcontainers/cli
elif [ "$PACKAGE_MANAGER" = "yarn" ]; then
yarn global add @devcontainers/cli --prefix "$(dirname "$CODER_SCRIPT_BIN_DIR")"
fi fi
pnpm add -g @devcontainers/cli
elif [ "$PACKAGE_MANAGER" = "yarn" ]; then
yarn global add @devcontainers/cli --prefix "$(dirname "$CODER_SCRIPT_BIN_DIR")"
fi
} }
if ! install; then if ! install; then
echo "Failed to install @devcontainers/cli" >&2 echo "Failed to install @devcontainers/cli" >&2
exit 1 exit 1
fi fi
if ! command -v devcontainer >/dev/null 2>&1; then if ! command -v devcontainer > /dev/null 2>&1; then
echo "Installation completed but 'devcontainer' command not found in PATH" >&2 echo "Installation completed but 'devcontainer' command not found in PATH" >&2
exit 1 exit 1
fi fi
echo "🥳 @devcontainers/cli has been installed into $(which devcontainer)!" echo "🥳 @devcontainers/cli has been installed into $(which devcontainer)!"
+2 -2
View File
@@ -7,7 +7,7 @@ BOLD='\033[[0;1m'
printf "$${BOLD}Installing filebrowser \n\n" printf "$${BOLD}Installing filebrowser \n\n"
# Check if filebrowser is installed # Check if filebrowser is installed
if ! command -v filebrowser &>/dev/null; then if ! command -v filebrowser &> /dev/null; then
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
fi fi
@@ -34,6 +34,6 @@ printf "👷 Starting filebrowser in background... \n\n"
printf "📂 Serving $${ROOT_DIR} at http://localhost:${PORT} \n\n" printf "📂 Serving $${ROOT_DIR} at http://localhost:${PORT} \n\n"
filebrowser >>${LOG_PATH} 2>&1 & filebrowser >> ${LOG_PATH} 2>&1 &
printf "📝 Logs at ${LOG_PATH} \n\n" printf "📝 Logs at ${LOG_PATH} \n\n"
+3 -1
View File
@@ -267,6 +267,8 @@ describe("goose", async () => {
await execModuleScript(id); await execModuleScript(id);
const agentapiMockOutput = await readFileContainer(id, agentapiStartLog); const agentapiMockOutput = await readFileContainer(id, agentapiStartLog);
expect(agentapiMockOutput).toContain("AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/goose/chat"); expect(agentapiMockOutput).toContain(
"AGENTAPI_CHAT_BASE_PATH=/@default/default.foo/apps/goose/chat",
);
}); });
}); });
+23 -23
View File
@@ -2,7 +2,7 @@
# Function to check if a command exists # Function to check if a command exists
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" > /dev/null 2>&1
} }
set -o nounset set -o nounset
@@ -18,40 +18,40 @@ echo "--------------------------------"
set +o nounset set +o nounset
if [ "${ARG_INSTALL}" = "true" ]; then if [ "${ARG_INSTALL}" = "true" ]; then
echo "Installing Goose..." echo "Installing Goose..."
parsed_version="${ARG_GOOSE_VERSION}" parsed_version="${ARG_GOOSE_VERSION}"
if [ "${ARG_GOOSE_VERSION}" = "stable" ]; then if [ "${ARG_GOOSE_VERSION}" = "stable" ]; then
parsed_version="" parsed_version=""
fi fi
curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | GOOSE_VERSION="${parsed_version}" CONFIGURE=false bash curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh | GOOSE_VERSION="${parsed_version}" CONFIGURE=false bash
echo "Goose installed" echo "Goose installed"
else else
echo "Skipping Goose installation" echo "Skipping Goose installation"
fi fi
if [ "${ARG_GOOSE_CONFIG}" != "" ]; then if [ "${ARG_GOOSE_CONFIG}" != "" ]; then
echo "Configuring Goose..." echo "Configuring Goose..."
mkdir -p "$HOME/.config/goose" mkdir -p "$HOME/.config/goose"
echo "GOOSE_PROVIDER: $ARG_PROVIDER" >"$HOME/.config/goose/config.yaml" echo "GOOSE_PROVIDER: $ARG_PROVIDER" > "$HOME/.config/goose/config.yaml"
echo "GOOSE_MODEL: $ARG_MODEL" >>"$HOME/.config/goose/config.yaml" echo "GOOSE_MODEL: $ARG_MODEL" >> "$HOME/.config/goose/config.yaml"
echo "$ARG_GOOSE_CONFIG" >>"$HOME/.config/goose/config.yaml" echo "$ARG_GOOSE_CONFIG" >> "$HOME/.config/goose/config.yaml"
else else
echo "Skipping Goose configuration" echo "Skipping Goose configuration"
fi fi
if [ "${GOOSE_SYSTEM_PROMPT}" != "" ]; then if [ "${GOOSE_SYSTEM_PROMPT}" != "" ]; then
echo "Setting Goose system prompt..." echo "Setting Goose system prompt..."
mkdir -p "$HOME/.config/goose" mkdir -p "$HOME/.config/goose"
echo "$GOOSE_SYSTEM_PROMPT" >"$HOME/.config/goose/.goosehints" echo "$GOOSE_SYSTEM_PROMPT" > "$HOME/.config/goose/.goosehints"
else else
echo "Goose system prompt not set. use the GOOSE_SYSTEM_PROMPT environment variable to set it." echo "Goose system prompt not set. use the GOOSE_SYSTEM_PROMPT environment variable to set it."
fi fi
if command_exists goose; then if command_exists goose; then
GOOSE_CMD=goose GOOSE_CMD=goose
elif [ -f "$HOME/.local/bin/goose" ]; then elif [ -f "$HOME/.local/bin/goose" ]; then
GOOSE_CMD="$HOME/.local/bin/goose" GOOSE_CMD="$HOME/.local/bin/goose"
else else
echo "Error: Goose is not installed. Please enable install_goose or install it manually." echo "Error: Goose is not installed. Please enable install_goose or install it manually."
exit 1 exit 1
fi fi
+13 -13
View File
@@ -4,16 +4,16 @@ set -o errexit
set -o pipefail set -o pipefail
command_exists() { command_exists() {
command -v "$1" >/dev/null 2>&1 command -v "$1" > /dev/null 2>&1
} }
if command_exists goose; then if command_exists goose; then
GOOSE_CMD=goose GOOSE_CMD=goose
elif [ -f "$HOME/.local/bin/goose" ]; then elif [ -f "$HOME/.local/bin/goose" ]; then
GOOSE_CMD="$HOME/.local/bin/goose" GOOSE_CMD="$HOME/.local/bin/goose"
else else
echo "Error: Goose is not installed. Please enable install_goose or install it manually." echo "Error: Goose is not installed. Please enable install_goose or install it manually."
exit 1 exit 1
fi fi
# this must be kept up to date with main.tf # this must be kept up to date with main.tf
@@ -21,15 +21,15 @@ MODULE_DIR="$HOME/.goose-module"
mkdir -p "$MODULE_DIR" mkdir -p "$MODULE_DIR"
if [ ! -z "$GOOSE_TASK_PROMPT" ]; then if [ ! -z "$GOOSE_TASK_PROMPT" ]; then
echo "Starting with a prompt" echo "Starting with a prompt"
PROMPT="Review your goosehints. Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GOOSE_TASK_PROMPT" PROMPT="Review your goosehints. Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GOOSE_TASK_PROMPT"
PROMPT_FILE="$MODULE_DIR/prompt.txt" PROMPT_FILE="$MODULE_DIR/prompt.txt"
echo -n "$PROMPT" >"$PROMPT_FILE" echo -n "$PROMPT" > "$PROMPT_FILE"
GOOSE_ARGS=(run --interactive --instructions "$PROMPT_FILE") GOOSE_ARGS=(run --interactive --instructions "$PROMPT_FILE")
else else
echo "Starting without a prompt" echo "Starting without a prompt"
GOOSE_ARGS=() GOOSE_ARGS=()
fi fi
agentapi server --term-width 67 --term-height 1190 -- \ agentapi server --term-width 67 --term-height 1190 -- \
bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")" bash -c "$(printf '%q ' "$GOOSE_CMD" "${GOOSE_ARGS[@]}")"
@@ -3,7 +3,9 @@
const http = require("http"); const http = require("http");
const args = process.argv.slice(2); const args = process.argv.slice(2);
console.log(args); console.log(args);
console.log(`AGENTAPI_CHAT_BASE_PATH=${process.env["AGENTAPI_CHAT_BASE_PATH"]}`); console.log(
`AGENTAPI_CHAT_BASE_PATH=${process.env["AGENTAPI_CHAT_BASE_PATH"]}`,
);
const port = 3284; const port = 3284;
console.log(`starting server on port ${port}`); console.log(`starting server on port ${port}`);
+2 -2
View File
@@ -3,6 +3,6 @@
set -e set -e
while true; do while true; do
echo "$(date) - goose-mock" echo "$(date) - goose-mock"
sleep 15 sleep 15
done done
+13 -6
View File
@@ -115,22 +115,29 @@ describe("jupyterlab", async () => {
port: 8888, port: 8888,
token: "test-token", token: "test-token",
password: "", password: "",
allow_origin: "*" allow_origin: "*",
} },
}; };
const configJson = JSON.stringify(config); const configJson = JSON.stringify(config);
const state = await runTerraformApply(import.meta.dir, { const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo", agent_id: "foo",
config: configJson, config: configJson,
}); });
const script = findResourceInstance(state, "coder_script", "jupyterlab_config").script; const script = findResourceInstance(
state,
"coder_script",
"jupyterlab_config",
).script;
const resp = await execContainer(id, ["sh", "-c", script]); const resp = await execContainer(id, ["sh", "-c", script]);
if (resp.exitCode !== 0) { if (resp.exitCode !== 0) {
console.log(resp.stdout); console.log(resp.stdout);
console.log(resp.stderr); console.log(resp.stderr);
} }
expect(resp.exitCode).toBe(0); expect(resp.exitCode).toBe(0);
const content = await readFileContainer(id, "/root/.jupyter/jupyter_server_config.json"); const content = await readFileContainer(
id,
"/root/.jupyter/jupyter_server_config.json",
);
// Parse both JSON strings and compare objects to avoid key ordering issues // Parse both JSON strings and compare objects to avoid key ordering issues
const actualConfig = JSON.parse(content); const actualConfig = JSON.parse(content);
expect(actualConfig).toEqual(config); expect(actualConfig).toEqual(config);
@@ -145,7 +152,7 @@ describe("jupyterlab", async () => {
config: "{}", config: "{}",
}); });
const configScripts = state.resources.filter( const configScripts = state.resources.filter(
(res) => res.type === "coder_script" && res.name === "jupyterlab_config" (res) => res.type === "coder_script" && res.name === "jupyterlab_config",
); );
expect(configScripts.length).toBe(1); expect(configScripts.length).toBe(1);
}); });
@@ -155,7 +162,7 @@ describe("jupyterlab", async () => {
agent_id: "foo", agent_id: "foo",
}); });
const configScripts = state.resources.filter( const configScripts = state.resources.filter(
(res) => res.type === "coder_script" && res.name === "jupyterlab_config" (res) => res.type === "coder_script" && res.name === "jupyterlab_config",
); );
expect(configScripts.length).toBe(1); expect(configScripts.length).toBe(1);
}); });
+14 -14
View File
@@ -3,13 +3,13 @@ INSTALLER=""
check_available_installer() { check_available_installer() {
# check if pipx is installed # check if pipx is installed
echo "Checking for a supported installer" echo "Checking for a supported installer"
if command -v pipx >/dev/null 2>&1; then if command -v pipx > /dev/null 2>&1; then
echo "pipx is installed" echo "pipx is installed"
INSTALLER="pipx" INSTALLER="pipx"
return return
fi fi
# check if uv is installed # check if uv is installed
if command -v uv >/dev/null 2>&1; then if command -v uv > /dev/null 2>&1; then
echo "uv is installed" echo "uv is installed"
INSTALLER="uv" INSTALLER="uv"
return return
@@ -26,21 +26,21 @@ fi
BOLD='\033[0;1m' BOLD='\033[0;1m'
# check if jupyterlab is installed # check if jupyterlab is installed
if ! command -v jupyter-lab >/dev/null 2>&1; then if ! command -v jupyter-lab > /dev/null 2>&1; then
# install jupyterlab # install jupyterlab
check_available_installer check_available_installer
printf "$${BOLD}Installing jupyterlab!\n" printf "$${BOLD}Installing jupyterlab!\n"
case $INSTALLER in case $INSTALLER in
uv) uv)
uv pip install -q jupyterlab && uv pip install -q jupyterlab \
printf "%s\n" "🥳 jupyterlab has been installed" && printf "%s\n" "🥳 jupyterlab has been installed"
JUPYTER="$HOME/.venv/bin/jupyter-lab" JUPYTER="$HOME/.venv/bin/jupyter-lab"
;; ;;
pipx) pipx)
pipx install jupyterlab && pipx install jupyterlab \
printf "%s\n" "🥳 jupyterlab has been installed" && printf "%s\n" "🥳 jupyterlab has been installed"
JUPYTER="$HOME/.local/bin/jupyter-lab" JUPYTER="$HOME/.local/bin/jupyter-lab"
;; ;;
esac esac
else else
printf "%s\n\n" "🥳 jupyterlab is already installed" printf "%s\n\n" "🥳 jupyterlab is already installed"
@@ -55,4 +55,4 @@ $JUPYTER --no-browser \
--ServerApp.port="${PORT}" \ --ServerApp.port="${PORT}" \
--ServerApp.token='' \ --ServerApp.token='' \
--ServerApp.password='' \ --ServerApp.password='' \
>"${LOG_PATH}" 2>&1 & > "${LOG_PATH}" 2>&1 &
+54 -24
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<title>Path-Sharing Bounce Page</title> <title>Path-Sharing Bounce Page</title>
@@ -6,40 +6,64 @@
:root { :root {
color-scheme: light dark; color-scheme: light dark;
--dark: #121212; --dark: #121212;
--header-bg: rgba(127,127,127,0.2); --header-bg: rgba(127, 127, 127, 0.2);
--light: white; --light: white;
--rule-color: light-dark(rgba(0,0,0,0.8), rgba(255,255,255,0.8)); --rule-color: light-dark(rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0.8));
background-color: light-dark(var(--light), var(--dark)); background-color: light-dark(var(--light), var(--dark));
color: light-dark(var(--dark), var(--light)); color: light-dark(var(--dark), var(--light));
} }
body, h1, p { body,
h1,
p {
box-sizing: border-box; box-sizing: border-box;
margin:0; padding:0; margin: 0;
padding: 0;
} }
body{ body {
font-family:Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; font-family:
Inter,
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Oxygen,
Ubuntu,
Cantarell,
"Open Sans",
"Helvetica Neue",
sans-serif;
} }
h1{ h1 {
width: 100%; width: 100%;
padding: 1rem; padding: 1rem;
letter-spacing: -1.5pt; letter-spacing: -1.5pt;
padding-bottom:10px; padding-bottom: 10px;
border-bottom: 1px solid var(--rule-color); border-bottom: 1px solid var(--rule-color);
background-color: var(--header-bg); background-color: var(--header-bg);
} }
p { p {
padding: 1rem; letter-spacing: -0.5pt;} padding: 1rem;
a.indent { display:inline-block; padding-top:0.5rem; padding-left: 2rem; font-size:0.8rem } letter-spacing: -0.5pt;
</style> }
a.indent {
display: inline-block;
padding-top: 0.5rem;
padding-left: 2rem;
font-size: 0.8rem;
}
</style>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
</head> </head>
<body> <body>
<h1>Path-Sharing Bounce Page</h1> <h1>Path-Sharing Bounce Page</h1>
<p> <p>
This application is being served via path sharing. This application is being served via path sharing. If you are not
If you are not redirected, <span id="help">check the redirected,
Javascript console in your browser's developer tools <span id="help"
for more information.</span> >check the Javascript console in your browser's developer tools for more
information.</span
>
</p> </p>
</body> </body>
<script language="javascript"> <script language="javascript">
@@ -58,24 +82,30 @@
// This apparently doesn't tolerate a leading `/` so we use a // This apparently doesn't tolerate a leading `/` so we use a
// function to tidy that up. // function to tidy that up.
function trimFirstCharIf(str, char) { function trimFirstCharIf(str, char) {
return str.charAt(0) === char ? str.slice(1) : str; return str.charAt(0) === char ? str.slice(1) : str;
} }
function trimLastCharIf(str, char) { function trimLastCharIf(str, char) {
return str.endsWith("/") ? str.slice(0,str.length-1) : str; return str.endsWith("/") ? str.slice(0, str.length - 1) : str;
} }
const newloc = new URL(window.location); const newloc = new URL(window.location);
const h = document.getElementById("help") const h = document.getElementById("help");
// Building the websockify path must happen before we append the filename to newloc.pathname // Building the websockify path must happen before we append the filename to newloc.pathname
newloc.searchParams.append("path", newloc.searchParams.append(
trimLastCharIf(trimFirstCharIf(newloc.pathname,"/"),"/")+"/websockify"); "path",
newloc.searchParams.append("encrypted", newloc.protocol==="https:"? true : false); trimLastCharIf(trimFirstCharIf(newloc.pathname, "/"), "/") +
"/websockify",
);
newloc.searchParams.append(
"encrypted",
newloc.protocol === "https:" ? true : false,
);
newloc.pathname += "vnc.html" newloc.pathname += "vnc.html";
console.log(newloc); console.log(newloc);
h.innerHTML = `click <a id="link" href="${newloc.toString()}">here</a> to go to the application. h.innerHTML = `click <a id="link" href="${newloc.toString()}">here</a> to go to the application.
<br/><br/>The rewritten URL is:<br/><a id="link" class="indent" href="${newloc.toString()}">${newloc.toString()}</a>` <br/><br/>The rewritten URL is:<br/><a id="link" class="indent" href="${newloc.toString()}">${newloc.toString()}</a>`;
window.location = newloc.href; window.location = newloc.href;
</script> </script>
</html> </html>
+24 -21
View File
@@ -3,7 +3,10 @@
# Exit on error, undefined variables, and pipe failures # Exit on error, undefined variables, and pipe failures
set -euo pipefail set -euo pipefail
error() { printf "💀 ERROR: %s\n" "$@"; exit 1; } error() {
printf "💀 ERROR: %s\n" "$@"
exit 1
}
# Function to check if vncserver is already installed # Function to check if vncserver is already installed
check_installed() { check_installed() {
@@ -248,30 +251,30 @@ get_http_dir() {
echo $httpd_directory echo $httpd_directory
} }
fix_server_index_file(){ fix_server_index_file() {
local fname=$${FUNCNAME[0]} # gets current function name local fname=$${FUNCNAME[0]} # gets current function name
if [[ $# -ne 1 ]]; then if [[ $# -ne 1 ]]; then
error "$fname requires exactly 1 parameter:\n\tpath to KasmVNC httpd_directory" error "$fname requires exactly 1 parameter:\n\tpath to KasmVNC httpd_directory"
fi fi
local httpdir="$1" local httpdir="$1"
if [[ ! -d "$httpdir" ]]; then if [[ ! -d "$httpdir" ]]; then
error "$fname: $httpdir is not a directory" error "$fname: $httpdir is not a directory"
fi fi
pushd "$httpdir" > /dev/null pushd "$httpdir" > /dev/null
cat <<'EOH' > /tmp/path_vnc.html cat << 'EOH' > /tmp/path_vnc.html
${PATH_VNC_HTML} ${PATH_VNC_HTML}
EOH EOH
$SUDO mv /tmp/path_vnc.html . $SUDO mv /tmp/path_vnc.html .
# check for the switcheroo # check for the switcheroo
if [[ -f "index.html" && -L "vnc.html" ]]; then if [[ -f "index.html" && -L "vnc.html" ]]; then
$SUDO mv $httpdir/index.html $httpdir/vnc.html $SUDO mv $httpdir/index.html $httpdir/vnc.html
fi fi
$SUDO ln -s -f path_vnc.html index.html $SUDO ln -s -f path_vnc.html index.html
popd > /dev/null popd > /dev/null
} }
patch_kasm_http_files(){ patch_kasm_http_files() {
homedir=$(get_http_dir) homedir=$(get_http_dir)
fix_server_index_file "$homedir" fix_server_index_file "$homedir"
} }
@@ -292,7 +295,7 @@ set -e
if [[ $RETVAL -ne 0 ]]; then if [[ $RETVAL -ne 0 ]]; then
echo "ERROR: Failed to start KasmVNC server. Return code: $RETVAL" echo "ERROR: Failed to start KasmVNC server. Return code: $RETVAL"
if [[ -f "$VNC_LOG" ]]; then if [[ -f "$VNC_LOG" ]]; then
echo "Full logs:" echo "Full logs:"
cat "$VNC_LOG" cat "$VNC_LOG"
else else
+7 -7
View File
@@ -9,11 +9,11 @@ CODER_OIDC_ACCESS_TOKEN=${CODER_OIDC_ACCESS_TOKEN}
fetch() { fetch() {
dest="$1" dest="$1"
url="$2" url="$2"
if command -v curl >/dev/null 2>&1; then if command -v curl > /dev/null 2>&1; then
curl -sSL --fail "$${url}" -o "$${dest}" curl -sSL --fail "$${url}" -o "$${dest}"
elif command -v wget >/dev/null 2>&1; then elif command -v wget > /dev/null 2>&1; then
wget -O "$${dest}" "$${url}" wget -O "$${dest}" "$${url}"
elif command -v busybox >/dev/null 2>&1; then elif command -v busybox > /dev/null 2>&1; then
busybox wget -O "$${dest}" "$${url}" busybox wget -O "$${dest}" "$${url}"
else else
printf "curl, wget, or busybox is not installed. Please install curl or wget in your image.\n" printf "curl, wget, or busybox is not installed. Please install curl or wget in your image.\n"
@@ -22,9 +22,9 @@ fetch() {
} }
unzip_safe() { unzip_safe() {
if command -v unzip >/dev/null 2>&1; then if command -v unzip > /dev/null 2>&1; then
command unzip "$@" command unzip "$@"
elif command -v busybox >/dev/null 2>&1; then elif command -v busybox > /dev/null 2>&1; then
busybox unzip "$@" busybox unzip "$@"
else else
printf "unzip or busybox is not installed. Please install unzip in your image.\n" printf "unzip or busybox is not installed. Please install unzip in your image.\n"
@@ -56,7 +56,7 @@ install() {
# Check if the vault CLI is installed and has the correct version # Check if the vault CLI is installed and has the correct version
installation_needed=1 installation_needed=1
if command -v vault >/dev/null 2>&1; then if command -v vault > /dev/null 2>&1; then
CURRENT_VERSION=$(vault version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') CURRENT_VERSION=$(vault version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
if [ "$${CURRENT_VERSION}" = "$${VAULT_CLI_VERSION}" ]; then if [ "$${CURRENT_VERSION}" = "$${VAULT_CLI_VERSION}" ]; then
printf "Vault version %s is already installed and up-to-date.\n\n" "$${CURRENT_VERSION}" printf "Vault version %s is already installed and up-to-date.\n\n" "$${CURRENT_VERSION}"
@@ -81,7 +81,7 @@ install() {
return 1 return 1
fi fi
rm vault.zip rm vault.zip
if sudo mv vault /usr/local/bin/vault 2>/dev/null; then if sudo mv vault /usr/local/bin/vault 2> /dev/null; then
printf "Vault installed successfully!\n\n" printf "Vault installed successfully!\n\n"
else else
mkdir -p ~/.local/bin mkdir -p ~/.local/bin
@@ -14,7 +14,7 @@ const defaultVariables = {
coder_app_slug: "vscode", coder_app_slug: "vscode",
coder_app_display_name: "VS Code Desktop", coder_app_display_name: "VS Code Desktop",
protocol: "vscode", protocol: "vscode",
} };
describe("vscode-desktop-core", async () => { describe("vscode-desktop-core", async () => {
await runTerraformInit(import.meta.dir); await runTerraformInit(import.meta.dir);
@@ -40,7 +40,7 @@ describe("vscode-desktop-core", async () => {
const state = await runTerraformApply(import.meta.dir, { const state = await runTerraformApply(import.meta.dir, {
folder: "/foo/bar", folder: "/foo/bar",
...defaultVariables ...defaultVariables,
}); });
expect(state.outputs.ide_uri.value).toBe( expect(state.outputs.ide_uri.value).toBe(
@@ -86,7 +86,7 @@ describe("vscode-desktop-core", async () => {
it("expect order to be set", async () => { it("expect order to be set", async () => {
const state = await runTerraformApply(import.meta.dir, { const state = await runTerraformApply(import.meta.dir, {
coder_app_order: "22", coder_app_order: "22",
...defaultVariables ...defaultVariables,
}); });
const coder_app = state.resources.find( const coder_app = state.resources.find(
+1 -1
View File
@@ -68,7 +68,7 @@ esac
# Detect the platform # Detect the platform
if [ -n "${PLATFORM}" ]; then if [ -n "${PLATFORM}" ]; then
DETECTED_PLATFORM="${PLATFORM}" DETECTED_PLATFORM="${PLATFORM}"
elif [ -f /etc/alpine-release ] || grep -qi 'ID=alpine' /etc/os-release 2>/dev/null || command -v apk > /dev/null 2>&1; then elif [ -f /etc/alpine-release ] || grep -qi 'ID=alpine' /etc/os-release 2> /dev/null || command -v apk > /dev/null 2>&1; then
DETECTED_PLATFORM="alpine" DETECTED_PLATFORM="alpine"
elif [ "$(uname -s)" = "Darwin" ]; then elif [ "$(uname -s)" = "Darwin" ]; then
DETECTED_PLATFORM="darwin" DETECTED_PLATFORM="darwin"
@@ -18,27 +18,33 @@ describe("aws-ami-snapshot", async () => {
}); });
it("missing variable: instance_id", async () => { it("missing variable: instance_id", async () => {
await expect(runTerraformApply(import.meta.dir, { await expect(
default_ami_id: "ami-12345678", runTerraformApply(import.meta.dir, {
template_name: "test-template", default_ami_id: "ami-12345678",
test_mode: true, template_name: "test-template",
})).rejects.toThrow(); test_mode: true,
}),
).rejects.toThrow();
}); });
it("missing variable: default_ami_id", async () => { it("missing variable: default_ami_id", async () => {
await expect(runTerraformApply(import.meta.dir, { await expect(
instance_id: "i-1234567890abcdef0", runTerraformApply(import.meta.dir, {
template_name: "test-template", instance_id: "i-1234567890abcdef0",
test_mode: true, template_name: "test-template",
})).rejects.toThrow(); test_mode: true,
}),
).rejects.toThrow();
}); });
it("missing variable: template_name", async () => { it("missing variable: template_name", async () => {
await expect(runTerraformApply(import.meta.dir, { await expect(
instance_id: "i-1234567890abcdef0", runTerraformApply(import.meta.dir, {
default_ami_id: "ami-12345678", instance_id: "i-1234567890abcdef0",
test_mode: true, default_ami_id: "ami-12345678",
})).rejects.toThrow(); test_mode: true,
}),
).rejects.toThrow();
}); });
it("supports optional variables", async () => { it("supports optional variables", async () => {