diff --git a/.icons/gemini.svg b/.icons/gemini.svg new file mode 100644 index 00000000..f1cf3575 --- /dev/null +++ b/.icons/gemini.svg @@ -0,0 +1 @@ +Gemini \ No newline at end of file diff --git a/registry/coder-labs/modules/gemini/README.md b/registry/coder-labs/modules/gemini/README.md new file mode 100644 index 00000000..65b9d9bc --- /dev/null +++ b/registry/coder-labs/modules/gemini/README.md @@ -0,0 +1,78 @@ +--- +display_name: Gemini CLI +icon: ../../../../.icons/gemini.svg +description: Run Gemini CLI in your workspace with AgentAPI integration +verified: true +tags: [agent, gemini, ai, google, tasks] +--- + +# Gemini CLI + +Run [Gemini CLI](https://ai.google.dev/gemini-api/docs/cli) in your workspace to access Google's Gemini AI models, and custom pre/post install scripts. This module integrates with [AgentAPI](https://github.com/coder/agentapi) for Coder Tasks compatibility. + +```tf +module "gemini" { + source = "registry.coder.com/coder-labs/gemini/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + gemini_api_key = var.gemini_api_key + gemini_model = "gemini-2.5-pro" + install_gemini = true + gemini_version = "latest" + agentapi_version = "latest" +} +``` + +## Prerequisites + +- You must add the [Coder Login](https://registry.coder.com/modules/coder-login/coder) module to your template +- Node.js and npm will be installed automatically if not present + +## Usage Example + +- Example 1: + +```tf +variable "gemini_api_key" { + type = string + description = "Gemini API key" + sensitive = true +} + +module "gemini" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder-labs/gemini/coder" + version = "1.0.0" + agent_id = coder_agent.example.id + gemini_api_key = var.gemini_api_key # we recommend providing this parameter inorder to have a smoother experience (i.e. no google sign-in) + gemini_model = "gemini-2.5-flash" + install_gemini = true + gemini_version = "latest" + gemini_instruction_prompt = "Start every response with `Gemini says:`" +} +``` + +## How it Works + +- **Install**: The module installs Gemini CLI using npm (installs Node.js via NVM if needed) +- **Instruction Prompt**: If `GEMINI_INSTRUCTION_PROMPT` and `GEMINI_START_DIRECTORY` are set, creates the directory (if needed) and writes the prompt to `GEMINI.md` +- **Start**: Launches Gemini CLI in the specified directory, wrapped by AgentAPI +- **Environment**: Sets `GEMINI_API_KEY`, `GOOGLE_GENAI_USE_VERTEXAI`, `GEMINI_MODEL` for the CLI (if variables provided) + +## Troubleshooting + +- If Gemini CLI is not found, ensure `install_gemini = true` and your API key is valid +- Node.js and npm are installed automatically if missing (using NVM) +- Check logs in `/home/coder/.gemini-module/` for install/start output +- We highly recommend using the `gemini_api_key` variable, this also ensures smooth tasks running without needing to sign in to Google. + +> [!IMPORTANT] +> To use tasks with Gemini CLI, ensure you have the `gemini_api_key` variable set, and **you pass the `AI Prompt` Parameter**. +> By default we inject the "theme": "Default" and "selectedAuthType": "gemini-api-key" to your ~/.gemini/settings.json along with the coder mcp server. +> In `gemini_instruction_prompt` and `AI Prompt` text we recommend using (\`\`) backticks instead of quotes to avoid escaping issues. Eg: gemini_instruction_prompt = "Start every response with \`Gemini says:\` " + +## References + +- [Gemini CLI Documentation](https://ai.google.dev/gemini-api/docs/cli) +- [AgentAPI Documentation](https://github.com/coder/agentapi) +- [Coder AI Agents Guide](https://coder.com/docs/tutorials/ai-agents) diff --git a/registry/coder-labs/modules/gemini/main.test.ts b/registry/coder-labs/modules/gemini/main.test.ts new file mode 100644 index 00000000..181b6114 --- /dev/null +++ b/registry/coder-labs/modules/gemini/main.test.ts @@ -0,0 +1,207 @@ +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"; + +let cleanupFunctions: (() => Promise)[] = []; +const registerCleanup = (cleanup: () => Promise) => { + 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; + skipGeminiMock?: boolean; + moduleVariables?: Record; + 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_gemini: props?.skipGeminiMock ? "true" : "false", + install_agentapi: props?.skipAgentAPIMock ? "true" : "false", + gemini_model: "test-model", + ...props?.moduleVariables, + }, + registerCleanup, + projectDir, + skipAgentAPIMock: props?.skipAgentAPIMock, + agentapiMockScript: props?.agentapiMockScript, + }); + if (!props?.skipGeminiMock) { + await writeExecutable({ + containerId: id, + filePath: "/usr/bin/gemini", + content: await loadTestFile(import.meta.dir, "gemini-mock.sh"), + }); + } + return { id }; +}; + +setDefaultTimeout(60 * 1000); + +describe("gemini", async () => { + beforeAll(async () => { + await runTerraformInit(import.meta.dir); + }); + + test("happy-path", async () => { + const { id } = await setup(); + await execModuleScript(id); + await expectAgentAPIStarted(id); + }); + + test("install-gemini-version", async () => { + const version_to_install = "0.1.13"; + const { id } = await setup({ + skipGeminiMock: true, + moduleVariables: { + install_gemini: "true", + gemini_version: version_to_install, + }, + }); + await execModuleScript(id); + const resp = await execContainer(id, [ + "bash", + "-c", + `cat /home/coder/.gemini-module/install.log || true`, + ]); + expect(resp.stdout).toContain(version_to_install); + }); + + test("gemini-settings-json", async () => { + const settings = '{"foo": "bar"}'; + const { id } = await setup({ + moduleVariables: { + gemini_settings_json: settings, + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.gemini/settings.json"); + expect(resp).toContain("foo"); + expect(resp).toContain("bar"); + }); + + test("gemini-api-key", async () => { + const apiKey = "test-api-key-123"; + const { id } = await setup({ + moduleVariables: { + gemini_api_key: apiKey, + }, + }); + await execModuleScript(id); + + const resp = await readFileContainer(id, "/home/coder/.gemini-module/agentapi-start.log"); + expect(resp).toContain("gemini_api_key provided !"); + }); + + test("use-vertexai", async () => { + const { id } = await setup({ + skipGeminiMock: false, + moduleVariables: { + use_vertexai: "true", + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.gemini-module/install.log"); + expect(resp).toContain('GOOGLE_GENAI_USE_VERTEXAI=\'true\''); + }); + + test("gemini-model", async () => { + const model = "gemini-2.5-pro"; + const { id } = await setup({ + skipGeminiMock: false, + moduleVariables: { + gemini_model: model, + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.gemini-module/install.log"); + expect(resp).toContain(model); + }); + + test("pre-post-install-scripts", async () => { + const { id } = await setup({ + moduleVariables: { + pre_install_script: "#!/bin/bash\necho 'pre-install-script'", + post_install_script: "#!/bin/bash\necho 'post-install-script'", + }, + }); + await execModuleScript(id); + const preInstallLog = await readFileContainer(id, "/home/coder/.gemini-module/pre_install.log"); + expect(preInstallLog).toContain("pre-install-script"); + const postInstallLog = await readFileContainer(id, "/home/coder/.gemini-module/post_install.log"); + expect(postInstallLog).toContain("post-install-script"); + }); + + test("folder-variable", async () => { + const folder = "/tmp/gemini-test-folder"; + const { id } = await setup({ + skipGeminiMock: false, + moduleVariables: { + folder, + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.gemini-module/install.log"); + expect(resp).toContain(folder); + }); + + test("additional-extensions", async () => { + const additional = '{"custom": {"enabled": true}}'; + const { id } = await setup({ + moduleVariables: { + additional_extensions: additional, + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/.gemini/settings.json"); + expect(resp).toContain("custom"); + expect(resp).toContain("enabled"); + }); + + test("gemini-system-prompt", async () => { + const prompt = "This is a system prompt for Gemini."; + const { id } = await setup({ + moduleVariables: { + gemini_system_prompt: prompt, + }, + }); + await execModuleScript(id); + const resp = await readFileContainer(id, "/home/coder/GEMINI.md"); + expect(resp).toContain(prompt); + }); + + test("start-without-prompt", async () => { + const { id } = await setup(); + await execModuleScript(id); + const prompt = await execContainer(id, ["ls", "-l", "/home/coder/GEMINI.md"]); + expect(prompt.exitCode).not.toBe(0); + expect(prompt.stderr).toContain("No such file or directory"); + }); +}); diff --git a/registry/coder-labs/modules/gemini/main.tf b/registry/coder-labs/modules/gemini/main.tf new file mode 100644 index 00000000..ab4fa945 --- /dev/null +++ b/registry/coder-labs/modules/gemini/main.tf @@ -0,0 +1,215 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.7" + } + } +} + +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/gemini.svg" +} + +variable "folder" { + type = string + description = "The folder to run Gemini in." + default = "/home/coder" +} + +variable "install_gemini" { + type = bool + description = "Whether to install Gemini." + default = true +} + +variable "gemini_version" { + type = string + description = "The version of Gemini to install." + default = "" +} + +variable "gemini_settings_json" { + type = string + description = "json to use in ~/.gemini/settings.json." + default = "" +} + +variable "gemini_api_key" { + type = string + description = "Gemini API Key" + default = "" +} + +variable "use_vertexai" { + type = bool + description = "Whether to use vertex ai" + default = false +} + +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.3.0" +} + +variable "gemini_model" { + type = string + description = "The model to use for Gemini (e.g., gemini-2.5-pro)." + default = "" +} + +variable "pre_install_script" { + type = string + description = "Custom script to run before installing Gemini." + default = null +} + +variable "post_install_script" { + type = string + description = "Custom script to run after installing Gemini." + default = null +} + +data "coder_parameter" "ai_prompt" { + type = "string" + name = "AI Prompt" + default = "" + description = "Initial prompt for the Gemini CLI" + mutable = true +} + +variable "additional_extensions" { + type = string + description = "Additional extensions configuration in json format to append to the config." + default = null +} + +variable "gemini_system_prompt" { + type = string + description = "System prompt for Gemini. It will be added to GEMINI.md in the specified folder." + default = "" +} + +resource "coder_env" "gemini_api_key" { + agent_id = var.agent_id + name = "GEMINI_API_KEY" + value = var.gemini_api_key +} + +resource "coder_env" "gemini_use_vertex_ai" { + agent_id = var.agent_id + name = "GOOGLE_GENAI_USE_VERTEXAI" + value = var.use_vertexai +} + +locals { + base_extensions = <<-EOT +{ + "coder": { + "args": [ + "exp", + "mcp", + "server" + ], + "command": "coder", + "description": "Report ALL tasks and statuses (in progress, done, failed) you are working on.", + "enabled": true, + "env": { + "CODER_MCP_APP_STATUS_SLUG": "${local.app_slug}", + "CODER_MCP_AI_AGENTAPI_URL": "http://localhost:3284" + }, + "name": "Coder", + "timeout": 3000, + "type": "stdio", + "trust": true + } +} +EOT + + app_slug = "gemini" + install_script = file("${path.module}/scripts/install.sh") + start_script = file("${path.module}/scripts/start.sh") + module_dir_name = ".gemini-module" +} + +module "agentapi" { + source = "registry.coder.com/coder/agentapi/coder" + version = "1.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 = "Gemini" + cli_app_slug = "${local.app_slug}-cli" + cli_app_display_name = "Gemini CLI" + 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 + GEMINI_API_KEY='${var.gemini_api_key}' \ + GOOGLE_GENAI_USE_VERTEXAI='${var.use_vertexai}' \ + GEMINI_MODEL='${var.gemini_model}' \ + GEMINI_START_DIRECTORY='${var.folder}' \ + GEMINI_TASK_PROMPT='${base64encode(data.coder_parameter.ai_prompt.value)}' \ + /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_INSTALL='${var.install_gemini}' \ + ARG_GEMINI_VERSION='${var.gemini_version}' \ + ARG_GEMINI_CONFIG='${base64encode(var.gemini_settings_json)}' \ + BASE_EXTENSIONS='${base64encode(replace(local.base_extensions, "'", "'\\''"))}' \ + ADDITIONAL_EXTENSIONS='${base64encode(replace(var.additional_extensions != null ? var.additional_extensions : "", "'", "'\\''"))}' \ + GEMINI_START_DIRECTORY='${var.folder}' \ + GEMINI_INSTRUCTION_PROMPT='${base64encode(var.gemini_system_prompt)}' \ + /tmp/install.sh + EOT +} \ No newline at end of file diff --git a/registry/coder-labs/modules/gemini/scripts/install.sh b/registry/coder-labs/modules/gemini/scripts/install.sh new file mode 100644 index 00000000..a800dbd2 --- /dev/null +++ b/registry/coder-labs/modules/gemini/scripts/install.sh @@ -0,0 +1,175 @@ +#!/bin/bash + +BOLD='\033[0;1m' + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +set -o nounset + +ARG_GEMINI_CONFIG=$(echo -n "$ARG_GEMINI_CONFIG" | base64 -d) +BASE_EXTENSIONS=$(echo -n "$BASE_EXTENSIONS" | base64 -d) +ADDITIONAL_EXTENSIONS=$(echo -n "$ADDITIONAL_EXTENSIONS" | base64 -d) +GEMINI_INSTRUCTION_PROMPT=$(echo -n "$GEMINI_INSTRUCTION_PROMPT" | base64 -d) + +echo "--------------------------------" +printf "gemini_config: %s\n" "$ARG_GEMINI_CONFIG" +printf "install: %s\n" "$ARG_INSTALL" +printf "gemini_version: %s\n" "$ARG_GEMINI_VERSION" +echo "--------------------------------" + +set +o nounset + +function install_node() { + # borrowed from claude-code module + if ! command_exists npm; then + printf "npm not found, checking for Node.js installation...\n" + if ! command_exists node; then + printf "Node.js not found, installing Node.js via NVM...\n" + export NVM_DIR="$HOME/.nvm" + if [ ! -d "$NVM_DIR" ]; then + mkdir -p "$NVM_DIR" + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + else + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" + fi + + nvm install --lts + nvm use --lts + nvm alias default node + + printf "Node.js installed: %s\n" "$(node --version)" + printf "npm installed: %s\n" "$(npm --version)" + else + printf "Node.js is installed but npm is not available. Please install npm manually.\n" + exit 1 + fi + fi +} + +function install_gemini() { + if [ "${ARG_INSTALL}" = "true" ]; then + # we need node to install and run gemini-cli + install_node + + # If nvm does not exist, we will create a global npm directory (this os to prevent the possibility of EACCESS issues on npm -g) + if ! command_exists nvm; then + printf "which node: %s\n" "$(which node)" + printf "which npm: %s\n" "$(which npm)" + + # Create a directory for global packages + mkdir -p "$HOME"/.npm-global + + # Configure npm to use it + npm config set prefix "$HOME/.npm-global" + + # Add to PATH for current session + export PATH="$HOME/.npm-global/bin:$PATH" + + # Add to shell profile for future sessions + if ! grep -q "export PATH=$HOME/.npm-global/bin:\$PATH" ~/.bashrc; then + echo "export PATH=$HOME/.npm-global/bin:\$PATH" >> ~/.bashrc + fi + fi + + printf "%s Installing Gemini CLI\n" "${BOLD}" + + if [ -n "$ARG_GEMINI_VERSION" ]; then + npm install -g "@google/gemini-cli@$ARG_GEMINI_VERSION" + else + npm install -g "@google/gemini-cli" + fi + printf "%s Successfully installed Gemini CLI. Version: %s\n" "${BOLD}" "$(gemini --version)" + fi +} + +function populate_settings_json() { + if [ "${ARG_GEMINI_CONFIG}" != "" ]; then + SETTINGS_PATH="$HOME/.gemini/settings.json" + mkdir -p "$(dirname "$SETTINGS_PATH")" + printf "Custom gemini_config is provided !\n" + echo "${ARG_GEMINI_CONFIG}" > "$HOME/.gemini/settings.json" + else + printf "No custom gemini_config provided, using default settings.json.\n" + append_extensions_to_settings_json + fi +} + +function append_extensions_to_settings_json() { + SETTINGS_PATH="$HOME/.gemini/settings.json" + mkdir -p "$(dirname "$SETTINGS_PATH")" + printf "[append_extensions_to_settings_json] Starting extension merge process...\n" + if [ -z "${BASE_EXTENSIONS:-}" ]; then + printf "[append_extensions_to_settings_json] BASE_EXTENSIONS is empty, skipping merge.\n" + return + fi + if [ ! -f "$SETTINGS_PATH" ]; then + printf "%s does not exist. Creating with merged mcpServers structure.\n" "$SETTINGS_PATH" + # If ADDITIONAL_EXTENSIONS is not set or empty, use '{}' + ADD_EXT_JSON='{}' + if [ -n "${ADDITIONAL_EXTENSIONS:-}" ]; then + ADD_EXT_JSON="$ADDITIONAL_EXTENSIONS" + fi + printf '{"mcpServers":%s}\n' "$(jq -s 'add' <(echo "$BASE_EXTENSIONS") <(echo "$ADD_EXT_JSON"))" > "$SETTINGS_PATH" + fi + + # Prepare temp files + TMP_SETTINGS=$(mktemp) + + # If ADDITIONAL_EXTENSIONS is not set or empty, use '{}' + ADD_EXT_JSON='{}' + if [ -n "${ADDITIONAL_EXTENSIONS:-}" ]; then + printf "[append_extensions_to_settings_json] ADDITIONAL_EXTENSIONS is set.\n" + ADD_EXT_JSON="$ADDITIONAL_EXTENSIONS" + else + printf "[append_extensions_to_settings_json] ADDITIONAL_EXTENSIONS is empty or not set.\n" + fi + + printf "[append_extensions_to_settings_json] Merging BASE_EXTENSIONS and ADDITIONAL_EXTENSIONS into mcpServers...\n" + jq --argjson base "$BASE_EXTENSIONS" --argjson add "$ADD_EXT_JSON" \ + '.mcpServers = (.mcpServers // {} + $base + $add)' \ + "$SETTINGS_PATH" > "$TMP_SETTINGS" && mv "$TMP_SETTINGS" "$SETTINGS_PATH" + + # Add theme and selectedAuthType fields + jq '.theme = "Default" | .selectedAuthType = "gemini-api-key"' "$SETTINGS_PATH" > "$TMP_SETTINGS" && mv "$TMP_SETTINGS" "$SETTINGS_PATH" + + printf "[append_extensions_to_settings_json] Merge complete.\n" +} + +function add_instruction_prompt_if_exists() { + if [ -n "${GEMINI_INSTRUCTION_PROMPT:-}" ]; then + if [ -d "${GEMINI_START_DIRECTORY}" ]; then + printf "Directory '%s' exists. Changing to it.\\n" "${GEMINI_START_DIRECTORY}" + cd "${GEMINI_START_DIRECTORY}" || { + printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" + exit 1 + } + else + printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${GEMINI_START_DIRECTORY}" + mkdir -p "${GEMINI_START_DIRECTORY}" || { + printf "Error: Could not create directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" + exit 1 + } + cd "${GEMINI_START_DIRECTORY}" || { + printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" + exit 1 + } + fi + touch GEMINI.md + printf "Setting GEMINI.md\n" + echo "${GEMINI_INSTRUCTION_PROMPT}" > GEMINI.md + else + printf "GEMINI.md is not set.\n" + fi +} + + +# Install Gemini +install_gemini +gemini --version +populate_settings_json +add_instruction_prompt_if_exists + diff --git a/registry/coder-labs/modules/gemini/scripts/start.sh b/registry/coder-labs/modules/gemini/scripts/start.sh new file mode 100644 index 00000000..00e0b5ed --- /dev/null +++ b/registry/coder-labs/modules/gemini/scripts/start.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Load shell environment +source "$HOME"/.bashrc + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +if [ -f "$HOME/.nvm/nvm.sh" ]; then + source "$HOME"/.nvm/nvm.sh +else + export PATH="$HOME/.npm-global/bin:$PATH" +fi + +printf "Version: %s\n" "$(gemini --version)" + +GEMINI_TASK_PROMPT=$(echo -n "$GEMINI_TASK_PROMPT" | base64 -d) + +if command_exists gemini; then + printf "Gemini is installed\n" +else + printf "Error: Gemini is not installed. Please enable install_gemini or install it manually :)\n" + exit 1 +fi + +if [ -d "${GEMINI_START_DIRECTORY}" ]; then + printf "Directory '%s' exists. Changing to it.\\n" "${GEMINI_START_DIRECTORY}" + cd "${GEMINI_START_DIRECTORY}" || { + printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" + exit 1 + } +else + printf "Directory '%s' does not exist. Creating and changing to it.\\n" "${GEMINI_START_DIRECTORY}" + mkdir -p "${GEMINI_START_DIRECTORY}" || { + printf "Error: Could not create directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" + exit 1 + } + cd "${GEMINI_START_DIRECTORY}" || { + printf "Error: Could not change to directory '%s'.\\n" "${GEMINI_START_DIRECTORY}" + exit 1 + } +fi + +if [ -n "$GEMINI_TASK_PROMPT" ]; then + printf "Running the task prompt %s\n" "$GEMINI_TASK_PROMPT" + PROMPT="Every step of the way, report tasks to Coder with proper descriptions and statuses. Your task at hand: $GEMINI_TASK_PROMPT" + GEMINI_ARGS=(--prompt-interactive "$PROMPT") +else + printf "No task prompt given.\n" + GEMINI_ARGS=() +fi + +if [ -n "$GEMINI_API_KEY" ]; then + printf "gemini_api_key provided !\n" +else + printf "gemini_api_key not provided\n" +fi + +# use low width to fit in the tasks UI sidebar. height is adjusted so that width x height ~= 80x1000 characters +# are visible in the terminal screen by default. +agentapi server --term-width 67 --term-height 1190 -- gemini "${GEMINI_ARGS[@]}" \ No newline at end of file diff --git a/registry/coder-labs/modules/gemini/testdata/gemini-mock.sh b/registry/coder-labs/modules/gemini/testdata/gemini-mock.sh new file mode 100644 index 00000000..53c9c41d --- /dev/null +++ b/registry/coder-labs/modules/gemini/testdata/gemini-mock.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [[ "$1" == "--version" ]]; then + echo "HELLO: $(bash -c env)" + echo "gemini version v2.5.0" + exit 0 +fi + +set -e + +while true; do + echo "$(date) - gemini-mock" + sleep 15 +done \ No newline at end of file