@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/compat.ts CHANGED
@@ -5,171 +5,208 @@ import type {
5
5
  CompatibilityManifestEntry,
6
6
  CompatibilityManifestEnvelope,
7
7
  } from '@cursor-pool/shared/manifest';
8
- import {
9
- CURSOR_AGENT_EXEC_RELATIVE_PATH,
10
- CURSOR_WORKBENCH_RELATIVE_PATH,
11
- } from '@cursor-pool/patcher';
8
+ import { CURSOR_AGENT_EXEC_RELATIVE_PATH } from '@cursor-pool/patcher';
12
9
  import type { CliEnvironment } from './environment';
13
10
  import type { CursorInfo } from './cursor';
14
11
 
15
- function macPatchTargets(agentSha256: string, workbenchSha256: string) {
16
- return [
17
- {
18
- name: 'agent-exec',
19
- targetRelativePath: CURSOR_AGENT_EXEC_RELATIVE_PATH,
20
- expectedSha256: agentSha256,
21
- patchStrategy: 'cursor-agent-exec-snippet',
22
- verifyMarker: 'cursor-pool',
23
- },
24
- {
25
- name: 'workbench',
26
- targetRelativePath: CURSOR_WORKBENCH_RELATIVE_PATH,
27
- expectedSha256: workbenchSha256,
28
- patchStrategy: 'cursor-workbench-auth-gate',
29
- verifyMarker: 'cursor-pool-workbench',
30
- },
31
- ];
32
- }
12
+ const LINUX_DEB_AGENT_EXEC_RELATIVE_PATH =
13
+ 'resources/app/extensions/cursor-agent-exec/dist/main.js';
33
14
 
