@cursorpool-dev/cli 0.5.8 → 0.5.10

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 (49) 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 +102 -19
  9. package/node_modules/@cursor-pool/patcher/test/patchCursorAgentExec.test.ts +88 -13
  10. package/node_modules/@cursor-pool/patcher/test/patchCursorWorkbench.test.ts +162 -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 +201 -194
  21. package/src/cursor.ts +45 -4
  22. package/src/extensionBundle.ts +1 -1
  23. package/src/extensionLink.ts +8 -29
  24. package/src/install.ts +10 -62
  25. package/src/installRecord.ts +0 -2
  26. package/src/patchSet.ts +49 -13
  27. package/src/platform.ts +3 -3
  28. package/src/repair.ts +9 -13
  29. package/src/restore.ts +11 -12
  30. package/src/status.ts +5 -9
  31. package/src/target.ts +12 -0
  32. package/src/trial.ts +2 -3
  33. package/src/uninstall.ts +6 -2
  34. package/test/compat.test.ts +146 -192
  35. package/test/cursor.test.ts +54 -0
  36. package/test/e2e-install.test.ts +29 -46
  37. package/test/extensionLink.test.ts +26 -49
  38. package/test/install.test.ts +4 -64
  39. package/test/patchSet.test.ts +71 -0
  40. package/test/repair.test.ts +131 -1
  41. package/test/restore.test.ts +59 -3
  42. package/test/status.test.ts +0 -1
  43. package/test/target.test.ts +28 -0
  44. package/test/trial.test.ts +15 -1
  45. package/node_modules/@cursor-pool/takeover-plans/package.json +0 -12
  46. package/node_modules/@cursor-pool/takeover-plans/src/index.ts +0 -22
  47. package/node_modules/@cursor-pool/takeover-plans/src/plans.ts +0 -37
  48. package/node_modules/@cursor-pool/takeover-plans/src/types.ts +0 -9
  49. package/node_modules/@cursor-pool/takeover-plans/test/registry.test.ts +0 -23
package/src/install.ts CHANGED
@@ -7,18 +7,16 @@ import {
7
7
  restoreCursorAgentExec,
8
8
  restoreCursorAlwaysLocal,
9
9
  restoreCursorWorkbenchAuthGate,
10
- containsCursorWorkbenchAuthGateMarker,
11
10
  } from '@cursor-pool/patcher';
12
11
  import { sha256File } from '@cursor-pool/patcher/hash';
13
12
  import { startServer } from '@cursor-pool/service';
14
- import { assertBundledTakeoverPlan } from '@cursor-pool/takeover-plans';
15
13
  import {
16
14
  readRuntimeInfo,
17
15
  resolveRuntimeFile,
18
16
  writeRuntimeInfo,
19
17
  type RuntimeInfo,
20
18
  } from '@cursor-pool/service';
21
- import type { CompatibilityManifestEntry, CompatibilityPatchTarget } from '@cursor-pool/shared/manifest';
19
+ import type { CompatibilityManifestEntry } from '@cursor-pool/shared/manifest';
22
20
  import { DEFAULT_RUNTIME_FILE } from '@cursor-pool/shared/runtime';
23
21
  import { writeClientConfig } from '@cursor-pool/shared/clientConfig';
24
22
  import { containsCursorPoolMarker } from '@cursor-pool/patcher/marker';
