diff --git a/apps/opencode/1.18.7/.env.sample b/apps/opencode/1.18.7/.env.sample new file mode 100644 index 000000000..daccdabd7 --- /dev/null +++ b/apps/opencode/1.18.7/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=opencode-compose-check +PANEL_APP_PORT_HTTP=4096 +APP_DATA_DIR=./data +OPENCODE_SERVER_USERNAME=opencode +OPENCODE_SERVER_PASSWORD=change-me-before-deploy +TZ=Asia/Shanghai diff --git a/apps/opencode/1.18.7/data.yml b/apps/opencode/1.18.7/data.yml new file mode 100644 index 000000000..54521c01a --- /dev/null +++ b/apps/opencode/1.18.7/data.yml @@ -0,0 +1,86 @@ +additionalProperties: + formFields: + - default: 4096 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web 端口 + label: + en: Web Port + zh: Web 端口 + zh-Hant: Web 連接埠 + ja: Web ポート + ko: 웹 포트 + ru: Веб-порт + ms: Port Web + pt-br: Porta Web + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text + - default: opencode + edit: true + envKey: OPENCODE_SERVER_USERNAME + labelEn: Server Username + labelZh: 服务器用户名 + label: + en: Server Username + zh: 服务器用户名 + zh-Hant: 伺服器使用者名稱 + ja: サーバーユーザー名 + ko: 서버 사용자 이름 + ru: Имя пользователя сервера + ms: Nama Pengguna Pelayan + pt-br: Nome de Usuario do Servidor + required: true + rule: paramCommon + type: text + - default: '' + edit: true + envKey: OPENCODE_SERVER_PASSWORD + labelEn: Server Password + labelZh: 服务器密码 + label: + en: Server Password + zh: 服务器密码 + zh-Hant: 伺服器密碼 + ja: サーバーパスワード + ko: 서버 비밀번호 + ru: Пароль сервера + ms: Kata Laluan Pelayan + pt-br: Senha do Servidor + random: true + required: true + rule: paramComplexity + type: password + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon Waktu + pt-br: Fuso Horário + required: true + type: text diff --git a/apps/opencode/1.18.7/docker-compose.yml b/apps/opencode/1.18.7/docker-compose.yml new file mode 100644 index 000000000..fb1e1008c --- /dev/null +++ b/apps/opencode/1.18.7/docker-compose.yml @@ -0,0 +1,35 @@ +services: + opencode: + image: "ghcr.io/anomalyco/opencode:1.18.7" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + command: ["serve", "--hostname", "0.0.0.0", "--port", "4096"] + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:4096" + volumes: + - "${APP_DATA_DIR}:/home/opencode" + environment: + - HOME=/home/opencode + - OPENCODE_SERVER_USERNAME=${OPENCODE_SERVER_USERNAME} + - OPENCODE_SERVER_PASSWORD=${OPENCODE_SERVER_PASSWORD} + - TZ=${TZ} + stop_grace_period: 15s + healthcheck: + test: + - CMD-SHELL + - >- + auth="$$(printf '%s:%s' "$${OPENCODE_SERVER_USERNAME}" "$${OPENCODE_SERVER_PASSWORD}" | base64 | tr -d '\n')"; + wget -q -O /dev/null --header "Authorization: Basic $$auth" http://127.0.0.1:4096/global/health + interval: 1m + timeout: 5s + retries: 5 + start_period: 20s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/opencode/1.18.7/scripts/init.sh b/apps/opencode/1.18.7/scripts/init.sh new file mode 100644 index 000000000..771c05f38 --- /dev/null +++ b/apps/opencode/1.18.7/scripts/init.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local value="" + [[ -f "$ENV_FILE" ]] || return 0 + value="$(sed -n "s/^APP_DATA_DIR=//p" "$ENV_FILE" | tail -n 1)" + value="${value#\"}" + value="${value%\"}" + value="${value#\'}" + value="${value%\'}" + printf '%s\n' "$value" +} + +data_dir_value="${APP_DATA_DIR:-$(read_env_value)}" +data_dir_value="${data_dir_value:-./data}" +data_dir_is_absolute=false +case "$data_dir_value" in + /) echo "refusing to use / as APP_DATA_DIR" >&2; exit 1 ;; + /*) data_dir="$data_dir_value"; data_dir_is_absolute=true ;; + *) data_dir="$ROOT_DIR/${data_dir_value#./}" ;; +esac + +data_dir="$(readlink -f -- "$data_dir")" +case "$data_dir" in + /) echo "refusing to use / as APP_DATA_DIR" >&2; exit 1 ;; +esac +if [[ "$data_dir_is_absolute" == false && "$data_dir" != "$ROOT_DIR/"* ]]; then + echo "refusing relative APP_DATA_DIR outside the app directory" >&2 + exit 1 +fi + +mkdir -p -- "$data_dir" +chown -R 1000:1000 -- "$data_dir" diff --git a/apps/opencode/1.18.7/scripts/uninstall.sh b/apps/opencode/1.18.7/scripts/uninstall.sh new file mode 100644 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/opencode/1.18.7/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/opencode/1.18.7/scripts/upgrade.sh b/apps/opencode/1.18.7/scripts/upgrade.sh new file mode 100644 index 000000000..71e688940 --- /dev/null +++ b/apps/opencode/1.18.7/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/init.sh" diff --git a/apps/opencode/README.md b/apps/opencode/README.md new file mode 100644 index 000000000..e1729f211 --- /dev/null +++ b/apps/opencode/README.md @@ -0,0 +1,79 @@ +# OpenCode + +## 产品介绍 + +OpenCode 是一个自托管 AI 编程助手,可通过浏览器管理 AI 编程会话。 + +## 主要功能 + +- 通过浏览器运行 OpenCode 的无头服务。 +- 为服务配置独立的 HTTP Basic Auth 凭据。 +- 在持久化数据目录中保存 OpenCode 状态。 + +## 访问说明 + +- 默认 Web 端口:`4096` +- 容器内部端口:`4096` +- 安装时必须设置服务器用户名和随机生成的密码。OpenCode 使用 HTTP Basic Auth 保护服务。 +- 直接端口访问使用 HTTP;公开访问前请在 1Panel 中配置 HTTPS 反向代理。 + +## 数据与备份 + +- `Data Directory` 映射到容器的 `/home/opencode`,其中包含 OpenCode 配置、会话和已配置的模型提供商凭据。 +- 升级或迁移前备份整个 `Data Directory`。 +- 卸载会停止并移除容器,但不会删除数据目录。 + +## 安全边界 + +- 此包不挂载 Docker socket、宿主机工作区或其他宿主机敏感路径。 +- 在 OpenCode 中配置的模型提供商凭据保存在持久化数据目录中;请将备份视为敏感数据。 + +## 版本 + +- `1.18.7` 固定到上游 OpenCode `v1.18.7` 镜像。 +- `latest` 跟随上游 OpenCode 的最新镜像。 + +## 参考资料 + +- 上游仓库: +- 官方文档: +- 服务命令与认证实现: + +## Introduction + +OpenCode is a self-hosted AI coding agent that manages AI-assisted coding sessions in the browser. + +## Features + +- Run OpenCode's headless service in the browser. +- Configure dedicated HTTP Basic Auth credentials for the service. +- Persist OpenCode state in a dedicated data directory. + +## Access + +- Default web port: `4096` +- Container port: `4096` +- Installation requires a server username and generated password. OpenCode protects the service with HTTP Basic Auth. +- Direct port access uses HTTP. Configure an HTTPS reverse proxy in 1Panel before public exposure. + +## Data and Backup + +- `Data Directory` maps to `/home/opencode` in the container and stores OpenCode configuration, sessions, and configured model-provider credentials. +- Back up the complete `Data Directory` before upgrades or migrations. +- Uninstall stops and removes the container but keeps the data directory. + +## Security Boundary + +- This package does not mount the Docker socket, a host workspace, or other sensitive host paths. +- Model-provider credentials configured in OpenCode live in the persistent data directory. Treat its backups as sensitive. + +## Versions + +- `1.18.7` is pinned to the upstream OpenCode `v1.18.7` image. +- `latest` follows the upstream OpenCode latest image. + +## Sources + +- Upstream repository: +- Official documentation: +- Serve command and authentication: diff --git a/apps/opencode/data.yml b/apps/opencode/data.yml new file mode 100644 index 000000000..fe96995ad --- /dev/null +++ b/apps/opencode/data.yml @@ -0,0 +1,31 @@ +name: OpenCode +tags: + - AI +title: 自托管 AI 编程助手 +description: 自托管 AI 编程助手 +additionalProperties: + key: opencode + name: OpenCode + tags: + - AI + shortDescZh: 自托管 AI 编程助手 + shortDescEn: A self-hosted AI coding agent + description: + en: A self-hosted AI coding agent. + zh: 自托管 AI 编程助手。 + zh-Hant: 自託管 AI 程式設計助理。 + ja: セルフホスト型 AI コーディングエージェントです。 + ko: 자체 호스팅 AI 코딩 에이전트입니다. + ru: Самостоятельно размещаемый ИИ-агент для программирования. + ms: Ejen pengekodan AI yang dihoskan sendiri. + pt-br: Agente de programacao por IA auto-hospedado. + type: tool + crossVersionUpdate: true + limit: 0 + recommend: 0 + website: https://opencode.ai/ + document: https://opencode.ai/docs + github: https://github.com/anomalyco/opencode + architectures: + - amd64 + - arm64 diff --git a/apps/opencode/latest/.env.sample b/apps/opencode/latest/.env.sample new file mode 100644 index 000000000..daccdabd7 --- /dev/null +++ b/apps/opencode/latest/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=opencode-compose-check +PANEL_APP_PORT_HTTP=4096 +APP_DATA_DIR=./data +OPENCODE_SERVER_USERNAME=opencode +OPENCODE_SERVER_PASSWORD=change-me-before-deploy +TZ=Asia/Shanghai diff --git a/apps/opencode/latest/data.yml b/apps/opencode/latest/data.yml new file mode 100644 index 000000000..54521c01a --- /dev/null +++ b/apps/opencode/latest/data.yml @@ -0,0 +1,86 @@ +additionalProperties: + formFields: + - default: 4096 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: Web Port + labelZh: Web 端口 + label: + en: Web Port + zh: Web 端口 + zh-Hant: Web 連接埠 + ja: Web ポート + ko: 웹 포트 + ru: Веб-порт + ms: Port Web + pt-br: Porta Web + required: true + rule: paramPort + type: number + - default: ./data + edit: true + envKey: APP_DATA_DIR + labelEn: Data Directory + labelZh: 数据目录 + label: + en: Data Directory + zh: 数据目录 + zh-Hant: 資料目錄 + ja: データディレクトリ + ko: 데이터 디렉터리 + ru: Каталог данных + ms: Direktori Data + pt-br: Diretório de Dados + required: true + type: text + - default: opencode + edit: true + envKey: OPENCODE_SERVER_USERNAME + labelEn: Server Username + labelZh: 服务器用户名 + label: + en: Server Username + zh: 服务器用户名 + zh-Hant: 伺服器使用者名稱 + ja: サーバーユーザー名 + ko: 서버 사용자 이름 + ru: Имя пользователя сервера + ms: Nama Pengguna Pelayan + pt-br: Nome de Usuario do Servidor + required: true + rule: paramCommon + type: text + - default: '' + edit: true + envKey: OPENCODE_SERVER_PASSWORD + labelEn: Server Password + labelZh: 服务器密码 + label: + en: Server Password + zh: 服务器密码 + zh-Hant: 伺服器密碼 + ja: サーバーパスワード + ko: 서버 비밀번호 + ru: Пароль сервера + ms: Kata Laluan Pelayan + pt-br: Senha do Servidor + random: true + required: true + rule: paramComplexity + type: password + - default: Asia/Shanghai + edit: true + envKey: TZ + labelEn: Timezone + labelZh: 时区 + label: + en: Timezone + zh: 时区 + zh-Hant: 時區 + ja: タイムゾーン + ko: 시간대 + ru: Часовой пояс + ms: Zon Waktu + pt-br: Fuso Horário + required: true + type: text diff --git a/apps/opencode/latest/docker-compose.yml b/apps/opencode/latest/docker-compose.yml new file mode 100644 index 000000000..fc43f0b38 --- /dev/null +++ b/apps/opencode/latest/docker-compose.yml @@ -0,0 +1,35 @@ +services: + opencode: + image: "ghcr.io/anomalyco/opencode:latest" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + command: ["serve", "--hostname", "0.0.0.0", "--port", "4096"] + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:4096" + volumes: + - "${APP_DATA_DIR}:/home/opencode" + environment: + - HOME=/home/opencode + - OPENCODE_SERVER_USERNAME=${OPENCODE_SERVER_USERNAME} + - OPENCODE_SERVER_PASSWORD=${OPENCODE_SERVER_PASSWORD} + - TZ=${TZ} + stop_grace_period: 15s + healthcheck: + test: + - CMD-SHELL + - >- + auth="$$(printf '%s:%s' "$${OPENCODE_SERVER_USERNAME}" "$${OPENCODE_SERVER_PASSWORD}" | base64 | tr -d '\n')"; + wget -q -O /dev/null --header "Authorization: Basic $$auth" http://127.0.0.1:4096/global/health + interval: 1m + timeout: 5s + retries: 5 + start_period: 20s + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/opencode/latest/scripts/init.sh b/apps/opencode/latest/scripts/init.sh new file mode 100644 index 000000000..771c05f38 --- /dev/null +++ b/apps/opencode/latest/scripts/init.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" + +read_env_value() { + local value="" + [[ -f "$ENV_FILE" ]] || return 0 + value="$(sed -n "s/^APP_DATA_DIR=//p" "$ENV_FILE" | tail -n 1)" + value="${value#\"}" + value="${value%\"}" + value="${value#\'}" + value="${value%\'}" + printf '%s\n' "$value" +} + +data_dir_value="${APP_DATA_DIR:-$(read_env_value)}" +data_dir_value="${data_dir_value:-./data}" +data_dir_is_absolute=false +case "$data_dir_value" in + /) echo "refusing to use / as APP_DATA_DIR" >&2; exit 1 ;; + /*) data_dir="$data_dir_value"; data_dir_is_absolute=true ;; + *) data_dir="$ROOT_DIR/${data_dir_value#./}" ;; +esac + +data_dir="$(readlink -f -- "$data_dir")" +case "$data_dir" in + /) echo "refusing to use / as APP_DATA_DIR" >&2; exit 1 ;; +esac +if [[ "$data_dir_is_absolute" == false && "$data_dir" != "$ROOT_DIR/"* ]]; then + echo "refusing relative APP_DATA_DIR outside the app directory" >&2 + exit 1 +fi + +mkdir -p -- "$data_dir" +chown -R 1000:1000 -- "$data_dir" diff --git a/apps/opencode/latest/scripts/uninstall.sh b/apps/opencode/latest/scripts/uninstall.sh new file mode 100644 index 000000000..ab8a8d674 --- /dev/null +++ b/apps/opencode/latest/scripts/uninstall.sh @@ -0,0 +1,2 @@ +#!/bin/bash +docker-compose down --volumes diff --git a/apps/opencode/latest/scripts/upgrade.sh b/apps/opencode/latest/scripts/upgrade.sh new file mode 100644 index 000000000..71e688940 --- /dev/null +++ b/apps/opencode/latest/scripts/upgrade.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail + +exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/init.sh" diff --git a/apps/opencode/logo.png b/apps/opencode/logo.png new file mode 100644 index 000000000..490d4916b Binary files /dev/null and b/apps/opencode/logo.png differ