Integrate pinned Cursor ACP runtime and explicit public setup

Consolidate Cursor v10 onto current controller behavior, retain admission gates, and prepare native interaction and cleanup qualification. Add the public provisioner and ordinary provider-pack assets.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-03 13:33:15 -05:00
1 parent 2a8a99e4a5
commit e2b89b6302
199 files changed
+20293 -851

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, expect, it } from "vitest";
import { resolveCursorProvisioner } from "../commands/runtime.js";
const roots: string[] = [];
afterEach(async () => { await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true }))); });
async function fixture() {
const root = await realpath(await mkdtemp(join(tmpdir(), "cursor-public-setup-")));
roots.push(root);
await mkdir(join(root, "dist/vendor/paperclip-runner/cli"), { recursive: true });
await writeFile(join(root, "package.json"), JSON.stringify({ name: "@paperclipai/server" }));
await writeFile(join(root, "dist/index.js"), "throw new Error('must not import server');");
const provisioner = join(root, "dist/vendor/paperclip-runner/cli/provision-cursor.cjs");
await writeFile(provisioner, "fixture");
return { root, provisioner, url: pathToFileURL(join(root, "dist/index.js")).href };
}
it("resolves the public provisioner without starting the server", async () => {
const f = await fixture();
expect(await resolveCursorProvisioner(f.url)).toBe(f.provisioner);
});
it("rejects repository layouts and wrong package identities", async () => {
const f = await fixture();
await mkdir(join(f.root, "src")); await writeFile(join(f.root, "src/index.ts"), "fixture");
await expect(resolveCursorProvisioner(pathToFileURL(join(f.root, "src/index.ts")).href)).rejects.toThrow("published server layout");
await writeFile(join(f.root, "package.json"), JSON.stringify({ name: "untrusted" }));
await expect(resolveCursorProvisioner(f.url)).rejects.toThrow("identity is invalid");
});
it("rejects a provisioner linked outside the installed package", async () => {
const f = await fixture(); const other = await fixture();
await rm(f.provisioner); await symlink(other.provisioner, f.provisioner);
await expect(resolveCursorProvisioner(f.url)).rejects.toThrow("escapes");
});
+50
View File
@@ -0,0 +1,50 @@
import { spawn } from "node:child_process";
import { lstat, readFile, realpath } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import type { Command } from "commander";
/** Resolve the installed public dependency without importing or starting it. */
export async function resolveCursorProvisioner(serverUrl: string): Promise<string> {
const url = new URL(serverUrl);
if (url.protocol !== "file:" || url.search || url.hash) throw new Error("Cursor setup requires an installed Paperclip server");
const entry = await realpath(fileURLToPath(url));
if (!entry.endsWith("/dist/index.js")) throw new Error("Cursor setup requires the published server layout");
const root = resolve(dirname(entry), "..");
const manifest = join(root, "package.json");
const info = await lstat(manifest);
if (!info.isFile() || info.isSymbolicLink() || info.size > 65536 || JSON.parse(await readFile(manifest, "utf8")).name !== "@paperclipai/server") throw new Error("Cursor setup server package identity is invalid");
const provisioner = join(root, "dist/vendor/paperclip-runner/cli/provision-cursor.cjs");
if (await realpath(provisioner) !== provisioner || !(await lstat(provisioner)).isFile()) throw new Error("Cursor setup entrypoint escapes its server package");
return provisioner;
}
export async function setupCursorRuntime(): Promise<void> {
const provisioner = await resolveCursorProvisioner(import.meta.resolve("@paperclipai/server"));
const child = spawn(process.execPath, [provisioner], {
stdio: "inherit", env: { PATH: process.env.PATH ?? "/usr/bin:/bin", LANG: "C.UTF-8" },
});
const cancel = () => { if (child.exitCode === null && child.signalCode === null) child.kill("SIGTERM"); };
process.on("SIGINT", cancel); process.on("SIGTERM", cancel);
try {
await new Promise<void>((accept, reject) => {
let spawnError: Error | undefined;
child.on("error", error => { spawnError = error; });
child.once("close", (code, signal) => {
if (spawnError) reject(spawnError);
else if (code !== 0 || signal) reject(new Error("Cursor setup did not finish. Review its error; an invalid installation is never replaced automatically."));
else accept();
});
});
} finally { process.off("SIGINT", cancel); process.off("SIGTERM", cancel); }
}
export function registerRuntimeCommands(program: Command): void {
program.command("runtime").description("Manage explicitly installed agent runtimes")
.command("setup <provider>")
.description("Install and verify the pinned Cursor runtime for this host (public downloads; no model calls)")
.action(async (provider: string) => {
if (provider !== "cursor") throw new Error("Supported runtime setup: paperclipai runtime setup cursor");
await setupCursorRuntime();
});
}
+2
View File
@@ -31,6 +31,7 @@ import { applyDataDirOverride, type DataDirOptionLike } from "./config/data-dir.
import { loadPaperclipEnvFile } from "./config/env.js";
import { initTelemetryFromConfigFile, flushTelemetry } from "./telemetry.js";
import { registerWorktreeCommands } from "./commands/worktree.js";
import { registerRuntimeCommands } from "./commands/runtime.js";
import { registerPluginCommands } from "./commands/client/plugin.js";
import { registerClientAuthCommands } from "./commands/client/auth.js";
import { registerConnectCommand } from "./commands/client/connect.js";
@@ -260,6 +261,7 @@ registerSecretCommands(program);
registerSkillsCommands(program);
registerTeamCommands(program);
registerWorktreeCommands(program);
registerRuntimeCommands(program);
registerEnvLabCommands(program);
registerPluginCommands(program);