34
- function macCompatEntry(input: {
35
- arch: 'arm64' | 'x64';
36
- officialMajorVersion: string;
37
- verifiedCursorVersion: string;
38
- cursorVersion: string;
39
- cursorCommit: string;
40
- adapterVersion: string;
41
- structureFamily: string;
42
- takeoverPlanId: string;
43
- agentSha256: string;
44
- workbenchSha256: string;
45
- userMessage: string;
46
- }): CompatibilityManifestEntry {
47
- return {
15
+ export const DEFAULT_COMPAT_ENTRIES: CompatibilityManifestEntry[] = [
16
+ {
48
17
  platform: 'darwin',
49
- arch: input.arch,
50
- officialSourceUrl: 'https://cursor.com/download',
51
- officialDownloadUrl: `https://api2.cursor.sh/updates/download/golden/darwin-${input.arch}/cursor/${input.officialMajorVersion}`,
52
- officialDownloadPlatform: `darwin-${input.arch}`,
53
- verifiedCursorVersion: input.verifiedCursorVersion,
54
- cursorVersion: input.officialMajorVersion,
55
- cursorCommit: input.cursorCommit,
18
+ arch: 'arm64',
19
+ cursorVersion: '3.4',
20
+ cursorCommit: '*',
56
21
  supportStatus: 'supported',
57
- adapterVersion: input.adapterVersion,
58
- takeoverPlanId: input.takeoverPlanId,
59
- structureFamily: input.structureFamily,
60
- patchTargets: macPatchTargets(input.agentSha256, input.workbenchSha256),
61
22
  targetRelativePath: CURSOR_AGENT_EXEC_RELATIVE_PATH,
62
- expectedSha256: input.agentSha256,
63
- structureSignature: input.structureFamily,
23
+ expectedSha256: '*',
24
+ structureSignature: 'ep1-cursor-3-4-family',
64
25
  patchStrategy: 'cursor-agent-exec-snippet',
65
26
  verifyMarker: 'cursor-pool',
66
27
  restoreStrategy: 'external-backup',
67
- minCliVersion: '0.5.8',
68
- minExtensionVersion: '0.5.8',
69
- minServiceVersion: '0.5.8',
28
+ minCliVersion: '0.5.10',
29
+ minExtensionVersion: '0.5.10',
30
+ minServiceVersion: '0.5.10',
70
31
  requiresWritableAppBundle: true,
71
32
  requiresAdHocResign: true,
72
- userMessage: input.userMessage,
73
- };
74
- }
75
-
76
- export const DEFAULT_COMPAT_ENTRIES: CompatibilityManifestEntry[] = [
77
- macCompatEntry({
78
- arch: 'arm64',
79
- officialMajorVersion: '3.4',
80
- verifiedCursorVersion: '3.4.20',
81
- cursorVersion: '3.4.20',
82
- cursorCommit: '0cf8b06883f54e26bb4f0fb8647c9500ccb43310',
83
- adapterVersion: '0.4.8',
84
- structureFamily: 'mac-agent-D-workbench-U0',
85
- takeoverPlanId: 'cursor-3.4-mac-agent-d-workbench-u0',
86
- agentSha256: 'c63ae78a3b72df82db224839d552b1f8ca38011e45a37a55ae4ccd75fcdc125f',
87
- workbenchSha256: '01a7005df6a76159b4edafee3c4a233003626e082212f9d042a81239a2bde388',
88
- userMessage: 'Cursor 3.4.20 macOS arm64 is supported by the v0.4 adapter.',
89
- }),
90
- macCompatEntry({
91
- arch: 'x64',
92
- officialMajorVersion: '3.4',
93
- verifiedCursorVersion: '3.4.20',
94
- cursorVersion: '3.4.20',
95
- cursorCommit: '0cf8b06883f54e26bb4f0fb8647c9500ccb43310',
96
- adapterVersion: '0.4.8',
97
- structureFamily: 'mac-agent-D-workbench-U0',
98
- takeoverPlanId: 'cursor-3.4-mac-agent-d-workbench-u0',
99
- agentSha256: 'c63ae78a3b72df82db224839d552b1f8ca38011e45a37a55ae4ccd75fcdc125f',
100
- workbenchSha256: '01a7005df6a76159b4edafee3c4a233003626e082212f9d042a81239a2bde388',
101
- userMessage: 'Cursor 3.4.20 macOS x64 is supported by the v0.4 adapter.',
102
- }),
103
- macCompatEntry({
33
+ userMessage: 'Cursor 3.4.x is supported for MVP-0.',
34
+ },
35
+ {
36
+ platform: 'darwin',
104
37
  arch: 'arm64',
105
- officialMajorVersion: '3.5',
106
- verifiedCursorVersion: '3.5.38',
107
- cursorVersion: '3.5.38',
108
- cursorCommit: '009bb5a3600dd98fe1c1f25798f767f686e14750',
109
- adapterVersion: '0.5.8',
110
- structureFamily: 'mac-agent-F-workbench-p-L0',
111
- takeoverPlanId: 'cursor-3.5-mac-agent-f-workbench-p-l0',
112
- agentSha256: 'cb18f0237278884a39e2ce2b8664255e12689ad0803c20096c38e86c36acc51f',
113
- workbenchSha256: '19762fb81b7d5f1a42ffa8c0c487648432874d0892cfcdb32824b107f6dcf99d',
114
- userMessage: 'Cursor 3.5.38 macOS arm64 is supported by the v0.5 adapter.',
115
- }),
116
- macCompatEntry({
38
+ cursorVersion: '3.5',
39
+ cursorCommit: '*',
40
+ supportStatus: 'supported',
41
+ targetRelativePath: CURSOR_AGENT_EXEC_RELATIVE_PATH,
42
+ expectedSha256: '*',
43
+ structureSignature: 'ep1-cursor-3-5-family',
44
+ patchStrategy: 'cursor-agent-exec-snippet',
45
+ verifyMarker: 'cursor-pool',
46
+ restoreStrategy: 'external-backup',
47
+ minCliVersion: '0.5.10',
48
+ minExtensionVersion: '0.5.10',
49
+ minServiceVersion: '0.5.10',
50
+ requiresWritableAppBundle: true,
51
+ requiresAdHocResign: true,
52
+ userMessage: 'Cursor 3.5.x is supported for MVP-0.',
53
+ },
54
+ {
55
+ platform: 'darwin',
117
56
  arch: 'x64',
118
- officialMajorVersion: '3.5',
119
- verifiedCursorVersion: '3.5.38',
120
- cursorVersion: '3.5.38',
121
- cursorCommit: '009bb5a3600dd98fe1c1f25798f767f686e14750',
122
- adapterVersion: '0.5.8',
123
- structureFamily: 'mac-agent-F-workbench-p-L0',
124
- takeoverPlanId: 'cursor-3.5-mac-agent-f-workbench-p-l0',
125
- agentSha256: 'cb18f0237278884a39e2ce2b8664255e12689ad0803c20096c38e86c36acc51f',
126
- workbenchSha256: '19762fb81b7d5f1a42ffa8c0c487648432874d0892cfcdb32824b107f6dcf99d',
127
- userMessage: 'Cursor 3.5.38 macOS x64 is supported by the v0.5 adapter.',
128
- }),
129
- macCompatEntry({
57
+ cursorVersion: '3.5',
58
+ cursorCommit: '*',
59
+ supportStatus: 'supported',
60
+ targetRelativePath: CURSOR_AGENT_EXEC_RELATIVE_PATH,
61
+ expectedSha256: '*',
62
+ structureSignature: 'ep1-cursor-3-5-family',
63
+ patchStrategy: 'cursor-agent-exec-snippet',
64
+ verifyMarker: 'cursor-pool',
65
+ restoreStrategy: 'external-backup',
66
+ minCliVersion: '0.5.10',
67
+ minExtensionVersion: '0.5.10',
68
+ minServiceVersion: '0.5.10',
69
+ requiresWritableAppBundle: true,
70
+ requiresAdHocResign: true,
71
+ userMessage: 'Cursor 3.5.x is supported for MVP-0 under Rosetta-launched installers.',
72
+ },
73
+ {
74
+ platform: 'darwin',
130
75
  arch: 'arm64',
131
- officialMajorVersion: '3.6',
132
- verifiedCursorVersion: '3.6.21',
133
- cursorVersion: '3.6.21',
134
- cursorCommit: 'e7a7e93f4d75f8272503ecf33cedbaae10114a10',
135
- adapterVersion: '0.5.8',
136
- structureFamily: 'mac-agent-c-workbench-h-uv',
137
- takeoverPlanId: 'cursor-3.6-mac-agent-c-workbench-h-uv',
138
- agentSha256: '222512631b78fddcdca3fa76c0dd458a7a86751dde19c998ceb31b3fe1905ebf',
139
- workbenchSha256: '97562e30eebf41b9e162eb100966a2caf09c1c2fcf55bc87d5a84f9de67511f5',
140
- userMessage: 'Cursor 3.6.21 macOS arm64 is supported by the v0.5 adapter.',
141
- }),
142
- macCompatEntry({
76
+ cursorVersion: '3.6',
77
+ cursorCommit: '*',
78
+ supportStatus: 'supported',
79
+ targetRelativePath: CURSOR_AGENT_EXEC_RELATIVE_PATH,
80
+ expectedSha256: '*',
81
+ structureSignature: 'ep1-cursor-3-6-family',
82
+ patchStrategy: 'cursor-agent-exec-snippet',
83
+ verifyMarker: 'cursor-pool',
84
+ restoreStrategy: 'external-backup',
85
+ minCliVersion: '0.5.10',
86
+ minExtensionVersion: '0.5.10',
87
+ minServiceVersion: '0.5.10',
88
+ requiresWritableAppBundle: true,
89
+ requiresAdHocResign: true,
90
+ userMessage: 'Cursor 3.6.x is supported for MVP-0.',
91
+ },
92
+ {
93
+ platform: 'darwin',
143
94
  arch: 'arm64',
144
- officialMajorVersion: '3.6',
145
- verifiedCursorVersion: '3.6.31',
95
+ cursorVersion: '3.7',
96
+ cursorCommit: '*',
97
+ supportStatus: 'supported',
98
+ targetRelativePath: CURSOR_AGENT_EXEC_RELATIVE_PATH,
99
+ expectedSha256: '*',
100
+ structureSignature: 'ep1-cursor-3-7-family',
101
+ patchStrategy: 'cursor-agent-exec-snippet',
102
+ verifyMarker: 'cursor-pool',
103
+ restoreStrategy: 'external-backup',
104
+ minCliVersion: '0.5.10',
105
+ minExtensionVersion: '0.5.10',
106
+ minServiceVersion: '0.5.10',
107
+ requiresWritableAppBundle: true,
108
+ requiresAdHocResign: true,
109
+ userMessage: 'Cursor 3.7.x is supported for MVP-0.',
110
+ },
111
+ {
112
+ platform: 'linux',
113
+ arch: 'x64',
146
114
  cursorVersion: '3.6.31',
147
115
  cursorCommit: '81fcf2931d7687b4ff3f3017858d0c6dee7e2a60',
148
- adapterVersion: '0.5.8',
149
- structureFamily: 'mac-agent-c-workbench-h-uv',
150
- takeoverPlanId: 'cursor-3.6-mac-agent-c-workbench-h-uv',
151
- agentSha256: '05bfa29eacb8271c378765ead4bf881f806b97549dd13367183aa7a9331c1131',
152
- workbenchSha256: '97562e30eebf41b9e162eb100966a2caf09c1c2fcf55bc87d5a84f9de67511f5',
153
- userMessage: 'Cursor 3.6.31 macOS arm64 is supported by the v0.5 adapter.',
154
- }),
155
- macCompatEntry({
116
+ supportStatus: 'supported',
117
+ targetRelativePath: 'usr/share/cursor/resources/app/extensions/cursor-agent-exec/dist/main.js',
118
+ expectedSha256: '05bfa29eacb8271c378765ead4bf881f806b97549dd13367183aa7a9331c1131',
119
+ structureSignature: 'linux-appimage-3-6-31-cursor-agent-exec',
120
+ patchStrategy: 'cursor-agent-exec-snippet',
121
+ verifyMarker: 'cursor-pool',
122
+ restoreStrategy: 'external-backup',
123
+ minCliVersion: '0.5.7',
124
+ minExtensionVersion: '0.5.7',
125
+ minServiceVersion: '0.5.7',
126
+ requiresWritableAppBundle: true,
127
+ requiresAdHocResign: false,
128
+ userMessage: 'Cursor 3.6.31 Linux x64 AppImage is supported.',
129
+ },
130
+ {
131
+ platform: 'linux',
132
+ arch: 'arm64',
133
+ cursorVersion: '3.4',
134
+ cursorCommit: '*',
135
+ supportStatus: 'supported',
136
+ targetRelativePath: LINUX_DEB_AGENT_EXEC_RELATIVE_PATH,
137
+ expectedSha256: '4ccb7526e5ece8f6a98a99724a048977698cbf52cb2033ec06f2b5a0a085fe71',
138
+ structureSignature: 'linux-arm64-deb-cursor-3-4-family',
139
+ patchStrategy: 'cursor-agent-exec-snippet',
140
+ verifyMarker: 'cursor-pool',
141
+ restoreStrategy: 'external-backup',
142
+ minCliVersion: '0.5.10',
143
+ minExtensionVersion: '0.5.10',
144
+ minServiceVersion: '0.5.10',
145
+ requiresWritableAppBundle: true,
146
+ requiresAdHocResign: false,
147
+ userMessage: 'Cursor 3.4.x Linux arm64 deb installs are supported.',
148
+ },
149
+ {
150
+ platform: 'linux',
156
151
  arch: 'arm64',
157
- officialMajorVersion: '3.7',
158
- verifiedCursorVersion: '3.7.12',
159
- cursorVersion: '3.7.12',
160
- cursorCommit: 'b887a26c4f70bd8136bfffeda812b24194ec9ce0',
161
- adapterVersion: '0.6.0',
162
- structureFamily: 'mac-3.7-agent-Et-workbench-wv',
163
- takeoverPlanId: 'cursor-3.7-mac-agent-et-workbench-wv',
164
- agentSha256: '9ce7a2f40a98a27eb1b609a79e0e1707bad5fbb02493693f6f18945a7640dde4',
165
- workbenchSha256: 'e91aa502a84d5b1653a2c1f3a71a2d4160ab5f1de5809dd230756ecc0cc27db9',
166
- userMessage: 'Cursor 3.7.12 macOS arm64 is supported by the v0.6 adapter.',
167
- }),
152
+ cursorVersion: '3.5',
153
+ cursorCommit: '*',
154
+ supportStatus: 'supported',
155
+ targetRelativePath: LINUX_DEB_AGENT_EXEC_RELATIVE_PATH,
156
+ expectedSha256: 'cb18f0237278884a39e2ce2b8664255e12689ad0803c20096c38e86c36acc51f',
157
+ structureSignature: 'linux-arm64-deb-cursor-3-5-family',
158
+ patchStrategy: 'cursor-agent-exec-snippet',
159
+ verifyMarker: 'cursor-pool',
160
+ restoreStrategy: 'external-backup',
161
+ minCliVersion: '0.5.10',
162
+ minExtensionVersion: '0.5.10',
163
+ minServiceVersion: '0.5.10',
164
+ requiresWritableAppBundle: true,
165
+ requiresAdHocResign: false,
166
+ userMessage: 'Cursor 3.5.x Linux arm64 deb installs are supported.',
167
+ },
168
+ {
169
+ platform: 'linux',
170
+ arch: 'arm64',
171
+ cursorVersion: '3.6',
172
+ cursorCommit: '*',
173
+ supportStatus: 'supported',
174
+ targetRelativePath: LINUX_DEB_AGENT_EXEC_RELATIVE_PATH,
175
+ expectedSha256: '05bfa29eacb8271c378765ead4bf881f806b97549dd13367183aa7a9331c1131',
176
+ structureSignature: 'linux-arm64-deb-cursor-3-6-family',
177
+ patchStrategy: 'cursor-agent-exec-snippet',
178
+ verifyMarker: 'cursor-pool',
179
+ restoreStrategy: 'external-backup',
180
+ minCliVersion: '0.5.10',
181
+ minExtensionVersion: '0.5.10',
182
+ minServiceVersion: '0.5.10',
183
+ requiresWritableAppBundle: true,
184
+ requiresAdHocResign: false,
185
+ userMessage: 'Cursor 3.6.x Linux arm64 deb installs are supported.',
186
+ },
187
+ {
188
+ platform: 'linux',
189
+ arch: 'arm64',
190
+ cursorVersion: '3.7',
191
+ cursorCommit: '*',
192
+ supportStatus: 'supported',
193
+ targetRelativePath: LINUX_DEB_AGENT_EXEC_RELATIVE_PATH,
194
+ expectedSha256: '9ce7a2f40a98a27eb1b609a79e0e1707bad5fbb02493693f6f18945a7640dde4',
195
+ structureSignature: 'linux-arm64-deb-cursor-3-7-family',
196
+ patchStrategy: 'cursor-agent-exec-snippet',
197
+ verifyMarker: 'cursor-pool',
198
+ restoreStrategy: 'external-backup',
199
+ minCliVersion: '0.5.10',
200
+ minExtensionVersion: '0.5.10',
201
+ minServiceVersion: '0.5.10',
202
+ requiresWritableAppBundle: true,
203
+ requiresAdHocResign: false,
204
+ userMessage: 'Cursor 3.7.x Linux arm64 deb installs are supported.',
205
+ },
168
206
  ];
169
207
 
170
208
  export type ResolveCompatOptions = {
171
209
  entries?: CompatibilityManifestEntry[];
172
- adapterVersion?: string;
173
210
  };
174
211
 
175
212
  export type CompatManifestFetchResponse = {
@@ -220,34 +257,6 @@ function asBoolean(value: unknown, field: string) {
220
257
  return value;
221
258
  }
222
259
 
223
- function asOptionalString(value: unknown, field: string) {
224
- if (value === undefined) {
225
- return undefined;
226
- }
227
- return asString(value, field);
228
- }
229
-
230
- function normalizePatchTargets(value: unknown) {
231
- if (value === undefined) {
232
- return undefined;
233
- }
234
- if (!Array.isArray(value)) {
235
- throw new Error('compat manifest patchTargets invalid');
236
- }
237
- return value.map((target) => {
238
- if (!isRecord(target)) {
239
- throw new Error('compat manifest patchTarget invalid');
240
- }
241
- return {
242
- name: asString(target.name, 'patchTargets.name'),
243
- targetRelativePath: asString(target.targetRelativePath, 'patchTargets.targetRelativePath'),
244
- expectedSha256: asString(target.expectedSha256, 'patchTargets.expectedSha256'),
245
- patchStrategy: asString(target.patchStrategy, 'patchTargets.patchStrategy'),
246
- verifyMarker: asString(target.verifyMarker, 'patchTargets.verifyMarker'),
247
- };
248
- });
249
- }
250
-
251
260
  function normalizeRule(value: unknown): RemoteCompatibilityManifestRule {
252
261
  if (!isRecord(value)) {
253
262
  throw new Error('compat manifest rule invalid');
@@ -263,23 +272,12 @@ function normalizeRule(value: unknown): RemoteCompatibilityManifestRule {
263
272
  ) {
264
273
  throw new Error('compat manifest revision invalid');
265
274
  }
266
- const adapterVersion = asOptionalString(value.adapterVersion, 'adapterVersion');
267
- const structureFamily = asOptionalString(value.structureFamily, 'structureFamily');
268
- const patchTargets = normalizePatchTargets(value.patchTargets);
269
275
  return {
270
276
  platform: asString(value.platform, 'platform'),
271
277
  arch: asString(value.arch, 'arch'),
272
- ...(value.officialSourceUrl === undefined ? {} : { officialSourceUrl: asString(value.officialSourceUrl, 'officialSourceUrl') }),
273
- ...(value.officialDownloadUrl === undefined ? {} : { officialDownloadUrl: asString(value.officialDownloadUrl, 'officialDownloadUrl') }),
274
- ...(value.officialDownloadPlatform === undefined ? {} : { officialDownloadPlatform: asString(value.officialDownloadPlatform, 'officialDownloadPlatform') }),
275
- ...(value.verifiedCursorVersion === undefined ? {} : { verifiedCursorVersion: asString(value.verifiedCursorVersion, 'verifiedCursorVersion') }),
276
278
  cursorVersion: asString(value.cursorVersion, 'cursorVersion'),
277
279
  cursorCommit: asString(value.cursorCommit, 'cursorCommit'),
278
280
  supportStatus,
279
- ...(adapterVersion === undefined ? {} : { adapterVersion }),
280
- takeoverPlanId: asString(value.takeoverPlanId, 'takeoverPlanId'),
281
- ...(structureFamily === undefined ? {} : { structureFamily }),
282
- ...(patchTargets === undefined ? {} : { patchTargets }),
283
281
  targetRelativePath: asString(value.targetRelativePath, 'targetRelativePath'),
284
282
  expectedSha256: asString(value.expectedSha256, 'expectedSha256'),
285
283
  structureSignature: asString(value.structureSignature, 'structureSignature'),
@@ -301,10 +299,8 @@ function canonicalRuleSegment(rule: RemoteCompatibilityManifestRule) {
301
299
  rule.platform,
302
300
  rule.arch,
303
301
  rule.cursorVersion,
304
- rule.verifiedCursorVersion ?? '',
305
302
  rule.cursorCommit,
306
303
  rule.supportStatus,
307
- rule.takeoverPlanId,
308
304
  String(rule.revision ?? 1),
309
305
  ].join(':');
310
306
  }
@@ -318,6 +314,27 @@ function safeEqualHex(left: string, right: string) {
318
314
  return timingSafeEqual(leftBuffer, rightBuffer);
319
315
  }
320
316
 
317
+ function cursorVersionFamily(version: string) {
318
+ const match = version.match(/^(\d+\.\d+)(?:\.|$)/);
319
+ return match?.[1];
320
+ }
321
+
322
+ function compatEntryMatches(
323
+ candidate: CompatibilityManifestEntry,
324
+ cursor: CursorInfo,
325
+ environment: CliEnvironment,
326
+ ) {
327
+ if (candidate.platform !== environment.platform || candidate.arch !== environment.arch) {
328
+ return false;
329
+ }
330
+ const versionMatches =
331
+ candidate.cursorVersion === cursor.version ||
332
+ candidate.cursorVersion === cursorVersionFamily(cursor.version);
333
+ const commitMatches =
334
+ candidate.cursorCommit === '*' || candidate.cursorCommit === cursor.commit;
335
+ return versionMatches && commitMatches;
336
+ }
337
+
321
338
  export function buildCompatManifestSignature(
322
339
  version: number,
323
340
  rules: RemoteCompatibilityManifestRule[],
@@ -356,11 +373,6 @@ export function verifyCompatManifestEnvelope(
356
373
  return rules.map(({ revision, ...rule }) => rule);
357
374
  }
358
375
 
359
- function officialCursorMajorVersion(cursorVersion: string) {
360
- const match = cursorVersion.match(/^(\d+\.\d+)(?:\.|$)/);
361
- return match?.[1] ?? cursorVersion;
362
- }
363
-
364
376
  export function compatManifestUrlFromApiBaseUrl(apiBaseUrl: string) {
365
377
  return `${apiBaseUrl.replace(/\/+$/, '')}/api/client/compatibility/manifest`;
366
378
  }
@@ -407,12 +419,7 @@ export function resolveCompatEntry(
407
419
  ) {
408
420
  const entries = options.entries ?? DEFAULT_COMPAT_ENTRIES;
409
421
  const entry = entries.find(
410
- (candidate) =>
411
- candidate.platform === environment.platform &&
412
- candidate.arch === environment.arch &&
413
- candidate.cursorVersion === officialCursorMajorVersion(cursor.version) &&
414
- candidate.cursorCommit === cursor.commit &&
415
- (!options.adapterVersion || candidate.adapterVersion === options.adapterVersion),
422
+ (candidate) => compatEntryMatches(candidate, cursor, environment),
416
423
  );
417
424
 
418
425
  if (!entry) {
package/src/cursor.ts CHANGED
@@ -1,12 +1,17 @@
1
- import { execFile } from 'node:child_process';
1
+ import { execFile, execFileSync } from 'node:child_process';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { promisify } from 'node:util';
4
4
  import { chmod, mkdir, readFile, rm, stat } from 'node:fs/promises';
5
- import { basename, join, win32 } from 'node:path';
5
+ import { basename, dirname, join, win32 } from 'node:path';
6
6
  import { homedir } from 'node:os';
7
7
 
8
8
  export const DEFAULT_MACOS_CURSOR_APP_PATH = '/Applications/Cursor.app';
9
9
  const execFileAsync = promisify(execFile);
10
+ type ExecFileForCursor = (
11
+ command: string,
12
+ args: string[],
13
+ ) => Promise<{ stdout: string }>;
14
+ type ExecFileSyncForCursor = (command: string, args: string[]) => string | Buffer;
10
15
 
11
16
  export type CursorInfo = {
12
17
  appPath: string;
@@ -20,6 +25,8 @@ export type FindCursorOptions = {
20
25
  platform?: NodeJS.Platform;
21
26
  env?: NodeJS.ProcessEnv;
22
27
  appImageExtract?: (appImagePath: string, outputRoot: string) => Promise<string>;
28
+ execFile?: ExecFileForCursor;
29
+ execFileSync?: ExecFileSyncForCursor;
23
30
  };
24
31
 
25
32
  type CursorProductJson = {
@@ -29,7 +36,17 @@ type CursorProductJson = {
29
36
  cursorCommit?: unknown;
30
37
  };
31
38
 
32
- export function defaultCursorAppPath(options: Pick<FindCursorOptions, 'platform' | 'env'> = {}) {
39
+ function inferLinuxCursorRoot(launcher: string) {
40
+ const binDir = dirname(launcher);
41
+ if (basename(binDir) === 'bin') {
42
+ return dirname(binDir);
43
+ }
44
+ throw new Error(`Cursor app auto-detection could not infer app root from ${launcher}`);
45
+ }
46
+
47
+ export function defaultCursorAppPath(
48
+ options: Pick<FindCursorOptions, 'platform' | 'env' | 'execFileSync'> = {},
49
+ ) {
33
50
  const platform = options.platform ?? process.platform;
34
51
  if (platform === 'darwin') {
35
52
  return DEFAULT_MACOS_CURSOR_APP_PATH;
@@ -41,15 +58,39 @@ export function defaultCursorAppPath(options: Pick<FindCursorOptions, 'platform'
41
58
  }
42
59
  return win32.join(localAppData, 'Programs', 'Cursor');
43
60
  }
61
+ if (platform === 'linux') {
62
+ const run = options.execFileSync ?? execFileSync;
63
+ const which = String(run('which', ['cursor'])).trim();
64
+ if (!which) {
65
+ throw new Error('Cursor app auto-detection could not find cursor on PATH');
66
+ }
67
+ const launcher = String(run('readlink', ['-f', which])).trim() || which;
68
+ return inferLinuxCursorRoot(launcher);
69
+ }
44
70
  throw new Error(`Cursor app auto-detection is not supported on ${platform}`);
45
71
  }
46
72
 
73
+ async function defaultLinuxCursorAppPath(options: Pick<FindCursorOptions, 'execFile'> = {}) {
74
+ const run = options.execFile ?? execFileAsync;
75
+ const which = (await run('which', ['cursor'])).stdout.trim();
76
+ if (!which) {
77
+ throw new Error('Cursor app auto-detection could not find cursor on PATH');
78
+ }
79
+ const launcher = (await run('readlink', ['-f', which])).stdout.trim() || which;
80
+ return inferLinuxCursorRoot(launcher);
81
+ }
82
+
47
83
  function readString(value: unknown) {
48
84
  return typeof value === 'string' && value.length > 0 ? value : undefined;
49
85
  }
50
86
 
51
87
  export async function findCursor(options: FindCursorOptions = {}): Promise<CursorInfo> {
52
- const rawAppPath = options.appPath ?? defaultCursorAppPath(options);
88
+ const platform = options.platform ?? process.platform;
89
+ const rawAppPath =
90
+ options.appPath ??
91
+ (platform === 'linux'
92
+ ? await defaultLinuxCursorAppPath(options)
93
+ : defaultCursorAppPath(options));
53
94
  const appPath = await resolveCursorAppPath(rawAppPath, options);
54
95
  const productJsonPath = await resolveProductJsonPath(appPath, options);
55
96
  const product = JSON.parse(await readFile(productJsonPath, 'utf8')) as CursorProductJson;
@@ -55,7 +55,7 @@ async function exists(path: string) {
55
55
  function buildRuntimeManifest(sourceManifest: Record<string, unknown>) {
56
56
  return {
57
57
  name: 'cursorpool',
58
- version: typeof sourceManifest.version === 'string' ? sourceManifest.version : '0.5.8',
58
+ version: typeof sourceManifest.version === 'string' ? sourceManifest.version : '0.5.10',
59
59
  displayName: 'Cursor Pool 平台模式',
60
60
  publisher: 'cursor-pool',
61
61
  type: sourceManifest.type,
@@ -4,8 +4,7 @@ import { basename, dirname, join, resolve } from 'node:path';
4
4
  import { resolveExtensionInstallPath } from './extensionBundle';
5
5
  import type { ExtensionState } from './trial';
6
6
 
7
- export const LINKED_EXTENSION_DIRNAME = 'cursor-pool.extension-0.5.8';
8
- const LINKED_EXTENSION_DIRNAME_PREFIX = 'cursor-pool.extension-';
7
+ export const LINKED_EXTENSION_DIRNAME = 'cursor-pool.extension-0.5.10';
9
8
  const RUNTIME_EXTENSION_ID = 'cursor-pool.cursorpool';
10
9
  const STALE_EXTENSION_IDS = new Set([
11
10
  RUNTIME_EXTENSION_ID,
@@ -49,17 +48,10 @@ async function readRuntimeManifest(linkedPath: string) {
49
48
  };
50
49
  const publisher = typeof manifest.publisher === 'string' ? manifest.publisher : 'cursor-pool';
51
50
  const name = typeof manifest.name === 'string' ? manifest.name : 'cursorpool';
52
- const version = typeof manifest.version === 'string' ? manifest.version : '0.5.8';
51
+ const version = typeof manifest.version === 'string' ? manifest.version : '0.5.10';
53
52
  return { id: `${publisher}.${name}`, version };
54
53
  }
55
54
 
56
- function linkedExtensionDirnameForVersion(version: string) {
57
- if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(version)) {
58
- throw new Error(`Unsafe extension version: ${version}`);
59
- }
60
- return `${LINKED_EXTENSION_DIRNAME_PREFIX}${version}`;
61
- }
62
-
63
55
  export async function refreshCursorExtensionsIndex(cursorExtensionsDir: string, linkedPath: string) {
64
56
  const indexPath = join(resolveExtensionInstallPath(cursorExtensionsDir), 'extensions.json');
65
57
  if (!(await exists(indexPath))) {
@@ -71,13 +63,11 @@ export async function refreshCursorExtensionsIndex(cursorExtensionsDir: string,
71
63
  relativeLocation?: string;
72
64
  }>;
73
65
  const manifest = await readRuntimeManifest(linkedPath);
74
- const relativeLocation = basename(linkedPath);
75
66
  const nextEntries = entries.filter((entry) => {
76
67
  const id = entry.identifier?.id;
77
- const relativeLocation = entry.relativeLocation ?? '';
78
68
  return (
79
69
  !STALE_EXTENSION_IDS.has(id ?? '') &&
80
- !relativeLocation.startsWith(LINKED_EXTENSION_DIRNAME_PREFIX) &&
70
+ entry.relativeLocation !== LINKED_EXTENSION_DIRNAME &&
81
71
  entry.relativeLocation !== 'keg1255.cursorpool-1.0.52'
82
72
  );
83
73
  });
@@ -89,7 +79,7 @@ export async function refreshCursorExtensionsIndex(cursorExtensionsDir: string,
89
79
  path: linkedPath,
90
80
  scheme: 'file',
91
81
  },
92
- relativeLocation,
82
+ relativeLocation: LINKED_EXTENSION_DIRNAME,
93
83
  version: manifest.version,
94
84
  });
95
85
  await writeFile(indexPath, `${JSON.stringify(nextEntries, null, 2)}\n`, 'utf8');
@@ -102,23 +92,15 @@ function assertSafeLinkedExtensionPath(linkedPath: string) {
102
92
  (segment) => segment === 'extensions' || segment === 'Extensions',
103
93
  );
104
94
 
105
- if (!basename(resolvedLinkedPath).startsWith(LINKED_EXTENSION_DIRNAME_PREFIX) || !hasExtensionsParent) {
95
+ if (basename(resolvedLinkedPath) !== LINKED_EXTENSION_DIRNAME || !hasExtensionsParent) {
106
96
  throw new Error(
107
- `Unsafe linked extension path for recursive removal: ${linkedPath}. Expected an extensions/${LINKED_EXTENSION_DIRNAME_PREFIX}<version> bundle path.`,
97
+ `Unsafe linked extension path for recursive removal: ${linkedPath}. Expected an extensions/${LINKED_EXTENSION_DIRNAME} bundle path.`,
108
98
  );
109
99
  }
110
100
  }
111
101
 
112
102
  export function linkedExtensionPathForDir(cursorExtensionsDir: string) {
113
- return join(resolveExtensionInstallPath(cursorExtensionsDir), LINKED_EXTENSION_DIRNAME);
114
- }
115
-
116
- export async function linkedExtensionPathForSource(cursorExtensionsDir: string, sourceBundlePath: string) {
117
- const manifest = await readRuntimeManifest(sourceBundlePath);
118
- return join(
119
- resolveExtensionInstallPath(cursorExtensionsDir),
120
- linkedExtensionDirnameForVersion(manifest.version),
121
- );
103
+ return join(resolve(resolveExtensionInstallPath(cursorExtensionsDir)), LINKED_EXTENSION_DIRNAME);
122
104
  }
123
105
 
124
106
  export async function getLinkedExtensionState(linkedPath: string | undefined): Promise<LinkedExtensionState> {
@@ -137,10 +119,7 @@ export async function linkExtensionBundle({
137
119
  cursorExtensionsDir,
138
120
  }: LinkExtensionBundleOptions): Promise<LinkExtensionBundleResult> {
139
121
  const resolvedSourceBundlePath = resolveExtensionInstallPath(sourceBundlePath);
140
- const linkedPath =
141
- (await getLinkedExtensionState(resolvedSourceBundlePath)) === 'missing'
142
- ? linkedExtensionPathForDir(cursorExtensionsDir)
143
- : await linkedExtensionPathForSource(cursorExtensionsDir, resolvedSourceBundlePath);
122
+ const linkedPath = linkedExtensionPathForDir(cursorExtensionsDir);
144
123
 
145
124
  if ((await getLinkedExtensionState(resolvedSourceBundlePath)) === 'missing') {
146
125
  return { state: 'missing', linkedPath };