effect-cursor-sdk 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -59
- package/dist/index.d.ts +244 -154
- package/dist/index.js +209 -158
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# effect-cursor-sdk
|
|
2
2
|
|
|
3
|
+
   
|
|
4
|
+
|
|
3
5
|
Effect-native access to the new [Cursor SDK](https://cursor.com/docs/sdk/typescript).
|
|
4
6
|
|
|
5
7
|
`effect-cursor-sdk` wraps `@cursor/sdk` with Effect services, layers, scoped resource management, tagged errors, observability hooks, deterministic mocks, and ready-made runtimes. The upstream SDK remains the source of truth for Cursor-owned types; this package adds Effect ergonomics without creating a parallel model that can drift.
|
|
@@ -27,7 +29,7 @@ Effect-native access to the new [Cursor SDK](https://cursor.com/docs/sdk/typescr
|
|
|
27
29
|
| `Agent.list`, `get`, `listRuns`, `getRun`, messages | `CursorInspectionService` |
|
|
28
30
|
| `Agent.archive`, `unarchive`, `delete` | `CursorInspectionService` |
|
|
29
31
|
| `Cursor.me`, models, repositories | `CursorInspectionService` |
|
|
30
|
-
| MCP servers, sub-agents, local/cloud options, model options | SDK
|
|
32
|
+
| MCP servers, sub-agents, local/cloud options, model options | Defaults via `CursorConfig` / `loadCursorConfig`; merged SDK `AgentOptions` (deprecated at agent entry) |
|
|
31
33
|
| Local run event helpers and platform helpers | Re-exported from `@cursor/sdk` |
|
|
32
34
|
|
|
33
35
|
## Install
|
|
@@ -44,18 +46,44 @@ bun run typecheck
|
|
|
44
46
|
bun run test
|
|
45
47
|
```
|
|
46
48
|
|
|
49
|
+
## Examples
|
|
50
|
+
|
|
51
|
+
The [`examples`](./examples) directory contains a guided learning path from a
|
|
52
|
+
minimal first script to production-style Effect composition:
|
|
53
|
+
|
|
54
|
+
| Example | What it demonstrates |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| [`quickstart`](./examples/quickstart) | First config-first local agent call with `loadCursorConfig`, `agentOptionsFromConfig`, and `collectText`. |
|
|
57
|
+
| [`cli`](./examples/cli) | A small terminal app with `liveRuntime`, offline `makeMockRuntime`, CLI overrides, and tagged error handling. |
|
|
58
|
+
| [`basic-agent-workflow`](./examples/basic-agent-workflow) | Scoped agents, run status listeners, streaming, capability checks, and artifact listing/downloads. |
|
|
59
|
+
| [`advanced-ops-dashboard`](./examples/advanced-ops-dashboard) | Inspection APIs, confirmation-gated lifecycle operations, parallel Effect composition, retries/timeouts, telemetry, redaction, and rich mocks. |
|
|
60
|
+
|
|
61
|
+
Run all example typechecks from the repo root:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bun run examples:typecheck
|
|
65
|
+
```
|
|
66
|
+
|
|
47
67
|
## Quick Start
|
|
48
68
|
|
|
69
|
+
Load environment defaults with `loadCursorConfig`, then create agents with `createFromConfig` (and `scopedFromConfig`, `promptFromConfig`, `resumeFromConfig` as needed). The API key stays in `Redacted` form until the merge step; `AgentOptions.apiKey` remains a plain string at the SDK boundary.
|
|
70
|
+
|
|
49
71
|
```ts
|
|
50
|
-
import {
|
|
51
|
-
|
|
72
|
+
import {
|
|
73
|
+
CursorAgentService,
|
|
74
|
+
CursorRunService,
|
|
75
|
+
loadCursorConfig,
|
|
76
|
+
liveLayer,
|
|
77
|
+
} from "effect-cursor-sdk";
|
|
78
|
+
import { Effect } from "effect";
|
|
52
79
|
|
|
53
80
|
const program = Effect.gen(function* () {
|
|
54
81
|
const agents = yield* CursorAgentService;
|
|
55
82
|
const runs = yield* CursorRunService;
|
|
56
83
|
|
|
57
|
-
const
|
|
58
|
-
|
|
84
|
+
const config = yield* loadCursorConfig;
|
|
85
|
+
const agent = yield* agents.createFromConfig(config, {
|
|
86
|
+
// Override the given config optionally with custom values
|
|
59
87
|
model: { id: "composer-2" },
|
|
60
88
|
local: { cwd: process.cwd() },
|
|
61
89
|
});
|
|
@@ -68,56 +96,50 @@ const program = Effect.gen(function* () {
|
|
|
68
96
|
}).pipe(Effect.provide(liveLayer));
|
|
69
97
|
```
|
|
70
98
|
|
|
71
|
-
`AgentOptions.apiKey` is a plain string at the SDK boundary. To keep the key in `Redacted` form until that boundary, read `CURSOR_API_KEY` (and optional `CURSOR_MODEL`, `CURSOR_LOCAL_CWD`) with `loadCursorConfig`, then merge into `AgentOptions` with `agentOptionsFromConfig`.
|
|
72
|
-
In a future version, we might drop the plain `AgentOptions` boundary and require all options to be passed through `CursorConfig` / `loadCursorConfig` / `agentOptionsFromConfig`.
|
|
73
|
-
|
|
74
99
|
Effect’s default `ConfigProvider` reads `process.env`, so you usually do not need to install a custom provider for this.
|
|
75
100
|
|
|
76
|
-
|
|
101
|
+
If you need full control over the merge into SDK options, you can still call `agentOptionsFromConfig` yourself and pass the result to deprecated `create`; prefer `createFromConfig` in application code.
|
|
102
|
+
|
|
103
|
+
## Plain `AgentOptions` at the agent boundary (deprecated)
|
|
104
|
+
|
|
105
|
+
Passing raw `AgentOptions` (for example `apiKey: process.env.CURSOR_API_KEY`) to `create`, `resume`, `prompt`, or `scoped` is **deprecated**. It still works for compatibility, but prefer the config flow above.
|
|
77
106
|
|
|
78
107
|
```ts
|
|
79
|
-
import {
|
|
80
|
-
CursorAgentService,
|
|
81
|
-
CursorRunService,
|
|
82
|
-
agentOptionsFromConfig,
|
|
83
|
-
loadCursorConfig,
|
|
84
|
-
liveLayer,
|
|
85
|
-
} from "effect-cursor-sdk";
|
|
108
|
+
import { CursorAgentService, CursorRunService, liveLayer } from "effect-cursor-sdk";
|
|
86
109
|
import { Effect } from "effect";
|
|
87
110
|
|
|
88
|
-
const
|
|
111
|
+
const legacyProgram = Effect.gen(function* () {
|
|
89
112
|
const agents = yield* CursorAgentService;
|
|
90
113
|
const runs = yield* CursorRunService;
|
|
91
114
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
local: { cwd: process.cwd() },
|
|
98
|
-
}),
|
|
99
|
-
);
|
|
115
|
+
const agent = yield* agents.create({
|
|
116
|
+
apiKey: process.env.CURSOR_API_KEY,
|
|
117
|
+
model: { id: "composer-2" },
|
|
118
|
+
local: { cwd: process.cwd() },
|
|
119
|
+
});
|
|
100
120
|
|
|
101
121
|
const run = yield* agents.send(agent, "Explain this repository");
|
|
102
122
|
const text = yield* runs.collectText(run);
|
|
103
|
-
|
|
104
123
|
yield* agents.dispose(agent);
|
|
105
124
|
return text;
|
|
106
125
|
}).pipe(Effect.provide(liveLayer));
|
|
107
126
|
```
|
|
108
127
|
|
|
128
|
+
Migrate by replacing `agents.create({ ... })` with `const config = yield* loadCursorConfig` and `agents.createFromConfig(config, { ... })` (or the other `*FromConfig` helpers).
|
|
129
|
+
|
|
109
130
|
## Scoped Agents
|
|
110
131
|
|
|
111
|
-
Prefer `
|
|
132
|
+
Prefer `scopedFromConfig` when an agent should be disposed automatically:
|
|
112
133
|
|
|
113
134
|
```ts
|
|
114
|
-
import { CursorAgentService, liveLayer } from "effect-cursor-sdk";
|
|
135
|
+
import { CursorAgentService, loadCursorConfig, liveLayer } from "effect-cursor-sdk";
|
|
115
136
|
import { Effect } from "effect";
|
|
116
137
|
|
|
117
138
|
const program = Effect.scoped(
|
|
118
139
|
Effect.gen(function* () {
|
|
119
140
|
const agents = yield* CursorAgentService;
|
|
120
|
-
const
|
|
141
|
+
const config = yield* loadCursorConfig;
|
|
142
|
+
const agent = yield* agents.scopedFromConfig(config, {
|
|
121
143
|
model: { id: "composer-2" },
|
|
122
144
|
local: { cwd: process.cwd() },
|
|
123
145
|
});
|
|
@@ -129,19 +151,19 @@ const program = Effect.scoped(
|
|
|
129
151
|
|
|
130
152
|
## Cloud Agents
|
|
131
153
|
|
|
132
|
-
Cloud options are
|
|
154
|
+
Cloud options are merged as SDK overrides on top of loaded config:
|
|
133
155
|
|
|
134
156
|
```ts
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
157
|
+
const config = yield* loadCursorConfig;
|
|
158
|
+
const agent = yield* agents.createFromConfig(config, {
|
|
159
|
+
model: { id: "composer-2" },
|
|
160
|
+
cloud: {
|
|
161
|
+
repos: [
|
|
162
|
+
{ url: "https://github.com/your-org/your-repo", startingRef: "main" },
|
|
163
|
+
],
|
|
164
|
+
autoCreatePR: true,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
145
167
|
```
|
|
146
168
|
|
|
147
169
|
## Streaming
|
|
@@ -149,18 +171,24 @@ const agent =
|
|
|
149
171
|
`CursorRunService.streamEvents` preserves SDK event shapes and returns an Effect `Stream`.
|
|
150
172
|
|
|
151
173
|
```ts
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
yield
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
174
|
+
import { Effect, Stream } from "effect";
|
|
175
|
+
|
|
176
|
+
const run = yield* agents.send(agent, "Refactor the auth module");
|
|
177
|
+
|
|
178
|
+
yield* runs.streamEvents(run).pipe(
|
|
179
|
+
Stream.runForEach((event) => {
|
|
180
|
+
if (event.type !== "assistant") {
|
|
181
|
+
return Effect.void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const text = event.message.content
|
|
185
|
+
.filter((block) => block.type === "text")
|
|
186
|
+
.map((block) => block.text)
|
|
187
|
+
.join("");
|
|
188
|
+
|
|
189
|
+
return Effect.sync(() => console.log(text));
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
164
192
|
```
|
|
165
193
|
|
|
166
194
|
## Inspection And Metadata
|
|
@@ -168,13 +196,73 @@ yield *
|
|
|
168
196
|
Use `CursorInspectionService` for agent/run listings, messages, lifecycle operations, account metadata, model discovery, and connected repositories.
|
|
169
197
|
|
|
170
198
|
```ts
|
|
171
|
-
const inspection = yield
|
|
199
|
+
const inspection = yield* CursorInspectionService;
|
|
172
200
|
|
|
173
|
-
const agents = yield
|
|
174
|
-
const models = yield
|
|
175
|
-
const repos = yield
|
|
201
|
+
const agents = yield* inspection.listAgents({ runtime: "cloud", includeArchived: true });
|
|
202
|
+
const models = yield* inspection.listModels();
|
|
203
|
+
const repos = yield* inspection.listRepositories();
|
|
176
204
|
```
|
|
177
205
|
|
|
206
|
+
## Integrate deeper with Effect
|
|
207
|
+
|
|
208
|
+
Because every Cursor call is an `Effect`, you compose it like the rest of your program: parallel requests, timeouts, retries, logging, and layers all work the same way.
|
|
209
|
+
|
|
210
|
+
This agent garden snapshot loads your catalog in parallel, adds a resilient boundary around the batch, logs a safe summary (counts and IDs only — never log API keys), then asks Cursor for a one-shot triage opinion via `promptFromConfig`:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import {
|
|
214
|
+
CursorAgentService,
|
|
215
|
+
CursorInspectionService,
|
|
216
|
+
loadCursorConfig,
|
|
217
|
+
liveLayer,
|
|
218
|
+
} from "effect-cursor-sdk";
|
|
219
|
+
import { Effect, Schedule } from "effect";
|
|
220
|
+
|
|
221
|
+
const agentGardenSnapshot = Effect.gen(function* () {
|
|
222
|
+
const inspection = yield* CursorInspectionService;
|
|
223
|
+
const agents = yield* CursorAgentService;
|
|
224
|
+
const config = yield* loadCursorConfig;
|
|
225
|
+
|
|
226
|
+
const catalog = yield* Effect.all(
|
|
227
|
+
{
|
|
228
|
+
cloud: inspection.listAgents({ runtime: "cloud", includeArchived: false }),
|
|
229
|
+
models: inspection.listModels(),
|
|
230
|
+
repos: inspection.listRepositories(),
|
|
231
|
+
},
|
|
232
|
+
{ concurrency: "unbounded" },
|
|
233
|
+
).pipe(
|
|
234
|
+
Effect.retry(
|
|
235
|
+
Schedule.exponential("150 millis").pipe(Schedule.both(Schedule.recurs(3))),
|
|
236
|
+
),
|
|
237
|
+
Effect.timeout("45 seconds"),
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
yield* Effect.logInfo("Cursor catalog loaded", {
|
|
241
|
+
cloudAgents: catalog.cloud.items.length,
|
|
242
|
+
models: catalog.models.length,
|
|
243
|
+
repos: catalog.repos.length,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const triage = yield* agents.promptFromConfig(
|
|
247
|
+
[
|
|
248
|
+
"You are helping on-call. Here is non-secret inventory:",
|
|
249
|
+
`- Cloud agents (ids): ${catalog.cloud.items.map((a) => a.agentId).join(", ") || "(none)"}`,
|
|
250
|
+
`- Models (ids): ${catalog.models.map((m) => m.id).join(", ") || "(none)"}`,
|
|
251
|
+
`- Repos (urls): ${catalog.repos.map((r) => r.url).join(", ") || "(none)"}`,
|
|
252
|
+
"In two short sentences: what should we verify first before trusting automation here?",
|
|
253
|
+
].join("\n"),
|
|
254
|
+
config,
|
|
255
|
+
{
|
|
256
|
+
model: { id: "composer-2" },
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return triage.result;
|
|
261
|
+
}).pipe(Effect.provide(liveLayer));
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Swap `liveLayer` for `mockLayer({ ... })` in tests and the same program shape exercises your orchestration without the network.
|
|
265
|
+
|
|
178
266
|
## Errors
|
|
179
267
|
|
|
180
268
|
SDK failures are mapped into tagged errors such as `CursorAuthenticationError`, `CursorRateLimitError`, `CursorConfigurationError`, `CursorNetworkError`, and `CursorUnsupportedOperationError`. The original SDK error is preserved as `cause`, with safe operation context and retryability where available.
|
|
@@ -201,12 +289,13 @@ Never log API keys, MCP credentials, authorization headers, or prompt image data
|
|
|
201
289
|
Use `mockLayer` for deterministic tests:
|
|
202
290
|
|
|
203
291
|
```ts
|
|
204
|
-
import { CursorAgentService, mockLayer } from "effect-cursor-sdk";
|
|
292
|
+
import { CursorAgentService, loadCursorConfig, mockLayer } from "effect-cursor-sdk";
|
|
205
293
|
import { Effect } from "effect";
|
|
206
294
|
|
|
207
295
|
const testProgram = Effect.gen(function* () {
|
|
208
296
|
const agents = yield* CursorAgentService;
|
|
209
|
-
const
|
|
297
|
+
const config = yield* loadCursorConfig;
|
|
298
|
+
const agent = yield* agents.createFromConfig(config, { model: { id: "composer-2" } });
|
|
210
299
|
return yield* agents.send(agent, "Hello");
|
|
211
300
|
}).pipe(
|
|
212
301
|
Effect.provide(
|
|
@@ -221,7 +310,7 @@ const testProgram = Effect.gen(function* () {
|
|
|
221
310
|
|
|
222
311
|
The main exports are:
|
|
223
312
|
|
|
224
|
-
- `CursorAgentService`
|
|
313
|
+
- `CursorAgentService` (prefer `createFromConfig`, `scopedFromConfig`, `promptFromConfig`, `resumeFromConfig` with `loadCursorConfig`)
|
|
225
314
|
- `CursorRunService`
|
|
226
315
|
- `CursorArtifactService`
|
|
227
316
|
- `CursorInspectionService`
|