feat: add opencode module (#515)
## Description This PR adds the opencode module to the registry. ## Type of Change - [x] New module - [ ] New template - [ ] Bug fix - [ ] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information <!-- Delete this section if not applicable --> **Path:** `registry/coder-labs/modules/opencode` **New version:** `v0.1.0` **Breaking change:** [ ] Yes [x] No ## Testing & Validation - [x] Tests pass (`bun test`) - [x] Code formatted (`bun fmt`) - [x] Changes tested locally ## Related Issues <!-- Link related issues or write "None" if not applicable --> --------- Co-authored-by: DevCats <christofer@coder.com>
This commit is contained in:
parent
dd412fbf34
commit
7b84d916e1
1
.icons/opencode.svg
Normal file
1
.icons/opencode.svg
Normal file
@ -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 |
108
registry/coder-labs/modules/opencode/README.md
Normal file
108
registry/coder-labs/modules/opencode/README.md
Normal file
@ -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)
|
||||||
362
registry/coder-labs/modules/opencode/main.test.ts
Normal file
362
registry/coder-labs/modules/opencode/main.test.ts
Normal file
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
203
registry/coder-labs/modules/opencode/main.tf
Normal file
203
registry/coder-labs/modules/opencode/main.tf
Normal file
@ -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
|
||||||
|
}
|
||||||
374
registry/coder-labs/modules/opencode/main.tftest.hcl
Normal file
374
registry/coder-labs/modules/opencode/main.tftest.hcl
Normal file
@ -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
registry/coder-labs/modules/opencode/scripts/install.sh
Executable file
131
registry/coder-labs/modules/opencode/scripts/install.sh
Executable file
@ -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
registry/coder-labs/modules/opencode/scripts/start.sh
Executable file
71
registry/coder-labs/modules/opencode/scripts/start.sh
Executable file
@ -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
|
||||||
25
registry/coder-labs/modules/opencode/testdata/opencode-mock.sh
vendored
Normal file
25
registry/coder-labs/modules/opencode/testdata/opencode-mock.sh
vendored
Normal file
@ -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
|
||||||
Loading…
x
Reference in New Issue
Block a user