@cursorpool-dev/cli 0.5.6 → 0.5.8

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 (108) hide show
  1. package/node_modules/@cursor-pool/extension/dist/extension.js +116 -46
  2. package/node_modules/@cursor-pool/extension/package.json +3 -3
  3. package/node_modules/@cursor-pool/extension/src/api.ts +17 -2
  4. package/node_modules/@cursor-pool/extension/src/panel.ts +26 -3
  5. package/node_modules/@cursor-pool/extension/test/panel.test.ts +34 -1
  6. package/node_modules/@cursor-pool/patcher/package.json +2 -2
  7. package/node_modules/@cursor-pool/patcher/src/marker.ts +5 -1
  8. package/node_modules/@cursor-pool/patcher/src/workbenchAuthGateMarker.ts +58 -7
  9. package/node_modules/@cursor-pool/patcher/test/patchCursorAgentExec.test.ts +20 -0
  10. package/node_modules/@cursor-pool/patcher/test/patchCursorWorkbench.test.ts +193 -2
  11. package/node_modules/@cursor-pool/service/package.json +2 -2
  12. package/node_modules/@cursor-pool/service/src/platformSession.ts +30 -7
  13. package/node_modules/@cursor-pool/service/src/server.ts +1 -0
  14. package/node_modules/@cursor-pool/service/test/platformSession.test.ts +5 -4
  15. package/node_modules/@cursor-pool/service/test/server.test.ts +130 -0
  16. package/node_modules/@cursor-pool/shared/package.json +1 -1
  17. package/node_modules/@cursor-pool/shared/src/manifest.ts +35 -0
  18. package/node_modules/@cursor-pool/shared/test/manifest.test.ts +43 -9
  19. package/node_modules/@cursor-pool/takeover-plans/package.json +12 -0
  20. package/node_modules/@cursor-pool/takeover-plans/src/index.ts +22 -0
  21. package/node_modules/@cursor-pool/takeover-plans/src/plans.ts +37 -0
  22. package/node_modules/@cursor-pool/takeover-plans/src/types.ts +9 -0
  23. package/node_modules/@cursor-pool/takeover-plans/test/registry.test.ts +23 -0
  24. package/node_modules/@esbuild/linux-x64/README.md +3 -0
  25. package/node_modules/@esbuild/linux-x64/bin/esbuild +0 -0
  26. package/node_modules/@esbuild/linux-x64/package.json +20 -0
  27. package/node_modules/esbuild/LICENSE.md +21 -0
  28. package/node_modules/esbuild/README.md +3 -0
  29. package/node_modules/esbuild/bin/esbuild +223 -0
  30. package/node_modules/esbuild/install.js +300 -0
  31. package/node_modules/esbuild/lib/main.d.ts +716 -0
  32. package/node_modules/esbuild/lib/main.js +2532 -0
  33. package/node_modules/esbuild/package.json +74 -0
  34. package/node_modules/tsx/LICENSE +21 -0
  35. package/node_modules/tsx/README.md +32 -0
  36. package/node_modules/tsx/dist/cjs/api/index.cjs +1 -0
  37. package/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
  38. package/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
  39. package/node_modules/tsx/dist/cjs/api/index.mjs +1 -0
  40. package/node_modules/tsx/dist/cjs/index.cjs +1 -0
  41. package/node_modules/tsx/dist/cjs/index.mjs +1 -0
  42. package/node_modules/tsx/dist/cli.cjs +54 -0
  43. package/node_modules/tsx/dist/cli.mjs +55 -0
  44. package/node_modules/tsx/dist/client-D3mGB526.cjs +1 -0
  45. package/node_modules/tsx/dist/client-D_mPDF5S.mjs +1 -0
  46. package/node_modules/tsx/dist/esm/api/index.cjs +1 -0
  47. package/node_modules/tsx/dist/esm/api/index.d.cts +35 -0
  48. package/node_modules/tsx/dist/esm/api/index.d.mts +35 -0
  49. package/node_modules/tsx/dist/esm/api/index.mjs +1 -0
  50. package/node_modules/tsx/dist/esm/index.cjs +1 -0
  51. package/node_modules/tsx/dist/esm/index.mjs +1 -0
  52. package/node_modules/tsx/dist/get-pipe-path-D4YM6rQt.cjs +1 -0
  53. package/node_modules/tsx/dist/get-pipe-path-_tAJyU_v.mjs +1 -0
  54. package/node_modules/tsx/dist/index-BWFBUo6r.cjs +1 -0
  55. package/node_modules/tsx/dist/index-D9F1FXzN.cjs +14 -0
  56. package/node_modules/tsx/dist/index-XurvG3JN.mjs +14 -0
  57. package/node_modules/tsx/dist/index-gbaejti9.mjs +1 -0
  58. package/node_modules/tsx/dist/lexer-DQCqS3nf.mjs +3 -0
  59. package/node_modules/tsx/dist/lexer-DgIbo0BU.cjs +3 -0
  60. package/node_modules/tsx/dist/loader.cjs +1 -0
  61. package/node_modules/tsx/dist/loader.mjs +1 -0
  62. package/node_modules/tsx/dist/node-features-B9BBLzwu.mjs +1 -0
  63. package/node_modules/tsx/dist/node-features-CQLdkVE6.cjs +1 -0
  64. package/node_modules/tsx/dist/package-CGdS2_oX.cjs +1 -0
  65. package/node_modules/tsx/dist/package-DyJMwVU5.mjs +1 -0
  66. package/node_modules/tsx/dist/patch-repl.cjs +1 -0
  67. package/node_modules/tsx/dist/patch-repl.mjs +1 -0
  68. package/node_modules/tsx/dist/preflight.cjs +1 -0
  69. package/node_modules/tsx/dist/preflight.mjs +1 -0
  70. package/node_modules/tsx/dist/register-BOkp8V6j.cjs +10 -0
  71. package/node_modules/tsx/dist/register-BnTWPeIB.mjs +10 -0
  72. package/node_modules/tsx/dist/register-CHVGxKtC.cjs +2 -0
  73. package/node_modules/tsx/dist/register-D_B8UL5H.mjs +2 -0
  74. package/node_modules/tsx/dist/repl.cjs +3 -0
  75. package/node_modules/tsx/dist/repl.mjs +3 -0
  76. package/node_modules/tsx/dist/require-CjvaJWEr.cjs +1 -0
  77. package/node_modules/tsx/dist/require-DzmC1hVr.mjs +1 -0
  78. package/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
  79. package/node_modules/tsx/dist/suppress-warnings.mjs +1 -0
  80. package/node_modules/tsx/dist/temporary-directory-B83uKxJF.cjs +1 -0
  81. package/node_modules/tsx/dist/temporary-directory-BDDVQOvU.mjs +1 -0
  82. package/node_modules/tsx/dist/types-Cxp8y2TL.d.ts +5 -0
  83. package/node_modules/tsx/package.json +67 -0
  84. package/package.json +11 -6
  85. package/src/autostart.ts +5 -1
  86. package/src/compat.ts +193 -47
  87. package/src/cursor.ts +59 -3
  88. package/src/extensionBundle.ts +1 -1
  89. package/src/extensionLink.ts +28 -7
  90. package/src/install.ts +176 -24
  91. package/src/installRecord.ts +2 -0
  92. package/src/patchSet.ts +12 -6
  93. package/src/platform.ts +3 -3
  94. package/src/repair.ts +10 -1
  95. package/src/restore.ts +12 -4
  96. package/src/serviceProcess.ts +2 -1
  97. package/src/status.ts +6 -0
  98. package/src/trial.ts +1 -0
  99. package/test/autostart.test.ts +23 -2
  100. package/test/compat.test.ts +238 -3
  101. package/test/cursor-pool-bin.test.ts +1 -0
  102. package/test/cursor.test.ts +60 -1
  103. package/test/e2e-install.test.ts +53 -0
  104. package/test/extensionLink.test.ts +48 -2
  105. package/test/install.test.ts +191 -6
  106. package/test/repair.test.ts +1 -0
  107. package/test/serviceProcess.test.ts +10 -1
  108. package/test/status.test.ts +1 -0
