@cursorpool-dev/cli 0.5.8 → 0.5.9

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.
Files changed (42) hide show
  1. package/node_modules/@cursor-pool/extension/dist/extension.js +46 -116
  2. package/node_modules/@cursor-pool/extension/package.json +3 -3
  3. package/node_modules/@cursor-pool/extension/src/api.ts +2 -17
  4. package/node_modules/@cursor-pool/extension/src/panel.ts +3 -26
  5. package/node_modules/@cursor-pool/extension/test/panel.test.ts +1 -34
  6. package/node_modules/@cursor-pool/patcher/package.json +2 -2
  7. package/node_modules/@cursor-pool/patcher/src/marker.ts +72 -7
  8. package/node_modules/@cursor-pool/patcher/src/workbenchAuthGateMarker.ts +80 -17
  9. package/node_modules/@cursor-pool/patcher/test/patchCursorAgentExec.test.ts +88 -13
  10. package/node_modules/@cursor-pool/patcher/test/patchCursorWorkbench.test.ts +151 -149
  11. package/node_modules/@cursor-pool/service/package.json +2 -2
  12. package/node_modules/@cursor-pool/service/src/platformSession.ts +7 -30
  13. package/node_modules/@cursor-pool/service/src/server.ts +1 -1
  14. package/node_modules/@cursor-pool/service/test/platformSession.test.ts +4 -5
  15. package/node_modules/@cursor-pool/service/test/server.test.ts +1 -130
  16. package/node_modules/@cursor-pool/shared/package.json +1 -1
  17. package/node_modules/@cursor-pool/shared/src/manifest.ts +0 -35
  18. package/node_modules/@cursor-pool/shared/test/manifest.test.ts +9 -43
  19. package/package.json +5 -7
  20. package/src/compat.ts +124 -196
  21. package/src/extensionBundle.ts +1 -1
  22. package/src/extensionLink.ts +8 -29
  23. package/src/install.ts +9 -62
  24. package/src/installRecord.ts +0 -2
  25. package/src/patchSet.ts +6 -12
  26. package/src/platform.ts +3 -3
  27. package/src/repair.ts +2 -10
  28. package/src/restore.ts +4 -12
  29. package/src/status.ts +0 -6
  30. package/src/trial.ts +2 -3
  31. package/test/compat.test.ts +59 -195
  32. package/test/e2e-install.test.ts +0 -53
  33. package/test/extensionLink.test.ts +26 -49
  34. package/test/install.test.ts +4 -64
  35. package/test/repair.test.ts +0 -1
  36. package/test/status.test.ts +0 -1
  37. package/test/trial.test.ts +15 -1
  38. package/node_modules/@cursor-pool/takeover-plans/package.json +0 -12
  39. package/node_modules/@cursor-pool/takeover-plans/src/index.ts +0 -22
  40. package/node_modules/@cursor-pool/takeover-plans/src/plans.ts +0 -37
  41. package/node_modules/@cursor-pool/takeover-plans/src/types.ts +0 -9
  42. package/node_modules/@cursor-pool/takeover-plans/test/registry.test.ts +0 -23
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
4
+ import { isAbsolute, join, relative } from 'node:path';
5
5
  import test from 'node:test';