@@ -34,7 +32,6 @@ import {
34
32
  import {
35
33
  getLinkedExtensionState,
36
34
  linkExtensionBundle,
37
- linkedExtensionPathForSource,
38
35
  linkedExtensionPathForDir,
39
36
  refreshCursorExtensionsIndex,
40
37
  removeLinkedExtensionBundle,
@@ -107,7 +104,7 @@ export type InstallOptions = FindCursorOptions &
107
104
  };
108
105
 
109
106
  const REAL_CURSOR_EXTENSIONS_DIR = '~/.cursor/extensions';
110
- const PACKAGE_VERSION = '0.5.8';
107
+ const PACKAGE_VERSION = '0.5.10';
111
108
  const AUTOSTART_SERVICE_STARTUP_TIMEOUT_MS = 30_000;
112
109
 
113
110
  function delay(ms: number) {
@@ -133,57 +130,17 @@ async function maybeAdHocResign({
133
130
  return 'ad-hoc' as const;
134
131
  }
135
132
 
136
- function markerPredicateForPatchTarget(target: CompatibilityPatchTarget) {
137
- if (target.name === 'workbench' || target.verifyMarker === 'cursor-pool-workbench') {
138
- return containsCursorWorkbenchAuthGateMarker;
139
- }
140
- return containsCursorPoolMarker;
141
- }
142
-
143
- async function assertExpectedHash(
144
- targetPath: string,
145
- expectedSha256: string,
146
- containsExpectedMarker = containsCursorPoolMarker,
147
- ) {
133
+ async function assertExpectedHash(targetPath: string, expectedSha256: string) {
148
134
  const currentHash = await sha256File(targetPath);
149
135
  if (currentHash !== expectedSha256) {
150
136
  const content = await readFile(targetPath, 'utf8');
151
- if (containsExpectedMarker(content)) {
137
+ if (containsCursorPoolMarker(content)) {
152
138
  return;
153
139
  }
154
140
  throw new Error(`Patch target hash mismatch: expected ${expectedSha256}, got ${currentHash}`);
155
141
  }
156
142
  }
157
143
 
158
- function compatPatchTargets(compat: CompatibilityManifestEntry): CompatibilityPatchTarget[] {
159
- if (compat.patchTargets?.length) {
160
- return compat.patchTargets;
161
- }
162
- return [
163
- {
164
- name: 'agent-exec',
165
- targetRelativePath: compat.targetRelativePath,
166
- expectedSha256: compat.expectedSha256,
167
- patchStrategy: compat.patchStrategy,
168
- verifyMarker: compat.verifyMarker,
169
- },
170
- ];
171
- }
172
-
173
- function workbenchTargetRelativePathFromCompat(compat: CompatibilityManifestEntry) {
174
- return compat.patchTargets?.find((target) => target.name === 'workbench')?.targetRelativePath;
175
- }
176
-
177
- async function assertExpectedPatchTargetHashes(appPath: string, compat: CompatibilityManifestEntry) {
178
- for (const target of compatPatchTargets(compat)) {
179
- await assertExpectedHash(
180
- join(appPath, target.targetRelativePath),
181
- target.expectedSha256,
182
- markerPredicateForPatchTarget(target),
183
- );
184
- }
185
- }
186
-
187
144
  async function startServiceFromAutostart(options: InstallOptions, targetMode: 'real' | 'disposable') {
188
145
  const autostart = await installAutostart(options, targetMode);
189
146
  const runtimeFile = options.runtimeFile ?? DEFAULT_RUNTIME_FILE;
@@ -418,12 +375,11 @@ async function linkInstallExtensionBundle({
418
375
  sourceBundlePath: string;
419
376
  cursorExtensionsDir: string;
420
377
  }): ReturnType<typeof linkExtensionBundle> {
378
+ const linkedPath = linkedExtensionPathForDir(cursorExtensionsDir);
421
379
  if ((await getLinkedExtensionState(sourceBundlePath)) === 'missing') {
422
- const linkedPath = linkedExtensionPathForDir(cursorExtensionsDir);
423
380
  return { state: 'missing', linkedPath };
424
381
  }
425
382
 
426
- const linkedPath = await linkedExtensionPathForSource(cursorExtensionsDir, sourceBundlePath);
427
383
  await removeInstallLinkedExtensionBundle(linkedPath, cursorExtensionsDir);
428
384
  await mkdir(dirname(linkedPath), { recursive: true });
429
385
  await cp(sourceBundlePath, linkedPath, { recursive: true });
@@ -447,19 +403,15 @@ export async function install(options: InstallOptions = {}) {
447
403
  compatManifestUrl: options.compatManifestUrl,
448
404
  fetchManifest: options.fetchCompatManifest,
449
405
  });
450
- const adapterVersion = PACKAGE_VERSION === '0.5.8'
451
- ? undefined
452
- : PACKAGE_VERSION.split('-', 1)[0].split('+', 1)[0];
453
- const compat = resolveCompatEntry(cursor, environment, { entries: compatEntries, adapterVersion });
454
- assertBundledTakeoverPlan(compat.takeoverPlanId);
455
- const workbenchTargetRelativePath = workbenchTargetRelativePathFromCompat(compat);
406
+ const compat = resolveCompatEntry(cursor, environment, { entries: compatEntries });
456
407
  const targetPath = join(cursor.appPath, compat.targetRelativePath);
457
408
  const originalSha256 = await sha256File(targetPath);
458
- await assertExpectedPatchTargetHashes(cursor.appPath, compat);
409
+ if (compat.expectedSha256 !== '*') {
410
+ await assertExpectedHash(targetPath, compat.expectedSha256);
411
+ }
459
412
  const patchSetBeforeInstall = await readCursorPatchSetState(cursor.appPath, {
460
413
  agentExecTargetRelativePath: compat.targetRelativePath,
461
414
  platform: environment.platform,
462
- workbenchTargetRelativePath,
463
415
  });
464
416
  const wasPatchedBeforeInstall = patchSetBeforeInstall.allApplied;
465
417
  const existingTrialRecord = await readTrialRecord(cursor.appPath, {
@@ -551,7 +503,6 @@ export async function install(options: InstallOptions = {}) {
551
503
  backupDir: options.backupDir,
552
504
  platform: environment.platform,
553
505
  agentExecTargetRelativePath: compat.targetRelativePath,
554
- workbenchTargetRelativePath,
555
506
  patchCursorAgentExec: options.patchCursorAgentExec,
556
507
  patchCursorWorkbenchAuthGate: options.patchCursorWorkbenchAuthGate,
557
508
  });
@@ -581,7 +532,6 @@ export async function install(options: InstallOptions = {}) {
581
532
  appPath: cursor.appPath,
582
533
  cursorVersion: cursor.version,
583
534
  cursorCommit: cursor.commit,
584
- takeoverPlanId: compat.takeoverPlanId,
585
535
  targetRelativePath: compat.targetRelativePath,
586
536
  originalSha256,
587
537
  compatSupportStatus: compat.supportStatus,
@@ -626,7 +576,6 @@ export async function install(options: InstallOptions = {}) {
626
576
  `mode: ${target.mode}`,
627
577
  `app: ${cursor.appPath}`,
628
578
  `compat: ${compat.supportStatus}`,
629
- `takeover-plan: ${compat.takeoverPlanId}`,
630
579
  `extension: ${extension.state}`,
631
580
  `service: running ${service.host}:${service.port}`,
632
581
  `autostart: ${autostartState}`,
@@ -646,7 +595,6 @@ export async function install(options: InstallOptions = {}) {
646
595
  (await readCursorPatchSetState(cursor.appPath, {
647
596
  agentExecTargetRelativePath: compat.targetRelativePath,
648
597
  platform: environment.platform,
649
- workbenchTargetRelativePath,
650
598
  })).appliedCount > 0);
651
599
  } catch (rollbackError) {
652
600
  rollbackErrors.push(rollbackError);
@@ -655,8 +603,8 @@ export async function install(options: InstallOptions = {}) {
655
603
  try {
656
604
  await restoreCursorSet(cursor.appPath, {
657
605
  backupDir: options.backupDir,
606
+ agentExecTargetRelativePath: compat.targetRelativePath,
658
607
  platform: environment.platform,
659
- workbenchTargetRelativePath,
660
608
  restoreCursorAgentExec: options.restoreCursorAgentExec,
661
609
  restoreCursorAlwaysLocal: options.restoreCursorAlwaysLocal,
662
610
  restoreCursorWorkbenchAuthGate: options.restoreCursorWorkbenchAuthGate,
@@ -13,7 +13,6 @@ export type InstallRecord = {
13
13
  appPath: string;
14
14
  cursorVersion: string;
15
15
  cursorCommit: string;
16
- takeoverPlanId: string;
17
16
  targetRelativePath: string;
18
17
  originalSha256: string;
19
18
  compatSupportStatus: CompatibilityManifestEntry['supportStatus'];
@@ -92,7 +91,6 @@ export function isInstallRecordStale(record: InstallRecord | null, actual: Insta
92
91
  'appPath',
93
92
  'cursorVersion',
94
93
  'cursorCommit',
95
- 'takeoverPlanId',
96
94
  'targetRelativePath',
97
95
  'originalSha256',
98
96
  'compatSupportStatus',
package/src/patchSet.ts CHANGED
@@ -54,13 +54,43 @@ const LINUX_WORKBENCH_RELATIVE_PATH =
54
54
  'usr/share/cursor/resources/app/out/vs/workbench/workbench.desktop.main.js';
55
55
  const LINUX_ALWAYS_LOCAL_RELATIVE_PATH =
56
56
  'usr/share/cursor/resources/app/extensions/cursor-always-local/dist/main.js';
57
+ const LINUX_DEB_WORKBENCH_RELATIVE_PATH =
58
+ 'resources/app/out/vs/workbench/workbench.desktop.main.js';
59
+ const LINUX_DEB_ALWAYS_LOCAL_RELATIVE_PATH =
60
+ 'resources/app/extensions/cursor-always-local/dist/main.js';
57
61
 
58
- function workbenchRelativePath(options: { platform?: NodeJS.Platform; workbenchTargetRelativePath?: string }) {
59
- return options.workbenchTargetRelativePath ?? (options.platform === 'linux' ? LINUX_WORKBENCH_RELATIVE_PATH : CURSOR_WORKBENCH_RELATIVE_PATH);
62
+ function linuxUsesDebRoot(options: { agentExecTargetRelativePath?: string }) {
63
+ return options.agentExecTargetRelativePath?.startsWith('resources/app/') ?? false;
60
64
  }
61
65
 
62
- function alwaysLocalRelativePath(options: { platform?: NodeJS.Platform; alwaysLocalTargetRelativePath?: string }) {
63
- return options.alwaysLocalTargetRelativePath ?? (options.platform === 'linux' ? LINUX_ALWAYS_LOCAL_RELATIVE_PATH : CURSOR_ALWAYS_LOCAL_RELATIVE_PATH);
66
+ function workbenchRelativePath(options: {
67
+ platform?: NodeJS.Platform;
68
+ agentExecTargetRelativePath?: string;
69
+ workbenchTargetRelativePath?: string;
70
+ }) {
71
+ if (options.workbenchTargetRelativePath) {
72
+ return options.workbenchTargetRelativePath;
73
+ }
74
+ if (options.platform === 'linux') {
75
+ return linuxUsesDebRoot(options) ? LINUX_DEB_WORKBENCH_RELATIVE_PATH : LINUX_WORKBENCH_RELATIVE_PATH;
76
+ }
77
+ return CURSOR_WORKBENCH_RELATIVE_PATH;
78
+ }
79
+
80
+ function alwaysLocalRelativePath(options: {
81
+ platform?: NodeJS.Platform;
82
+ agentExecTargetRelativePath?: string;
83
+ alwaysLocalTargetRelativePath?: string;
84
+ }) {
85
+ if (options.alwaysLocalTargetRelativePath) {
86
+ return options.alwaysLocalTargetRelativePath;
87
+ }
88
+ if (options.platform === 'linux') {
89
+ return linuxUsesDebRoot(options)
90
+ ? LINUX_DEB_ALWAYS_LOCAL_RELATIVE_PATH
91
+ : LINUX_ALWAYS_LOCAL_RELATIVE_PATH;
92
+ }
93
+ return CURSOR_ALWAYS_LOCAL_RELATIVE_PATH;
64
94
  }
65
95
 
66
96
  async function fileContainsMarker(
@@ -143,11 +173,12 @@ export async function patchCursorSet(appPath: string, options: PatchCursorSetOpt
143
173
  }
144
174
 
145
175
  export async function restoreCursorSet(appPath: string, options: RestoreCursorSetOptions = {}) {
146
- const restoreErrors: unknown[] = [];
147
176
  const before = await readCursorPatchSetState(appPath, {
177
+ agentExecTargetRelativePath: options.agentExecTargetRelativePath,
148
178
  platform: options.platform,
149
179
  workbenchTargetRelativePath: options.workbenchTargetRelativePath,
150
180
  });
181
+ const restoreErrors: unknown[] = [];
151
182
  const restore = async (operation: () => Promise<unknown>) => {
152
183
  try {
153
184
  await operation();
@@ -156,14 +187,12 @@ export async function restoreCursorSet(appPath: string, options: RestoreCursorSe
156
187
  }
157
188
  };
158
189
 
159
- if (before.patches.some((patch) => patch.name === 'workbench' && patch.markerPresent)) {
160
- await restore(() =>
161
- (options.restoreCursorWorkbenchAuthGate ?? restoreCursorWorkbenchAuthGate)(appPath, {
162
- backupDir: options.backupDir,
163
- targetRelativePath: workbenchRelativePath(options),
164
- }),
165
- );
166
- }
190
+ await restore(() =>
191
+ (options.restoreCursorWorkbenchAuthGate ?? restoreCursorWorkbenchAuthGate)(appPath, {
192
+ backupDir: options.backupDir,
193
+ targetRelativePath: workbenchRelativePath(options),
194
+ }),
195
+ );
167
196
  const alwaysLocalPath = resolveCursorAlwaysLocalPath(appPath, alwaysLocalRelativePath(options));
168
197
  const shouldRestoreAlwaysLocal =
169
198
  Boolean(options.restoreCursorAlwaysLocal) ||
@@ -179,10 +208,17 @@ export async function restoreCursorSet(appPath: string, options: RestoreCursorSe
179
208
  await restore(() =>
180
209
  (options.restoreCursorAgentExec ?? restoreCursorAgentExec)(appPath, {
181
210
  backupDir: options.backupDir,
211
+ targetRelativePath: options.agentExecTargetRelativePath,
182
212
  }),
183
213
  );
184
214
 
185
215
  if (restoreErrors.length > 0) {
186
216
  throw new AggregateError(restoreErrors, 'Failed to restore one or more Cursor patches.');
187
217
  }
218
+ const after = await readCursorPatchSetState(appPath, {
219
+ agentExecTargetRelativePath: options.agentExecTargetRelativePath,
220
+ platform: options.platform,
221
+ workbenchTargetRelativePath: options.workbenchTargetRelativePath,
222
+ });
223
+ return { before, after };
188
224
  }
package/src/platform.ts CHANGED
@@ -82,9 +82,9 @@ function buildDeviceInfo() {
82
82
  name: hostname(),
83
83
  os: osPlatform(),
84
84
  arch: arch(),
85
- cliVersion: '0.5.8',
86
- serviceVersion: '0.5.8',
87
- extensionVersion: '0.5.8',
85
+ cliVersion: '0.5.10',
86
+ serviceVersion: '0.5.10',
87
+ extensionVersion: '0.5.10',
88
88
  };
89
89
  }
90
90
 
package/src/repair.ts CHANGED
@@ -69,7 +69,7 @@ export type RepairOptions = FindCursorOptions &
69
69
  adHocResignApp?: typeof adHocResignApp;
70
70
  };
71
71
 
72
- const PACKAGE_VERSION = '0.5.8';
72
+ const PACKAGE_VERSION = '0.5.10';
73
73
 
74
74
  function normalizeAppPath(path: string) {
75
75
  return normalize(path).replace(/\/+$/, '');
@@ -96,10 +96,6 @@ function assertRealRepairInstallRecord(
96
96
  return record;
97
97
  }
98
98
 
99
- function workbenchTargetRelativePathFromCompat(compat: CompatibilityManifestEntry) {
100
- return compat.patchTargets?.find((target) => target.name === 'workbench')?.targetRelativePath;
101
- }
102
-
103
99
  function resolveRepairCompatEntry({
104
100
  cursorVersion,
105
101
  cursorCommit,
@@ -114,12 +110,14 @@ function resolveRepairCompatEntry({
114
110
  entries?: CompatibilityManifestEntry[];
115
111
  }) {
116
112
  const compatEntries = entries ?? DEFAULT_COMPAT_ENTRIES;
113
+ const versionFamily = cursorVersion.match(/^(\d+\.\d+)(?:\.|$)/)?.[1];
117
114
  const entry = compatEntries.find(
118
115
  (candidate) =>
119
116
  candidate.platform === platform &&
120
117
  candidate.arch === arch &&
121
- candidate.cursorVersion === cursorVersion &&
122
- candidate.cursorCommit === cursorCommit,
118
+ (candidate.cursorVersion === cursorVersion ||
119
+ candidate.cursorVersion === versionFamily) &&
120
+ (candidate.cursorCommit === '*' || candidate.cursorCommit === cursorCommit),
123
121
  );
124
122
 
125
123
  if (!entry) {
@@ -200,13 +198,13 @@ async function maybeAdHocResign({
200
198
  }
201
199
 
202
200
  export async function repair(options: RepairOptions = {}) {
203
- const target = resolveCursorTarget(options);
201
+ const environment = detectEnvironment(options);
202
+ const target = resolveCursorTarget({ ...options, platform: environment.platform });
204
203
  if (target.mode !== 'real') {
205
204
  throw new Error('repair is only supported for real Cursor installs in MVP-1');
206
205
  }
207
206
 
208
207
  const cursor = await findCursor({ ...options, appPath: target.appPath });
209
- const environment = detectEnvironment(options);
210
208
  const compatEntries = await loadCompatEntries({
211
209
  compatEntries: options.compatEntries,
212
210
  apiBaseUrl: options.apiBaseUrl,
@@ -227,7 +225,6 @@ export async function repair(options: RepairOptions = {}) {
227
225
  if (compat.supportStatus === 'blocked' || compat.supportStatus === 'unknown') {
228
226
  throw new Error(`compat: ${compat.supportStatus}`);
229
227
  }
230
- const workbenchTargetRelativePath = workbenchTargetRelativePathFromCompat(compat);
231
228
  const targetPath = join(cursor.appPath, compat.targetRelativePath);
232
229
  const runtimeFile = options.runtimeFile ?? installRecord.runtimeFile ?? DEFAULT_RUNTIME_FILE;
233
230
  const backupDir = options.backupDir ?? installRecord.backupDir;
@@ -261,6 +258,7 @@ export async function repair(options: RepairOptions = {}) {
261
258
  installRecord.originalSha256 && patchState.currentHash === installRecord.originalSha256;
262
259
  if (
263
260
  !patchState.legacyMarkerPresent &&
261
+ compat.expectedSha256 !== '*' &&
264
262
  patchState.currentHash !== compat.expectedSha256 &&
265
263
  !matchesInstallOriginal
266
264
  ) {
@@ -327,9 +325,8 @@ export async function repair(options: RepairOptions = {}) {
327
325
  }
328
326
 
329
327
  const patchSetState = await readCursorPatchSetState(cursor.appPath, {
330
- agentExecTargetRelativePath: compat.targetRelativePath,
331
328
  platform: environment.platform,
332
- workbenchTargetRelativePath,
329
+ agentExecTargetRelativePath: compat.targetRelativePath,
333
330
  });
334
331
 
335
332
  if (patchSetState.allApplied) {
@@ -340,7 +337,6 @@ export async function repair(options: RepairOptions = {}) {
340
337
  backupDir,
341
338
  platform: environment.platform,
342
339
  agentExecTargetRelativePath: compat.targetRelativePath,
343
- workbenchTargetRelativePath,
344
340
  patchCursorAgentExec: options.patchCursorAgentExec,
345
341
  patchCursorWorkbenchAuthGate: options.patchCursorWorkbenchAuthGate,
346
342
  });
package/src/restore.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  type ConfirmRealOperationOptions,
8
8
  } from './confirm';
9
9
  import { findCursor, type FindCursorOptions } from './cursor';
10
+ import { detectEnvironment, type DetectEnvironmentOptions } from './environment';
10
11
  import {
11
12
  installIdForAppPath,
12
13
  readInstallRecord,
@@ -14,15 +15,12 @@ import {
14
15
  type InstallRecordOptions,
15
16
  } from './installRecord';
16
17
  import { stopRuntimeService } from './serviceProcess';
17
- import {
18
- formatCursorPatchSetState,
19
- readCursorPatchSetState,
20
- restoreCursorSet,
21
- } from './patchSet';
22
18
  import { resolveCursorTarget, type CursorTargetOptions } from './target';
23
19
  import { assertDisposableCursorAppPath, readTrialRecord, type TrialRecordOptions } from './trial';
20
+ import { formatCursorPatchSetState, restoreCursorSet } from './patchSet';
24
21
 
25
22
  export type RestoreOptions = FindCursorOptions &
23
+ DetectEnvironmentOptions &
26
24
  CursorTargetOptions &
27
25
  TrialRecordOptions &
28
26
  InstallRecordOptions & {
@@ -90,9 +88,12 @@ function formatRestoreConfirmation({
90
88
  }
91
89
 
92
90
  export async function restore(options: RestoreOptions = {}) {
93
- const target = resolveCursorTarget(options);
91
+ const environment = detectEnvironment(options);
92
+ const target = resolveCursorTarget({ ...options, platform: environment.platform });
94
93
  const appPath =
95
- target.mode === 'disposable' ? assertDisposableCursorAppPath(target.appPath) : target.appPath;
94
+ target.mode === 'disposable'
95
+ ? assertDisposableCursorAppPath(target.appPath, { platform: environment.platform })
96
+ : target.appPath;
96
97
  const cursor = await findCursor({ ...options, appPath });
97
98
  const installRecord =
98
99
  target.mode === 'real'
@@ -137,12 +138,10 @@ export async function restore(options: RestoreOptions = {}) {
137
138
  });
138
139
  }
139
140
 
140
- await restoreCursorSet(cursor.appPath, {
141
+ const result = await restoreCursorSet(cursor.appPath, {
141
142
  backupDir,
142
143
  agentExecTargetRelativePath: targetRelativePath,
143
- });
144
- const patchSetState = await readCursorPatchSetState(cursor.appPath, {
145
- agentExecTargetRelativePath: targetRelativePath,
144
+ platform: environment.platform,
146
145
  });
147
146
  if (target.mode === 'real') {
148
147
  await stopRuntimeServiceIfRecorded(runtimeFile);
@@ -153,7 +152,7 @@ export async function restore(options: RestoreOptions = {}) {
153
152
  `mode: ${target.mode}`,
154
153
  `app: ${cursor.appPath}`,
155
154
  'restore: ok',
156
- `patch: ${formatCursorPatchSetState(patchSetState)}`,
155
+ `patch: ${formatCursorPatchSetState(result.after)}`,
157
156
  target.mode === 'real'
158
157
  ? `install-record: ${realInstallRecord ? 'recorded' : 'missing'}`
159
158
  : `trial: ${trialRecord ? 'recorded' : 'missing'}`,
package/src/status.ts CHANGED
@@ -49,10 +49,6 @@ type LatestTakeoverResponse = {
49
49
  };
50
50
  };
51
51
 
52
- function workbenchTargetRelativePathFromCompat(compat: CompatibilityManifestEntry) {
53
- return compat.patchTargets?.find((target) => target.name === 'workbench')?.targetRelativePath;
54
- }
55
-
56
52
  const MAX_CANARY_FIELD_LENGTH = 256;
57
53
 
58
54
  function isSafeCanaryString(value: unknown): value is string {
@@ -150,10 +146,12 @@ async function getTakeoverStatus(
150
146
  }
151
147
 
152
148
  export async function status(options: StatusOptions = {}) {
153
- const target = resolveCursorTarget(options);
154
- const appPath =
155
- target.mode === 'disposable' ? assertDisposableCursorAppPath(target.appPath) : target.appPath;
156
149
  const environment = detectEnvironment(options);
150
+ const target = resolveCursorTarget({ ...options, platform: environment.platform });
151
+ const appPath =
152
+ target.mode === 'disposable'
153
+ ? assertDisposableCursorAppPath(target.appPath, { platform: environment.platform })
154
+ : target.appPath;
157
155
  const cursor = await findCursor({ ...options, appPath });
158
156
  const compatEntries = await loadCompatEntries({
159
157
  compatEntries: options.compatEntries,
@@ -162,7 +160,6 @@ export async function status(options: StatusOptions = {}) {
162
160
  fetchManifest: options.fetchCompatManifest,
163
161
  });
164
162
  const compat = resolveCompatEntry(cursor, environment, { entries: compatEntries });
165
- const workbenchTargetRelativePath = workbenchTargetRelativePathFromCompat(compat);
166
163
  const installRecord =
167
164
  target.mode === 'real'
168
165
  ? await readInstallRecord({ installRecordFile: options.installRecordFile })
@@ -194,7 +191,6 @@ export async function status(options: StatusOptions = {}) {
194
191
  const patchSetState = await readCursorPatchSetState(cursor.appPath, {
195
192
  agentExecTargetRelativePath: compat.targetRelativePath,
196
193
  platform: environment.platform,
197
- workbenchTargetRelativePath,
198
194
  });
199
195
  const serviceRunning = await isRuntimeHealthy(runtime);
200
196
  const takeoverStatus = await getTakeoverStatus(runtime, serviceRunning);
package/src/target.ts CHANGED
@@ -20,8 +20,20 @@ export type CursorTarget =
20
20
  requiresConfirmation: true;
21
21
  };
22
22
 
23
+ function isLinuxRealCursorPath(appPath: string, platform: NodeJS.Platform | undefined) {
24
+ return platform === 'linux' && appPath.replace(/\/+$/, '') === '/usr/share/cursor';
25
+ }
26
+
23
27
  export function resolveCursorTarget(options: CursorTargetOptions = {}): CursorTarget {
24
28
  if (options.appPath) {
29
+ if (isLinuxRealCursorPath(options.appPath, options.platform)) {
30
+ return {
31
+ mode: 'real',
32
+ appPath: options.appPath.replace(/\/+$/, ''),
33
+ requiresConfirmation: true,
34
+ };
35
+ }
36
+
25
37
  return {
26
38
  mode: 'disposable',
27
39
  appPath: assertDisposableCursorAppPath(options.appPath, { platform: options.platform }),
package/src/trial.ts CHANGED
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
3
  import { realpathSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { dirname, join, normalize } from 'node:path';
5
+ import { dirname, join, normalize, resolve } from 'node:path';
6
6
  import { DEFAULT_MACOS_CURSOR_APP_PATH } from './cursor';
7
7
 
8
8
  export type ExtensionState = 'bundled' | 'linked' | 'manual-step-required' | 'missing';
@@ -12,7 +12,6 @@ export type TrialRecord = {
12
12
  appPath: string;
13
13
  cursorVersion: string;
14
14
  cursorCommit: string;
15
- takeoverPlanId: string;
16
15
  targetRelativePath: string;
17
16
  originalSha256: string;
18
17
  compatSupportStatus: 'supported' | 'canary' | 'warning' | 'blocked' | 'unknown';
@@ -40,7 +39,7 @@ function resolveHomePath(path: string) {
40
39
  }
41
40
 
42
41
  function normalizeAppPath(path: string) {
43
- return normalize(path).replace(/\/+$/, '');
42
+ return resolve(normalize(path).replace(/\/+$/, ''));
44
43
  }
45
44
 
46
45
  function isRealCursorAppPath(path: string) {
package/src/uninstall.ts CHANGED
@@ -28,6 +28,7 @@ import { restore } from './restore';
28
28
  import type { RestoreOptions } from './restore';
29
29
  import { stopRuntimeService } from './serviceProcess';
30
30
  import { removeUserAutostart } from './autostart';
31
+ import { detectEnvironment } from './environment';
31
32
  import { resolveCursorTarget, type CursorTargetOptions } from './target';
32
33
  import {
33
34
  assertDisposableCursorAppPath,
@@ -107,9 +108,12 @@ async function removeRecordedLinkedExtensionBundle(
107
108
  }
108
109
 
109
110
  export async function uninstall(options: UninstallOptions = {}) {
110
- const target = resolveCursorTarget(options);
111
+ const environment = detectEnvironment(options);
112
+ const target = resolveCursorTarget({ ...options, platform: environment.platform });
111
113
  const appPath =
112
- target.mode === 'disposable' ? assertDisposableCursorAppPath(target.appPath) : target.appPath;
114
+ target.mode === 'disposable'
115
+ ? assertDisposableCursorAppPath(target.appPath, { platform: environment.platform })
116
+ : target.appPath;
113
117
  const cursor = await findCursor({ ...options, appPath });
114
118
  const installRecord =
115
119
  target.mode === 'real'