1// @effect-diagnostics nodeBuiltinImport:off
2import * as NodeChildProcess from "node:child_process";
3import * as NodeFSP from "node:fs/promises";
4import * as NodeFS from "node:fs";
5import * as NodeModule from "node:module";
6import * as NodeOS from "node:os";
7import * as NodePath from "node:path";
8import * as NodeURL from "node:url";
9import { assert, it } from "@effect/vitest";
10import * as Schema from "effect/Schema";
11import { build } from "vite-plus/pack";
12
13import serverPackage from "../../apps/server/package.json" with { type: "json" };
14import {
15 isExternalCliDependency,
16 selectCliRuntimeExternalDependencies,
17 shouldBundleCliDependency,
18} from "./cli-external-packages.ts";
19import { findEsmImportsOfExternalPackages } from "./cli-executable-imports.ts";
20
21const decodeManifest = Schema.decodeUnknownSync(
22 Schema.fromJsonString(
23 Schema.Struct({
24 name: Schema.optionalKey(Schema.String),
25 dependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
26 peerDependencies: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
27 }),
28 ),
29);
30
31const repoRoot = NodeURL.fileURLToPath(new URL("../..", import.meta.url));
32
33// Copy the installed production JS dependency graph, with no pnpm symlinks back
34// into the checkout. Optional platform executables are covered by desktop staging
35// tests; this probe never creates a local agent or contacts Cursor.
36async function stagePackage(name: string, from: string, destination: string): Promise<void> {
37 const require = NodeModule.createRequire(from);
38 let source = NodePath.dirname(require.resolve(name));
39 while (!NodeFS.existsSync(NodePath.join(source, "package.json"))) {
40 const parent = NodePath.dirname(source);
41 if (parent === source) throw new Error(`Cannot locate ${name}`);
42 source = parent;
43 }
44 let manifest = decodeManifest(
45 await NodeFSP.readFile(NodePath.join(source, "package.json"), "utf8"),
46 );
47 while (manifest.name !== name) {
48 source = NodePath.dirname(source);
49 if (source === NodePath.dirname(source)) throw new Error(`Cannot locate ${name}`);
50 if (NodeFS.existsSync(NodePath.join(source, "package.json"))) {
51 manifest = decodeManifest(
52 await NodeFSP.readFile(NodePath.join(source, "package.json"), "utf8"),
53 );
54 }
55 }
56 const target = NodePath.join(destination, "node_modules", name);
57 await NodeFSP.mkdir(NodePath.dirname(target), { recursive: true });
58 await NodeFSP.cp(source, target, {
59 recursive: true,
60 filter: (entry) => NodePath.basename(entry) !== "node_modules",
61 });
62 const dependencies = { ...manifest.dependencies, ...manifest.peerDependencies };
63 for (const dependency of Object.keys(dependencies)) {
64 await stagePackage(dependency, NodePath.join(source, "package.json"), target);
65 }
66}
67
68it("loads packaged Cursor catalog chunks without credentials or checkout dependencies", async () => {
69 const scratch = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-cursor-package-"));
70 try {
71 // Missing staged dependencies must not resolve from a developer's /tmp tree.
72 for (let parent = NodePath.dirname(scratch); ; parent = NodePath.dirname(parent)) {
73 assert.isFalse(NodeFS.existsSync(NodePath.join(parent, "node_modules")));
74 if (parent === NodePath.dirname(parent)) break;
75 }
76 const entry = NodePath.join(scratch, "probe.mjs");
77 const output = NodePath.join(scratch, "package");
78 await NodeFSP.writeFile(
79 entry,
80 `
81 import assert from 'node:assert/strict';
82 import { Cursor } from ${JSON.stringify(NodePath.join(repoRoot, "packages/provider-cursor/src/server/sdk.ts"))};
83 for (const [operation, request] of [
84 ['Cursor.models.list', () => Cursor.models.list({ apiKey: '' })],
85 ['Cursor.me', () => Cursor.me({ apiKey: '' })],
86 ]) {
87 await assert.rejects(request, error => {
88 assert.equal(error.name, 'ConfigurationError');
89 assert.equal(error.operation, operation);
90 assert.match(error.message, /empty apiKey explicitly/);
91 return true;
92 });
93 }
94 console.log('Cursor catalog chunks loaded; empty keys rejected locally');
95 `,
96 );
97 await build({
98 config: false,
99 entry: [entry],
100 outDir: output,
101 platform: "node",
102 format: "esm",
103 dts: false,
104 logLevel: "error",
105 deps: {
106 alwaysBundle: shouldBundleCliDependency,
107 neverBundle: isExternalCliDependency,
108 onlyBundle: false,
109 },
110 });
111 const roots = selectCliRuntimeExternalDependencies({
112 "@cursor/sdk": serverPackage.dependencies["@cursor/sdk"],
113 });
114 for (const name of Object.keys(roots)) {
115 await stagePackage(name, NodePath.join(repoRoot, "apps/server/package.json"), output);
116 }
117 const probe = NodePath.join(output, "probe.mjs");
118 assert.deepEqual(findEsmImportsOfExternalPackages(await NodeFSP.readFile(probe, "utf8")), []);
119 const stdout = NodeChildProcess.execFileSync(
120 process.execPath,
121 ["--no-global-search-paths", probe],
122 {
123 cwd: output,
124 env: {
125 HOME: scratch,
126 USERPROFILE: scratch,
127 PATH: "",
128 SystemRoot: process.env.SystemRoot ?? "",
129 },
130 encoding: "utf8",
131 timeout: 30_000,
132 },
133 );
134 assert.include(stdout, "Cursor catalog chunks loaded; empty keys rejected locally");
135 } finally {
136 await NodeFSP.rm(scratch, { recursive: true, force: true });
137 }
138}, 60_000);