6
6
  import {
7
7
  getLinkedExtensionState,
@@ -12,12 +12,12 @@ import {
12
12
  snapshotLinkedExtensionBundle,
13
13
  } from '../src/extensionLink';
14
14
 
15
- async function createSourceBundle(tempDir: string, version = '0.0.0') {
15
+ async function createSourceBundle(tempDir: string) {
16
16
  const sourceBundlePath = join(tempDir, 'source/extensions/cursor-pool-status');
17
17
  await mkdir(join(sourceBundlePath, 'dist'), { recursive: true });
18
18
  await writeFile(
19
19
  join(sourceBundlePath, 'package.json'),
20
- JSON.stringify({ name: 'cursorpool', publisher: 'cursor-pool', version }),
20
+ JSON.stringify({ name: 'cursorpool', publisher: 'cursor-pool', version: '0.0.0' }),
21
21
  'utf8',
22
22
  );
23
23
  await writeFile(join(sourceBundlePath, 'dist/extension.js'), 'export function activate() {}\n', 'utf8');
@@ -54,52 +54,6 @@ test('linkExtensionBundle copies source bundle into Cursor extensions directory'
54
54
  }
55
55
  });
56
56
 
57
- test('linkExtensionBundle uses the source manifest version for the linked directory and index', async () => {
58
- const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-extension-link-version-'));
59
- const sourceBundlePath = await createSourceBundle(tempDir, '0.5.6');
60
- const cursorExtensionsDir = join(tempDir, 'Extensions');
61
- await mkdir(cursorExtensionsDir, { recursive: true });
62
- await writeFile(join(cursorExtensionsDir, 'extensions.json'), '[]', 'utf8');
63
-
64
- try {
65
- const result = await linkExtensionBundle({ sourceBundlePath, cursorExtensionsDir });
66
- const index = JSON.parse(await readFile(join(cursorExtensionsDir, 'extensions.json'), 'utf8'));
67
-
68
- assert.deepEqual(result, {
69
- state: 'linked',
70
- linkedPath: join(cursorExtensionsDir, 'cursor-pool.extension-0.5.6'),
71
- });
72
- assert.equal(await getLinkedExtensionState(result.linkedPath), 'linked');
73
- assert.deepEqual(index.at(-1), {
74
- identifier: { id: 'cursor-pool.cursorpool' },
75
- location: {
76
- $mid: 1,
77
- path: result.linkedPath,
78
- scheme: 'file',
79
- },
80
- relativeLocation: 'cursor-pool.extension-0.5.6',
81
- version: '0.5.6',
82
- });
83
- } finally {
84
- await rm(tempDir, { recursive: true, force: true });
85
- }
86
- });
87
-
88
- test('linkExtensionBundle rejects unsafe source manifest versions before linking', async () => {
89
- const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-extension-link-version-unsafe-'));
90
- const sourceBundlePath = await createSourceBundle(tempDir, '../../bad');
91
- const cursorExtensionsDir = join(tempDir, 'Extensions');
92
-
93
- try {
94
- await assert.rejects(
95
- linkExtensionBundle({ sourceBundlePath, cursorExtensionsDir }),
96
- /Unsafe extension version/,
97
- );
98
- } finally {
99
- await rm(tempDir, { recursive: true, force: true });
100
- }
101
- });
102
-
103
57
  test('linkExtensionBundle refreshes Cursor extensions index with runtime extension id', async () => {
104
58
  const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-extension-link-index-'));
105
59
  const sourceBundlePath = await createSourceBundle(tempDir);
@@ -169,6 +123,29 @@ test('linkExtensionBundle refreshes Cursor extensions index with runtime extensi
169
123
  }
170
124
  });
171
125
 
126
+ test('linkExtensionBundle writes an absolute linked extension location for relative cursor extensions dir', async () => {
127
+ const tempDir = await mkdtemp(join(process.cwd(), '.cursor-pool-extension-link-relative-'));
128
+ const sourceBundlePath = await createSourceBundle(tempDir);
129
+ const cursorExtensionsDir = join(tempDir, 'Extensions');
130
+ const relativeCursorExtensionsDir = relative(process.cwd(), cursorExtensionsDir);
131
+ await mkdir(cursorExtensionsDir, { recursive: true });
132
+ await writeFile(join(cursorExtensionsDir, 'extensions.json'), '[]\n', 'utf8');
133
+
134
+ try {
135
+ const result = await linkExtensionBundle({
136
+ sourceBundlePath,
137
+ cursorExtensionsDir: relativeCursorExtensionsDir,
138
+ });
139
+ const index = JSON.parse(await readFile(join(cursorExtensionsDir, 'extensions.json'), 'utf8'));
140
+
141
+ assert.equal(isAbsolute(result.linkedPath), true);
142
+ assert.equal(isAbsolute(index.at(-1).location.path), true);
143
+ assert.equal(index.at(-1).location.path, result.linkedPath);
144
+ } finally {
145
+ await rm(tempDir, { recursive: true, force: true });
146
+ }
147
+ });
148
+
172
149
  test('linkExtensionBundle reports missing when source bundle is absent', async () => {
173
150
  const tempDir = await mkdtemp(join(tmpdir(), 'cursor-pool-extension-link-missing-'));
174
151
  const cursorExtensionsDir = join(tempDir, 'Extensions');
@@ -69,36 +69,15 @@ async function createFixtureApp() {
69
69
  );
70
70
  await writeFile(targetPath, targetContent, 'utf8');
71
71
  await writeFile(join(appPath, alwaysLocalRelativePath), 'function alwaysLocal(){}\n', 'utf8');
72
- const workbenchContent = workbenchFixture();
73
- await writeFile(join(appPath, workbenchRelativePath), workbenchContent, 'utf8');
72
+ await writeFile(join(appPath, workbenchRelativePath), workbenchFixture(), 'utf8');
74
73
 
75
74
  const expectedSha256 = createHash('sha256').update(targetContent).digest('hex');
76
- const workbenchSha256 = createHash('sha256').update(workbenchContent).digest('hex');
77
75
  const compatEntry: CompatibilityManifestEntry = {
78
76
  platform: process.platform,
79
77
  arch: process.arch,
80
- cursorVersion: '3.5',
78
+ cursorVersion: '3.5.38',
81
79
  cursorCommit: '009bb5a3600dd98fe1c1f25798f767f686e14750',
82
80
  supportStatus: 'supported',
83
- adapterVersion: '0.5.8',
84
- takeoverPlanId: 'cursor-3.5-mac-agent-f-workbench-p-l0',
85
- structureFamily: 'mac-agent-F-workbench-p-L0',
86
- patchTargets: [
87
- {
88
- name: 'agent-exec',
89
- targetRelativePath,
90
- expectedSha256,
91
- patchStrategy: 'cursor-agent-exec-snippet',
92
- verifyMarker: 'cursor-pool',
93
- },
94
- {
95
- name: 'workbench',
96
- targetRelativePath: workbenchRelativePath,
97
- expectedSha256: workbenchSha256,
98
- patchStrategy: 'cursor-workbench-auth-gate',
99
- verifyMarker: 'cursor-pool-workbench',
100
- },
101
- ],
102
81
  targetRelativePath,
103
82
  expectedSha256,
104
83
  structureSignature: 'fixture',
@@ -162,10 +141,9 @@ async function createLinuxFixtureApp() {
162
141
  const compatEntry: CompatibilityManifestEntry = {
163
142
  platform: 'linux',
164
143
  arch: process.arch,
165
- cursorVersion: '3.6',
144
+ cursorVersion: '3.6.31',
166
145
  cursorCommit: 'linux-commit',
167
146
  supportStatus: 'supported',
168
- takeoverPlanId: 'cursor-3.6-mac-agent-c-workbench-h-uv',
169
147
  targetRelativePath,
170
148
  expectedSha256,
171
149
  structureSignature: 'linux-fixture',
@@ -210,7 +188,7 @@ async function createRemoteOnlyFixtureApp() {
210
188
  );
211
189
  const compatEntry = {
212
190
  ...fixture.compatEntry,
213
- cursorVersion: '3.7',
191
+ cursorVersion: '3.7.0',
214
192
  cursorCommit: 'remote-commit',
215
193
  userMessage: 'remote fixture supported',
216
194
  };
@@ -234,7 +212,6 @@ test('install reports Cursor version, simulated extension, service, patch, and h
234
212
  assert.match(output, /Cursor 3\.5\.38/);
235
213
  assert.match(output, /mode: disposable/);
236
214
  assert.match(output, /app: .*Cursor\.app/);
237
- assert.match(output, /takeover-plan: cursor-3\.5-mac-agent-f-workbench-p-l0/);
238
215
  assert.match(output, /extension: bundled/);
239
216
  assert.match(output, /trial: recorded/);
240
217
  assert.match(output, /service: running/);
@@ -310,36 +287,6 @@ test('install can use a signed remote compatibility manifest from api base url',
310
287
  }
311
288
  });
312
289
 
313
- test('install rejects when a secondary patch target hash does not match', async () => {
314
- const fixture = await createFixtureApp();
315
- const compatEntry: CompatibilityManifestEntry = {
316
- ...fixture.compatEntry,
317
- patchTargets: fixture.compatEntry.patchTargets?.map((target) =>
318
- target.name === 'workbench'
319
- ? { ...target, expectedSha256: 'f'.repeat(64) }
320
- : target,
321
- ),
322
- };
323
-
324
- try {
325
- await assert.rejects(
326
- () =>
327
- install({
328
- appPath: fixture.appPath,
329
- runtimeFile: fixture.runtimeFile,
330
- backupDir: fixture.backupDir,
331
- trialRecordDir: join(fixture.tempDir, 'trials'),
332
- extensionInstallPath: join(fixture.tempDir, 'extensions/cursor-pool-status'),
333
- compatEntries: [compatEntry],
334
- stopServiceAfterInstall: true,
335
- }),
336
- /Patch target hash mismatch/,
337
- );
338
- } finally {
339
- await rm(fixture.tempDir, { recursive: true, force: true });
340
- }
341
- });
342
-
343
290
  test('install passes API base URL to detached service startup', async () => {
344
291
  const fixture = await createFixtureApp();
345
292
  const serviceCalls: Record<string, unknown>[] = [];
@@ -548,7 +495,6 @@ test('real-mode install writes install record and reports real mode', async () =
548
495
  });
549
496
 
550
497
  assert.match(output, /mode: real/);
551
- assert.match(output, /takeover-plan: cursor-3\.5-mac-agent-f-workbench-p-l0/);
552
498
  assert.match(output, /patch: applied/);
553
499
  assert.match(output, /install-record: recorded/);
554
500
  assert.doesNotMatch(output, /trial: recorded/);
@@ -559,7 +505,6 @@ test('real-mode install writes install record and reports real mode', async () =
559
505
  assert.equal(record.mode, 'real');
560
506
  assert.equal(record.appPath, fixture.appPath);
561
507
  assert.equal(record.cursorVersion, '3.5.38');
562
- assert.equal(record.takeoverPlanId, 'cursor-3.5-mac-agent-f-workbench-p-l0');
563
508
  assert.equal(record.extensionInstallPath, extensionInstallPath);
564
509
  assert.equal(record.extensionLinkedPath, linkedExtensionPathForDir(cursorExtensionsDir));
565
510
  assert.equal(record.lastOperation, 'install');
@@ -573,7 +518,6 @@ test('real-mode install rolls back install record after health failure', async (
573
518
  const fixture = await createFixtureApp();
574
519
  const installRecordFile = join(fixture.tempDir, 'install.json');
575
520
  const extensionInstallPath = join(fixture.tempDir, 'extensions/cursor-pool-status');
576
- const cursorExtensionsDir = join(fixture.tempDir, 'real-extensions');
577
521
 
578
522
  try {
579
523
  await assert.rejects(
@@ -584,7 +528,6 @@ test('real-mode install rolls back install record after health failure', async (
584
528
  backupDir: fixture.backupDir,
585
529
  installRecordFile,
586
530
  extensionInstallPath,
587
- cursorExtensionsDir,
588
531
  compatEntries: [fixture.compatEntry],
589
532
  stopServiceAfterInstall: true,
590
533
  fetchHealth: async () => ({ ok: false, healthy: false }),
@@ -603,7 +546,6 @@ test('real-mode install requires confirmation when yes is missing', async () =>
603
546
  const originalTargetContent = await readFile(fixture.targetPath, 'utf8');
604
547
  const installRecordFile = join(fixture.tempDir, 'install.json');
605
548
  const extensionInstallPath = join(fixture.tempDir, 'extensions/cursor-pool-status');
606
- const cursorExtensionsDir = join(fixture.tempDir, 'real-extensions');
607
549
 
608
550
  try {
609
551
  await assert.rejects(
@@ -614,7 +556,6 @@ test('real-mode install requires confirmation when yes is missing', async () =>
614
556
  backupDir: fixture.backupDir,
615
557
  installRecordFile,
616
558
  extensionInstallPath,
617
- cursorExtensionsDir,
618
559
  compatEntries: [fixture.compatEntry],
619
560
  stopServiceAfterInstall: true,
620
561
  }),
@@ -653,7 +594,6 @@ test('install links extension into an explicit Cursor extensions directory', asy
653
594
  assert.equal(await getLinkedExtensionState(linkedPath), 'linked');
654
595
 
655
596
  const trialRecord = await readTrialRecord(fixture.appPath, { trialRecordDir });
656
- assert.equal(trialRecord?.takeoverPlanId, 'cursor-3.5-mac-agent-f-workbench-p-l0');
657
597
  assert.equal(trialRecord?.extensionState, 'linked');
658
598
  assert.equal(trialRecord?.extensionInstallPath, extensionInstallPath);
659
599
  assert.equal(trialRecord?.extensionLinkedPath, linkedPath);
@@ -89,7 +89,6 @@ async function createFixtureApp() {
89
89
  cursorVersion: '3.5.38',
90
90
  cursorCommit: '009bb5a3600dd98fe1c1f25798f767f686e14750',
91
91
  supportStatus: 'supported',
92
- takeoverPlanId: 'cursor-3.6-mac-agent-c-workbench-h-uv',
93
92
  targetRelativePath,
94
93
  expectedSha256: createHash('sha256').update(targetContent).digest('hex'),
95
94
  structureSignature: 'fixture',
@@ -136,7 +136,6 @@ async function createFixtureApp() {
136
136
  cursorVersion: '3.5.38',
137
137
  cursorCommit: '009bb5a3600dd98fe1c1f25798f767f686e14750',
138
138
  supportStatus: 'supported',
139
- takeoverPlanId: 'cursor-3.6-mac-agent-c-workbench-h-uv',
140
139
  targetRelativePath,
141
140
  expectedSha256: createHash('sha256').update(targetContent).digest('hex'),
142
141
  structureSignature: 'fixture',
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { mkdir, mkdtemp, readFile, rm, symlink } from 'node:fs/promises';
3
3
  import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
4
+ import { join, relative, resolve } from 'node:path';
5
5
  import test from 'node:test';
6
6
  import {
7
7
  assertDisposableCursorAppPath,
@@ -55,6 +55,20 @@ test('assertDisposableCursorAppPath accepts a disposable app copy path', () => {
55
55
  );
56
56
  });
57
57
 
58
+ test('assertDisposableCursorAppPath normalizes relative disposable app paths to absolute paths', async () => {
59
+ const tempDir = await mkdtemp(join(process.cwd(), '.cursor-pool-relative-app-path-'));
60
+ const appPath = join(tempDir, 'Cursor-Trial.app');
61
+
62
+ try {
63
+ await mkdir(appPath, { recursive: true });
64
+ const relativeAppPath = relative(process.cwd(), appPath);
65
+
66
+ assert.equal(assertDisposableCursorAppPath(relativeAppPath), resolve(appPath));
67
+ } finally {
68
+ await rm(tempDir, { recursive: true, force: true });
69
+ }
70
+ });
71
+
58
72
  test('assertDisposableCursorAppPath rejects non app bundle paths', () => {
59
73
  assert.throws(
60
74
  () => assertDisposableCursorAppPath('/Users/example/Desktop/Cursor-Pool-Trial'),
@@ -1,12 +0,0 @@
1
- {
2
- "name": "@cursor-pool/takeover-plans",
3
- "version": "0.5.8",
4
- "type": "module",
5
- "main": "./src/index.ts",
6
- "exports": {
7
- ".": "./src/index.ts"
8
- },
9
- "scripts": {
10
- "test": "tsx --test test/*.test.ts"
11
- }
12
- }
@@ -1,22 +0,0 @@
1
- import { TAKEOVER_PLANS } from './plans';
2
-
3
- export { TAKEOVER_PLANS } from './plans';
4
- export type { TakeoverPlan } from './types';
5
-
6
- export const BUNDLED_TAKEOVER_PLAN_IDS = TAKEOVER_PLANS.map((plan) => plan.id);
7
-
8
- export function isBundledTakeoverPlanId(value: string) {
9
- return BUNDLED_TAKEOVER_PLAN_IDS.includes(value);
10
- }
11
-
12
- export function assertBundledTakeoverPlan(value: string) {
13
- if (!isBundledTakeoverPlanId(value)) {
14
- throw new Error(`takeover plan ${value} is not bundled in this client`);
15
- }
16
- return value;
17
- }
18
-
19
- export function getTakeoverPlan(value: string) {
20
- return TAKEOVER_PLANS.find((plan) => plan.id === value);
21
- }
22
-
@@ -1,37 +0,0 @@
1
- import type { TakeoverPlan } from './types';
2
-
3
- export const TAKEOVER_PLANS: TakeoverPlan[] = [
4
- {
5
- id: 'cursor-3.4-mac-agent-d-workbench-u0',
6
- adapterVersion: '0.4.8',
7
- cursorOfficialMajor: '3.4',
8
- platform: 'darwin',
9
- arch: 'arm64',
10
- structureFamily: 'mac-agent-D-workbench-U0',
11
- },
12
- {
13
- id: 'cursor-3.5-mac-agent-f-workbench-p-l0',
14
- adapterVersion: '0.5.8',
15
- cursorOfficialMajor: '3.5',
16
- platform: 'darwin',
17
- arch: 'arm64',
18
- structureFamily: 'mac-agent-F-workbench-p-L0',
19
- },
20
- {
21
- id: 'cursor-3.6-mac-agent-c-workbench-h-uv',
22
- adapterVersion: '0.5.8',
23
- cursorOfficialMajor: '3.6',
24
- platform: 'darwin',
25
- arch: 'arm64',
26
- structureFamily: 'mac-agent-c-workbench-h-uv',
27
- },
28
- {
29
- id: 'cursor-3.7-mac-agent-et-workbench-wv',
30
- adapterVersion: '0.6.0',
31
- cursorOfficialMajor: '3.7',
32
- platform: 'darwin',
33
- arch: 'arm64',
34
- structureFamily: 'mac-3.7-agent-Et-workbench-wv',
35
- },
36
- ];
37
-
@@ -1,9 +0,0 @@
1
- export type TakeoverPlan = {
2
- id: string;
3
- adapterVersion: string;
4
- cursorOfficialMajor: string;
5
- platform: NodeJS.Platform | string;
6
- arch: string;
7
- structureFamily: string;
8
- };
9
-
@@ -1,23 +0,0 @@
1
- import assert from 'node:assert/strict';
2
- import test from 'node:test';
3
- import { BUNDLED_TAKEOVER_PLAN_IDS, assertBundledTakeoverPlan } from '../src/index';
4
-
5
- test('bundles current macOS takeover plans by stable id', () => {
6
- assert.deepEqual(
7
- [...BUNDLED_TAKEOVER_PLAN_IDS].sort(),
8
- [
9
- 'cursor-3.4-mac-agent-d-workbench-u0',
10
- 'cursor-3.5-mac-agent-f-workbench-p-l0',
11
- 'cursor-3.6-mac-agent-c-workbench-h-uv',
12
- 'cursor-3.7-mac-agent-et-workbench-wv',
13
- ],
14
- );
15
- });
16
-
17
- test('rejects a compatibility rule whose takeover plan is not bundled', () => {
18
- assert.throws(
19
- () => assertBundledTakeoverPlan('cursor-9.9-not-bundled'),
20
- /not bundled in this client/,
21
- );
22
- });
23
-