From 2a4431b500b0cf7a13169b3cca66ad88d2da4023 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 22 Sep 2026 17:38:36 +0000 Subject: [PATCH 1/4] feat(agent-relay-cursor): add credential_kind for shared service-account keys Agent Relay's insecure_shared_token stamps the pool's team service-account key instead of a per-user sub-token. The Cursor CLI accepts that key only as CURSOR_API_KEY; as --auth-token it fails with "Failed to validate worker account settings". credential_kind = "api_key" exports the credential as CURSOR_API_KEY in the supervisor and drops --auth-token. The default worker_token is unchanged. Coder Agents on behalf of @Emyrk. --- .../modules/agent-relay-cursor/README.md | 30 ++++++++++++++----- .../modules/agent-relay-cursor/main.test.ts | 29 ++++++++++++++++++ .../coder/modules/agent-relay-cursor/main.tf | 22 +++++++++++--- .../agent-relay-cursor/main.tftest.hcl | 28 ++++++++++++++++- .../modules/agent-relay-cursor/start.sh.tftpl | 7 +++++ 5 files changed, 103 insertions(+), 13 deletions(-) diff --git a/registry/coder/modules/agent-relay-cursor/README.md b/registry/coder/modules/agent-relay-cursor/README.md index 0687a3210..ce3a512e5 100644 --- a/registry/coder/modules/agent-relay-cursor/README.md +++ b/registry/coder/modules/agent-relay-cursor/README.md @@ -53,17 +53,31 @@ when to reap the workspace. ## Worker credential -The pool's service-account API key never leaves Agent Relay. At dispatch the -relay exchanges it for a Cursor sub-token scoped to the requesting user and -stamps that as the ephemeral `agent_relay_credential` parameter. The module -exports it as `AGENT_RELAY_CURSOR_TOKEN` and starts the worker with +By default the pool's service-account API key never leaves Agent Relay. At +dispatch the relay exchanges it for a Cursor sub-token scoped to the requesting +user and stamps that as the ephemeral `agent_relay_credential` parameter. The +module exports it as `AGENT_RELAY_CURSOR_TOKEN` and starts the worker with `--auth-token`. It is never written to disk, though as a command-line argument it is visible in the worker's `/proc//cmdline` to any process running as the same user inside the workspace; the CLI offers no environment variable for -it. The token acts only as that user, -cannot mint further tokens, and expires after an hour. It is not refreshed: a -worker that has to reconnect after expiry fails and Cursor re-queues the -request for a fresh workspace. +it. The token acts only as that user, cannot mint further tokens, and expires +after an hour. It is not refreshed: a worker that has to reconnect after expiry +fails and Cursor re-queues the request for a fresh workspace. + +Some Cursor CLI releases refuse a delegated sub-token for pool workers +(`Delegated service-account tokens cannot start pool workers`). Agent Relay's +per-pool `insecure_shared_token` then stamps the service-account key itself. +The CLI accepts that key only as an API key, never as `--auth-token` +(`Failed to validate worker account settings`), so the template must set +`credential_kind = "api_key"` to match: the supervisor exports the credential +as `CURSOR_API_KEY` and drops `--auth-token`. Every workspace owner in the pool +can then read a team-wide key from the worker's environment; pair the two +settings deliberately. + +| pool `insecure_shared_token` | module `credential_kind` | credential handed to the CLI as | +| ---------------------------- | ------------------------ | ------------------------------- | +| `false` (default) | `worker_token` (default) | `--auth-token` | +| `true` | `api_key` | `CURSOR_API_KEY` | ## Parameters diff --git a/registry/coder/modules/agent-relay-cursor/main.test.ts b/registry/coder/modules/agent-relay-cursor/main.test.ts index 65d61257a..e6b8d508f 100644 --- a/registry/coder/modules/agent-relay-cursor/main.test.ts +++ b/registry/coder/modules/agent-relay-cursor/main.test.ts @@ -365,6 +365,35 @@ describe("agent-relay-cursor", () => { expect(env.stdout).not.toContain("CURSOR_API_KEY="); }); + it("hands a shared service-account key over as CURSOR_API_KEY", async () => { + // insecure_shared_token pools stamp the team key; the CLI only + // accepts it as an API key, so --auth-token must be absent and the + // key must reach the worker's environment. + const { id, scripts } = await setup({ credential_kind: "api_key" }); + await stubAgent( + id, + 'printf "%s\\n" "$@" >/tmp/agent-args; printf "%s" "$CURSOR_API_KEY" >/tmp/agent-api-key; sleep 30', + ); + const { start } = await runDispatched(id, scripts); + expect(start.exitCode).toBe(0); + const args = (await readFileContainer(id, "/tmp/agent-args")).split("\n"); + expect(args).not.toContain("--auth-token"); + expect(args).toContain("--pool"); + expect(await readFileContainer(id, "/tmp/agent-api-key")).toBe( + "test-user-token", + ); + // The key is still read from the environment at run time, never + // written into the supervisor. + const supervisor = await readFileContainer( + id, + `${MODULE_DIR}/scripts/supervise.sh`, + ); + expect(supervisor).toContain( + 'export CURSOR_API_KEY="$AGENT_RELAY_CURSOR_TOKEN"', + ); + expect(supervisor).not.toContain("test-user-token"); + }); + it("passes --computer-use when enabled", async () => { const { id, scripts } = await setup({ computer_use: "true" }); await stubAgent(id, 'printf "%s\\n" "$@" >/tmp/agent-args; sleep 30'); diff --git a/registry/coder/modules/agent-relay-cursor/main.tf b/registry/coder/modules/agent-relay-cursor/main.tf index 33298200a..091f46c81 100644 --- a/registry/coder/modules/agent-relay-cursor/main.tf +++ b/registry/coder/modules/agent-relay-cursor/main.tf @@ -62,6 +62,17 @@ variable "install_cli" { description = "Install the Cursor CLI (curl https://cursor.com/install -fsSL | bash) when the workspace starts and the CLI is not already on PATH. Defaults to true so a template works against an image that has no CLI. A CLI already in the image is used as is and never upgraded, which is the recommended and fastest path: the download only runs when the binary is missing, and it then needs outbound access to cursor.com and spends part of the claim-to-ready window." } +variable "credential_kind" { + type = string + default = "worker_token" + description = "How the worker authenticates with the stamped agent_relay_credential. worker_token (default) passes it as --auth-token, for the per-user sub-token Agent Relay mints. api_key exports it as CURSOR_API_KEY, for pools that opt into Agent Relay's insecure_shared_token and stamp the team service-account key itself; the CLI rejects that key as --auth-token and only accepts it as an API key." + + validation { + condition = contains(["worker_token", "api_key"], var.credential_kind) + error_message = "credential_kind must be worker_token or api_key." + } +} + variable "computer_use" { type = bool default = false @@ -194,10 +205,12 @@ data "coder_parameter" "agent_relay_credential" { } # CURSOR_AGENT_WORKER_ID is the Cursor CLI's contract; do not rename it. -# The worker token has no CLI env var (only the --auth-token flag), so it -# travels under an Agent Relay name and the script passes it on the -# command line. It is never CURSOR_API_KEY: the CLI would treat the -# token as a service account key and reject it. +# The credential travels under an Agent Relay name and the supervisor +# decides how to hand it to the CLI (credential_kind): a per-user worker +# token has no CLI env var and goes on the command line as --auth-token; +# a team service-account key is only accepted as CURSOR_API_KEY. Each is +# rejected through the other door, so the kind must match what the pool +# stamps. resource "coder_env" "agent_relay_cursor_token" { agent_id = var.agent_id name = "AGENT_RELAY_CURSOR_TOKEN" @@ -241,6 +254,7 @@ locals { cli_binary = var.cli_binary install_cli = var.install_cli computer_use = var.computer_use + credential_kind = var.credential_kind state_file = var.state_file log_file = var.log_file }) diff --git a/registry/coder/modules/agent-relay-cursor/main.tftest.hcl b/registry/coder/modules/agent-relay-cursor/main.tftest.hcl index 9a5e2495d..824c0b20f 100644 --- a/registry/coder/modules/agent-relay-cursor/main.tftest.hcl +++ b/registry/coder/modules/agent-relay-cursor/main.tftest.hcl @@ -145,7 +145,7 @@ run "worker_wiring" { # environment; it must not be expanded into the supervisor file the # script writes to disk. assert { - condition = strcontains(local.start_script, "--auth-token \"\\$AGENT_RELAY_CURSOR_TOKEN\"") + condition = strcontains(local.start_script, "--auth-token \"\\$AGENT_RELAY_CURSOR_TOKEN\"") && !strcontains(local.start_script, "CURSOR_API_KEY") error_message = "the worker must authenticate with --auth-token read from the environment at run time" } @@ -174,6 +174,32 @@ run "worker_wiring" { } } +# A pool that stamps its service-account key (insecure_shared_token) +# needs the credential handed over as CURSOR_API_KEY; the CLI rejects +# that key as --auth-token. +run "api_key_credential" { + command = plan + + variables { + credential_kind = "api_key" + } + + assert { + condition = strcontains(local.start_script, "export CURSOR_API_KEY=\"\\$AGENT_RELAY_CURSOR_TOKEN\"") && !strcontains(local.start_script, "--auth-token \"") + error_message = "api_key must export CURSOR_API_KEY from the stamped credential and drop --auth-token" + } +} + +run "credential_kind_rejects_unknown" { + command = plan + + variables { + credential_kind = "cookie" + } + + expect_failures = [var.credential_kind] +} + run "computer_use_off_by_default" { command = plan diff --git a/registry/coder/modules/agent-relay-cursor/start.sh.tftpl b/registry/coder/modules/agent-relay-cursor/start.sh.tftpl index 67e32841f..382eed34d 100644 --- a/registry/coder/modules/agent-relay-cursor/start.sh.tftpl +++ b/registry/coder/modules/agent-relay-cursor/start.sh.tftpl @@ -108,10 +108,17 @@ write_state() { } export PATH="\$HOME/.local/bin:\$PATH" +%{ if credential_kind == "api_key" ~} +# The pool stamps its team service-account key; the CLI accepts that +# only as an API key, never as --auth-token. +export CURSOR_API_KEY="\$AGENT_RELAY_CURSOR_TOKEN" +%{ endif ~} ${cli_binary} worker \\ --pool "\$AGENT_RELAY_CURSOR_POOL_NAME" \\ --idle-release-timeout "\$AGENT_RELAY_CURSOR_IDLE_RELEASE_TIMEOUT" \\ +%{ if credential_kind == "worker_token" ~} --auth-token "\$AGENT_RELAY_CURSOR_TOKEN" \\ +%{ endif ~} %{ if computer_use ~} --computer-use \\ %{ endif ~} From c995c106c5e118ff8e0f36b42ea49c2bdfd74976 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 22 Sep 2026 17:40:35 +0000 Subject: [PATCH 2/4] docs(agent-relay-cursor): avoid a line the version-bump script reads as a module block Coder Agents on behalf of @Emyrk. --- registry/coder/modules/agent-relay-cursor/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/registry/coder/modules/agent-relay-cursor/README.md b/registry/coder/modules/agent-relay-cursor/README.md index ce3a512e5..fc946a8b1 100644 --- a/registry/coder/modules/agent-relay-cursor/README.md +++ b/registry/coder/modules/agent-relay-cursor/README.md @@ -55,8 +55,8 @@ when to reap the workspace. By default the pool's service-account API key never leaves Agent Relay. At dispatch the relay exchanges it for a Cursor sub-token scoped to the requesting -user and stamps that as the ephemeral `agent_relay_credential` parameter. The -module exports it as `AGENT_RELAY_CURSOR_TOKEN` and starts the worker with +user and stamps that as the ephemeral `agent_relay_credential` parameter. It +is exported as `AGENT_RELAY_CURSOR_TOKEN` and the worker starts with `--auth-token`. It is never written to disk, though as a command-line argument it is visible in the worker's `/proc//cmdline` to any process running as the same user inside the workspace; the CLI offers no environment variable for From 53b3aca982eb5cdf52c197a782a0aedf59884406 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 22 Sep 2026 17:41:12 +0000 Subject: [PATCH 3/4] chore(agent-relay-cursor): bump to 0.2.0 Coder Agents on behalf of @Emyrk. --- registry/coder/modules/agent-relay-cursor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/registry/coder/modules/agent-relay-cursor/README.md b/registry/coder/modules/agent-relay-cursor/README.md index fc946a8b1..37302d055 100644 --- a/registry/coder/modules/agent-relay-cursor/README.md +++ b/registry/coder/modules/agent-relay-cursor/README.md @@ -16,7 +16,7 @@ parameters the relay stamps on each build and runs the Cursor CLI worker. ```tf module "cursor_worker" { source = "registry.coder.com/coder/agent-relay-cursor/coder" - version = "0.1.1" + version = "0.2.0" agent_id = coder_agent.main.id # Downloads the Cursor CLI at start when it is not in the image. Bake From 57b5ac70a0069058569690e870f1b20195ca9998 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 22 Sep 2026 17:47:41 +0000 Subject: [PATCH 4/4] refactor(agent-relay-cursor): take the credential kind from a stamped parameter Agent Relay knows whether a pool stamps a per-user worker token or the shared service-account key, so it stamps agent_relay_cursor_credential_kind alongside the credential and the supervisor branches on it at run time. The template needs no variable to keep in sync with the pool config. Coder Agents on behalf of @Emyrk. --- .../modules/agent-relay-cursor/README.md | 25 +++++---- .../modules/agent-relay-cursor/main.test.ts | 15 +++-- .../coder/modules/agent-relay-cursor/main.tf | 56 ++++++++++++------- .../agent-relay-cursor/main.tftest.hcl | 37 +++--------- .../modules/agent-relay-cursor/start.sh.tftpl | 20 ++++--- 5 files changed, 81 insertions(+), 72 deletions(-) diff --git a/registry/coder/modules/agent-relay-cursor/README.md b/registry/coder/modules/agent-relay-cursor/README.md index 37302d055..5266ba931 100644 --- a/registry/coder/modules/agent-relay-cursor/README.md +++ b/registry/coder/modules/agent-relay-cursor/README.md @@ -66,18 +66,19 @@ fails and Cursor re-queues the request for a fresh workspace. Some Cursor CLI releases refuse a delegated sub-token for pool workers (`Delegated service-account tokens cannot start pool workers`). Agent Relay's -per-pool `insecure_shared_token` then stamps the service-account key itself. -The CLI accepts that key only as an API key, never as `--auth-token` -(`Failed to validate worker account settings`), so the template must set -`credential_kind = "api_key"` to match: the supervisor exports the credential -as `CURSOR_API_KEY` and drops `--auth-token`. Every workspace owner in the pool -can then read a team-wide key from the worker's environment; pair the two -settings deliberately. - -| pool `insecure_shared_token` | module `credential_kind` | credential handed to the CLI as | -| ---------------------------- | ------------------------ | ------------------------------- | -| `false` (default) | `worker_token` (default) | `--auth-token` | -| `true` | `api_key` | `CURSOR_API_KEY` | +per-pool `insecure_shared_token` then stamps the service-account key itself, +together with `agent_relay_cursor_credential_kind = api_key`. The CLI accepts +that key only as an API key, never as `--auth-token` +(`Failed to validate worker account settings`), so the supervisor exports the +credential as `CURSOR_API_KEY` and drops `--auth-token` when the stamped kind +says so. Every workspace owner in such a pool can read a team-wide key from +the worker's environment. No template change is needed; the relay stamps the +kind and the module follows it. + +| pool `insecure_shared_token` | stamped `agent_relay_cursor_credential_kind` | credential handed to the CLI as | +| ---------------------------- | -------------------------------------------- | ------------------------------- | +| `false` (default) | `worker_token` | `--auth-token` | +| `true` | `api_key` | `CURSOR_API_KEY` | ## Parameters diff --git a/registry/coder/modules/agent-relay-cursor/main.test.ts b/registry/coder/modules/agent-relay-cursor/main.test.ts index e6b8d508f..dda52b7a7 100644 --- a/registry/coder/modules/agent-relay-cursor/main.test.ts +++ b/registry/coder/modules/agent-relay-cursor/main.test.ts @@ -53,6 +53,7 @@ const DISPATCH_ENV = [ "CURSOR_AGENT_WORKER_ID=worker-123", `AGENT_RELAY_CURSOR_POOL_NAME=${POOL_NAME}`, "AGENT_RELAY_CURSOR_IDLE_RELEASE_TIMEOUT=600", + "AGENT_RELAY_CURSOR_CREDENTIAL_KIND=worker_token", ]; const setup = async (vars: Record = {}) => { @@ -366,15 +367,19 @@ describe("agent-relay-cursor", () => { }); it("hands a shared service-account key over as CURSOR_API_KEY", async () => { - // insecure_shared_token pools stamp the team key; the CLI only - // accepts it as an API key, so --auth-token must be absent and the - // key must reach the worker's environment. - const { id, scripts } = await setup({ credential_kind: "api_key" }); + // insecure_shared_token pools stamp the team key and + // agent_relay_cursor_credential_kind=api_key; the CLI only accepts + // that key as an API key, so --auth-token must be absent and the key + // must reach the worker's environment. + const { id, scripts } = await setup(); await stubAgent( id, 'printf "%s\\n" "$@" >/tmp/agent-args; printf "%s" "$CURSOR_API_KEY" >/tmp/agent-api-key; sleep 30', ); - const { start } = await runDispatched(id, scripts); + const { start } = await runScripts(id, scripts, [ + ...DISPATCH_ENV, + "AGENT_RELAY_CURSOR_CREDENTIAL_KIND=api_key", + ]); expect(start.exitCode).toBe(0); const args = (await readFileContainer(id, "/tmp/agent-args")).split("\n"); expect(args).not.toContain("--auth-token"); diff --git a/registry/coder/modules/agent-relay-cursor/main.tf b/registry/coder/modules/agent-relay-cursor/main.tf index 091f46c81..2b5f7aae5 100644 --- a/registry/coder/modules/agent-relay-cursor/main.tf +++ b/registry/coder/modules/agent-relay-cursor/main.tf @@ -14,7 +14,8 @@ # # - agent_relay_session_id, agent_relay_delivery_id, agent_relay_pool, # agent_relay_cursor_pool_name, agent_relay_cursor_idle_release_timeout, -# and agent_relay_cursor_repo_url are persistent state: coderd only +# agent_relay_cursor_repo_url, and agent_relay_cursor_credential_kind +# are persistent state: coderd only # stores parameter values the template declares, and Agent Relay's # dedupe and reconciliation query workspaces with `param:` search # filters on them. agent_relay_cursor_repo_url is stamped on every @@ -62,17 +63,6 @@ variable "install_cli" { description = "Install the Cursor CLI (curl https://cursor.com/install -fsSL | bash) when the workspace starts and the CLI is not already on PATH. Defaults to true so a template works against an image that has no CLI. A CLI already in the image is used as is and never upgraded, which is the recommended and fastest path: the download only runs when the binary is missing, and it then needs outbound access to cursor.com and spends part of the claim-to-ready window." } -variable "credential_kind" { - type = string - default = "worker_token" - description = "How the worker authenticates with the stamped agent_relay_credential. worker_token (default) passes it as --auth-token, for the per-user sub-token Agent Relay mints. api_key exports it as CURSOR_API_KEY, for pools that opt into Agent Relay's insecure_shared_token and stamp the team service-account key itself; the CLI rejects that key as --auth-token and only accepts it as an API key." - - validation { - condition = contains(["worker_token", "api_key"], var.credential_kind) - error_message = "credential_kind must be worker_token or api_key." - } -} - variable "computer_use" { type = bool default = false @@ -188,6 +178,29 @@ data "coder_parameter" "agent_relay_cursor_repo_url" { }) } +data "coder_parameter" "agent_relay_cursor_credential_kind" { + name = "agent_relay_cursor_credential_kind" + display_name = "Cursor credential kind" + description = "How the worker hands agent_relay_credential to the Cursor CLI. worker_token passes the per-user sub-token as --auth-token. api_key exports the pool's service-account key as CURSOR_API_KEY, which is the only form the CLI accepts it in; Agent Relay sets this to api_key for pools with insecure_shared_token. A human never fills it in." + type = "string" + mutable = true + default = "worker_token" + order = 1006 + styling = jsonencode({ + disabled = true + placeholder = "Set by Agent Relay on dispatch" + }) + + option { + name = "Worker token (--auth-token)" + value = "worker_token" + } + option { + name = "Service-account API key (CURSOR_API_KEY)" + value = "api_key" + } +} + data "coder_parameter" "agent_relay_credential" { name = "agent_relay_credential" display_name = "Agent Relay credential" @@ -196,7 +209,7 @@ data "coder_parameter" "agent_relay_credential" { ephemeral = true mutable = true default = "" - order = 1006 + order = 1007 styling = jsonencode({ disabled = true mask_input = true @@ -206,11 +219,11 @@ data "coder_parameter" "agent_relay_credential" { # CURSOR_AGENT_WORKER_ID is the Cursor CLI's contract; do not rename it. # The credential travels under an Agent Relay name and the supervisor -# decides how to hand it to the CLI (credential_kind): a per-user worker -# token has no CLI env var and goes on the command line as --auth-token; -# a team service-account key is only accepted as CURSOR_API_KEY. Each is -# rejected through the other door, so the kind must match what the pool -# stamps. +# decides how to hand it to the CLI from the stamped credential kind: a +# per-user worker token has no CLI env var and goes on the command line +# as --auth-token; a team service-account key is only accepted as +# CURSOR_API_KEY. Each is rejected through the other door, which is why +# the relay stamps the kind alongside the credential. resource "coder_env" "agent_relay_cursor_token" { agent_id = var.agent_id name = "AGENT_RELAY_CURSOR_TOKEN" @@ -238,6 +251,12 @@ resource "coder_env" "agent_relay_cursor_idle_release_timeout" { value = data.coder_parameter.agent_relay_cursor_idle_release_timeout.value } +resource "coder_env" "agent_relay_cursor_credential_kind" { + agent_id = var.agent_id + name = "AGENT_RELAY_CURSOR_CREDENTIAL_KIND" + value = data.coder_parameter.agent_relay_cursor_credential_kind.value +} + locals { # coder-utils requires this exact layout. Scripts land in scripts/ and # their output in logs/; the worker state and log default to the same @@ -254,7 +273,6 @@ locals { cli_binary = var.cli_binary install_cli = var.install_cli computer_use = var.computer_use - credential_kind = var.credential_kind state_file = var.state_file log_file = var.log_file }) diff --git a/registry/coder/modules/agent-relay-cursor/main.tftest.hcl b/registry/coder/modules/agent-relay-cursor/main.tftest.hcl index 824c0b20f..36340084d 100644 --- a/registry/coder/modules/agent-relay-cursor/main.tftest.hcl +++ b/registry/coder/modules/agent-relay-cursor/main.tftest.hcl @@ -55,6 +55,7 @@ run "parameter_contract" { data.coder_parameter.agent_relay_cursor_pool_name.ephemeral == false, data.coder_parameter.agent_relay_cursor_repo_url.ephemeral == false, data.coder_parameter.agent_relay_cursor_idle_release_timeout.ephemeral == false, + data.coder_parameter.agent_relay_cursor_credential_kind.ephemeral == false, data.coder_parameter.agent_relay_credential.ephemeral == true, ]) error_message = "parameter persistence does not match the Agent Relay contract" @@ -145,8 +146,14 @@ run "worker_wiring" { # environment; it must not be expanded into the supervisor file the # script writes to disk. assert { - condition = strcontains(local.start_script, "--auth-token \"\\$AGENT_RELAY_CURSOR_TOKEN\"") && !strcontains(local.start_script, "CURSOR_API_KEY") - error_message = "the worker must authenticate with --auth-token read from the environment at run time" + condition = strcontains(local.start_script, "auth_args=(--auth-token \"\\$AGENT_RELAY_CURSOR_TOKEN\")") && strcontains(local.start_script, "export CURSOR_API_KEY=\"\\$AGENT_RELAY_CURSOR_TOKEN\"") + error_message = "the supervisor must offer both credential paths, each reading the token from the environment at run time" + } + + # The relay stamps which path applies; the supervisor branches on it. + assert { + condition = data.coder_parameter.agent_relay_cursor_credential_kind.name == "agent_relay_cursor_credential_kind" && data.coder_parameter.agent_relay_cursor_credential_kind.default == "worker_token" && coder_env.agent_relay_cursor_credential_kind.name == "AGENT_RELAY_CURSOR_CREDENTIAL_KIND" + error_message = "the credential kind is a stamped parameter defaulting to worker_token, exported for the supervisor" } # Reaping reads this file through the agent_relay_status metadata item, @@ -174,32 +181,6 @@ run "worker_wiring" { } } -# A pool that stamps its service-account key (insecure_shared_token) -# needs the credential handed over as CURSOR_API_KEY; the CLI rejects -# that key as --auth-token. -run "api_key_credential" { - command = plan - - variables { - credential_kind = "api_key" - } - - assert { - condition = strcontains(local.start_script, "export CURSOR_API_KEY=\"\\$AGENT_RELAY_CURSOR_TOKEN\"") && !strcontains(local.start_script, "--auth-token \"") - error_message = "api_key must export CURSOR_API_KEY from the stamped credential and drop --auth-token" - } -} - -run "credential_kind_rejects_unknown" { - command = plan - - variables { - credential_kind = "cookie" - } - - expect_failures = [var.credential_kind] -} - run "computer_use_off_by_default" { command = plan diff --git a/registry/coder/modules/agent-relay-cursor/start.sh.tftpl b/registry/coder/modules/agent-relay-cursor/start.sh.tftpl index 382eed34d..5e70a712f 100644 --- a/registry/coder/modules/agent-relay-cursor/start.sh.tftpl +++ b/registry/coder/modules/agent-relay-cursor/start.sh.tftpl @@ -108,17 +108,21 @@ write_state() { } export PATH="\$HOME/.local/bin:\$PATH" -%{ if credential_kind == "api_key" ~} -# The pool stamps its team service-account key; the CLI accepts that -# only as an API key, never as --auth-token. -export CURSOR_API_KEY="\$AGENT_RELAY_CURSOR_TOKEN" -%{ endif ~} +# The relay stamps how the credential must reach the CLI. A pool with +# insecure_shared_token sends its team service-account key, which the +# CLI accepts only as CURSOR_API_KEY; a per-user worker token is only +# accepted as --auth-token. Both are read from the environment at run +# time so neither value lands in this file. +auth_args=() +if [ "\$${AGENT_RELAY_CURSOR_CREDENTIAL_KIND:-worker_token}" = api_key ]; then + export CURSOR_API_KEY="\$AGENT_RELAY_CURSOR_TOKEN" +else + auth_args=(--auth-token "\$AGENT_RELAY_CURSOR_TOKEN") +fi ${cli_binary} worker \\ --pool "\$AGENT_RELAY_CURSOR_POOL_NAME" \\ --idle-release-timeout "\$AGENT_RELAY_CURSOR_IDLE_RELEASE_TIMEOUT" \\ -%{ if credential_kind == "worker_token" ~} - --auth-token "\$AGENT_RELAY_CURSOR_TOKEN" \\ -%{ endif ~} + \$${auth_args[@]+"\$${auth_args[@]}"} \\ %{ if computer_use ~} --computer-use \\ %{ endif ~}