@@ -1,7 +1,7 @@
1
- // packages/extension/src/api.ts
1
+ // ../extension/src/api.ts
2
2
  import { arch, hostname, platform as osPlatform } from "node:os";
3
3
 
4
- // packages/service/src/health.ts
4
+ // ../service/src/health.ts
5
5
  function buildHealth(runtime) {
6
6
  return {
7
7
  ok: true,
@@ -11,7 +11,7 @@ function buildHealth(runtime) {
11
11
  };
12
12
  }
13
13
 
14
- // packages/shared/src/metadata.ts
14
+ // ../shared/src/metadata.ts
15
15
  var SAFE_METADATA_KEYS = [
16
16
  "requestId",
17
17
  "model",
@@ -31,7 +31,7 @@ function sanitizeRequestMetadata(input) {
31
31
  return output;
32
32
  }
33
33
 
34
- // packages/service/src/metadata.ts
34
+ // ../service/src/metadata.ts
35
35
  var EXTENSION_STATUS_KEYS = ["connected", "cursorVersion", "clientVersion"];
36
36
  function sanitizeServiceMetadata(input) {
37
37
  return {
@@ -49,7 +49,7 @@ function sanitizeExtensionStatus(input) {
49
49
  return output;
50
50
  }
51
51
 
52
- // packages/service/src/platformSession.ts
52
+ // ../service/src/platformSession.ts
53
53
  import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
54
54
  import { homedir } from "node:os";
55
55
  import { dirname, join } from "node:path";
@@ -95,7 +95,7 @@ function asStatus(session) {
95
95
  state: "logged-in",
96
96
  user: session.user,
97
97
  device: session.device,
98
- mode: platformModeFromSession(session)
98
+ mode: statusModeFromSession(session)
99
99
  };
100
100
  }
101
101
  function isPlatformModeReleaseReason(value) {
@@ -121,6 +121,24 @@ function platformModeFromSession(session) {
121
121
  }
122
122
  return inactiveModeFromSession(session);
123
123
  }
124
+ function routeStateFromSession(session) {
125
+ const routeToken = session?.routeToken;
126
+ if (!routeToken) {
127
+ return { state: "missing" };
128
+ }
129
+ const expiresAt = Date.parse(routeToken.expiresAt);
130
+ if (Number.isNaN(expiresAt)) {
131
+ return { state: "missing" };
132
+ }
133
+ if (expiresAt <= Date.now()) {
134
+ return { state: "expired", expiresAt: routeToken.expiresAt };
135
+ }
136
+ return { state: "ready", expiresAt: routeToken.expiresAt };
137
+ }
138
+ function statusModeFromSession(session) {
139
+ const mode = platformModeFromSession(session);
140
+ return mode.state === "active" ? { ...mode, route: routeStateFromSession(session) } : mode;
141
+ }
124
142
  function clearPlatformMode(session) {
125
143
  const {
126
144
  platformMode: _platformMode,
@@ -379,10 +397,10 @@ function poolFailureToModeResult(reason, product, currentCredits) {
379
397
  async function statusFromRequestError(error, session, options = {}) {
380
398
  if (error.status === 401) {
381
399
  const currentSession = await readPlatformSession(options) ?? session;
382
- const { mode } = options.releaseActiveModeOnUnauthorized ? await releaseActivePlatformMode(currentSession, "invalid-token", options) : { mode: platformModeFromSession(currentSession) };
400
+ const { mode } = options.releaseActiveModeOnUnauthorized ? await releaseActivePlatformMode(currentSession, "invalid-token", options) : { mode: statusModeFromSession(currentSession) };
383
401
  return { state: "invalid-token", user: currentSession.user, device: currentSession.device, mode };
384
402
  }
385
- return { state: "offline", user: session.user, device: session.device, mode: platformModeFromSession(session) };
403
+ return { state: "offline", user: session.user, device: session.device, mode: statusModeFromSession(session) };
386
404
  }
387
405
  async function loginWithCode(options) {
388
406
  const tokenResponse = await (options.exchangeDeviceToken ?? exchangeDeviceToken)({
@@ -442,7 +460,7 @@ async function platformStatus(options = {}) {
442
460
  state: "logged-in",
443
461
  user: released.session.user,
444
462
  device: released.session.device,
445
- mode: released.mode
463
+ mode: released.mode.state === "active" ? statusModeFromSession(released.session) : released.mode
446
464
  };
447
465
  }
448
466
  async function platformCatalog(options = {}) {
@@ -494,7 +512,7 @@ async function platformCatalog(options = {}) {
494
512
  return {
495
513
  state: "logged-in",
496
514
  account: { credits: account.value.credits },
497
- mode,
515
+ mode: mode.state === "active" ? statusModeFromSession(updatedSession) : mode,
498
516
  ...selectedProductId ? { selectedProductId } : {},
499
517
  products: catalogProducts
500
518
  };
@@ -665,7 +683,7 @@ async function sendHeartbeat(options = {}) {
665
683
  state: "logged-in",
666
684
  user: released.session.user,
667
685
  device: released.session.device,
668
- mode: released.mode
686
+ mode: released.mode.state === "active" ? statusModeFromSession(released.session) : released.mode
669
687
  };
670
688
  }
671
689
  async function logoutPlatform(options = {}) {
@@ -680,17 +698,17 @@ async function logoutPlatform(options = {}) {
680
698
  return { state: "logged-out" };
681
699
  }
682
700
 
683
- // packages/service/src/runtime.ts
701
+ // ../service/src/runtime.ts
684
702
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
685
703
  import { homedir as homedir2 } from "node:os";
686
704
  import { dirname as dirname2, join as join2 } from "node:path";
687
705
  import { randomUUID } from "node:crypto";
688
706
 
689
- // packages/shared/src/runtime.ts
707
+ // ../shared/src/runtime.ts
690
708
  var DEFAULT_RUNTIME_FILE = "~/.cursor-pool/runtime.json";
691
709
  var DEFAULT_DIAGNOSTICS_FILE = "~/.cursor-pool/diagnostics.jsonl";
692
710
 
693
- // packages/service/src/runtime.ts
711
+ // ../service/src/runtime.ts
694
712
  function createRuntimeId() {
695
713
  return randomUUID();
696
714
  }
@@ -721,7 +739,7 @@ async function readRuntimeInfo(options = {}) {
721
739
  }
722
740
  }
723
741
 
724
- // packages/service/src/requestCheck.ts
742
+ // ../service/src/requestCheck.ts
725
743
  import { randomUUID as randomUUID2 } from "node:crypto";
726
744
  var SAFE_MODEL_PATTERN = /^[A-Za-z0-9._:-]{1,96}$/;
727
745
  var SECRET_PATTERN = /(api[_-]?key|authorization|bearer|cursor[_-]?auth|provider[_-]?secret|secret|token|sk-[A-Za-z0-9])/i;
@@ -763,10 +781,10 @@ function createRequestCheckStore() {
763
781
  };
764
782
  }
765
783
 
766
- // packages/service/src/server.ts
784
+ // ../service/src/server.ts
767
785
  import { createServer } from "node:http";
768
786
 
769
- // packages/service/src/canary.ts
787
+ // ../service/src/canary.ts
770
788
  import { randomUUID as randomUUID3 } from "node:crypto";
771
789
  var isValidRequestId2 = (value) => typeof value === "string" && value.length > 0 && value.length <= 128;
772
790
  function sanitizeAgentCanary(input, options) {
@@ -795,12 +813,12 @@ function createCanaryStore() {
795
813
  };
796
814
  }
797
815
 
798
- // packages/service/src/diagnostics.ts
816
+ // ../service/src/diagnostics.ts
799
817
  import { mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
800
818
  import { dirname as dirname3, join as join3 } from "node:path";
801
819
  import { homedir as homedir3 } from "node:os";
802
820
 
803
- // packages/service/src/requestGateway.ts
821
+ // ../service/src/requestGateway.ts
804
822
  import { randomUUID as randomUUID4 } from "node:crypto";
805
823
  var SAFE_MODEL_PATTERN2 = /^[A-Za-z0-9._:-]{1,96}$/;
806
824
  var SAFE_REQUEST_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
@@ -1036,7 +1054,7 @@ function createGatewayStore() {
1036
1054
  };
1037
1055
  }
1038
1056
 
1039
- // packages/service/src/diagnostics.ts
1057
+ // ../service/src/diagnostics.ts
1040
1058
  var DEFAULT_MAX_DIAGNOSTICS = 20;
1041
1059
  var BLOCKED_GATE_REASONS = /* @__PURE__ */ new Set([
1042
1060
  "logged-out",
@@ -1299,7 +1317,7 @@ async function appendAgentGatewayDiagnostic(gateway, options = {}) {
1299
1317
  await writeFile3(diagnosticsFile, content, "utf8");
1300
1318
  }
1301
1319
 
1302
- // packages/service/src/requestGate.ts
1320
+ // ../service/src/requestGate.ts
1303
1321
  var INVALID_SESSION_GATE = { state: "blocked", reason: "invalid-session" };
1304
1322
  function invalidSessionGate() {
1305
1323
  return { ...INVALID_SESSION_GATE };
@@ -1356,7 +1374,7 @@ async function evaluateRouteState(options = {}) {
1356
1374
  return { state: "ready", expiresAt: routeToken.expiresAt };
1357
1375
  }
1358
1376
 
1359
- // packages/service/src/takeover.ts
1377
+ // ../service/src/takeover.ts
1360
1378
  import { randomUUID as randomUUID5 } from "node:crypto";
1361
1379
  var SAFE_TOKEN_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
1362
1380
  var SAFE_MODEL_PATTERN3 = /^[A-Za-z0-9._:-]{1,96}$/;
@@ -1407,7 +1425,7 @@ function createAgentTakeoverStore() {
1407
1425
  };
1408
1426
  }
1409
1427
 
1410
- // packages/shared/src/clientConfig.ts
1428
+ // ../shared/src/clientConfig.ts
1411
1429
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
1412
1430
  import { homedir as homedir4 } from "node:os";
1413
1431
  import { dirname as dirname4, join as join4 } from "node:path";
@@ -1435,9 +1453,13 @@ async function readClientConfig(options = {}) {
1435
1453
  }
1436
1454
  }
1437
1455
 
1438
- // packages/service/src/server.ts
1456
+ // ../service/src/server.ts
1439
1457
  var LOOPBACK_HOST = "127.0.0.1";
1440
1458
  var DEFAULT_PORT = 56393;
1459
+ var TRUSTED_RENDERER_ORIGINS = /* @__PURE__ */ new Set([
1460
+ "vscode-file://vscode-app",
1461
+ "null"
1462
+ ]);
1441
1463
  var localServices = /* @__PURE__ */ new Map();
1442
1464
  async function resolvePlatformApiBaseUrl(options) {
1443
1465
  if (options.platformApiBaseUrl) {
@@ -1470,18 +1492,37 @@ function writeJson(response, statusCode, payload) {
1470
1492
  const body = JSON.stringify(payload);
1471
1493
  response.writeHead(statusCode, {
1472
1494
  "content-type": "application/json",
1473
- "content-length": Buffer.byteLength(body),
1474
- "access-control-allow-origin": "*",
1475
- "access-control-allow-methods": "GET,POST,OPTIONS",
1476
- "access-control-allow-headers": "content-type"
1495
+ "content-length": Buffer.byteLength(body)
1477
1496
  });
1478
1497
  response.end(body);
1479
1498
  }
1480
- function writeCorsPreflight(response) {
1499
+ function trustedRendererOrigin(request) {
1500
+ const origin = request.headers.origin;
1501
+ if (typeof origin !== "string") {
1502
+ return void 0;
1503
+ }
1504
+ if (TRUSTED_RENDERER_ORIGINS.has(origin) || origin.startsWith("vscode-webview://")) {
1505
+ return origin;
1506
+ }
1507
+ return void 0;
1508
+ }
1509
+ function applyCorsHeaders(request, response) {
1510
+ const origin = trustedRendererOrigin(request);
1511
+ if (!origin) {
1512
+ return false;
1513
+ }
1514
+ response.setHeader("access-control-allow-origin", origin);
1515
+ response.setHeader("access-control-allow-methods", "GET,POST,OPTIONS");
1516
+ response.setHeader("access-control-allow-headers", "content-type,authorization");
1517
+ return true;
1518
+ }
1519
+ function writeCorsPreflight(request, response) {
1520
+ if (!applyCorsHeaders(request, response)) {
1521
+ response.writeHead(403);
1522
+ response.end();
1523
+ return;
1524
+ }
1481
1525
  response.writeHead(204, {
1482
- "access-control-allow-origin": "*",
1483
- "access-control-allow-methods": "GET,POST,OPTIONS",
1484
- "access-control-allow-headers": "content-type",
1485
1526
  "access-control-max-age": "600"
1486
1527
  });
1487
1528
  response.end();
@@ -1496,10 +1537,7 @@ function writeEventStream(response, events) {
1496
1537
  "content-type": "text/event-stream; charset=utf-8",
1497
1538
  "cache-control": "no-cache",
1498
1539
  "connection": "keep-alive",
1499
- "content-length": Buffer.byteLength(body),
1500
- "access-control-allow-origin": "*",
1501
- "access-control-allow-methods": "GET,POST,OPTIONS",
1502
- "access-control-allow-headers": "content-type,authorization"
1540
+ "content-length": Buffer.byteLength(body)
1503
1541
  });
1504
1542
  response.end(body);
1505
1543
  }
@@ -1723,9 +1761,10 @@ async function completeForwardAfterClientResponse(context) {
1723
1761
  async function routeRequest(request, response, runtime, options, canaryStore, requestCheckStore, gatewayStore, takeoverStore, stop) {
1724
1762
  try {
1725
1763
  if (request.method === "OPTIONS") {
1726
- writeCorsPreflight(response);
1764
+ writeCorsPreflight(request, response);
1727
1765
  return;
1728
1766
  }
1767
+ applyCorsHeaders(request, response);
1729
1768
  if (request.method === "GET" && request.url === "/health") {
1730
1769
  writeJson(response, 200, buildHealth(runtime));
1731
1770
  return;
@@ -2050,7 +2089,7 @@ async function routeRequest(request, response, runtime, options, canaryStore, re
2050
2089
  if (request.method === "POST" && request.url === "/shutdown") {
2051
2090
  writeJson(response, 200, { ok: true });
2052
2091
  response.once("finish", () => {
2053
- void stop();
2092
+ void stop().then(() => options.onShutdown?.());
2054
2093
  });
2055
2094
  return;
2056
2095
  }
@@ -2137,7 +2176,7 @@ async function startServer(options = {}) {
2137
2176
  }
2138
2177
  }
2139
2178
 
2140
- // packages/extension/src/runtime.ts
2179
+ // ../extension/src/runtime.ts
2141
2180
  async function readRuntimeInfo2(options = {}) {
2142
2181
  return readRuntimeInfo({ runtimeFile: options.runtimeFile });
2143
2182
  }
@@ -2145,7 +2184,7 @@ async function writeRuntimeInfo2(runtime, options = {}) {
2145
2184
  await writeRuntimeInfo(runtime, { runtimeFile: options.runtimeFile });
2146
2185
  }
2147
2186
 
2148
- // packages/extension/src/api.ts
2187
+ // ../extension/src/api.ts
2149
2188
  var DEFAULT_SERVICE_PORT = 56393;
2150
2189
  var localExtensionServices = /* @__PURE__ */ new Map();
2151
2190
  function buildExtensionDeviceInfo() {
@@ -2153,7 +2192,7 @@ function buildExtensionDeviceInfo() {
2153
2192
  name: hostname(),
2154
2193
  os: osPlatform(),
2155
2194
  arch: arch(),
2156
- extensionVersion: "0.5.6"
2195
+ extensionVersion: "0.5.8"
2157
2196
  };
2158
2197
  }
2159
2198
  function serviceUrl(runtime, path) {
@@ -2248,6 +2287,16 @@ function normalizePlatformMode(value) {
2248
2287
  };
2249
2288
  }
2250
2289
  if (mode?.state === "active" && typeof mode.productId === "string" && typeof mode.startedAt === "string") {
2290
+ const route = mode.route;
2291
+ if (route?.state === "ready" && typeof route.expiresAt === "string") {
2292
+ return { state: "active", productId: mode.productId, startedAt: mode.startedAt, route };
2293
+ }
2294
+ if (route?.state === "expired" && typeof route.expiresAt === "string") {
2295
+ return { state: "active", productId: mode.productId, startedAt: mode.startedAt, route };
2296
+ }
2297
+ if (route?.state === "missing") {
2298
+ return { state: "active", productId: mode.productId, startedAt: mode.startedAt, route };
2299
+ }
2251
2300
  return { state: "active", productId: mode.productId, startedAt: mode.startedAt };
2252
2301
  }
2253
2302
  return null;
@@ -2449,7 +2498,22 @@ async function logoutPlatform2(runtimeFile, options = {}) {
2449
2498
  return postJson({ runtimeFile, ...options }, "/platform/logout");
2450
2499
  }
2451
2500
 
2452
- // packages/extension/src/panel.ts
2501
+ // ../extension/src/panel.ts
2502
+ function panelModeIsTakeoverReady(mode) {
2503
+ return mode.startsWith("active ") && mode.endsWith(" route-ready");
2504
+ }
2505
+ function panelModeDisplay(mode) {
2506
+ if (panelModeIsTakeoverReady(mode)) {
2507
+ return "\u5E73\u53F0\u63A5\u7BA1";
2508
+ }
2509
+ if (mode.endsWith(" route-missing")) {
2510
+ return "\u5E73\u53F0\u672A\u63A5\u7BA1\uFF08\u8DEF\u7531\u7F3A\u5931\uFF09";
2511
+ }
2512
+ if (mode.endsWith(" route-expired")) {
2513
+ return "\u5E73\u53F0\u672A\u63A5\u7BA1\uFF08\u8DEF\u7531\u8FC7\u671F\uFF09";
2514
+ }
2515
+ return "\u5B98\u65B9\u6A21\u5F0F";
2516
+ }
2453
2517
  function createPanelViewModel(status) {
2454
2518
  const rows = [
2455
2519
  { label: "patch", value: status.patch },
@@ -2477,7 +2541,7 @@ function createPanelViewModel(status) {
2477
2541
  rows.push({ label: "current product", value: status.selectedProductId });
2478
2542
  }
2479
2543
  const platformState = status.platform ?? "logged-out";
2480
- const takeoverActive = status.mode.startsWith("active ");
2544
+ const takeoverActive = panelModeIsTakeoverReady(status.mode);
2481
2545
  const platformLoggedIn = platformState === "logged-in" || platformState === "offline" || platformState === "invalid-token";
2482
2546
  return {
2483
2547
  title: "Cursor Pool \u53F7\u6C60\u5BA2\u6237\u7AEF",
@@ -2498,7 +2562,13 @@ function createPanelViewModel(status) {
2498
2562
  }
2499
2563
  function formatPanelMode(mode) {
2500
2564
  if (mode?.state === "active") {
2501
- return `active ${mode.productId}`;
2565
+ if (mode.route?.state === "ready") {
2566
+ return `active ${mode.productId} route-ready`;
2567
+ }
2568
+ if (mode.route?.state === "expired") {
2569
+ return `active ${mode.productId} route-expired`;
2570
+ }
2571
+ return `active ${mode.productId} route-missing`;
2502
2572
  }
2503
2573
  if (mode?.state === "inactive" && mode.releaseReason) {
2504
2574
  return `inactive ${mode.releaseReason}`;
@@ -2584,7 +2654,7 @@ function buildPanelHtml(viewModel) {
2584
2654
  ...valueFor("credits", "") ? [{ label: "\u79EF\u5206\u4F59\u989D", value: valueFor("credits") }] : [],
2585
2655
  ...valueFor("user", "") ? [{
2586
2656
  label: "\u5F53\u524D\u6A21\u5F0F",
2587
- value: valueFor("mode").startsWith("active ") ? "\u5E73\u53F0\u63A5\u7BA1" : "\u5B98\u65B9\u6A21\u5F0F"
2657
+ value: panelModeDisplay(valueFor("mode"))
2588
2658
  }] : []
2589
2659
  ];
2590
2660
  const rows = statusItems.map((row) => `<li><span>${escapeHtml(row.label)}</span><strong>${escapeHtml(row.value)}</strong></li>`).join("");
@@ -2835,7 +2905,7 @@ function registerStatusPanel(context, vscode, options = {}) {
2835
2905
  return provider;
2836
2906
  }
2837
2907
 
2838
- // packages/extension/src/extension.ts
2908
+ // ../extension/src/extension.ts
2839
2909
  async function appendDiagnostic(message) {
2840
2910
  try {
2841
2911
  const os = await new Function("specifier", "return import(specifier)")("node:os");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor-pool/extension",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "displayName": "Cursor Pool 平台模式",
5
5
  "publisher": "cursor-pool",
6
6
  "engines": {
@@ -21,8 +21,8 @@
21
21
  "./runtime": "./src/runtime.ts"
22
22
  },
23
23
  "dependencies": {
24
- "@cursor-pool/service": "0.5.6",
25
- "@cursor-pool/shared": "0.5.6"
24
+ "@cursor-pool/service": "0.5.8",
25
+ "@cursor-pool/shared": "0.5.8"
26
26
  },
27
27
  "contributes": {
28
28
  "viewsContainers": {
@@ -23,7 +23,12 @@ export type PlatformModeReleaseReason =
23
23
  | 'device-inactive';
24
24
  export type PlatformModeSnapshot =
25
25
  | { state: 'inactive'; releaseReason?: PlatformModeReleaseReason; releasedAt?: string }
26
- | { state: 'active'; productId: string; startedAt: string };
26
+ | {
27
+ state: 'active';
28
+ productId: string;
29
+ startedAt: string;
30
+ route?: { state: 'missing' } | { state: 'ready'; expiresAt: string } | { state: 'expired'; expiresAt: string };
31
+ };
27
32
  export type PlatformModeResult =
28
33
  | PlatformModeSnapshot
29
34
  | { state: 'logged-out' }
@@ -114,7 +119,7 @@ export function buildExtensionDeviceInfo() {
114
119
  name: hostname(),
115
120
  os: osPlatform(),
116
121
  arch: arch(),
117
- extensionVersion: '0.5.6',
122
+ extensionVersion: '0.5.8',
118
123
  };
119
124
  }
120
125
 
@@ -255,6 +260,16 @@ function normalizePlatformMode(value: unknown): PlatformModeSnapshot | null {
255
260
  typeof mode.productId === 'string' &&
256
261
  typeof mode.startedAt === 'string'
257
262
  ) {
263
+ const route = mode.route;
264
+ if (route?.state === 'ready' && typeof route.expiresAt === 'string') {
265
+ return { state: 'active', productId: mode.productId, startedAt: mode.startedAt, route };
266
+ }
267
+ if (route?.state === 'expired' && typeof route.expiresAt === 'string') {
268
+ return { state: 'active', productId: mode.productId, startedAt: mode.startedAt, route };
269
+ }
270
+ if (route?.state === 'missing') {
271
+ return { state: 'active', productId: mode.productId, startedAt: mode.startedAt, route };
272
+ }
258
273
  return { state: 'active', productId: mode.productId, startedAt: mode.startedAt };
259
274
  }
260
275
  return null;
@@ -49,6 +49,23 @@ export type PanelViewModel = {
49
49
  error?: string;
50
50
  };
51
51
 
52
+ function panelModeIsTakeoverReady(mode: string) {
53
+ return mode.startsWith('active ') && mode.endsWith(' route-ready');
54
+ }
55
+
56
+ function panelModeDisplay(mode: string) {
57
+ if (panelModeIsTakeoverReady(mode)) {
58
+ return '平台接管';
59
+ }
60
+ if (mode.endsWith(' route-missing')) {
61
+ return '平台未接管(路由缺失)';
62
+ }
63
+ if (mode.endsWith(' route-expired')) {
64
+ return '平台未接管(路由过期)';
65
+ }
66
+ return '官方模式';
67
+ }
68
+
52
69
  type ExtensionContext = {
53
70
  subscriptions?: { push: (subscription: unknown) => unknown };
54
71
  };
@@ -129,7 +146,7 @@ export function createPanelViewModel(status: PanelStatus): PanelViewModel {
129
146
  }
130
147
 
131
148
  const platformState = status.platform ?? 'logged-out';
132
- const takeoverActive = status.mode.startsWith('active ');
149
+ const takeoverActive = panelModeIsTakeoverReady(status.mode);
133
150
  const platformLoggedIn =
134
151
  platformState === 'logged-in' || platformState === 'offline' || platformState === 'invalid-token';
135
152
 
@@ -154,7 +171,13 @@ export function createPanelViewModel(status: PanelStatus): PanelViewModel {
154
171
 
155
172
  function formatPanelMode(mode: PlatformStatus['mode']) {
156
173
  if (mode?.state === 'active') {
157
- return `active ${mode.productId}`;
174
+ if (mode.route?.state === 'ready') {
175
+ return `active ${mode.productId} route-ready`;
176
+ }
177
+ if (mode.route?.state === 'expired') {
178
+ return `active ${mode.productId} route-expired`;
179
+ }
180
+ return `active ${mode.productId} route-missing`;
158
181
  }
159
182
  if (mode?.state === 'inactive' && mode.releaseReason) {
160
183
  return `inactive ${mode.releaseReason}`;
@@ -254,7 +277,7 @@ export function buildPanelHtml(viewModel: PanelViewModel): string {
254
277
  ...(valueFor('user', '')
255
278
  ? [{
256
279
  label: '当前模式',
257
- value: valueFor('mode').startsWith('active ') ? '平台接管' : '官方模式',
280
+ value: panelModeDisplay(valueFor('mode')),
258
281
  }]
259
282
  : []),
260
283
  ];
@@ -652,6 +652,39 @@ test('panel keeps active platform mode out of rendered status list', () => {
652
652
  assert.doesNotMatch(html, /active prod_basic/);
653
653
  });
654
654
 
655
+ test('panel only displays takeover when active route is ready', () => {
656
+ const ready = buildPanelHtml(createPanelViewModel({
657
+ patch: 'applied',
658
+ service: 'running',
659
+ compat: 'supported',
660
+ mode: 'active prod_basic route-ready',
661
+ platform: 'logged-in',
662
+ user: 'user@example.test',
663
+ }));
664
+ const missing = buildPanelHtml(createPanelViewModel({
665
+ patch: 'applied',
666
+ service: 'running',
667
+ compat: 'supported',
668
+ mode: 'active prod_basic route-missing',
669
+ platform: 'logged-in',
670
+ user: 'user@example.test',
671
+ }));
672
+ const expired = buildPanelHtml(createPanelViewModel({
673
+ patch: 'applied',
674
+ service: 'running',
675
+ compat: 'supported',
676
+ mode: 'active prod_basic route-expired',
677
+ platform: 'logged-in',
678
+ user: 'user@example.test',
679
+ }));
680
+
681
+ assert.match(ready, /当前模式<\/span><strong>平台接管<\/strong>/);
682
+ assert.match(missing, /当前模式<\/span><strong>平台未接管(路由缺失)<\/strong>/);
683
+ assert.match(expired, /当前模式<\/span><strong>平台未接管(路由过期)<\/strong>/);
684
+ assert.doesNotMatch(missing, /当前模式<\/span><strong>平台接管<\/strong>/);
685
+ assert.doesNotMatch(expired, /当前模式<\/span><strong>平台接管<\/strong>/);
686
+ });
687
+
655
688
  test('sidebar view model shows inactive mode release reason', () => {
656
689
  const viewModel = createPanelViewModel({
657
690
  patch: 'applied',
@@ -1655,7 +1688,7 @@ test('API can login and logout through local service platform routes', async ()
1655
1688
  assert.equal(typeof (requests[0]?.body.device as Record<string, unknown>).name, 'string');
1656
1689
  assert.equal(typeof (requests[0]?.body.device as Record<string, unknown>).os, 'string');
1657
1690
  assert.equal(typeof (requests[0]?.body.device as Record<string, unknown>).arch, 'string');
1658
- assert.equal((requests[0]?.body.device as Record<string, unknown>).extensionVersion, '0.5.6');
1691
+ assert.equal((requests[0]?.body.device as Record<string, unknown>).extensionVersion, '0.5.8');
1659
1692
  assert.equal(requests[1]?.method, 'POST');
1660
1693
  assert.equal(requests[1]?.url, '/platform/logout');
1661
1694
  } finally {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor-pool/patcher",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "exports": {
@@ -9,7 +9,7 @@
9
9
  "./marker": "./src/marker.ts"
10
10
  },
11
11
  "dependencies": {
12
- "@cursor-pool/shared": "0.5.6"
12
+ "@cursor-pool/shared": "0.5.8"
13
13
  },
14
14
  "scripts": {
15
15
  "test": "tsx --test test/*.test.ts"
@@ -9,6 +9,8 @@ export const CURSOR_POOL_AGENT_EXEC_PROVIDER_REGISTER_ANCHOR =
9
9
  export const CURSOR_POOL_AGENT_EXEC_PROVIDER_REGISTER_ANCHORS = [
10
10
  CURSOR_POOL_AGENT_EXEC_PROVIDER_REGISTER_ANCHOR,
11
11
  'const ht=c.cursor.registerAgentExecProvider(mt);',
12
+ 'const Et=c.cursor.registerAgentExecProvider(vt);',
13
+ 'const dt=D.cursor.registerAgentExecProvider(ut);',
12
14
  ];
13
15
 
14
16
  export function buildCursorAgentExecPatchSnippet() {
@@ -109,7 +111,9 @@ ${CURSOR_POOL_PATCH_BEGIN_MARKER}
109
111
  ? F
110
112
  : typeof c !== 'undefined' && c.cursor?.registerAgentExecProvider
111
113
  ? c
112
- : undefined;
114
+ : typeof D !== 'undefined' && D.cursor?.registerAgentExecProvider
115
+ ? D
116
+ : undefined;
113
117
  if (cursorApi?.cursor?.registerAgentExecProvider) {
114
118
  const originalRegisterAgentExecProvider = cursorApi.cursor.registerAgentExecProvider.bind(cursorApi.cursor);
115
119
  cursorApi.cursor.registerAgentExecProvider = (provider) =>