Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 120a0e3

Browse files
authored
feat(cursor): Add Cursor IDE module (#290)
1 parent b51932d commit 120a0e3

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

.icons/cursor.svg

Lines changed: 1 addition & 0 deletions
Loading

cursor/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
display_name: Cursor IDE
3+
description: Add a one-click button to launch Cursor IDE
4+
icon: ../.icons/cursor.svg
5+
maintainer_github: coder
6+
verified: true
7+
tags: [ide, cursor, helper]
8+
---
9+
10+
# Cursor IDE
11+
12+
Add a button to open any workspace with a single click in Cursor IDE.
13+
14+
Uses the [Coder Remote VS Code Extension](https://github.com/coder/cursor-coder).
15+
16+
```tf
17+
module "cursor" {
18+
source = "registry.coder.com/modules/cursor/coder"
19+
version = "1.0.18"
20+
agent_id = coder_agent.example.id
21+
}
22+
```
23+
24+
## Examples
25+
26+
### Open in a specific directory
27+
28+
```tf
29+
module "cursor" {
30+
source = "registry.coder.com/modules/cursor/coder"
31+
version = "1.0.18"
32+
agent_id = coder_agent.example.id
33+
folder = "/home/coder/project"
34+
}
35+
```

cursor/main.test.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { describe, expect, it } from "bun:test";
2+
import {
3+
executeScriptInContainer,
4+
runTerraformApply,
5+
runTerraformInit,
6+
testRequiredVariables,
7+
} from "../test";
8+
9+
describe("cursor", async () => {
10+
await runTerraformInit(import.meta.dir);
11+
12+
testRequiredVariables(import.meta.dir, {
13+
agent_id: "foo",
14+
});
15+
16+
it("default output", async () => {
17+
const state = await runTerraformApply(import.meta.dir, {
18+
agent_id: "foo",
19+
});
20+
expect(state.outputs.cursor_url.value).toBe(
21+
"cursor://coder.coder-remote/open?owner=default&workspace=default&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
22+
);
23+
24+
const coder_app = state.resources.find(
25+
(res) => res.type === "coder_app" && res.name === "cursor",
26+
);
27+
28+
expect(coder_app).not.toBeNull();
29+
expect(coder_app?.instances.length).toBe(1);
30+
expect(coder_app?.instances[0].attributes.order).toBeNull();
31+
});
32+
33+
it("adds folder", async () => {
34+
const state = await runTerraformApply(import.meta.dir, {
35+
agent_id: "foo",
36+
folder: "/foo/bar",
37+
});
38+
expect(state.outputs.cursor_url.value).toBe(
39+
"cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
40+
);
41+
});
42+
43+
it("adds folder and open_recent", async () => {
44+
const state = await runTerraformApply(import.meta.dir, {
45+
agent_id: "foo",
46+
folder: "/foo/bar",
47+
open_recent: "true",
48+
});
49+
expect(state.outputs.cursor_url.value).toBe(
50+
"cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&openRecent&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
51+
);
52+
});
53+
54+
it("adds folder but not open_recent", async () => {
55+
const state = await runTerraformApply(import.meta.dir, {
56+
agent_id: "foo",
57+
folder: "/foo/bar",
58+
openRecent: "false",
59+
});
60+
expect(state.outputs.cursor_url.value).toBe(
61+
"cursor://coder.coder-remote/open?owner=default&workspace=default&folder=/foo/bar&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
62+
);
63+
});
64+
65+
it("adds open_recent", async () => {
66+
const state = await runTerraformApply(import.meta.dir, {
67+
agent_id: "foo",
68+
open_recent: "true",
69+
});
70+
expect(state.outputs.cursor_url.value).toBe(
71+
"cursor://coder.coder-remote/open?owner=default&workspace=default&openRecent&url=https://mydeployment.coder.com&token=$SESSION_TOKEN",
72+
);
73+
});
74+
75+
it("expect order to be set", async () => {
76+
const state = await runTerraformApply(import.meta.dir, {
77+
agent_id: "foo",
78+
order: "22",
79+
});
80+
81+
const coder_app = state.resources.find(
82+
(res) => res.type === "coder_app" && res.name === "cursor",
83+
);
84+
85+
expect(coder_app).not.toBeNull();
86+
expect(coder_app?.instances.length).toBe(1);
87+
expect(coder_app?.instances[0].attributes.order).toBe(22);
88+
});
89+
});

cursor/main.tf

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
terraform {
2+
required_version = ">= 1.0"
3+
4+
required_providers {
5+
coder = {
6+
source = "coder/coder"
7+
version = ">= 0.23"
8+
}
9+
}
10+
}
11+
12+
variable "agent_id" {
13+
type = string
14+
description = "The ID of a Coder agent."
15+
}
16+
17+
variable "folder" {
18+
type = string
19+
description = "The folder to open in Cursor IDE."
20+
default = ""
21+
}
22+
23+
variable "open_recent" {
24+
type = bool
25+
description = "Open the most recent workspace or folder. Falls back to the folder if there is no recent workspace or folder to open."
26+
default = false
27+
}
28+
29+
variable "order" {
30+
type = number
31+
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)."
32+
default = null
33+
}
34+
35+
data "coder_workspace" "me" {}
36+
data "coder_workspace_owner" "me" {}
37+
38+
resource "coder_app" "cursor" {
39+
agent_id = var.agent_id
40+
external = true
41+
icon = "/icon/cursor.svg"
42+
slug = "cursor"
43+
display_name = "Cursor IDE"
44+
order = var.order
45+
url = join("", [
46+
"cursor://coder.coder-remote/open",
47+
"?owner=",
48+
data.coder_workspace_owner.me.name,
49+
"&workspace=",
50+
data.coder_workspace.me.name,
51+
var.folder != "" ? join("", ["&folder=", var.folder]) : "",
52+
var.open_recent ? "&openRecent" : "",
53+
"&url=",
54+
data.coder_workspace.me.access_url,
55+
"&token=$SESSION_TOKEN",
56+
])
57+
}
58+
59+
output "cursor_url" {
60+
value = coder_app.cursor.url
61+
description = "Cursor IDE Desktop URL."
62+
}

0 commit comments

Comments
 (0)