mirror of
https://github.com/coder/registry.git
synced 2026-06-03 13:08:14 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b84d916e1 |
@@ -0,0 +1 @@
|
||||
<svg width='240' height='300' viewBox='0 0 240 300' fill='none' xmlns='http://www.w3.org/2000/svg'><g clip-path='url(#clip0_1401_86283)'><mask id='mask0_1401_86283' style='mask-type:luminance' maskUnits='userSpaceOnUse' x='0' y='0' width='240' height='300'><path d='M240 0H0V300H240V0Z' fill='white'/></mask><g mask='url(#mask0_1401_86283)'><path d='M180 240H60V120H180V240Z' fill='#4B4646'/><path d='M180 60H60V240H180V60ZM240 300H0V0H240V300Z' fill='#F1ECEC'/></g></g><defs><clipPath id='clip0_1401_86283'><rect width='240' height='300' fill='white'/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 577 B |
@@ -0,0 +1,108 @@
|
||||
---
|
||||
display_name: OpenCode
|
||||
icon: ../../../../.icons/opencode.svg
|
||||
description: Run OpenCode AI coding assistant for AI-powered terminal assistance
|
||||
verified: false
|
||||
tags: [agent, opencode, ai, tasks]
|
||||
---
|
||||
|
||||
# OpenCode
|
||||
|
||||
Run [OpenCode](https://opencode.ai) AI coding assistant in your workspace for intelligent code generation, analysis, and development assistance. This module integrates with [AgentAPI](https://github.com/coder/agentapi) for seamless task reporting in the Coder UI.
|
||||
|
||||
```tf
|
||||
module "opencode" {
|
||||
source = "registry.coder.com/coder-labs/opencode/coder"
|
||||
version = "0.1.0"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/project"
|
||||
}
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Authentication credentials** - OpenCode auth.json file is required for non-interactive authentication, you can find this file on your system: `$HOME/.local/share/opencode/auth.json`
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Usage with Tasks
|
||||
|
||||
```tf
|
||||
resource "coder_ai_task" "task" {
|
||||
app_id = module.opencode.task_app_id
|
||||
}
|
||||
|
||||
module "opencode" {
|
||||
source = "registry.coder.com/coder-labs/opencode/coder"
|
||||
version = "0.1.0"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder/project"
|
||||
|
||||
ai_prompt = coder_ai_task.task.prompt
|
||||
|
||||
auth_json = <<-EOT
|
||||
{
|
||||
"google": {
|
||||
"type": "api",
|
||||
"key": "gem-xxx-xxxx"
|
||||
},
|
||||
"anthropic": {
|
||||
"type": "api",
|
||||
"key": "sk-ant-api03-xxx-xxxxxxx"
|
||||
}
|
||||
}
|
||||
EOT
|
||||
|
||||
config_json = jsonencode({
|
||||
"$schema" = "https://opencode.ai/config.json"
|
||||
mcp = {
|
||||
filesystem = {
|
||||
command = ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/home/coder/projects"]
|
||||
enabled = true
|
||||
type = "local"
|
||||
environment = {
|
||||
SOME_VARIABLE_X = "value"
|
||||
}
|
||||
}
|
||||
playwright = {
|
||||
command = ["npx", "-y", "@playwright/mcp@latest", "--headless", "--isolated"]
|
||||
enabled = true
|
||||
type = "local"
|
||||
}
|
||||
}
|
||||
model = "anthropic/claude-sonnet-4-20250514"
|
||||
})
|
||||
|
||||
pre_install_script = <<-EOT
|
||||
#!/bin/bash
|
||||
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
EOT
|
||||
}
|
||||
```
|
||||
|
||||
### Standalone CLI Mode
|
||||
|
||||
Run OpenCode as a command-line tool without web interface or task reporting:
|
||||
|
||||
```tf
|
||||
module "opencode" {
|
||||
source = "registry.coder.com/coder-labs/opencode/coder"
|
||||
version = "0.1.0"
|
||||
agent_id = coder_agent.example.id
|
||||
workdir = "/home/coder"
|
||||
report_tasks = false
|
||||
cli_app = true
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter any issues, check the log files in the `~/.opencode-module` directory within your workspace for detailed information.
|
||||
|
||||
## References
|
||||
|
||||
- [Opencode JSON Config](https://opencode.ai/docs/config/)
|
||||
- [OpenCode Documentation](https://opencode.ai/docs)
|
||||
- [AgentAPI Documentation](https://github.com/coder/agentapi)
|
||||
- [Coder AI Agents Guide](https://coder.com/docs/tutorials/ai-agents)
|
||||
@@ -0,0 +1,362 @@
|
||||
import {
|
||||
test,
|
||||
afterEach,
|
||||
describe,
|
||||
setDefaultTimeout,
|
||||
beforeAll,
|
||||
expect,
|
||||
} from "bun:test";
|
||||
import { execContainer, readFileContainer, runTerraformInit } from "~test";
|
||||
import {
|
||||
loadTestFile,
|
||||
writeExecutable,
|
||||
setup as setupUtil,
|
||||
execModuleScript,
|
||||
expectAgentAPIStarted,
|
||||
} from "../../../coder/modules/agentapi/test-util";
|
||||
import dedent from "dedent";
|
||||
|
||||
let cleanupFunctions: (() => Promise<void>)[] = [];
|
||||
const registerCleanup = (cleanup: () => Promise<void>) => {
|
||||
cleanupFunctions.push(cleanup);
|
||||
};
|
||||
afterEach(async () => {
|
||||
const cleanupFnsCopy = cleanupFunctions.slice().reverse();
|
||||
cleanupFunctions = [];
|
||||
for (const cleanup of cleanupFnsCopy) {
|
||||
try {
|
||||
await cleanup();
|
||||
} catch (error) {
|
||||
console.error("Error during cleanup:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
interface SetupProps {
|
||||
skipAgentAPIMock?: boolean;
|
||||
skipOpencodeMock?: boolean;
|
||||
moduleVariables?: Record<string, string>;
|
||||
agentapiMockScript?: string;
|
||||
}
|
||||
|
||||
const setup = async (props?: SetupProps): Promise<{ id: string }> => {
|
||||
const projectDir = "/home/coder/project";
|
||||
const { id } = await setupUtil({
|
||||
moduleDir: import.meta.dir,
|
||||
moduleVariables: {
|
||||
install_opencode: props?.skipOpencodeMock ? "true" : "false",
|
||||
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
||||
workdir: projectDir,
|
||||
...props?.moduleVariables,
|
||||
},
|
||||
registerCleanup,
|
||||
projectDir,
|
||||
skipAgentAPIMock: props?.skipAgentAPIMock,
|
||||
agentapiMockScript: props?.agentapiMockScript,
|
||||
});
|
||||
if (!props?.skipOpencodeMock) {
|
||||
await writeExecutable({
|
||||
containerId: id,
|
||||
filePath: "/usr/bin/opencode",
|
||||
content: await loadTestFile(import.meta.dir, "opencode-mock.sh"),
|
||||
});
|
||||
}
|
||||
return { id };
|
||||
};
|
||||
|
||||
setDefaultTimeout(60 * 1000);
|
||||
|
||||
describe("opencode", async () => {
|
||||
beforeAll(async () => {
|
||||
await runTerraformInit(import.meta.dir);
|
||||
});
|
||||
|
||||
test("happy-path", async () => {
|
||||
const { id } = await setup();
|
||||
await execModuleScript(id);
|
||||
await expectAgentAPIStarted(id);
|
||||
});
|
||||
|
||||
test("install-opencode-version", async () => {
|
||||
const version_to_install = "0.1.0";
|
||||
const { id } = await setup({
|
||||
skipOpencodeMock: true,
|
||||
moduleVariables: {
|
||||
install_opencode: "true",
|
||||
opencode_version: version_to_install,
|
||||
pre_install_script: dedent`
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Mock the opencode install for testing
|
||||
mkdir -p /home/coder/.opencode/bin
|
||||
echo '#!/bin/bash\necho "opencode mock version ${version_to_install}"' > /home/coder/.opencode/bin/opencode
|
||||
chmod +x /home/coder/.opencode/bin/opencode
|
||||
`,
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
const resp = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
`cat /home/coder/.opencode-module/install.log`,
|
||||
]);
|
||||
expect(resp.stdout).toContain(version_to_install);
|
||||
});
|
||||
|
||||
test("check-latest-opencode-version-works", async () => {
|
||||
const { id } = await setup({
|
||||
skipOpencodeMock: true,
|
||||
skipAgentAPIMock: true,
|
||||
moduleVariables: {
|
||||
install_opencode: "true",
|
||||
pre_install_script: dedent`
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Mock the opencode install for testing
|
||||
mkdir -p /home/coder/.opencode/bin
|
||||
echo '#!/bin/bash\necho "opencode mock latest version"' > /home/coder/.opencode/bin/opencode
|
||||
chmod +x /home/coder/.opencode/bin/opencode
|
||||
`,
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
await expectAgentAPIStarted(id);
|
||||
});
|
||||
|
||||
test("opencode-auth-json", async () => {
|
||||
const authJson = JSON.stringify({
|
||||
token: "test-auth-token-123",
|
||||
user: "test-user",
|
||||
});
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
auth_json: authJson,
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const authFile = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.local/share/opencode/auth.json",
|
||||
);
|
||||
|
||||
expect(authFile).toContain("test-auth-token-123");
|
||||
expect(authFile).toContain("test-user");
|
||||
});
|
||||
|
||||
test("opencode-config-json", async () => {
|
||||
const configJson = JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
test: {
|
||||
command: ["test-cmd"],
|
||||
type: "local",
|
||||
},
|
||||
},
|
||||
model: "anthropic/claude-sonnet-4-20250514",
|
||||
});
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
config_json: configJson,
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const configFile = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.config/opencode/opencode.json",
|
||||
);
|
||||
expect(configFile).toContain("test-cmd");
|
||||
expect(configFile).toContain("anthropic/claude-sonnet-4-20250514");
|
||||
});
|
||||
|
||||
test("opencode-ai-prompt", async () => {
|
||||
const prompt = "This is a task prompt for OpenCode.";
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
ai_prompt: prompt,
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const resp = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
`cat /home/coder/.opencode-module/agentapi-start.log`,
|
||||
]);
|
||||
expect(resp.stdout).toContain(prompt);
|
||||
});
|
||||
|
||||
test("opencode-continue-flag", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
continue: "true",
|
||||
ai_prompt: "test prompt",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const startLog = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /home/coder/.opencode-module/agentapi-start.log",
|
||||
]);
|
||||
expect(startLog.stdout).toContain("--continue");
|
||||
});
|
||||
|
||||
test("opencode-continue-with-session-id", async () => {
|
||||
const sessionId = "session-123";
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
continue: "true",
|
||||
session_id: sessionId,
|
||||
ai_prompt: "test prompt",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const startLog = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /home/coder/.opencode-module/agentapi-start.log",
|
||||
]);
|
||||
expect(startLog.stdout).toContain("--continue");
|
||||
expect(startLog.stdout).toContain(`--session ${sessionId}`);
|
||||
});
|
||||
|
||||
test("opencode-session-id", async () => {
|
||||
const sessionId = "session-123";
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
session_id: sessionId,
|
||||
ai_prompt: "test prompt",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const startLog = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /home/coder/.opencode-module/agentapi-start.log",
|
||||
]);
|
||||
expect(startLog.stdout).toContain(`--session ${sessionId}`);
|
||||
});
|
||||
|
||||
test("opencode-report-tasks-enabled", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
report_tasks: "true",
|
||||
ai_prompt: "test prompt",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const startLog = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /home/coder/.opencode-module/agentapi-start.log",
|
||||
]);
|
||||
expect(startLog.stdout).toContain(
|
||||
"report your progress using coder_report_task",
|
||||
);
|
||||
});
|
||||
|
||||
test("opencode-report-tasks-disabled", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
report_tasks: "false",
|
||||
ai_prompt: "test prompt",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const startLog = await execContainer(id, [
|
||||
"bash",
|
||||
"-c",
|
||||
"cat /home/coder/.opencode-module/agentapi-start.log",
|
||||
]);
|
||||
expect(startLog.stdout).not.toContain(
|
||||
"report your progress using coder_report_task",
|
||||
);
|
||||
});
|
||||
|
||||
test("cli-app-creation", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
cli_app: "true",
|
||||
cli_app_display_name: "OpenCode Terminal",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
// CLI app creation is handled by the agentapi module
|
||||
// We just verify the setup completed successfully
|
||||
await expectAgentAPIStarted(id);
|
||||
});
|
||||
|
||||
test("pre-post-install-scripts", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
pre_install_script: "#!/bin/bash\necho 'opencode-pre-install-script'",
|
||||
post_install_script: "#!/bin/bash\necho 'opencode-post-install-script'",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const preInstallLog = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.opencode-module/pre_install.log",
|
||||
);
|
||||
expect(preInstallLog).toContain("opencode-pre-install-script");
|
||||
|
||||
const postInstallLog = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.opencode-module/post_install.log",
|
||||
);
|
||||
expect(postInstallLog).toContain("opencode-post-install-script");
|
||||
});
|
||||
|
||||
test("workdir-variable", async () => {
|
||||
const workdir = "/home/coder/opencode-test-folder";
|
||||
const { id } = await setup({
|
||||
skipOpencodeMock: false,
|
||||
moduleVariables: {
|
||||
workdir,
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
|
||||
const resp = await readFileContainer(
|
||||
id,
|
||||
"/home/coder/.opencode-module/agentapi-start.log",
|
||||
);
|
||||
expect(resp).toContain(workdir);
|
||||
});
|
||||
|
||||
test("subdomain-enabled", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
subdomain: "true",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
// Subdomain configuration is handled by the agentapi module
|
||||
// We just verify the setup completed successfully
|
||||
await expectAgentAPIStarted(id);
|
||||
});
|
||||
|
||||
test("custom-display-names", async () => {
|
||||
const { id } = await setup({
|
||||
moduleVariables: {
|
||||
web_app_display_name: "Custom OpenCode Web",
|
||||
cli_app_display_name: "Custom OpenCode CLI",
|
||||
cli_app: "true",
|
||||
},
|
||||
});
|
||||
await execModuleScript(id);
|
||||
// Display names are handled by the agentapi module
|
||||
// We just verify the setup completed successfully
|
||||
await expectAgentAPIStarted(id);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,203 @@
|
||||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
|
||||
required_providers {
|
||||
coder = {
|
||||
source = "coder/coder"
|
||||
version = ">= 2.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "agent_id" {
|
||||
type = string
|
||||
description = "The ID of a Coder agent."
|
||||
}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
variable "order" {
|
||||
type = number
|
||||
description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "group" {
|
||||
type = string
|
||||
description = "The name of a group that this app belongs to."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "icon" {
|
||||
type = string
|
||||
description = "The icon to use for the app."
|
||||
default = "/icon/opencode.svg"
|
||||
}
|
||||
|
||||
variable "workdir" {
|
||||
type = string
|
||||
description = "The folder to run OpenCode in."
|
||||
}
|
||||
|
||||
variable "report_tasks" {
|
||||
type = bool
|
||||
description = "Whether to enable task reporting to Coder UI via AgentAPI"
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "cli_app" {
|
||||
type = bool
|
||||
description = "Whether to create a CLI app for OpenCode"
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "web_app_display_name" {
|
||||
type = string
|
||||
description = "Display name for the web app"
|
||||
default = "OpenCode"
|
||||
}
|
||||
|
||||
variable "cli_app_display_name" {
|
||||
type = string
|
||||
description = "Display name for the CLI app"
|
||||
default = "OpenCode CLI"
|
||||
}
|
||||
|
||||
variable "pre_install_script" {
|
||||
type = string
|
||||
description = "Custom script to run before installing OpenCode."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "post_install_script" {
|
||||
type = string
|
||||
description = "Custom script to run after installing OpenCode."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "install_agentapi" {
|
||||
type = bool
|
||||
description = "Whether to install AgentAPI."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "agentapi_version" {
|
||||
type = string
|
||||
description = "The version of AgentAPI to install."
|
||||
default = "v0.11.2"
|
||||
}
|
||||
|
||||
variable "ai_prompt" {
|
||||
type = string
|
||||
description = "Initial task prompt for OpenCode."
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "subdomain" {
|
||||
type = bool
|
||||
description = "Whether to use a subdomain for AgentAPI."
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "install_opencode" {
|
||||
type = bool
|
||||
description = "Whether to install OpenCode."
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "opencode_version" {
|
||||
type = string
|
||||
description = "The version of OpenCode to install."
|
||||
default = "latest"
|
||||
}
|
||||
|
||||
variable "continue" {
|
||||
type = bool
|
||||
description = "continue the last session. Uses the --continue flag"
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "session_id" {
|
||||
type = string
|
||||
description = "Session id to continue. Passed via --session"
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "auth_json" {
|
||||
type = string
|
||||
description = "Your auth.json from $HOME/.local/share/opencode/auth.json, Required for non-interactive authentication"
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "config_json" {
|
||||
type = string
|
||||
description = "OpenCode JSON config. https://opencode.ai/docs/config/"
|
||||
default = ""
|
||||
}
|
||||
|
||||
locals {
|
||||
workdir = trimsuffix(var.workdir, "/")
|
||||
app_slug = "opencode"
|
||||
install_script = file("${path.module}/scripts/install.sh")
|
||||
start_script = file("${path.module}/scripts/start.sh")
|
||||
module_dir_name = ".opencode-module"
|
||||
}
|
||||
|
||||
module "agentapi" {
|
||||
source = "registry.coder.com/coder/agentapi/coder"
|
||||
version = "2.0.0"
|
||||
|
||||
agent_id = var.agent_id
|
||||
web_app_slug = local.app_slug
|
||||
web_app_order = var.order
|
||||
web_app_group = var.group
|
||||
web_app_icon = var.icon
|
||||
web_app_display_name = var.web_app_display_name
|
||||
cli_app = var.cli_app
|
||||
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
|
||||
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
|
||||
agentapi_subdomain = var.subdomain
|
||||
folder = local.workdir
|
||||
module_dir_name = local.module_dir_name
|
||||
install_agentapi = var.install_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_WORKDIR='${local.workdir}' \
|
||||
ARG_AI_PROMPT='${base64encode(var.ai_prompt)}' \
|
||||
ARG_SESSION_ID='${var.session_id}' \
|
||||
ARG_REPORT_TASKS='${var.report_tasks}' \
|
||||
ARG_CONTINUE='${var.continue}' \
|
||||
/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_OPENCODE_VERSION='${var.opencode_version}' \
|
||||
ARG_MCP_APP_STATUS_SLUG='${local.app_slug}' \
|
||||
ARG_INSTALL_OPENCODE='${var.install_opencode}' \
|
||||
ARG_REPORT_TASKS='${var.report_tasks}' \
|
||||
ARG_WORKDIR='${local.workdir}' \
|
||||
ARG_AUTH_JSON='${var.auth_json != null ? base64encode(replace(var.auth_json, "'", "'\\''")) : ""}' \
|
||||
ARG_OPENCODE_CONFIG='${var.config_json != null ? base64encode(replace(var.config_json, "'", "'\\''")) : ""}' \
|
||||
/tmp/install.sh
|
||||
EOT
|
||||
}
|
||||
|
||||
output "task_app_id" {
|
||||
value = module.agentapi.task_app_id
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
run "defaults_are_correct" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.install_opencode == true
|
||||
error_message = "OpenCode installation should be enabled by default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.install_agentapi == true
|
||||
error_message = "AgentAPI installation should be enabled by default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.agentapi_version == "v0.11.2"
|
||||
error_message = "Default AgentAPI version should be 'v0.11.2'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.opencode_version == "latest"
|
||||
error_message = "Default OpenCode version should be 'latest'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.report_tasks == true
|
||||
error_message = "Task reporting should be enabled by default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.cli_app == false
|
||||
error_message = "CLI app should be disabled by default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.subdomain == false
|
||||
error_message = "Subdomain should be disabled by default"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.web_app_display_name == "OpenCode"
|
||||
error_message = "Default web app display name should be 'OpenCode'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.cli_app_display_name == "OpenCode CLI"
|
||||
error_message = "Default CLI app display name should be 'OpenCode CLI'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = local.app_slug == "opencode"
|
||||
error_message = "App slug should be 'opencode'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = local.module_dir_name == ".opencode-module"
|
||||
error_message = "Module dir name should be '.opencode-module'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = local.workdir == "/home/coder/project"
|
||||
error_message = "Workdir should be trimmed of trailing slash"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.continue == false
|
||||
error_message = "Continue flag should be disabled by default"
|
||||
}
|
||||
}
|
||||
|
||||
run "workdir_trailing_slash_trimmed" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project/"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = local.workdir == "/home/coder/project"
|
||||
error_message = "Workdir should be trimmed of trailing slash"
|
||||
}
|
||||
}
|
||||
|
||||
run "opencode_version_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
opencode_version = "v1.0.0"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.opencode_version == "v1.0.0"
|
||||
error_message = "OpenCode version should be set correctly"
|
||||
}
|
||||
}
|
||||
|
||||
run "agentapi_version_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
agentapi_version = "v0.9.0"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.agentapi_version == "v0.9.0"
|
||||
error_message = "AgentAPI version should be set correctly"
|
||||
}
|
||||
}
|
||||
|
||||
run "cli_app_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
cli_app = true
|
||||
cli_app_display_name = "Custom OpenCode CLI"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.cli_app == true
|
||||
error_message = "CLI app should be enabled when specified"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.cli_app_display_name == "Custom OpenCode CLI"
|
||||
error_message = "Custom CLI app display name should be set"
|
||||
}
|
||||
}
|
||||
|
||||
run "web_app_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
web_app_display_name = "Custom OpenCode Web"
|
||||
order = 5
|
||||
group = "AI Tools"
|
||||
icon = "/custom/icon.svg"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.web_app_display_name == "Custom OpenCode Web"
|
||||
error_message = "Custom web app display name should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.order == 5
|
||||
error_message = "Custom order should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.group == "AI Tools"
|
||||
error_message = "Custom group should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.icon == "/custom/icon.svg"
|
||||
error_message = "Custom icon should be set"
|
||||
}
|
||||
}
|
||||
|
||||
run "ai_configuration_variables" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
ai_prompt = "This is a test prompt"
|
||||
session_id = "session-123"
|
||||
continue = true
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.ai_prompt == "This is a test prompt"
|
||||
error_message = "AI prompt should be set correctly"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.session_id == "session-123"
|
||||
error_message = "Session ID should be set correctly"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.continue == true
|
||||
error_message = "Continue flag should be set correctly"
|
||||
}
|
||||
}
|
||||
|
||||
run "auth_json_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
auth_json = "{\"token\": \"test-token\", \"user\": \"test-user\"}"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.auth_json != ""
|
||||
error_message = "Auth JSON should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(jsondecode(var.auth_json))
|
||||
error_message = "Auth JSON should be valid JSON"
|
||||
}
|
||||
}
|
||||
|
||||
run "config_json_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
config_json = "{\"$schema\": \"https://opencode.ai/config.json\", \"mcp\": {\"test\": {\"command\": [\"test-cmd\"], \"type\": \"local\"}}, \"model\": \"anthropic/claude-sonnet-4-20250514\"}"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.config_json != ""
|
||||
error_message = "OpenCode JSON configuration should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(jsondecode(var.config_json))
|
||||
error_message = "OpenCode JSON configuration should be valid JSON"
|
||||
}
|
||||
}
|
||||
|
||||
run "task_reporting_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
report_tasks = false
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.report_tasks == false
|
||||
error_message = "Task reporting should be disabled when specified"
|
||||
}
|
||||
}
|
||||
|
||||
run "subdomain_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
subdomain = true
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.subdomain == true
|
||||
error_message = "Subdomain should be enabled when specified"
|
||||
}
|
||||
}
|
||||
|
||||
run "install_flags_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
install_opencode = false
|
||||
install_agentapi = false
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.install_opencode == false
|
||||
error_message = "OpenCode installation should be disabled when specified"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.install_agentapi == false
|
||||
error_message = "AgentAPI installation should be disabled when specified"
|
||||
}
|
||||
}
|
||||
|
||||
run "custom_scripts_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
pre_install_script = "#!/bin/bash\necho 'pre-install'"
|
||||
post_install_script = "#!/bin/bash\necho 'post-install'"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.pre_install_script != null
|
||||
error_message = "Pre-install script should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.post_install_script != null
|
||||
error_message = "Post-install script should be set"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("pre-install", var.pre_install_script))
|
||||
error_message = "Pre-install script should contain expected content"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = can(regex("post-install", var.post_install_script))
|
||||
error_message = "Post-install script should contain expected content"
|
||||
}
|
||||
}
|
||||
|
||||
run "empty_variables_handled_correctly" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
ai_prompt = ""
|
||||
session_id = ""
|
||||
auth_json = ""
|
||||
config_json = ""
|
||||
continue = false
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.ai_prompt == ""
|
||||
error_message = "Empty AI prompt should be handled correctly"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.session_id == ""
|
||||
error_message = "Empty session ID should be handled correctly"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.auth_json == ""
|
||||
error_message = "Empty auth JSON should be handled correctly"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.config_json == ""
|
||||
error_message = "Empty config JSON should be handled correctly"
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.continue == false
|
||||
error_message = "Continue flag default should be handled correctly"
|
||||
}
|
||||
}
|
||||
|
||||
run "continue_flag_configuration" {
|
||||
command = plan
|
||||
|
||||
variables {
|
||||
agent_id = "test-agent"
|
||||
workdir = "/home/coder/project"
|
||||
continue = true
|
||||
}
|
||||
|
||||
assert {
|
||||
condition = var.continue == true
|
||||
error_message = "Continue flag should be enabled when specified"
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
|
||||
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
|
||||
ARG_MCP_APP_STATUS_SLUG=${ARG_MCP_APP_STATUS_SLUG:-}
|
||||
ARG_OPENCODE_VERSION=${ARG_OPENCODE_VERSION:-latest}
|
||||
ARG_INSTALL_OPENCODE=${ARG_INSTALL_OPENCODE:-true}
|
||||
ARG_AUTH_JSON=$(echo -n "$ARG_AUTH_JSON" | base64 -d 2> /dev/null || echo "")
|
||||
ARG_OPENCODE_CONFIG=$(echo -n "$ARG_OPENCODE_CONFIG" | base64 -d 2> /dev/null || echo "")
|
||||
|
||||
# Print all received environment variables
|
||||
printf "=== INSTALL CONFIG ===\n"
|
||||
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
|
||||
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
|
||||
printf "ARG_MCP_APP_STATUS_SLUG: %s\n" "$ARG_MCP_APP_STATUS_SLUG"
|
||||
printf "ARG_OPENCODE_VERSION: %s\n" "$ARG_OPENCODE_VERSION"
|
||||
printf "ARG_INSTALL_OPENCODE: %s\n" "$ARG_INSTALL_OPENCODE"
|
||||
if [ -n "$ARG_AUTH_JSON" ]; then
|
||||
printf "ARG_AUTH_JSON: [AUTH DATA RECEIVED]\n"
|
||||
else
|
||||
printf "ARG_AUTH_JSON: [NOT PROVIDED]\n"
|
||||
fi
|
||||
if [ -n "$ARG_OPENCODE_CONFIG" ]; then
|
||||
printf "ARG_OPENCODE_CONFIG: [RECEIVED]\n"
|
||||
else
|
||||
printf "ARG_OPENCODE_CONFIG: [NOT PROVIDED]\n"
|
||||
fi
|
||||
printf "==================================\n"
|
||||
|
||||
install_opencode() {
|
||||
if [ "$ARG_INSTALL_OPENCODE" = "true" ]; then
|
||||
if ! command_exists opencode; then
|
||||
echo "Installing OpenCode (version: ${ARG_OPENCODE_VERSION})..."
|
||||
if [ "$ARG_OPENCODE_VERSION" = "latest" ]; then
|
||||
curl -fsSL https://opencode.ai/install | bash
|
||||
else
|
||||
VERSION=$ARG_OPENCODE_VERSION curl -fsSL https://opencode.ai/install | bash
|
||||
fi
|
||||
export PATH=/home/coder/.opencode/bin:$PATH
|
||||
printf "Opencode location: %s\n" "$(which opencode)"
|
||||
if ! command_exists opencode; then
|
||||
echo "ERROR: Failed to install OpenCode"
|
||||
exit 1
|
||||
fi
|
||||
echo "OpenCode installed successfully"
|
||||
else
|
||||
echo "OpenCode already installed"
|
||||
fi
|
||||
else
|
||||
echo "OpenCode installation skipped (ARG_INSTALL_OPENCODE=false)"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_opencode_config() {
|
||||
local opencode_config_file="$HOME/.config/opencode/opencode.json"
|
||||
local auth_json_file="$HOME/.local/share/opencode/auth.json"
|
||||
|
||||
mkdir -p "$(dirname "$auth_json_file")"
|
||||
mkdir -p "$(dirname "$opencode_config_file")"
|
||||
|
||||
setup_opencode_auth "$auth_json_file"
|
||||
|
||||
if [ -n "$ARG_OPENCODE_CONFIG" ]; then
|
||||
echo "Writing to the config file"
|
||||
echo "$ARG_OPENCODE_CONFIG" > "$opencode_config_file"
|
||||
fi
|
||||
|
||||
if [ "$ARG_REPORT_TASKS" = "true" ]; then
|
||||
setup_coder_mcp_server "$opencode_config_file"
|
||||
fi
|
||||
|
||||
echo "MCP configuration completed: $opencode_config_file"
|
||||
}
|
||||
|
||||
setup_opencode_auth() {
|
||||
local auth_json_file="$1"
|
||||
|
||||
if [ -n "$ARG_AUTH_JSON" ]; then
|
||||
echo "$ARG_AUTH_JSON" > "$auth_json_file"
|
||||
printf "added auth json to %s" "$auth_json_file"
|
||||
else
|
||||
printf "auth json not provided"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_coder_mcp_server() {
|
||||
local opencode_config_file="$1"
|
||||
|
||||
# Set environment variables based on task reporting setting
|
||||
echo "Configuring OpenCode task reporting"
|
||||
export CODER_MCP_APP_STATUS_SLUG="$ARG_MCP_APP_STATUS_SLUG"
|
||||
export CODER_MCP_AI_AGENTAPI_URL="http://localhost:3284"
|
||||
echo "Coder integration configured for task reporting"
|
||||
|
||||
# Add coder MCP server configuration to the JSON file
|
||||
echo "Adding Coder MCP server configuration"
|
||||
|
||||
# Create the coder server configuration JSON
|
||||
coder_config=$(
|
||||
cat << EOF
|
||||
{
|
||||
"type": "local",
|
||||
"command": ["coder", "exp", "mcp", "server"],
|
||||
"enabled": true,
|
||||
"environment": {
|
||||
"CODER_MCP_APP_STATUS_SLUG": "${CODER_MCP_APP_STATUS_SLUG:-}",
|
||||
"CODER_MCP_AI_AGENTAPI_URL": "${CODER_MCP_AI_AGENTAPI_URL:-}",
|
||||
"CODER_AGENT_URL": "${CODER_AGENT_URL:-}",
|
||||
"CODER_AGENT_TOKEN": "${CODER_AGENT_TOKEN:-}",
|
||||
"CODER_MCP_ALLOWED_TOOLS": "coder_report_task"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
temp_file=$(mktemp)
|
||||
jq --argjson coder_config "$coder_config" '.mcp.coder = $coder_config' "$opencode_config_file" > "$temp_file"
|
||||
mv "$temp_file" "$opencode_config_file"
|
||||
echo "Coder MCP server configuration added"
|
||||
|
||||
}
|
||||
|
||||
install_opencode
|
||||
setup_opencode_config
|
||||
|
||||
echo "OpenCode module setup completed."
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
export PATH=/home/coder/.opencode/bin:$PATH
|
||||
|
||||
command_exists() {
|
||||
command -v "$1" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
ARG_WORKDIR=${ARG_WORKDIR:-"$HOME"}
|
||||
ARG_AI_PROMPT=$(echo -n "${ARG_AI_PROMPT:-}" | base64 -d 2> /dev/null || echo "")
|
||||
ARG_REPORT_TASKS=${ARG_REPORT_TASKS:-true}
|
||||
ARG_SESSION_ID=${ARG_SESSION_ID:-}
|
||||
ARG_CONTINUE=${ARG_CONTINUE:-false}
|
||||
|
||||
# Print all received environment variables
|
||||
printf "=== START CONFIG ===\n"
|
||||
printf "ARG_WORKDIR: %s\n" "$ARG_WORKDIR"
|
||||
printf "ARG_REPORT_TASKS: %s\n" "$ARG_REPORT_TASKS"
|
||||
printf "ARG_CONTINUE: %s\n" "$ARG_CONTINUE"
|
||||
printf "ARG_SESSION_ID: %s\n" "$ARG_SESSION_ID"
|
||||
if [ -n "$ARG_AI_PROMPT" ]; then
|
||||
printf "ARG_AI_PROMPT: [AI PROMPT RECEIVED]\n"
|
||||
else
|
||||
printf "ARG_AI_PROMPT: [NOT PROVIDED]\n"
|
||||
fi
|
||||
printf "==================================\n"
|
||||
|
||||
OPENCODE_ARGS=()
|
||||
AGENTAPI_ARGS=()
|
||||
|
||||
validate_opencode_installation() {
|
||||
if ! command_exists opencode; then
|
||||
printf "ERROR: OpenCode not installed. Set install_opencode to true\n"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
build_opencode_args() {
|
||||
|
||||
if [ -n "$ARG_SESSION_ID" ]; then
|
||||
OPENCODE_ARGS+=(--session "$ARG_SESSION_ID")
|
||||
fi
|
||||
|
||||
if [ "$ARG_CONTINUE" = "true" ]; then
|
||||
OPENCODE_ARGS+=(--continue)
|
||||
fi
|
||||
|
||||
if [ -n "$ARG_AI_PROMPT" ]; then
|
||||
if [ "$ARG_REPORT_TASKS" = "true" ]; then
|
||||
PROMPT="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"
|
||||
else
|
||||
PROMPT="$ARG_AI_PROMPT"
|
||||
fi
|
||||
AGENTAPI_ARGS+=(-I "$PROMPT")
|
||||
fi
|
||||
}
|
||||
|
||||
start_agentapi() {
|
||||
printf "Starting in directory: %s\n" "$ARG_WORKDIR"
|
||||
cd "$ARG_WORKDIR"
|
||||
|
||||
build_opencode_args
|
||||
|
||||
printf "Running OpenCode with args: %s\n" "${OPENCODE_ARGS[*]}"
|
||||
echo agentapi server "${AGENTAPI_ARGS[@]}" --type opencode --term-width 67 --term-height 1190 -- opencode "${OPENCODE_ARGS[@]}"
|
||||
agentapi server "${AGENTAPI_ARGS[@]}" --type opencode --term-width 67 --term-height 1190 -- opencode "${OPENCODE_ARGS[@]}"
|
||||
}
|
||||
|
||||
validate_opencode_installation
|
||||
start_agentapi
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Mock OpenCode CLI for testing purposes
|
||||
# This script simulates the OpenCode command-line interface
|
||||
|
||||
echo "OpenCode Mock CLI - Test Version"
|
||||
echo "Args received: $*"
|
||||
|
||||
# Simulate opencode behavior based on arguments
|
||||
case "$1" in
|
||||
--version | -v)
|
||||
echo "opencode mock version 0.1.0-test"
|
||||
;;
|
||||
--help | -h)
|
||||
echo "OpenCode Mock Help"
|
||||
echo "Usage: opencode [options] [command]"
|
||||
echo "This is a mock version for testing"
|
||||
;;
|
||||
*)
|
||||
echo "Running OpenCode mock with arguments: $*"
|
||||
echo "Mock execution completed successfully"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user