diff --git a/.gitignore b/.gitignore index 63be609..4a08b30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ node_modules/ dist/ +packages/*/dist coverage/ *.tgz .env diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fc791c..0840bfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.3.0 + +- Split into three packages: `cursor-cloud-core`, `openclaw-plugin-cursor-cloud`, and `cursor-cloud-mcp`. +- MCP is a standalone stdio binary (`cursor-cloud-mcp`). The OpenClaw plugin remains the gateway door. + ## 0.2.0 - `cursor_cloud_launch` accepts `effort` (`low|med|high|xhigh`) and `fast` and sends them as `model.params`. `med` maps to Cursor `medium`; Grok 4.7 uses `reasoning_effort`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a79312e..ce1439f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,19 +4,13 @@ | Path | Role | | --- | --- | -| `src/client.ts` | Cursor Cloud Agents API v1 | -| `src/registry.ts` | Env ids → `env` payload; base vs project allocation | -| `src/ledger.ts` | Local `agents.json` of launched `bc-…` | -| `src/env-catalog.ts` | Harvest named envs from `GET /v1/agents` | -| `src/harvest.ts` | Gateway init harvest service | -| `src/placement.ts` | Fresh vs reuse; session-bound agents | -| `src/actions.ts` | Shared launch/reply/status/cancel/watch | -| `src/plugin.ts` | OpenClaw tools | -| `src/cli.ts` / `src/mcp.ts` | Other surfaces | -| `src/setup.ts` | One-command gateway install | -| `skills/cursor-cloud/` | Agent door and first-proof | -| `examples/gateway-plugin.json` | Gateway plugin config | -| `examples/cli-config.json` | CLI config | +| `packages/core` | `cursor-cloud-core` — API client, harvest, actions, CLI | +| `packages/plugin` | `openclaw-plugin-cursor-cloud` — OpenClaw tools, skill, setup | +| `packages/mcp` | `cursor-cloud-mcp` — stdio MCP server | +| `packages/core/src/client.ts` | Cursor Cloud Agents API v1 | +| `packages/core/src/actions.ts` | launch/reply/status/cancel/watch | +| `packages/plugin/src/plugin.ts` | `defineToolPlugin` | +| `packages/mcp/src/mcp.ts` | JSON-RPC tools/list + tools/call | Re-read [Cursor Cloud Agents API](https://cursor.com/docs/cloud-agent/api/endpoints) when changing the client. Do not copy community MCP source into this tree. @@ -25,19 +19,20 @@ Re-read [Cursor Cloud Agents API](https://cursor.com/docs/cloud-agent/api/endpoi ```bash npm install npm test +npm run build npm run plugin:build npm run plugin:validate ``` -Commit the generated `openclaw.plugin.json` when tool names or config schema change. +Commit `packages/plugin/openclaw.plugin.json` when tool names or config schema change. ## Release 1. `./scripts/release.sh ` 2. Commit the version bump (`chore: release `). -3. Tag `v` and push the tag (publishes npm when `NPM_TOKEN` is set). -4. `clawhub package publish .` when the ClawHub owner is ready. -5. After ClawHub exists, set `package.json#openclaw.install.clawhubSpec`. +3. Tag `v` and push the tag. +4. Publish **core**, then **mcp**, then **plugin** (`npm publish --access public` in each package). +5. `clawhub package publish packages/plugin` when the ClawHub owner is ready. ## Rules that should not regress @@ -48,3 +43,4 @@ Commit the generated `openclaw.plugin.json` when tool names or config schema cha 5. Do not add archive/delete/artifact tools to the default set. 6. Operator and skill first proof stay `cursor_cloud_me` then a `bc-…`. 7. Launched-agent JSON stays off git (`agents.json` / `state/`). +8. Core has no OpenClaw or MCP dependency. Plugin and MCP both depend on core only. diff --git a/README.md b/README.md index a31f6ee..8098d48 100644 --- a/README.md +++ b/README.md @@ -1,106 +1,89 @@ -# openclaw-plugin-cursor-cloud +# cursor-cloud-agent -OpenClaw plugin that delegates coding work to **Cursor Cloud Agents on named saved environments**. Also a CLI and stdio MCP server. +Three packages, one coordinator. Coding work runs on **Cursor Cloud Agents** (`bc-…`), not on the host. -**Door:** `cursor_cloud_*` tools and the `cursor-cloud` skill. -**Implementer:** a Cloud agent (`bc-…`). -**Not the door:** `agent -p` (runs here), or `repos` together with a named cloud env. +| Package | npm | Role | +| --- | --- | --- | +| `packages/core` | `cursor-cloud-core` | API client, harvest, launch/reply/status/watch, `cursor-cloud` CLI | +| `packages/plugin` | `openclaw-plugin-cursor-cloud` | OpenClaw `cursor_cloud_*` tools + skill | +| `packages/mcp` | `cursor-cloud-mcp` | Stdio MCP server for the same tools | -Official API: [Cloud Agents API v1](https://cursor.com/docs/cloud-agent/api/endpoints). Host pin: `https://api.cursor.com`. Key: `CURSOR_API_KEY` on the gateway environment, never in git. +**Not the door:** `agent -p` on the coordinator, or `repos` together with a named cloud env. + +Official API: [Cloud Agents API v1](https://cursor.com/docs/cloud-agent/api/endpoints). Host pin: `https://api.cursor.com`. Key: `CURSOR_API_KEY`, never in git. ## You are done when -1. `cursor_cloud_me` (or `openclaw-cursor-cloud me`) returns `ok: true` and a key name. -2. A **new** chat can see `cursor_cloud_launch`. -3. `cursor_cloud_envs` lists a **base** env (clone the target) and any **project** envs (repos already loaded). -4. `cursor_cloud_launch` with a listed id returns `agent.id` (`bc-…`) and `agent.url`. A later chat reads `cursor_cloud_agents` and replies on that id. +1. `cursor_cloud_me` (or `cursor-cloud me`) returns `ok: true` and a key name. +2. A **new** OpenClaw chat can see `cursor_cloud_launch`, **or** an MCP client lists the same tools. +3. `cursor_cloud_envs` lists a **base** env and any **project** envs. +4. Launch returns `agent.id` (`bc-…`) and `agent.url`. Follow-ups use `cursor_cloud_reply`. 5. A finished run is judged by `proof.prUrls` (or an exact blocker). `IDLE` only means follow-ups are accepted. -## Install - -One command after npm publish: - -```bash -npx -y openclaw-plugin-cursor-cloud@0.2.0 setup -``` - -From this checkout: +## OpenClaw plugin ```bash +npx -y openclaw-plugin-cursor-cloud@0.3.0 setup +# from this checkout: ./scripts/install-gateway.sh ``` -`setup` installs the plugin, enables it, enables the `cursor-cloud` skill, and adds `cursor-cloud` to `tools.alsoAllow`. It does not write keys. - -Then: +`setup` links **this repo’s `packages/plugin`**, enables the plugin and `cursor-cloud` skill, and adds `cursor-cloud` to `tools.alsoAllow`. It does not write keys. ```bash -# gateway EnvironmentFile — not openclaw.json -CURSOR_API_KEY=... +CURSOR_API_KEY=... # gateway EnvironmentFile — not openclaw.json ``` -Restart the gateway. Open a new chat. Run the first proof above. - -Pinned install without `npx` (still needs the key, env registry, restart, new chat): +Restart the gateway. Open a **new** chat. ```bash -openclaw plugins install npm:openclaw-plugin-cursor-cloud@0.2.0 --force --accept-capabilities +openclaw plugins install npm:openclaw-plugin-cursor-cloud@0.3.0 --force --accept-capabilities ``` -After a ClawHub publish, use `clawhub:/openclaw-plugin-cursor-cloud` the same way. - -## Register an environment - -The model passes a **registry id** (`env: "payments"`), never an invented `{ type, name }`. - -- **base** — bootstrap env. Clone the target when it is not the env primary. -- **project** — repos already loaded and prepared. Set `allowRepos` to those URLs and a `project` key. +Gateway config: merge `packages/plugin/examples/gateway-plugin.json` into `plugins.entries.cursor-cloud`. -Plugin init (gateway startup service) harvests named environments from documented `GET /v1/agents` + `GET /v1/agents/{id}` into `~/.local/state/openclaw-cursor-cloud/envs.json`. Official v1 has no environment-list route. Unnamed dashboard fallbacks cannot be launched by `env.name` and are skipped. `cursor_cloud_envs` reads that catalog; `refresh: true` only forces a new harvest. +## MCP server -`cursor_cloud_launch` is the placement operation. OpenClaw gives the plugin `sessionId` (new on `/new` and `/reset`, kept across compact). If this chat is already bound to a `bc-…`, launch reuses it. If the user was not explicit, launch returns `phase: choose` — ask that tree, then recall with the option's `recall` fields. The CLI skips the ask and uses `defaultEnv`. - -Gateway: merge `examples/gateway-plugin.json` into `plugins.entries.cursor-cloud`. -CLI: copy `examples/cli-config.json` to `~/.config/openclaw-cursor-cloud/config.json`. -Allowlist only: `examples/instance-enable.batch.json`. - -Named `cloud` envs omit `repos` on the wire even when `allowRepos` lists a URL. - -Launched `bc-…` ids are written to `~/.local/state/openclaw-cursor-cloud/agents.json` (or `$XDG_STATE_HOME/...`). That file is not git. Override with `ledgerPath` (for example `state/agents.json` in a checkout — `state/` is gitignored). - -## Tools - -Always on: `cursor_cloud_launch`, `cursor_cloud_reply`, `cursor_cloud_status`, `cursor_cloud_cancel`, `cursor_cloud_watch`, `cursor_cloud_me`, `cursor_cloud_envs`, `cursor_cloud_agents`, `cursor_cloud_models`. - -Optional until `setup` / `tools.alsoAllow`: `cursor_cloud_list`. - -`cursor_cloud_status` returns final/partial result, `messages[]`, and short-lived artifact download refs. It does not download artifact bytes onto the coordinator. +```json +{ + "mcpServers": { + "cursor-cloud": { + "command": "cursor-cloud-mcp", + "env": { "CURSOR_API_KEY": "${CURSOR_API_KEY}" } + } + } +} +``` -Not shipped: archive, delete, GitHub repository listing. +See `packages/mcp/examples/mcp.json`. Same config file as the CLI: `CURSOR_CLOUD_CONFIG` or `~/.config/openclaw-cursor-cloud/config.json` (`packages/core/examples/cli-config.json`). -When to call which: `skills/cursor-cloud/SKILL.md`. +`openclaw-cursor-cloud mcp` still delegates to `cursor-cloud-mcp`. -## CLI and MCP +## CLI ```bash export CURSOR_API_KEY=... -openclaw-cursor-cloud me -openclaw-cursor-cloud envs --refresh -openclaw-cursor-cloud agents -openclaw-cursor-cloud launch --env base --prompt "Smoke the env, then stop." -openclaw-cursor-cloud reply --agent-id bc-… --prompt "Continue on the same branch." -openclaw-cursor-cloud mcp +cursor-cloud me +cursor-cloud envs --refresh +cursor-cloud launch --env base --prompt "Smoke the env, then stop." +cursor-cloud reply --agent-id bc-… --prompt "Continue on the same branch." ``` -MCP (`examples/mcp.json`) uses the same CLI config as above unless `CURSOR_CLOUD_CONFIG` is set. +Launch: `model` plus optional `effort` and `fast`. Effort spellings go through one alias table; the table's preferred value is what the catalog receives (`med` prefers `medium`). Omit both to keep the model's own variant. Reply cannot change those (`model_locked`). + +## Environments and tools + +The model passes a **registry id** (`env: "payments"`), never an invented `{ type, name }`. Harvest (plugin init or `envs --refresh`) fills named envs from `GET /v1/agents`. Unnamed dashboard envs are skipped. + +Always-on tools: launch, reply, status, cancel, watch, me, envs, agents, models. Optional: list. -Watch notify: the waiter injects `openclaw agent --session-key` / `--session-id` for the originating chat. Override with `watch.notifyCommand`. Safe placeholders: `{agentId} {runId} {runStatus} {url} {prUrl}`. Result text stays in `CURSOR_CLOUD_RESULT`, not the command line. Watch is not the transcript — poll `cursor_cloud_status`. +`cursor_cloud_status` returns final/partial result, `messages[]`, and short-lived artifact download refs. Watch is not the transcript. -Launch: `model` plus optional `effort` and `fast`. Effort spellings go through one alias table; the table's preferred value is what the catalog receives (`med` prefers `medium`). Reply cannot change those (`model_locked`). Default when `effort`/`fast` are omitted: the model's own variant (often fast). +Skill: `packages/plugin/skills/cursor-cloud/SKILL.md`. ## Versioning -`package.json` and `openclaw.plugin.json` share the semver. Authors: `CONTRIBUTING.md`. +Workspace packages share the semver. `./scripts/release.sh ` bumps all three. Authors: `CONTRIBUTING.md`. ## License diff --git a/SECURITY.md b/SECURITY.md index 8a890e6..04edd73 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -29,7 +29,7 @@ auth bypass, or host-pinning failure. ## Default blast radius -Default tools: launch, reply, status, cancel, watch, me, envs, agents, models. +Packages: `cursor-cloud-core`, `openclaw-plugin-cursor-cloud`, `cursor-cloud-mcp`. Same default tools on plugin and MCP. Optional tools (must be allowlisted): list. diff --git a/package-lock.json b/package-lock.json index d998e36..319dddb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,18 @@ { - "name": "openclaw-plugin-cursor-cloud", - "version": "0.2.0", + "name": "cursor-cloud-agent", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "openclaw-plugin-cursor-cloud", - "version": "0.2.0", + "name": "cursor-cloud-agent", + "version": "0.3.0", "license": "MIT", - "dependencies": { - "typebox": "^1.1.38" - }, - "bin": { - "openclaw-cursor-cloud": "dist/cli.js" - }, + "workspaces": [ + "packages/core", + "packages/plugin", + "packages/mcp" + ], "devDependencies": { "@types/node": "^24.5.2", "openclaw": "2026.9.2", @@ -22,9 +21,6 @@ }, "engines": { "node": ">=22.22.3" - }, - "peerDependencies": { - "openclaw": ">=2026.5.17" } }, "node_modules/@agentclientprotocol/sdk": { @@ -2865,6 +2861,14 @@ "dev": true, "license": "MIT" }, + "node_modules/cursor-cloud-core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/cursor-cloud-mcp": { + "resolved": "packages/mcp", + "link": true + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -4852,6 +4856,10 @@ "sqlite-vec": "0.1.9" } }, + "node_modules/openclaw-plugin-cursor-cloud": { + "resolved": "packages/plugin", + "link": true + }, "node_modules/openclaw/node_modules/typebox": { "version": "1.3.18", "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.18.tgz", @@ -6684,6 +6692,55 @@ "peerDependencies": { "zod": "^3.25.28 || ^4" } + }, + "packages/core": { + "name": "cursor-cloud-core", + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "typebox": "^1.1.38" + }, + "bin": { + "cursor-cloud": "dist/cli.js" + }, + "engines": { + "node": ">=22.22.3" + } + }, + "packages/mcp": { + "name": "cursor-cloud-mcp", + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "cursor-cloud-core": "0.3.0" + }, + "bin": { + "cursor-cloud-mcp": "dist/cli.js" + }, + "engines": { + "node": ">=22.22.3" + } + }, + "packages/plugin": { + "name": "openclaw-plugin-cursor-cloud", + "version": "0.3.0", + "license": "MIT", + "dependencies": { + "cursor-cloud-core": "0.3.0", + "cursor-cloud-mcp": "0.3.0" + }, + "bin": { + "openclaw-cursor-cloud": "dist/cli.js" + }, + "devDependencies": { + "openclaw": "2026.9.2" + }, + "engines": { + "node": ">=22.22.3" + }, + "peerDependencies": { + "openclaw": ">=2026.5.17" + } } } } diff --git a/package.json b/package.json index 079c2ff..1e83b6c 100644 --- a/package.json +++ b/package.json @@ -1,76 +1,29 @@ { - "name": "openclaw-plugin-cursor-cloud", - "version": "0.2.0", - "description": "OpenClaw plugin and CLI that launch Cursor Cloud Agents on named saved environments — without running the coding work on the coordinator host.", + "name": "cursor-cloud-agent", + "version": "0.3.0", + "private": true, + "description": "Monorepo for Cursor Cloud coordinator: core lib, OpenClaw plugin, MCP server.", "type": "module", "license": "MIT", - "keywords": [ - "openclaw", - "plugin", - "cursor", - "cloud-agents", - "clawhub", - "mcp" + "workspaces": [ + "packages/core", + "packages/plugin", + "packages/mcp" ], "engines": { "node": ">=22.22.3" }, - "bin": { - "openclaw-cursor-cloud": "./dist/cli.js" - }, - "exports": { - ".": "./dist/index.js", - "./client": "./dist/client.js", - "./mcp": "./dist/mcp.js" - }, "scripts": { - "build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"", - "plugin:build": "npm run build && openclaw plugins build --entry ./dist/index.js && node scripts/preserve-manifest-skills.mjs", - "plugin:validate": "npm run build && openclaw plugins validate --entry ./dist/index.js", + "build": "npm run build -w cursor-cloud-core && npm run build -w cursor-cloud-mcp && npm run build -w openclaw-plugin-cursor-cloud", + "plugin:build": "npm run plugin:build -w openclaw-plugin-cursor-cloud", + "plugin:validate": "npm run plugin:validate -w openclaw-plugin-cursor-cloud", "test": "vitest run --config ./vitest.config.ts", - "prepack": "npm run plugin:build", "release": "./scripts/release.sh" }, - "files": [ - "dist", - "openclaw.plugin.json", - "README.md", - "LICENSE", - "SECURITY.md", - "skills", - "examples", - "scripts/install-gateway.sh" - ], - "publishConfig": { - "access": "public" - }, - "peerDependencies": { - "openclaw": ">=2026.5.17" - }, - "dependencies": { - "typebox": "^1.1.38" - }, "devDependencies": { "@types/node": "^24.5.2", "openclaw": "2026.9.2", "typescript": "^5.9.0", "vitest": "^3.2.0" - }, - "openclaw": { - "extensions": [ - "./dist/index.js" - ], - "compat": { - "pluginApi": ">=2026.5.17", - "minGatewayVersion": "2026.5.17" - }, - "install": { - "npmSpec": "openclaw-plugin-cursor-cloud@0.2.0", - "defaultChoice": "npm", - "minHostVersion": ">=2026.5.17" - }, - "build": { - "openclawVersion": "2026.9.2" - } } } diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000..e8c3c83 --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,5 @@ +# cursor-cloud-core + +Shared library for coordinating Cursor Cloud Agents: pinned `api.cursor.com` client, env harvest, model params, launch/reply/status/watch. + +Used by `openclaw-plugin-cursor-cloud` and `cursor-cloud-mcp`. CLI: `cursor-cloud`. diff --git a/examples/cli-config.json b/packages/core/examples/cli-config.json similarity index 100% rename from examples/cli-config.json rename to packages/core/examples/cli-config.json diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..7fced11 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,33 @@ +{ + "name": "cursor-cloud-core", + "version": "0.3.0", + "description": "Shared Cursor Cloud Agents coordinator: API client, env harvest, launch/reply/status/watch.", + "type": "module", + "license": "MIT", + "engines": { + "node": ">=22.22.3" + }, + "bin": { + "cursor-cloud": "./dist/cli.js" + }, + "exports": { + ".": "./dist/index.js", + "./client": "./dist/client.js", + "./cli": "./dist/cli.js", + "./ledger": "./dist/ledger.js" + }, + "files": [ + "dist", + "examples", + "README.md" + ], + "scripts": { + "build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "typebox": "^1.1.38" + } +} diff --git a/src/actions.test.ts b/packages/core/src/actions.test.ts similarity index 100% rename from src/actions.test.ts rename to packages/core/src/actions.test.ts diff --git a/src/actions.ts b/packages/core/src/actions.ts similarity index 100% rename from src/actions.ts rename to packages/core/src/actions.ts diff --git a/src/artifacts.ts b/packages/core/src/artifacts.ts similarity index 100% rename from src/artifacts.ts rename to packages/core/src/artifacts.ts diff --git a/src/brief.test.ts b/packages/core/src/brief.test.ts similarity index 100% rename from src/brief.test.ts rename to packages/core/src/brief.test.ts diff --git a/src/brief.ts b/packages/core/src/brief.ts similarity index 100% rename from src/brief.ts rename to packages/core/src/brief.ts diff --git a/src/cli.ts b/packages/core/src/cli.ts similarity index 92% rename from src/cli.ts rename to packages/core/src/cli.ts index be029e8..fa1d6ee 100644 --- a/src/cli.ts +++ b/packages/core/src/cli.ts @@ -15,9 +15,7 @@ import { import { formatError } from "./errors.js"; import { TOOL_EFFORT_LEVELS } from "./model.js"; import { loadCliConfig } from "./load-config.js"; -import { startMcpServer } from "./mcp.js"; import { createRuntime } from "./runtime.js"; -import { setupGateway } from "./setup.js"; import { VERSION } from "./version.js"; type Flags = Record; @@ -32,20 +30,6 @@ async function main(argv = process.argv.slice(2)): Promise { console.log(VERSION); return; } - if (command === "mcp") { - await startMcpServer(); - return; - } - if (command === "setup") { - const source = str(flags.source); - printJson( - await setupGateway({ - source: source === "link" || source === "npm" || source === "skip" ? source : undefined, - }), - ); - return; - } - const runtime = createRuntime(loadCliConfig()); const prompt = readPrompt(flags, rest); @@ -138,7 +122,7 @@ async function main(argv = process.argv.slice(2)): Promise { ); return; default: - throw new Error(`Unknown command '${command}'. Try openclaw-cursor-cloud help.`); + throw new Error(`Unknown command '${command}'. Try cursor-cloud help.`); } } @@ -228,7 +212,7 @@ function printJson(value: unknown): void { function printHelp(): void { writeFileSync( 1, - `openclaw-cursor-cloud ${VERSION} + `cursor-cloud ${VERSION} Commands: launch --env --prompt [--name] [--model] [--effort ${TOOL_EFFORT_LEVELS.join("|")}] [--fast] [--repo] [--watch] @@ -241,11 +225,12 @@ Commands: me envs [--refresh] [--role base|project] [--project ] [--repo ] agents [--env ] [--project ] [--repo ] - mcp - setup [--source link|npm|skip] Auth: CURSOR_API_KEY (or apiKeyEnv). Config: CURSOR_CLOUD_CONFIG or ~/.config/openclaw-cursor-cloud/config.json + +OpenClaw setup: openclaw-cursor-cloud setup +MCP stdio: cursor-cloud-mcp `, ); } diff --git a/src/client.test.ts b/packages/core/src/client.test.ts similarity index 100% rename from src/client.test.ts rename to packages/core/src/client.test.ts diff --git a/src/client.ts b/packages/core/src/client.ts similarity index 100% rename from src/client.ts rename to packages/core/src/client.ts diff --git a/src/config.ts b/packages/core/src/config.ts similarity index 100% rename from src/config.ts rename to packages/core/src/config.ts diff --git a/src/env-catalog.test.ts b/packages/core/src/env-catalog.test.ts similarity index 100% rename from src/env-catalog.test.ts rename to packages/core/src/env-catalog.test.ts diff --git a/src/env-catalog.ts b/packages/core/src/env-catalog.ts similarity index 100% rename from src/env-catalog.ts rename to packages/core/src/env-catalog.ts diff --git a/src/errors.ts b/packages/core/src/errors.ts similarity index 100% rename from src/errors.ts rename to packages/core/src/errors.ts diff --git a/src/harvest.ts b/packages/core/src/harvest.ts similarity index 69% rename from src/harvest.ts rename to packages/core/src/harvest.ts index 2c78b67..5b9a207 100644 --- a/src/harvest.ts +++ b/packages/core/src/harvest.ts @@ -3,28 +3,6 @@ import { createRuntime, type Runtime } from "./runtime.js"; const CATALOG_MAX_AGE_MS = 6 * 60 * 60 * 1000; -export type HarvestApi = { - pluginConfig?: unknown; - registerService: (service: { - id: string; - start: (ctx: { serviceHealth?: { clearFailure?: () => void; reportFailure?: (error: unknown) => void } }) => void; - stop: () => void; - }) => void; -}; - -export function registerHarvestService(api: HarvestApi): void { - api.registerService({ - id: "cursor-cloud-env-harvest", - start(ctx) { - void runInitHarvest(api.pluginConfig).then( - () => ctx.serviceHealth?.clearFailure?.(), - (error) => ctx.serviceHealth?.reportFailure?.(error), - ); - }, - stop() {}, - }); -} - export async function runInitHarvest(pluginConfig: unknown, env: NodeJS.ProcessEnv = process.env): Promise { const runtime = createRuntime(pluginConfig as never, env); await ensureHarvested(runtime); diff --git a/src/host.test.ts b/packages/core/src/host.test.ts similarity index 100% rename from src/host.test.ts rename to packages/core/src/host.test.ts diff --git a/src/host.ts b/packages/core/src/host.ts similarity index 100% rename from src/host.ts rename to packages/core/src/host.ts diff --git a/src/ids.ts b/packages/core/src/ids.ts similarity index 100% rename from src/ids.ts rename to packages/core/src/ids.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..6d1f14d --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,42 @@ +export { CursorCloudClient } from "./client.js"; +export { pluginConfigSchema, resolveConfig, publicConfig } from "./config.js"; +export { composePrompt, proofFromRun } from "./brief.js"; +export { presentModel, resolveModelSelection } from "./model.js"; +export { FollowUpError, ModelLockedError, formatError } from "./errors.js"; +export { ensureHarvested, runInitHarvest } from "./harvest.js"; +export { resolvePlacement } from "./placement.js"; +export type { SessionRef } from "./placement.js"; +export { catalogFromAgents, fetchEnvCatalog, mergeCatalogIntoConfig } from "./env-catalog.js"; +export { listRegisteredEnvs, resolveLaunchTarget } from "./registry.js"; +export { watchRun } from "./waiter.js"; +export { createRuntime } from "./runtime.js"; +export type { Runtime } from "./runtime.js"; +export { loadCliConfig } from "./load-config.js"; +export { upsertLedger } from "./ledger.js"; +export { + agentsAction, + cancelAction, + envsAction, + launchAction, + ledgerAction, + listAction, + meAction, + modelsAction, + replyAction, + statusAction, + watchAction, + watchForeground, +} from "./actions.js"; +export { + agentsParamsSchema, + cancelParamsSchema, + emptyParamsSchema, + envsParamsSchema, + jsonSchema, + launchParamsSchema, + listParamsSchema, + replyParamsSchema, + statusParamsSchema, + watchParamsSchema, +} from "./tool-schemas.js"; +export { PACKAGE_NAME, VERSION, DEFAULT_API_BASE_URL } from "./version.js"; diff --git a/src/ledger.test.ts b/packages/core/src/ledger.test.ts similarity index 100% rename from src/ledger.test.ts rename to packages/core/src/ledger.test.ts diff --git a/src/ledger.ts b/packages/core/src/ledger.ts similarity index 100% rename from src/ledger.ts rename to packages/core/src/ledger.ts diff --git a/src/load-config.ts b/packages/core/src/load-config.ts similarity index 100% rename from src/load-config.ts rename to packages/core/src/load-config.ts diff --git a/src/lock.ts b/packages/core/src/lock.ts similarity index 100% rename from src/lock.ts rename to packages/core/src/lock.ts diff --git a/src/model.test.ts b/packages/core/src/model.test.ts similarity index 100% rename from src/model.test.ts rename to packages/core/src/model.test.ts diff --git a/src/model.ts b/packages/core/src/model.ts similarity index 100% rename from src/model.ts rename to packages/core/src/model.ts diff --git a/src/notify.test.ts b/packages/core/src/notify.test.ts similarity index 100% rename from src/notify.test.ts rename to packages/core/src/notify.test.ts diff --git a/src/notify.ts b/packages/core/src/notify.ts similarity index 100% rename from src/notify.ts rename to packages/core/src/notify.ts diff --git a/src/paths.ts b/packages/core/src/paths.ts similarity index 100% rename from src/paths.ts rename to packages/core/src/paths.ts diff --git a/src/placement.test.ts b/packages/core/src/placement.test.ts similarity index 100% rename from src/placement.test.ts rename to packages/core/src/placement.test.ts diff --git a/src/placement.ts b/packages/core/src/placement.ts similarity index 100% rename from src/placement.ts rename to packages/core/src/placement.ts diff --git a/src/registry.test.ts b/packages/core/src/registry.test.ts similarity index 100% rename from src/registry.test.ts rename to packages/core/src/registry.test.ts diff --git a/src/registry.ts b/packages/core/src/registry.ts similarity index 100% rename from src/registry.ts rename to packages/core/src/registry.ts diff --git a/src/runtime.ts b/packages/core/src/runtime.ts similarity index 92% rename from src/runtime.ts rename to packages/core/src/runtime.ts index 4d278a5..53a3e33 100644 --- a/src/runtime.ts +++ b/packages/core/src/runtime.ts @@ -25,7 +25,7 @@ export function createRuntime( const apiKey = env[config.apiKeyEnv]?.trim(); if (!apiKey) { throw new ConfigError( - `Missing ${config.apiKeyEnv}. Set it on the OpenClaw gateway environment; do not put the key in plugin config.`, + `Missing ${config.apiKeyEnv}. Set it on the host environment; do not put the key in config.`, ); } return { diff --git a/src/sse.test.ts b/packages/core/src/sse.test.ts similarity index 100% rename from src/sse.test.ts rename to packages/core/src/sse.test.ts diff --git a/src/sse.ts b/packages/core/src/sse.ts similarity index 100% rename from src/sse.ts rename to packages/core/src/sse.ts diff --git a/src/tool-schemas.ts b/packages/core/src/tool-schemas.ts similarity index 100% rename from src/tool-schemas.ts rename to packages/core/src/tool-schemas.ts diff --git a/src/transcript.ts b/packages/core/src/transcript.ts similarity index 100% rename from src/transcript.ts rename to packages/core/src/transcript.ts diff --git a/src/types.ts b/packages/core/src/types.ts similarity index 100% rename from src/types.ts rename to packages/core/src/types.ts diff --git a/src/version.ts b/packages/core/src/version.ts similarity index 85% rename from src/version.ts rename to packages/core/src/version.ts index 2a31642..245615f 100644 --- a/src/version.ts +++ b/packages/core/src/version.ts @@ -2,7 +2,7 @@ import { createRequire } from "node:module"; const require = createRequire(import.meta.url); -export const PACKAGE_NAME = "openclaw-plugin-cursor-cloud"; +export const PACKAGE_NAME = "cursor-cloud-core"; export const VERSION: string = require("../package.json").version; export const USER_AGENT = `${PACKAGE_NAME}/${VERSION}`; export const DEFAULT_API_BASE_URL = "https://api.cursor.com"; diff --git a/src/waiter.test.ts b/packages/core/src/waiter.test.ts similarity index 100% rename from src/waiter.test.ts rename to packages/core/src/waiter.test.ts diff --git a/src/waiter.ts b/packages/core/src/waiter.ts similarity index 100% rename from src/waiter.ts rename to packages/core/src/waiter.ts diff --git a/src/watch-process.test.ts b/packages/core/src/watch-process.test.ts similarity index 100% rename from src/watch-process.test.ts rename to packages/core/src/watch-process.test.ts diff --git a/src/watch-process.ts b/packages/core/src/watch-process.ts similarity index 100% rename from src/watch-process.ts rename to packages/core/src/watch-process.ts diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..4a26fb1 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/packages/mcp/README.md b/packages/mcp/README.md new file mode 100644 index 0000000..5ff178c --- /dev/null +++ b/packages/mcp/README.md @@ -0,0 +1,18 @@ +# cursor-cloud-mcp + +Stdio MCP server for the same `cursor_cloud_*` tools as the OpenClaw plugin. + +```json +{ + "mcpServers": { + "cursor-cloud": { + "command": "cursor-cloud-mcp", + "env": { + "CURSOR_API_KEY": "${CURSOR_API_KEY}" + } + } + } +} +``` + +Config: `CURSOR_CLOUD_CONFIG` or `~/.config/openclaw-cursor-cloud/config.json` (see `cursor-cloud-core` examples). diff --git a/examples/mcp.json b/packages/mcp/examples/mcp.json similarity index 65% rename from examples/mcp.json rename to packages/mcp/examples/mcp.json index 598e4b0..9334fd5 100644 --- a/examples/mcp.json +++ b/packages/mcp/examples/mcp.json @@ -1,8 +1,7 @@ { "mcpServers": { "cursor-cloud": { - "command": "openclaw-cursor-cloud", - "args": ["mcp"], + "command": "cursor-cloud-mcp", "env": { "CURSOR_API_KEY": "${CURSOR_API_KEY}" } diff --git a/packages/mcp/package.json b/packages/mcp/package.json new file mode 100644 index 0000000..07c648d --- /dev/null +++ b/packages/mcp/package.json @@ -0,0 +1,36 @@ +{ + "name": "cursor-cloud-mcp", + "version": "0.3.0", + "description": "MCP stdio server for Cursor Cloud Agents, built on cursor-cloud-core.", + "type": "module", + "license": "MIT", + "keywords": [ + "mcp", + "cursor", + "cloud-agents" + ], + "engines": { + "node": ">=22.22.3" + }, + "bin": { + "cursor-cloud-mcp": "./dist/cli.js" + }, + "exports": { + ".": "./dist/index.js", + "./cli": "./dist/cli.js" + }, + "files": [ + "dist", + "examples", + "README.md" + ], + "scripts": { + "build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "cursor-cloud-core": "0.3.0" + } +} diff --git a/packages/mcp/src/cli.ts b/packages/mcp/src/cli.ts new file mode 100644 index 0000000..310e3b5 --- /dev/null +++ b/packages/mcp/src/cli.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node +import { startMcpServer } from "./mcp.js"; + +startMcpServer().catch((error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exitCode = 1; +}); diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts new file mode 100644 index 0000000..cb27aa9 --- /dev/null +++ b/packages/mcp/src/index.ts @@ -0,0 +1 @@ +export { handle, startMcpServer } from "./mcp.js"; diff --git a/src/mcp.test.ts b/packages/mcp/src/mcp.test.ts similarity index 89% rename from src/mcp.test.ts rename to packages/mcp/src/mcp.test.ts index de47a86..7474c1a 100644 --- a/src/mcp.test.ts +++ b/packages/mcp/src/mcp.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it, vi } from "vitest"; +import type { Runtime } from "cursor-cloud-core"; import { handle } from "./mcp.js"; -import type { Runtime } from "./runtime.js"; describe("mcp", () => { it("lists tools and calls launch through the runtime", async () => { @@ -27,8 +27,9 @@ describe("mcp", () => { }, env: { CURSOR_API_KEY: "k" }, ledgerPath: "/tmp/ledger-unused", + catalogPath: "/tmp/catalog-unused", } as unknown as Runtime; - vi.spyOn(await import("./ledger.js"), "upsertLedger").mockReturnValue([]); + vi.spyOn(await import("../../core/src/ledger.js"), "upsertLedger").mockReturnValue([]); const called = await handle( { jsonrpc: "2.0", diff --git a/src/mcp.ts b/packages/mcp/src/mcp.ts similarity index 96% rename from src/mcp.ts rename to packages/mcp/src/mcp.ts index c49c59e..180d459 100644 --- a/src/mcp.ts +++ b/packages/mcp/src/mcp.ts @@ -1,31 +1,30 @@ import { createInterface } from "node:readline"; import { agentsAction, - cancelAction, - envsAction, - launchAction, - listAction, - meAction, - modelsAction, - replyAction, - statusAction, - watchAction, -} from "./actions.js"; -import { formatError } from "./errors.js"; -import { loadCliConfig } from "./load-config.js"; -import { createRuntime, type Runtime } from "./runtime.js"; -import { agentsParamsSchema, + cancelAction, cancelParamsSchema, + createRuntime, emptyParamsSchema, + envsAction, envsParamsSchema, + formatError, jsonSchema, + launchAction, launchParamsSchema, + listAction, listParamsSchema, + loadCliConfig, + meAction, + modelsAction, + replyAction, replyParamsSchema, + statusAction, statusParamsSchema, + watchAction, watchParamsSchema, -} from "./tool-schemas.js"; + type Runtime, +} from "cursor-cloud-core"; import { PACKAGE_NAME, VERSION } from "./version.js"; type JsonRpc = { diff --git a/packages/mcp/src/version.ts b/packages/mcp/src/version.ts new file mode 100644 index 0000000..a22919c --- /dev/null +++ b/packages/mcp/src/version.ts @@ -0,0 +1,6 @@ +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); + +export const PACKAGE_NAME = "cursor-cloud-mcp"; +export const VERSION: string = require("../package.json").version; diff --git a/packages/mcp/tsconfig.json b/packages/mcp/tsconfig.json new file mode 100644 index 0000000..4a26fb1 --- /dev/null +++ b/packages/mcp/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/packages/plugin/README.md b/packages/plugin/README.md new file mode 100644 index 0000000..515a470 --- /dev/null +++ b/packages/plugin/README.md @@ -0,0 +1,11 @@ +# openclaw-plugin-cursor-cloud + +OpenClaw plugin for `cursor_cloud_*` tools. Depends on `cursor-cloud-core`. + +```bash +npx -y openclaw-plugin-cursor-cloud@0.3.0 setup +# or from this repo +./scripts/install-gateway.sh +``` + +Then set `CURSOR_API_KEY` on the gateway and restart. diff --git a/examples/gateway-plugin.json b/packages/plugin/examples/gateway-plugin.json similarity index 100% rename from examples/gateway-plugin.json rename to packages/plugin/examples/gateway-plugin.json diff --git a/examples/instance-enable.batch.json b/packages/plugin/examples/instance-enable.batch.json similarity index 100% rename from examples/instance-enable.batch.json rename to packages/plugin/examples/instance-enable.batch.json diff --git a/openclaw.plugin.json b/packages/plugin/openclaw.plugin.json similarity index 99% rename from openclaw.plugin.json rename to packages/plugin/openclaw.plugin.json index 83aeea9..b7629d1 100644 --- a/openclaw.plugin.json +++ b/packages/plugin/openclaw.plugin.json @@ -2,7 +2,7 @@ "id": "cursor-cloud", "name": "Cursor Cloud", "description": "Launch and coordinate Cursor Cloud Agents on named saved environments. Does not run coding work on the OpenClaw host.", - "version": "0.2.0", + "version": "0.3.0", "configSchema": { "type": "object", "properties": { diff --git a/packages/plugin/package.json b/packages/plugin/package.json new file mode 100644 index 0000000..d40faad --- /dev/null +++ b/packages/plugin/package.json @@ -0,0 +1,66 @@ +{ + "name": "openclaw-plugin-cursor-cloud", + "version": "0.3.0", + "description": "OpenClaw plugin that launches Cursor Cloud Agents on named saved environments.", + "type": "module", + "license": "MIT", + "keywords": [ + "openclaw", + "plugin", + "cursor", + "cloud-agents", + "clawhub" + ], + "engines": { + "node": ">=22.22.3" + }, + "bin": { + "openclaw-cursor-cloud": "./dist/cli.js" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist", + "openclaw.plugin.json", + "README.md", + "skills", + "examples", + "scripts/install-gateway.sh" + ], + "scripts": { + "build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"", + "plugin:build": "npm run build -w cursor-cloud-core && npm run build && openclaw plugins build --entry ./dist/index.js && node scripts/preserve-manifest-skills.mjs", + "plugin:validate": "npm run build -w cursor-cloud-core && npm run build && openclaw plugins validate --entry ./dist/index.js" + }, + "publishConfig": { + "access": "public" + }, + "peerDependencies": { + "openclaw": ">=2026.5.17" + }, + "dependencies": { + "cursor-cloud-core": "0.3.0", + "cursor-cloud-mcp": "0.3.0" + }, + "devDependencies": { + "openclaw": "2026.9.2" + }, + "openclaw": { + "extensions": [ + "./dist/index.js" + ], + "compat": { + "pluginApi": ">=2026.5.17", + "minGatewayVersion": "2026.5.17" + }, + "install": { + "npmSpec": "openclaw-plugin-cursor-cloud@0.3.0", + "defaultChoice": "npm", + "minHostVersion": ">=2026.5.17" + }, + "build": { + "openclawVersion": "2026.9.2" + } + } +} diff --git a/packages/plugin/scripts/install-gateway.sh b/packages/plugin/scripts/install-gateway.sh new file mode 100755 index 0000000..3abb6c8 --- /dev/null +++ b/packages/plugin/scripts/install-gateway.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +plugin="$(cd "$(dirname "$0")/.." && pwd)" +root="$(cd "$plugin/../.." && pwd)" + +if [[ ! -f "$plugin/dist/cli.js" ]]; then + (cd "$root" && npm install && npm run plugin:build) +fi + +exec node "$plugin/dist/cli.js" setup --source link diff --git a/scripts/preserve-manifest-skills.mjs b/packages/plugin/scripts/preserve-manifest-skills.mjs similarity index 100% rename from scripts/preserve-manifest-skills.mjs rename to packages/plugin/scripts/preserve-manifest-skills.mjs diff --git a/skills/cursor-cloud/SKILL.md b/packages/plugin/skills/cursor-cloud/SKILL.md similarity index 100% rename from skills/cursor-cloud/SKILL.md rename to packages/plugin/skills/cursor-cloud/SKILL.md diff --git a/skills/cursor-cloud/references/smoke.md b/packages/plugin/skills/cursor-cloud/references/smoke.md similarity index 100% rename from skills/cursor-cloud/references/smoke.md rename to packages/plugin/skills/cursor-cloud/references/smoke.md diff --git a/packages/plugin/src/cli.ts b/packages/plugin/src/cli.ts new file mode 100644 index 0000000..3db0e37 --- /dev/null +++ b/packages/plugin/src/cli.ts @@ -0,0 +1,61 @@ +#!/usr/bin/env node +import { spawn } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import { formatError } from "cursor-cloud-core"; +import { setupGateway } from "./setup.js"; +import { VERSION } from "./version.js"; + +async function main(argv = process.argv.slice(2)): Promise { + const command = argv.find((token) => !token.startsWith("-")) ?? ""; + if (!command || command === "help" || argv.includes("--help")) { + process.stdout.write( + `openclaw-cursor-cloud ${VERSION} + +Commands: + setup [--source link|npm|skip] + mcp (delegates to cursor-cloud-mcp) + * other commands delegate to cursor-cloud + +`, + ); + return; + } + if (command === "version" || argv.includes("--version")) { + console.log(VERSION); + return; + } + if (command === "setup") { + const sourceFlag = readFlag(argv, "source"); + const source = sourceFlag === "link" || sourceFlag === "npm" || sourceFlag === "skip" ? sourceFlag : undefined; + process.stdout.write(`${JSON.stringify(await setupGateway({ source }), null, 2)}\n`); + return; + } + const target = command === "mcp" ? "cursor-cloud-mcp/cli" : "cursor-cloud-core/cli"; + const cli = fileURLToPath(import.meta.resolve(target)); + const child = spawn(process.execPath, [cli, ...argv], { stdio: "inherit" }); + await new Promise((resolve, reject) => { + child.on("error", reject); + child.on("exit", (code) => { + process.exitCode = code ?? 0; + resolve(); + }); + }); +} + +function readFlag(argv: string[], name: string): string | undefined { + const idx = argv.findIndex((token) => token === `--${name}` || token.startsWith(`--${name}=`)); + if (idx === -1) { + return undefined; + } + const token = argv[idx]; + if (token.includes("=")) { + return token.split("=", 2)[1]; + } + const next = argv[idx + 1]; + return next && !next.startsWith("-") ? next : undefined; +} + +main().catch((error) => { + process.stdout.write(`${JSON.stringify(formatError(error), null, 2)}\n`); + process.exitCode = 1; +}); diff --git a/packages/plugin/src/harvest.ts b/packages/plugin/src/harvest.ts new file mode 100644 index 0000000..bc16231 --- /dev/null +++ b/packages/plugin/src/harvest.ts @@ -0,0 +1,28 @@ +import { ensureHarvested, createRuntime } from "cursor-cloud-core"; + +export type HarvestApi = { + pluginConfig?: unknown; + registerService: (service: { + id: string; + start: (ctx: { serviceHealth?: { clearFailure?: () => void; reportFailure?: (error: unknown) => void } }) => void; + stop: () => void; + }) => void; +}; + +export function registerHarvestService(api: HarvestApi): void { + api.registerService({ + id: "cursor-cloud-env-harvest", + start(ctx) { + void runInitHarvest(api.pluginConfig).then( + () => ctx.serviceHealth?.clearFailure?.(), + (error) => ctx.serviceHealth?.reportFailure?.(error), + ); + }, + stop() {}, + }); +} + +export async function runInitHarvest(pluginConfig: unknown, env: NodeJS.ProcessEnv = process.env): Promise { + const runtime = createRuntime(pluginConfig as never, env); + await ensureHarvested(runtime); +} diff --git a/src/index.test.ts b/packages/plugin/src/index.test.ts similarity index 100% rename from src/index.test.ts rename to packages/plugin/src/index.test.ts diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts new file mode 100644 index 0000000..3838a8a --- /dev/null +++ b/packages/plugin/src/index.ts @@ -0,0 +1,22 @@ +export { + CursorCloudClient, + agentsAction, + cancelAction, + createRuntime, + ensureHarvested, + envsAction, + launchAction, + listAction, + meAction, + modelsAction, + pluginConfigSchema, + presentModel, + replyAction, + resolveConfig, + resolveModelSelection, + statusAction, + watchAction, +} from "cursor-cloud-core"; +export { registerHarvestService } from "./harvest.js"; +export { setupGateway } from "./setup.js"; +export { default } from "./plugin.js"; diff --git a/src/plugin.ts b/packages/plugin/src/plugin.ts similarity index 96% rename from src/plugin.ts rename to packages/plugin/src/plugin.ts index 82deb3f..d049e1b 100644 --- a/src/plugin.ts +++ b/packages/plugin/src/plugin.ts @@ -1,32 +1,30 @@ import { defineToolPlugin } from "openclaw/plugin-sdk/tool-plugin"; import { agentsAction, + agentsParamsSchema, cancelAction, + cancelParamsSchema, + createRuntime, + emptyParamsSchema, envsAction, + envsParamsSchema, + formatError, launchAction, + launchParamsSchema, listAction, + listParamsSchema, meAction, modelsAction, + pluginConfigSchema, replyAction, - statusAction, - watchAction, -} from "./actions.js"; -import { pluginConfigSchema } from "./config.js"; -import { formatError } from "./errors.js"; -import { registerHarvestService } from "./harvest.js"; -import type { SessionRef } from "./placement.js"; -import { createRuntime } from "./runtime.js"; -import { - agentsParamsSchema, - cancelParamsSchema, - emptyParamsSchema, - envsParamsSchema, - launchParamsSchema, - listParamsSchema, replyParamsSchema, + statusAction, statusParamsSchema, + watchAction, watchParamsSchema, -} from "./tool-schemas.js"; + type SessionRef, +} from "cursor-cloud-core"; +import { registerHarvestService } from "./harvest.js"; function toolResult(value: unknown, failed = false) { return { diff --git a/src/setup.test.ts b/packages/plugin/src/setup.test.ts similarity index 100% rename from src/setup.test.ts rename to packages/plugin/src/setup.test.ts diff --git a/src/setup.ts b/packages/plugin/src/setup.ts similarity index 100% rename from src/setup.ts rename to packages/plugin/src/setup.ts diff --git a/packages/plugin/src/version.ts b/packages/plugin/src/version.ts new file mode 100644 index 0000000..18cf2a2 --- /dev/null +++ b/packages/plugin/src/version.ts @@ -0,0 +1,6 @@ +import { createRequire } from "node:module"; + +const require = createRequire(import.meta.url); + +export const PACKAGE_NAME = "openclaw-plugin-cursor-cloud"; +export const VERSION: string = require("../package.json").version; diff --git a/packages/plugin/tsconfig.json b/packages/plugin/tsconfig.json new file mode 100644 index 0000000..4a26fb1 --- /dev/null +++ b/packages/plugin/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "skipLibCheck": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/scripts/install-gateway.sh b/scripts/install-gateway.sh index 1253e0b..4aab21b 100755 --- a/scripts/install-gateway.sh +++ b/scripts/install-gateway.sh @@ -1,12 +1,3 @@ #!/usr/bin/env bash set -euo pipefail - -root="$(cd "$(dirname "$0")/.." && pwd)" -cd "$root" - -if [[ ! -f dist/cli.js ]]; then - npm install - npm run plugin:build -fi - -exec node "$root/dist/cli.js" setup --source link +exec "$(cd "$(dirname "$0")/.." && pwd)/packages/plugin/scripts/install-gateway.sh" diff --git a/scripts/release.sh b/scripts/release.sh index 5c11ef2..fbfcbfc 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Bump package metadata for a release. Does not publish or push. -# Usage: ./scripts/release.sh 0.1.1 +# Bump workspace package versions. Does not publish or push. +# Usage: ./scripts/release.sh 0.3.1 set -euo pipefail version="${1:-}" @@ -19,17 +19,39 @@ const version = process.env.VERSION; if (!version) { throw new Error("VERSION is required"); } -const pkg = JSON.parse(readFileSync("package.json", "utf8")); -const previous = pkg.version; -pkg.version = version; -pkg.openclaw = pkg.openclaw ?? {}; -pkg.openclaw.install = pkg.openclaw.install ?? {}; -pkg.openclaw.install.npmSpec = `${pkg.name}@${version}`; -writeFileSync("package.json", `${JSON.stringify(pkg, null, 2)}\n`); - -const manifest = JSON.parse(readFileSync("openclaw.plugin.json", "utf8")); + +function bump(path, mutate) { + const pkg = JSON.parse(readFileSync(path, "utf8")); + const previous = pkg.version; + pkg.version = version; + mutate?.(pkg); + writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`); + return previous; +} + +const previous = bump("package.json"); +bump("packages/core/package.json"); +bump("packages/mcp/package.json", (pkg) => { + if (pkg.dependencies?.["cursor-cloud-core"]) { + pkg.dependencies["cursor-cloud-core"] = version; + } +}); +bump("packages/plugin/package.json", (pkg) => { + if (pkg.dependencies?.["cursor-cloud-core"]) { + pkg.dependencies["cursor-cloud-core"] = version; + } + if (pkg.dependencies?.["cursor-cloud-mcp"]) { + pkg.dependencies["cursor-cloud-mcp"] = version; + } + pkg.openclaw = pkg.openclaw ?? {}; + pkg.openclaw.install = pkg.openclaw.install ?? {}; + pkg.openclaw.install.npmSpec = `${pkg.name}@${version}`; +}); + +const manifestPath = "packages/plugin/openclaw.plugin.json"; +const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); manifest.version = version; -writeFileSync("openclaw.plugin.json", `${JSON.stringify(manifest, null, 2)}\n`); +writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); const date = new Date().toISOString().slice(0, 10); const changelog = readFileSync("CHANGELOG.md", "utf8"); @@ -47,4 +69,4 @@ EOF npm install --package-lock-only echo "Next: git add -u && git commit && git tag v${version} && git push --follow-tags" -echo "Then: npm publish --access public and/or clawhub package publish ." +echo "Then publish packages/core, packages/mcp, packages/plugin (in that order)." diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 5423c58..0000000 --- a/src/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export { CursorCloudClient } from "./client.js"; -export { pluginConfigSchema, resolveConfig } from "./config.js"; -export { composePrompt, proofFromRun } from "./brief.js"; -export { resolveModelSelection } from "./model.js"; -export { FollowUpError, ModelLockedError } from "./errors.js"; -export { registerHarvestService, ensureHarvested } from "./harvest.js"; -export { resolvePlacement } from "./placement.js"; -export { catalogFromAgents, fetchEnvCatalog, mergeCatalogIntoConfig } from "./env-catalog.js"; -export { listRegisteredEnvs, resolveLaunchTarget } from "./registry.js"; -export { watchRun } from "./waiter.js"; -export { setupGateway } from "./setup.js"; -export { - agentsAction, - cancelAction, - envsAction, - launchAction, - ledgerAction, - listAction, - meAction, - modelsAction, - replyAction, - statusAction, - watchAction, -} from "./actions.js"; -export { default } from "./plugin.js"; diff --git a/tsconfig.json b/tsconfig.json index 4a26fb1..da51150 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,8 @@ { - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "declaration": true, - "outDir": "dist", - "rootDir": "src", - "skipLibCheck": true, - "types": ["node"] - }, - "include": ["src/**/*.ts"], - "exclude": ["src/**/*.test.ts"] + "files": [], + "references": [ + { "path": "./packages/core" }, + { "path": "./packages/mcp" }, + { "path": "./packages/plugin" } + ] } diff --git a/vitest.config.ts b/vitest.config.ts index 9aeb760..05e2e20 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,9 +1,15 @@ +import { fileURLToPath } from "node:url"; import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "node", - include: ["src/**/*.test.ts"], + include: ["packages/*/src/**/*.test.ts"], restoreMocks: true, }, + resolve: { + alias: { + "cursor-cloud-core": fileURLToPath(new URL("./packages/core/src/index.ts", import.meta.url)), + }, + }, });