mirror of
https://github.com/coder/registry.git
synced 2026-06-03 04:58:15 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6802df9d13 | |||
| a7e0b09aa4 | |||
| 229056b344 | |||
| f257efd8e1 | |||
| 9258f1857f | |||
| 5c4480daa3 | |||
| 0ffd71d443 | |||
| 60ed61368e | |||
| 5f4d7bf1b4 | |||
| c411657a67 | |||
| d8a96435c6 | |||
| d4efc09b20 | |||
| 4b03bce6f7 | |||
| ae48c1043b | |||
| 73af151f8d | |||
| c115d860f7 | |||
| 395f170d07 | |||
| 65189bc068 | |||
| d3b5057819 | |||
| dd86d3d1d8 | |||
| 1dee0012f5 | |||
| ef0f597d54 | |||
| aaf2c4e0dd | |||
| 090fa7dd1d | |||
| a630ffa42a | |||
| 93f9ec3708 | |||
| 5870805d0f | |||
| 6806985778 | |||
| 149e65b49f | |||
| 327f05487d | |||
| 19dc50db3e | |||
| 48564621ad | |||
| d0ef4f426b | |||
| c2fa87aea6 | |||
| 63eff436eb | |||
| 250b64e44f | |||
| 78a0d14863 | |||
| 0d0bfa7131 | |||
| b32b2d4329 | |||
| 8664ded490 | |||
| 2ed4be2172 | |||
| d718c3b4e9 | |||
| 19f2a8f3ec | |||
| e12cd61e45 | |||
| 09386a43cd | |||
| 50eb191eaa | |||
| bdc8aea37f | |||
| c6a7d049bd | |||
| 65a73a8708 | |||
| 05c5724561 | |||
| 0d03fa4e58 | |||
| f3bfa9cc8d | |||
| a91c8845cb | |||
| 2c00575203 | |||
| c5d83570bc | |||
| dd96e8c74b |
@@ -11,7 +11,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run check.sh
|
||||
run: |
|
||||
|
||||
+10
-10
@@ -12,9 +12,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
- name: Detect changed files
|
||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
|
||||
uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
list-files: shell
|
||||
@@ -37,9 +37,9 @@ jobs:
|
||||
all:
|
||||
- '**'
|
||||
- name: Set up Terraform
|
||||
uses: coder/coder/.github/actions/setup-tf@59cdd7e21f4d7da12567c0c29964d298fbf38f27 # v2.29.1
|
||||
uses: coder/coder/.github/actions/setup-tf@main
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
# We're using the latest version of Bun for now, but it might be worth
|
||||
# reconsidering. They've pushed breaking changes in patch releases
|
||||
@@ -80,20 +80,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
- name: Install Bun
|
||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
# Need Terraform for its formatter
|
||||
- name: Install Terraform
|
||||
uses: coder/coder/.github/actions/setup-tf@59cdd7e21f4d7da12567c0c29964d298fbf38f27 # v2.29.1
|
||||
uses: coder/coder/.github/actions/setup-tf@main
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
- name: Validate formatting
|
||||
run: bun fmt:ci
|
||||
- name: Check for typos
|
||||
uses: crate-ci/typos@bb4666ad77b539a6b4ce4eda7ebb6de553704021 # v1.42.0
|
||||
uses: crate-ci/typos@v1.42.0
|
||||
with:
|
||||
config: .github/typos.toml
|
||||
validate-readme-files:
|
||||
@@ -104,9 +104,9 @@ jobs:
|
||||
needs: validate-style
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.24.0"
|
||||
- name: Validate contributors
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
- name: Authenticate with Google Cloud
|
||||
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093
|
||||
with:
|
||||
|
||||
@@ -14,11 +14,11 @@ jobs:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: stable
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9
|
||||
uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: v2.1
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
@@ -89,9 +89,9 @@ jobs:
|
||||
|
||||
for sha in $MODULE_COMMIT_SHAS; do
|
||||
SHORT_SHA=${sha:0:7}
|
||||
|
||||
|
||||
COMMIT_LINES=$(echo "$FULL_CHANGELOG" | grep -E "$SHORT_SHA|$(git log --format='%s' -n 1 $sha)" || true)
|
||||
|
||||
|
||||
if [ -n "$COMMIT_LINES" ]; then
|
||||
FILTERED_CHANGELOG="${FILTERED_CHANGELOG}${COMMIT_LINES}\n"
|
||||
else
|
||||
|
||||
@@ -20,28 +20,26 @@ jobs:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Set up Terraform
|
||||
uses: coder/coder/.github/actions/setup-tf@59cdd7e21f4d7da12567c0c29964d298fbf38f27 # v2.29.1
|
||||
uses: coder/coder/.github/actions/setup-tf@main
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Extract bump type from label
|
||||
env:
|
||||
LABEL_NAME: ${{ github.event.label.name }}
|
||||
id: bump-type
|
||||
run: |
|
||||
case "$LABEL_NAME" in in
|
||||
case "${{ github.event.label.name }}" in
|
||||
"version:patch")
|
||||
echo "type=patch" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
@@ -52,7 +50,7 @@ jobs:
|
||||
echo "type=major" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
*)
|
||||
echo "Invalid version label: ${LABEL_NAME}"
|
||||
echo "Invalid version label: ${{ github.event.label.name }}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -62,7 +60,7 @@ jobs:
|
||||
|
||||
- name: Comment on PR - Version bump required
|
||||
if: failure()
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
name: GitHub Actions Security Analysis (zizmor)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: ["**"]
|
||||
paths:
|
||||
- ".github/workflows/**"
|
||||
push:
|
||||
branches: ["main"]
|
||||
paths:
|
||||
- ".github/workflows/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
zizmor_pr_blocking:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor (blocking, HIGH only)
|
||||
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
|
||||
with:
|
||||
advanced-security: false
|
||||
annotations: true
|
||||
min-severity: high
|
||||
inputs: |
|
||||
.github/workflows
|
||||
|
||||
zizmor_main_sarif:
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor (SARIF)
|
||||
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
|
||||
with:
|
||||
inputs: |
|
||||
.github/workflows
|
||||
@@ -1,41 +1,168 @@
|
||||
# AGENTS.md
|
||||
|
||||
Coder Registry: Terraform modules/templates for Coder workspaces under `registry/[namespace]/modules/` and `registry/[namespace]/templates/`.
|
||||
This file provides guidance to AI coding assistants when working with code in this repository.
|
||||
|
||||
## Commands
|
||||
## Project Overview
|
||||
|
||||
The Coder Registry is a community-driven repository for Terraform modules and templates that extend Coder workspaces. It's organized with:
|
||||
|
||||
- **Modules**: Individual components and tools (IDEs, auth integrations, dev tools)
|
||||
- **Templates**: Complete workspace configurations for different platforms
|
||||
- **Namespaces**: Each contributor has their own namespace under `/registry/[namespace]/`
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
### Formatting
|
||||
|
||||
```bash
|
||||
bun run fmt # Format code (Prettier + Terraform) - run before commits
|
||||
bun run tftest # Run all Terraform tests
|
||||
bun run tstest # Run all TypeScript tests
|
||||
terraform init -upgrade && terraform test -verbose # Test single module (run from module dir)
|
||||
bun test main.test.ts # Run single TS test (from module dir)
|
||||
./scripts/terraform_validate.sh # Validate Terraform syntax
|
||||
./scripts/new_module.sh ns/name # Create new module scaffold
|
||||
.github/scripts/version-bump.sh patch | minor | major # Bump module version after changes
|
||||
bun run fmt # Format all code (Prettier + Terraform)
|
||||
bun run fmt:ci # Check formatting (CI mode)
|
||||
```
|
||||
|
||||
## Structure
|
||||
### Testing
|
||||
|
||||
- **Modules**: `registry/[ns]/modules/[name]/` with `main.tf`, `README.md` (YAML frontmatter), `.tftest.hcl` (required)
|
||||
- **Templates**: `registry/[ns]/templates/[name]/` with `main.tf`, `README.md`
|
||||
- **Validation**: `cmd/readmevalidation/` (Go) validates structure/frontmatter; URLs must be relative, not absolute
|
||||
```bash
|
||||
# Test all modules with .tftest.hcl files
|
||||
bun run test
|
||||
|
||||
## Code Style
|
||||
# Test specific module (from module directory)
|
||||
terraform init -upgrade
|
||||
terraform test -verbose
|
||||
|
||||
- Every module MUST have `.tftest.hcl` tests; optional `main.test.ts` for container/script tests
|
||||
- README frontmatter: `display_name`, `description`, `icon`, `verified: false`, `tags`
|
||||
- Use semantic versioning; bump version via script when modifying modules
|
||||
- Docker tests require Linux or Colima/OrbStack (not Docker Desktop)
|
||||
- Use `tf` (not `hcl`) for code blocks in README; use relative icon paths (e.g., `../../../../.icons/`)
|
||||
# Validate Terraform syntax
|
||||
./scripts/terraform_validate.sh
|
||||
```
|
||||
|
||||
## PR Review Checklist
|
||||
### Module Creation
|
||||
|
||||
- Version bumped via `.github/scripts/version-bump.sh` if module changed (patch=bugfix, minor=feature, major=breaking)
|
||||
- Breaking changes documented: removed inputs, changed defaults, new required variables
|
||||
- New variables have sensible defaults to maintain backward compatibility
|
||||
- Tests pass (`bun run tftest`, `bun run tstest`); add diagnostic logging for test failures
|
||||
- README examples updated with new version number; tooltip/behavior changes noted
|
||||
- Shell scripts handle errors gracefully (use `|| echo "Warning..."` for non-fatal failures)
|
||||
- No hardcoded values that should be configurable; no absolute URLs (use relative paths)
|
||||
- If AI-assisted: include model and tool/agent name at footer of PR body (e.g., "Generated with [Amp](thread-url) using Claude")
|
||||
```bash
|
||||
# Generate new module scaffold
|
||||
./scripts/new_module.sh namespace/module-name
|
||||
```
|
||||
|
||||
### TypeScript Testing & Setup
|
||||
|
||||
The repository uses Bun for TypeScript testing with utilities:
|
||||
|
||||
- `test/test.ts` - Testing utilities for container management and Terraform operations
|
||||
- `setup.ts` - Test cleanup (removes .tfstate files and test containers)
|
||||
- Container-based testing with Docker for module validation
|
||||
|
||||
## Architecture & Organization
|
||||
|
||||
### Directory Structure
|
||||
|
||||
```
|
||||
registry/[namespace]/
|
||||
├── README.md # Contributor info with frontmatter
|
||||
├── .images/ # Namespace avatar (avatar.png/svg)
|
||||
├── modules/ # Individual components
|
||||
│ └── [module]/ # Each module has main.tf, README.md, tests
|
||||
└── templates/ # Complete workspace configs
|
||||
└── [template]/ # Each template has main.tf, README.md
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
**Module Structure**: Each module contains:
|
||||
|
||||
- `main.tf` - Terraform implementation
|
||||
- `README.md` - Documentation with YAML frontmatter
|
||||
- `.tftest.hcl` - Terraform test files (required)
|
||||
- `run.sh` - Optional startup script
|
||||
|
||||
**Template Structure**: Each template contains:
|
||||
|
||||
- `main.tf` - Complete Coder template configuration
|
||||
- `README.md` - Documentation with YAML frontmatter
|
||||
- Additional configs, scripts as needed
|
||||
|
||||
### README Frontmatter Requirements
|
||||
|
||||
All modules/templates require YAML frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
display_name: "Module Name"
|
||||
description: "Brief description"
|
||||
icon: "../../../../.icons/tool.svg"
|
||||
verified: false
|
||||
tags: ["tag1", "tag2"]
|
||||
---
|
||||
```
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
### Module Testing
|
||||
|
||||
- Every module MUST have `.tftest.hcl` test files
|
||||
- Optional `main.test.ts` files for container-based testing or complex business logic validation
|
||||
- Tests use Docker containers with `--network=host` flag
|
||||
- Linux required for testing (Docker Desktop on macOS/Windows won't work)
|
||||
- Use Colima or OrbStack on macOS instead of Docker Desktop
|
||||
|
||||
### Test Utilities
|
||||
|
||||
The `test/test.ts` file provides:
|
||||
|
||||
- `runTerraformApply()` - Execute Terraform with variables
|
||||
- `executeScriptInContainer()` - Run coder_script resources in containers
|
||||
- `testRequiredVariables()` - Validate required variables
|
||||
- Container management functions
|
||||
|
||||
## Validation & Quality
|
||||
|
||||
### Automated Validation
|
||||
|
||||
The Go validation tool (`cmd/readmevalidation/`) checks:
|
||||
|
||||
- Repository structure integrity
|
||||
- Contributor README files
|
||||
- Module and template documentation
|
||||
- Frontmatter format compliance
|
||||
|
||||
### Versioning
|
||||
|
||||
Use semantic versioning for modules:
|
||||
|
||||
- **Patch** (1.2.3 → 1.2.4): Bug fixes
|
||||
- **Minor** (1.2.3 → 1.3.0): New features, adding inputs
|
||||
- **Major** (1.2.3 → 2.0.0): Breaking changes
|
||||
|
||||
## Dependencies & Tools
|
||||
|
||||
### Required Tools
|
||||
|
||||
- **Terraform** - Module development and testing
|
||||
- **Docker** - Container-based testing
|
||||
- **Bun** - JavaScript runtime for formatting/scripts
|
||||
- **Go 1.23+** - Validation tooling
|
||||
|
||||
### Development Dependencies
|
||||
|
||||
- Prettier with Terraform and shell plugins
|
||||
- TypeScript for test utilities
|
||||
- Various npm packages for documentation processing
|
||||
|
||||
## Workflow Notes
|
||||
|
||||
### Contributing Process
|
||||
|
||||
1. Create namespace (first-time contributors)
|
||||
2. Generate module/template files using scripts
|
||||
3. Implement functionality and tests
|
||||
4. Run formatting and validation
|
||||
5. Submit PR with appropriate template
|
||||
|
||||
### Testing Workflow
|
||||
|
||||
- All modules must pass `terraform test`
|
||||
- Use `bun run test` for comprehensive testing
|
||||
- Format code with `bun run fmt` before submission
|
||||
- Manual testing recommended for templates
|
||||
|
||||
### Namespace Management
|
||||
|
||||
- Each contributor gets unique namespace
|
||||
- Namespace avatar required (avatar.png/svg in .images/)
|
||||
- Namespace README with contributor info and frontmatter
|
||||
|
||||
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
claude_api_key = "xxxx-xxxxx-xxxx"
|
||||
@@ -45,7 +45,7 @@ This example shows how to configure the Claude Code module to run the agent behi
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
enable_boundary = true
|
||||
@@ -64,7 +64,7 @@ For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
enable_aibridge = true
|
||||
@@ -93,7 +93,7 @@ data "coder_task" "me" {}
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
claude_api_key = "xxxx-xxxxx-xxxx"
|
||||
@@ -114,7 +114,7 @@ This example shows additional configuration options for version pinning, custom
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
|
||||
@@ -139,30 +139,9 @@ module "claude-code" {
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
mcp_config_remote_path = [
|
||||
"https://gist.githubusercontent.com/35C4n0r/cd8dce70360e5d22a070ae21893caed4/raw/",
|
||||
"https://raw.githubusercontent.com/coder/coder/main/.mcp.json"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Remote URLs should return a JSON body in the following format:
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "mcpServers": {
|
||||
> "server-name": {
|
||||
> "command": "some-command",
|
||||
> "args": ["arg1", "arg2"]
|
||||
> }
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> The `Content-Type` header doesn't matter—both `text/plain` and `application/json` work fine.
|
||||
|
||||
### Standalone Mode
|
||||
|
||||
Run and configure Claude Code as a standalone CLI in your workspace.
|
||||
@@ -170,7 +149,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
|
||||
```tf
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
install_claude_code = true
|
||||
@@ -192,7 +171,7 @@ variable "claude_code_oauth_token" {
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
claude_code_oauth_token = var.claude_code_oauth_token
|
||||
@@ -265,7 +244,7 @@ resource "coder_env" "bedrock_api_key" {
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
@@ -322,7 +301,7 @@ resource "coder_env" "google_application_credentials" {
|
||||
|
||||
module "claude-code" {
|
||||
source = "registry.coder.com/coder/claude-code/coder"
|
||||
version = "4.6.0"
|
||||
version = "4.5.0"
|
||||
agent_id = coder_agent.main.id
|
||||
workdir = "/home/coder/project"
|
||||
model = "claude-sonnet-4@20250514"
|
||||
|
||||
@@ -461,54 +461,4 @@ EOF`,
|
||||
expect(startLog.stdout).toContain(taskSessionId);
|
||||
expect(startLog.stdout).not.toContain("manual-456");
|
||||
});
|
||||
|
||||
test("mcp-config-remote-path", async () => {
|
||||
const failingUrl = "http://localhost:19999/mcp.json";
|
||||
const successUrl =
|
||||
"https://raw.githubusercontent.com/coder/coder/main/.mcp.json";
|
||||
|
||||
const { id, coderEnvVars } = await setup({
|
||||
skipClaudeMock: true,
|
||||
moduleVariables: {
|
||||
mcp_config_remote_path: JSON.stringify([failingUrl, successUrl]),
|
||||
},
|
||||
});
|
||||
await execModuleScript(id, coderEnvVars);
|
||||
|
||||
const installLog = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.claude-module/install.log",
|
||||
);
|
||||
|
||||
// Verify both URLs are attempted
|
||||
expect(installLog).toContain(failingUrl);
|
||||
expect(installLog).toContain(successUrl);
|
||||
|
||||
// First URL should fail gracefully
|
||||
expect(installLog).toContain(
|
||||
`Warning: Failed to fetch MCP configuration from '${failingUrl}'`,
|
||||
);
|
||||
|
||||
// Second URL should succeed - no failure warning for it
|
||||
expect(installLog).not.toContain(
|
||||
`Warning: Failed to fetch MCP configuration from '${successUrl}'`,
|
||||
);
|
||||
|
||||
// Should contain the MCP server add command from successful fetch
|
||||
expect(installLog).toContain(
|
||||
"Added stdio MCP server go-language-server to local config",
|
||||
);
|
||||
|
||||
expect(installLog).toContain(
|
||||
"Added stdio MCP server typescript-language-server to local config",
|
||||
);
|
||||
|
||||
// Verify the MCP config was added to claude.json
|
||||
const claudeConfig = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.claude.json",
|
||||
);
|
||||
expect(claudeConfig).toContain("typescript-language-server");
|
||||
expect(claudeConfig).toContain("go-language-server");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -166,12 +166,6 @@ variable "mcp" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "mcp_config_remote_path" {
|
||||
type = list(string)
|
||||
description = "List of URLs that return JSON MCP server configurations (text/plain with valid JSON)"
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "allowed_tools" {
|
||||
type = string
|
||||
description = "A list of tools that should be allowed without prompting the user for permission, in addition to settings.json files."
|
||||
@@ -250,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
|
||||
@@ -350,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
|
||||
@@ -372,50 +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_MCP_CONFIG_REMOTE_PATH='${base64encode(jsonencode(var.mcp_config_remote_path))}' \
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ ARG_INSTALL_VIA_NPM=${ARG_INSTALL_VIA_NPM:-false}
|
||||
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
|
||||
ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
|
||||
ARG_MCP=$(echo -n "${ARG_MCP:-}" | base64 -d)
|
||||
ARG_MCP_CONFIG_REMOTE_PATH=$(echo -n "${ARG_MCP_CONFIG_REMOTE_PATH:-}" | base64 -d)
|
||||
ARG_ALLOWED_TOOLS=${ARG_ALLOWED_TOOLS:-}
|
||||
ARG_DISALLOWED_TOOLS=${ARG_DISALLOWED_TOOLS:-}
|
||||
ARG_ENABLE_AIBRIDGE=${ARG_ENABLE_AIBRIDGE:-false}
|
||||
@@ -31,26 +30,12 @@ printf "ARG_INSTALL_VIA_NPM: %s\n" "$ARG_INSTALL_VIA_NPM"
|
||||
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
|
||||
printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG"
|
||||
printf "ARG_MCP: %s\n" "$ARG_MCP"
|
||||
printf "ARG_MCP_CONFIG_REMOTE_PATH: %s\n" "$ARG_MCP_CONFIG_REMOTE_PATH"
|
||||
printf "ARG_ALLOWED_TOOLS: %s\n" "$ARG_ALLOWED_TOOLS"
|
||||
printf "ARG_DISALLOWED_TOOLS: %s\n" "$ARG_DISALLOWED_TOOLS"
|
||||
printf "ARG_ENABLE_AIBRIDGE: %s\n" "$ARG_ENABLE_AIBRIDGE"
|
||||
|
||||
echo "--------------------------------"
|
||||
|
||||
function add_mcp_servers() {
|
||||
local mcp_json="$1"
|
||||
local source_desc="$2"
|
||||
|
||||
while IFS= read -r server_name && IFS= read -r server_json; do
|
||||
echo "------------------------"
|
||||
echo "Executing: claude mcp add-json \"$server_name\" '$server_json' ($source_desc)"
|
||||
claude mcp add-json "$server_name" "$server_json" || echo "Warning: Failed to add MCP server '$server_name', continuing..."
|
||||
echo "------------------------"
|
||||
echo ""
|
||||
done < <(echo "$mcp_json" | jq -r '.mcpServers | to_entries[] | .key, (.value | @json)')
|
||||
}
|
||||
|
||||
function ensure_claude_in_path() {
|
||||
if [ -z "${CODER_SCRIPT_BIN_DIR:-}" ]; then
|
||||
echo "CODER_SCRIPT_BIN_DIR not set, skipping PATH setup"
|
||||
@@ -127,25 +112,13 @@ function setup_claude_configurations() {
|
||||
if [ "$ARG_MCP" != "" ]; then
|
||||
(
|
||||
cd "$ARG_WORKDIR"
|
||||
add_mcp_servers "$ARG_MCP" "in $ARG_WORKDIR"
|
||||
)
|
||||
fi
|
||||
|
||||
if [ -n "$ARG_MCP_CONFIG_REMOTE_PATH" ] && [ "$ARG_MCP_CONFIG_REMOTE_PATH" != "[]" ]; then
|
||||
(
|
||||
cd "$ARG_WORKDIR"
|
||||
for url in $(echo "$ARG_MCP_CONFIG_REMOTE_PATH" | jq -r '.[]'); do
|
||||
echo "Fetching MCP configuration from: $url"
|
||||
mcp_json=$(curl -fsSL "$url") || {
|
||||
echo "Warning: Failed to fetch MCP configuration from '$url', continuing..."
|
||||
continue
|
||||
}
|
||||
if ! echo "$mcp_json" | jq -e '.mcpServers' > /dev/null 2>&1; then
|
||||
echo "Warning: Invalid MCP configuration from '$url' (missing mcpServers), continuing..."
|
||||
continue
|
||||
fi
|
||||
add_mcp_servers "$mcp_json" "from $url"
|
||||
done
|
||||
while IFS= read -r server_name && IFS= read -r server_json; do
|
||||
echo "------------------------"
|
||||
echo "Executing: claude mcp add-json \"$server_name\" '$server_json' (in $ARG_WORKDIR)"
|
||||
claude mcp add-json "$server_name" "$server_json" || echo "Warning: Failed to add MCP server '$server_name', continuing..."
|
||||
echo "------------------------"
|
||||
echo ""
|
||||
done < <(echo "$ARG_MCP" | jq -r '.mcpServers | to_entries[] | .key, (.value | @json)')
|
||||
)
|
||||
fi
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user