@kaitranntt/ccs 7.43.0-dev.1 → 7.43.0-dev.11

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 (203) hide show
  1. package/dist/api/services/profile-reader.d.ts.map +1 -1
  2. package/dist/api/services/profile-reader.js +4 -1
  3. package/dist/api/services/profile-reader.js.map +1 -1
  4. package/dist/auth/profile-detector.d.ts +14 -3
  5. package/dist/auth/profile-detector.d.ts.map +1 -1
  6. package/dist/auth/profile-detector.js +35 -15
  7. package/dist/auth/profile-detector.js.map +1 -1
  8. package/dist/ccs.js +11 -0
  9. package/dist/ccs.js.map +1 -1
  10. package/dist/cliproxy/composite-validator.d.ts +20 -0
  11. package/dist/cliproxy/composite-validator.d.ts.map +1 -0
  12. package/dist/cliproxy/composite-validator.js +85 -0
  13. package/dist/cliproxy/composite-validator.js.map +1 -0
  14. package/dist/cliproxy/config/env-builder.d.ts +24 -0
  15. package/dist/cliproxy/config/env-builder.d.ts.map +1 -1
  16. package/dist/cliproxy/config/env-builder.js +72 -1
  17. package/dist/cliproxy/config/env-builder.js.map +1 -1
  18. package/dist/cliproxy/config/thinking-config.d.ts +19 -2
  19. package/dist/cliproxy/config/thinking-config.d.ts.map +1 -1
  20. package/dist/cliproxy/config/thinking-config.js +59 -9
  21. package/dist/cliproxy/config/thinking-config.js.map +1 -1
  22. package/dist/cliproxy/executor/env-resolver.d.ts +16 -0
  23. package/dist/cliproxy/executor/env-resolver.d.ts.map +1 -1
  24. package/dist/cliproxy/executor/env-resolver.js +82 -26
  25. package/dist/cliproxy/executor/env-resolver.js.map +1 -1
  26. package/dist/cliproxy/executor/index.d.ts.map +1 -1
  27. package/dist/cliproxy/executor/index.js +147 -34
  28. package/dist/cliproxy/executor/index.js.map +1 -1
  29. package/dist/cliproxy/executor/retry-handler.d.ts +11 -0
  30. package/dist/cliproxy/executor/retry-handler.d.ts.map +1 -1
  31. package/dist/cliproxy/executor/retry-handler.js +30 -1
  32. package/dist/cliproxy/executor/retry-handler.js.map +1 -1
  33. package/dist/cliproxy/provider-capabilities.d.ts +22 -0
  34. package/dist/cliproxy/provider-capabilities.d.ts.map +1 -0
  35. package/dist/cliproxy/provider-capabilities.js +95 -0
  36. package/dist/cliproxy/provider-capabilities.js.map +1 -0
  37. package/dist/cliproxy/quota-fetcher-codex.d.ts.map +1 -1
  38. package/dist/cliproxy/quota-fetcher-codex.js +106 -87
  39. package/dist/cliproxy/quota-fetcher-codex.js.map +1 -1
  40. package/dist/cliproxy/remote-auth-fetcher.d.ts.map +1 -1
  41. package/dist/cliproxy/remote-auth-fetcher.js +3 -26
  42. package/dist/cliproxy/remote-auth-fetcher.js.map +1 -1
  43. package/dist/cliproxy/services/index.d.ts +1 -1
  44. package/dist/cliproxy/services/index.d.ts.map +1 -1
  45. package/dist/cliproxy/services/index.js +3 -1
  46. package/dist/cliproxy/services/index.js.map +1 -1
  47. package/dist/cliproxy/services/variant-config-adapter.d.ts +15 -0
  48. package/dist/cliproxy/services/variant-config-adapter.d.ts.map +1 -1
  49. package/dist/cliproxy/services/variant-config-adapter.js +84 -11
  50. package/dist/cliproxy/services/variant-config-adapter.js.map +1 -1
  51. package/dist/cliproxy/services/variant-service.d.ts +26 -0
  52. package/dist/cliproxy/services/variant-service.d.ts.map +1 -1
  53. package/dist/cliproxy/services/variant-service.js +163 -9
  54. package/dist/cliproxy/services/variant-service.js.map +1 -1
  55. package/dist/cliproxy/services/variant-settings.d.ts +18 -0
  56. package/dist/cliproxy/services/variant-settings.d.ts.map +1 -1
  57. package/dist/cliproxy/services/variant-settings.js +126 -4
  58. package/dist/cliproxy/services/variant-settings.js.map +1 -1
  59. package/dist/cliproxy/types.d.ts +13 -0
  60. package/dist/cliproxy/types.d.ts.map +1 -1
  61. package/dist/cliproxy/types.js.map +1 -1
  62. package/dist/commands/cliproxy/auth-subcommand.d.ts.map +1 -1
  63. package/dist/commands/cliproxy/auth-subcommand.js +2 -1
  64. package/dist/commands/cliproxy/auth-subcommand.js.map +1 -1
  65. package/dist/commands/cliproxy/help-subcommand.d.ts.map +1 -1
  66. package/dist/commands/cliproxy/help-subcommand.js +2 -0
  67. package/dist/commands/cliproxy/help-subcommand.js.map +1 -1
  68. package/dist/commands/cliproxy/index.d.ts.map +1 -1
  69. package/dist/commands/cliproxy/index.js +4 -0
  70. package/dist/commands/cliproxy/index.js.map +1 -1
  71. package/dist/commands/cliproxy/quota-subcommand.d.ts.map +1 -1
  72. package/dist/commands/cliproxy/quota-subcommand.js +106 -4
  73. package/dist/commands/cliproxy/quota-subcommand.js.map +1 -1
  74. package/dist/commands/cliproxy/variant-subcommand.d.ts +1 -0
  75. package/dist/commands/cliproxy/variant-subcommand.d.ts.map +1 -1
  76. package/dist/commands/cliproxy/variant-subcommand.js +307 -3
  77. package/dist/commands/cliproxy/variant-subcommand.js.map +1 -1
  78. package/dist/commands/command-execution-contract.d.ts +23 -0
  79. package/dist/commands/command-execution-contract.d.ts.map +1 -0
  80. package/dist/commands/command-execution-contract.js +21 -0
  81. package/dist/commands/command-execution-contract.js.map +1 -0
  82. package/dist/commands/cursor-command.d.ts +12 -0
  83. package/dist/commands/cursor-command.d.ts.map +1 -0
  84. package/dist/commands/cursor-command.js +225 -0
  85. package/dist/commands/cursor-command.js.map +1 -0
  86. package/dist/commands/help-command.d.ts.map +1 -1
  87. package/dist/commands/help-command.js +14 -0
  88. package/dist/commands/help-command.js.map +1 -1
  89. package/dist/commands/shell-completion-command.d.ts +20 -0
  90. package/dist/commands/shell-completion-command.d.ts.map +1 -1
  91. package/dist/commands/shell-completion-command.js +45 -30
  92. package/dist/commands/shell-completion-command.js.map +1 -1
  93. package/dist/config/reserved-names.d.ts +1 -1
  94. package/dist/config/reserved-names.d.ts.map +1 -1
  95. package/dist/config/reserved-names.js +2 -0
  96. package/dist/config/reserved-names.js.map +1 -1
  97. package/dist/config/unified-config-loader.d.ts +6 -1
  98. package/dist/config/unified-config-loader.d.ts.map +1 -1
  99. package/dist/config/unified-config-loader.js +56 -2
  100. package/dist/config/unified-config-loader.js.map +1 -1
  101. package/dist/config/unified-config-types.d.ts +71 -2
  102. package/dist/config/unified-config-types.d.ts.map +1 -1
  103. package/dist/config/unified-config-types.js +27 -2
  104. package/dist/config/unified-config-types.js.map +1 -1
  105. package/dist/cursor/cursor-daemon.d.ts +47 -0
  106. package/dist/cursor/cursor-daemon.d.ts.map +1 -0
  107. package/dist/cursor/cursor-daemon.js +332 -0
  108. package/dist/cursor/cursor-daemon.js.map +1 -0
  109. package/dist/cursor/cursor-executor.d.ts +81 -0
  110. package/dist/cursor/cursor-executor.d.ts.map +1 -0
  111. package/dist/cursor/cursor-executor.js +821 -0
  112. package/dist/cursor/cursor-executor.js.map +1 -0
  113. package/dist/cursor/cursor-models.d.ts +43 -0
  114. package/dist/cursor/cursor-models.d.ts.map +1 -0
  115. package/dist/cursor/cursor-models.js +205 -0
  116. package/dist/cursor/cursor-models.js.map +1 -0
  117. package/dist/cursor/cursor-protobuf-decoder.d.ts +52 -0
  118. package/dist/cursor/cursor-protobuf-decoder.d.ts.map +1 -0
  119. package/dist/cursor/cursor-protobuf-decoder.js +311 -0
  120. package/dist/cursor/cursor-protobuf-decoder.js.map +1 -0
  121. package/dist/cursor/cursor-protobuf-encoder.d.ts +54 -0
  122. package/dist/cursor/cursor-protobuf-encoder.d.ts.map +1 -0
  123. package/dist/cursor/cursor-protobuf-encoder.js +173 -0
  124. package/dist/cursor/cursor-protobuf-encoder.js.map +1 -0
  125. package/dist/cursor/cursor-protobuf-schema.d.ts +215 -0
  126. package/dist/cursor/cursor-protobuf-schema.d.ts.map +1 -0
  127. package/dist/cursor/cursor-protobuf-schema.js +120 -0
  128. package/dist/cursor/cursor-protobuf-schema.js.map +1 -0
  129. package/dist/cursor/cursor-protobuf.d.ts +21 -0
  130. package/dist/cursor/cursor-protobuf.d.ts.map +1 -0
  131. package/dist/cursor/cursor-protobuf.js +116 -0
  132. package/dist/cursor/cursor-protobuf.js.map +1 -0
  133. package/dist/cursor/cursor-stream-parser.d.ts +52 -0
  134. package/dist/cursor/cursor-stream-parser.d.ts.map +1 -0
  135. package/dist/cursor/cursor-stream-parser.js +142 -0
  136. package/dist/cursor/cursor-stream-parser.js.map +1 -0
  137. package/dist/cursor/cursor-translator.d.ts +39 -0
  138. package/dist/cursor/cursor-translator.d.ts.map +1 -0
  139. package/dist/cursor/cursor-translator.js +118 -0
  140. package/dist/cursor/cursor-translator.js.map +1 -0
  141. package/dist/cursor/index.d.ts +11 -0
  142. package/dist/cursor/index.d.ts.map +1 -0
  143. package/dist/cursor/index.js +53 -0
  144. package/dist/cursor/index.js.map +1 -0
  145. package/dist/cursor/types.d.ts +33 -1
  146. package/dist/cursor/types.d.ts.map +1 -1
  147. package/dist/cursor/types.js +1 -1
  148. package/dist/types/index.d.ts +1 -1
  149. package/dist/types/index.d.ts.map +1 -1
  150. package/dist/types/index.js.map +1 -1
  151. package/dist/ui/assets/{accounts-CkN_roEn.js → accounts-D8ceTwko.js} +1 -1
  152. package/dist/ui/assets/{alert-dialog-Dy-8Z_Ud.js → alert-dialog-KnsD6NpU.js} +1 -1
  153. package/dist/ui/assets/{api-2TN7yVwm.js → api-Bp0E7mYy.js} +1 -1
  154. package/dist/ui/assets/{auth-section-CeKCNzzC.js → auth-section-D4quwqhL.js} +1 -1
  155. package/dist/ui/assets/{backups-section-CDuqhiZo.js → backups-section-D-oq--M3.js} +1 -1
  156. package/dist/ui/assets/cliproxy-BsgW2jbH.js +3 -0
  157. package/dist/ui/assets/{cliproxy-control-panel-DJd_zNR3.js → cliproxy-control-panel-B6xlfLnN.js} +1 -1
  158. package/dist/ui/assets/{confirm-dialog-DtYrWaa7.js → confirm-dialog-ConYUjNo.js} +1 -1
  159. package/dist/ui/assets/{copilot-DHdywZ9L.js → copilot-BO0JhNVL.js} +3 -3
  160. package/dist/ui/assets/{globalenv-section-Cn5ufgRh.js → globalenv-section-CGiUottp.js} +1 -1
  161. package/dist/ui/assets/{health-BsMhSeFz.js → health-DnkFdo0B.js} +1 -1
  162. package/dist/ui/assets/index-ByiEOnlQ.css +1 -0
  163. package/dist/ui/assets/{index-CEF_VhSR.js → index-C_MPOoUZ.js} +1 -1
  164. package/dist/ui/assets/index-CxWM9Sgz.js +1 -0
  165. package/dist/ui/assets/index-D4L2ZIJs.js +47 -0
  166. package/dist/ui/assets/{index-B2GQ_Joi.js → index-DcYq7jVv.js} +1 -1
  167. package/dist/ui/assets/{index-DkMR3J96.js → index-aB5MHYio.js} +1 -1
  168. package/dist/ui/assets/{proxy-status-widget-Cb7KfEqq.js → proxy-status-widget-BbZeFxlJ.js} +1 -1
  169. package/dist/ui/assets/{shared-BsWeldW8.js → shared-B_aV9M2M.js} +1 -1
  170. package/dist/ui/assets/{switch-D6XSRTo_.js → switch-60ycFRmr.js} +1 -1
  171. package/dist/ui/index.html +2 -2
  172. package/dist/utils/config-manager.d.ts.map +1 -1
  173. package/dist/utils/config-manager.js +17 -6
  174. package/dist/utils/config-manager.js.map +1 -1
  175. package/dist/web-server/jsonl-parser.js +1 -1
  176. package/dist/web-server/jsonl-parser.js.map +1 -1
  177. package/dist/web-server/routes/account-routes.d.ts.map +1 -1
  178. package/dist/web-server/routes/account-routes.js +5 -11
  179. package/dist/web-server/routes/account-routes.js.map +1 -1
  180. package/dist/web-server/routes/cliproxy-stats-routes.d.ts.map +1 -1
  181. package/dist/web-server/routes/cliproxy-stats-routes.js +43 -4
  182. package/dist/web-server/routes/cliproxy-stats-routes.js.map +1 -1
  183. package/dist/web-server/routes/cursor-routes.d.ts +6 -0
  184. package/dist/web-server/routes/cursor-routes.d.ts.map +1 -0
  185. package/dist/web-server/routes/cursor-routes.js +163 -0
  186. package/dist/web-server/routes/cursor-routes.js.map +1 -0
  187. package/dist/web-server/routes/cursor-settings-routes.d.ts +6 -0
  188. package/dist/web-server/routes/cursor-settings-routes.d.ts.map +1 -0
  189. package/dist/web-server/routes/cursor-settings-routes.js +171 -0
  190. package/dist/web-server/routes/cursor-settings-routes.js.map +1 -0
  191. package/dist/web-server/routes/index.d.ts.map +1 -1
  192. package/dist/web-server/routes/index.js +3 -0
  193. package/dist/web-server/routes/index.js.map +1 -1
  194. package/dist/web-server/routes/variant-routes.d.ts.map +1 -1
  195. package/dist/web-server/routes/variant-routes.js +107 -6
  196. package/dist/web-server/routes/variant-routes.js.map +1 -1
  197. package/package.json +5 -2
  198. package/scripts/hardening-inventory.js +526 -0
  199. package/scripts/maintainability-baseline.js +309 -0
  200. package/dist/ui/assets/cliproxy-626zUpA_.js +0 -3
  201. package/dist/ui/assets/index-Bmgylhgn.js +0 -1
  202. package/dist/ui/assets/index-Cr6iEc8x.js +0 -47
  203. package/dist/ui/assets/index-QvMpU4Lc.css +0 -1
@@ -0,0 +1,821 @@
1
+ "use strict";
2
+ /**
3
+ * Cursor Executor
4
+ * Handles HTTP/2 requests to Cursor API with protobuf encoding/decoding
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.CursorExecutor = void 0;
31
+ const crypto = __importStar(require("crypto"));
32
+ const cursor_protobuf_js_1 = require("./cursor-protobuf.js");
33
+ const cursor_translator_js_1 = require("./cursor-translator.js");
34
+ const cursor_stream_parser_js_1 = require("./cursor-stream-parser.js");
35
+ /** Lazy import http2 */
36
+ let http2Module = null;
37
+ async function getHttp2() {
38
+ if (http2Module)
39
+ return http2Module;
40
+ try {
41
+ http2Module = await Promise.resolve().then(() => __importStar(require('http2')));
42
+ return http2Module;
43
+ }
44
+ catch (err) {
45
+ if (process.env.CCS_DEBUG) {
46
+ console.error('[cursor] http2 module not available, falling back to fetch:', err);
47
+ }
48
+ return null;
49
+ }
50
+ }
51
+ /**
52
+ * Create error response from JSON error
53
+ */
54
+ function createErrorResponse(jsonError) {
55
+ const errorMsg = jsonError?.error?.details?.[0]?.debug?.details?.title ||
56
+ jsonError?.error?.details?.[0]?.debug?.details?.detail ||
57
+ jsonError?.error?.message ||
58
+ 'API Error';
59
+ const isRateLimit = jsonError?.error?.code === 'resource_exhausted';
60
+ return new Response(JSON.stringify({
61
+ error: {
62
+ message: errorMsg,
63
+ type: isRateLimit ? 'rate_limit_error' : 'api_error',
64
+ code: jsonError?.error?.details?.[0]?.debug?.error || 'unknown',
65
+ },
66
+ }), {
67
+ status: isRateLimit ? 429 : 400,
68
+ headers: { 'Content-Type': 'application/json' },
69
+ });
70
+ }
71
+ class CursorExecutor {
72
+ constructor() {
73
+ this.baseUrl = 'https://api2.cursor.sh';
74
+ this.chatPath = '/aiserver.v1.AiService/StreamChat';
75
+ this.CURSOR_CLIENT_VERSION = '2.3.41';
76
+ this.CURSOR_USER_AGENT = 'connect-es/1.6.1';
77
+ }
78
+ buildUrl() {
79
+ return `${this.baseUrl}${this.chatPath}`;
80
+ }
81
+ /**
82
+ * Generate checksum using Jyh cipher (time-based XOR with rolling key seed=165)
83
+ */
84
+ generateChecksum(machineId) {
85
+ const timestamp = Math.floor(Date.now() / 1000000);
86
+ // JS bitwise shifts wrap modulo 32, so >>40 and >>32 give wrong results.
87
+ // Use Math.trunc division for upper bytes that exceed 32-bit range.
88
+ const byteArray = new Uint8Array([
89
+ Math.trunc(timestamp / 2 ** 40) & 0xff,
90
+ Math.trunc(timestamp / 2 ** 32) & 0xff,
91
+ (timestamp >>> 24) & 0xff,
92
+ (timestamp >>> 16) & 0xff,
93
+ (timestamp >>> 8) & 0xff,
94
+ timestamp & 0xff,
95
+ ]);
96
+ let t = 165;
97
+ for (let i = 0; i < byteArray.length; i++) {
98
+ byteArray[i] = ((byteArray[i] ^ t) + (i % 256)) & 0xff;
99
+ t = byteArray[i];
100
+ }
101
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
102
+ let encoded = '';
103
+ for (let i = 0; i < byteArray.length; i += 3) {
104
+ const a = byteArray[i];
105
+ const b = i + 1 < byteArray.length ? byteArray[i + 1] : 0;
106
+ const c = i + 2 < byteArray.length ? byteArray[i + 2] : 0;
107
+ encoded += alphabet[a >> 2];
108
+ encoded += alphabet[((a & 3) << 4) | (b >> 4)];
109
+ if (i + 1 < byteArray.length) {
110
+ encoded += alphabet[((b & 15) << 2) | (c >> 6)];
111
+ }
112
+ if (i + 2 < byteArray.length) {
113
+ encoded += alphabet[c & 63];
114
+ }
115
+ }
116
+ return `${encoded}${machineId}`;
117
+ }
118
+ buildHeaders(credentials) {
119
+ const accessToken = credentials.accessToken;
120
+ const machineId = credentials.machineId;
121
+ const ghostMode = credentials.ghostMode !== false;
122
+ if (!machineId) {
123
+ throw new Error('Machine ID is required for Cursor API');
124
+ }
125
+ const delimIdx = accessToken.indexOf('::');
126
+ const cleanToken = delimIdx !== -1 ? accessToken.slice(delimIdx + 2) : accessToken;
127
+ if (!cleanToken) {
128
+ throw new Error('Access token is empty after parsing');
129
+ }
130
+ return {
131
+ authorization: `Bearer ${cleanToken}`,
132
+ 'connect-accept-encoding': 'gzip',
133
+ 'connect-protocol-version': '1',
134
+ 'content-type': 'application/connect+proto',
135
+ 'user-agent': this.CURSOR_USER_AGENT,
136
+ 'x-amzn-trace-id': `Root=${crypto.randomUUID()}`,
137
+ 'x-client-key': crypto.createHash('sha256').update(cleanToken).digest('hex'),
138
+ 'x-cursor-checksum': this.generateChecksum(machineId),
139
+ 'x-cursor-client-version': this.CURSOR_CLIENT_VERSION,
140
+ 'x-cursor-client-type': 'ide',
141
+ 'x-cursor-client-os': process.platform === 'win32'
142
+ ? 'windows'
143
+ : process.platform === 'darwin'
144
+ ? 'macos'
145
+ : 'linux',
146
+ 'x-cursor-client-arch': process.arch === 'arm64' ? 'aarch64' : 'x64',
147
+ 'x-cursor-client-device-type': 'desktop',
148
+ 'x-cursor-config-version': crypto.randomUUID(),
149
+ 'x-cursor-timezone': Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
150
+ 'x-ghost-mode': ghostMode ? 'true' : 'false',
151
+ 'x-request-id': crypto.randomUUID(),
152
+ 'x-session-id': crypto.createHash('sha256').update(cleanToken).digest('hex').substring(0, 36),
153
+ };
154
+ }
155
+ transformRequest(model, body, stream, credentials) {
156
+ const translatedBody = (0, cursor_translator_js_1.buildCursorRequest)(model, body, stream, credentials);
157
+ const messages = translatedBody.messages || [];
158
+ const tools = (translatedBody.tools || body.tools || []);
159
+ const reasoningEffort = body.reasoning_effort || null;
160
+ return (0, cursor_protobuf_js_1.generateCursorBody)(messages, model, tools, reasoningEffort);
161
+ }
162
+ async makeFetchRequest(url, headers, body, signal) {
163
+ const response = await fetch(url, {
164
+ method: 'POST',
165
+ headers,
166
+ body,
167
+ signal,
168
+ });
169
+ const responseHeaders = {};
170
+ response.headers.forEach((value, key) => {
171
+ responseHeaders[key] = value;
172
+ });
173
+ return {
174
+ status: response.status,
175
+ headers: responseHeaders,
176
+ body: Buffer.from(await response.arrayBuffer()),
177
+ };
178
+ }
179
+ async makeHttp2Request(url, headers, body, signal) {
180
+ const http2 = await getHttp2();
181
+ if (!http2) {
182
+ throw new Error('http2 module not available');
183
+ }
184
+ return new Promise((resolve, reject) => {
185
+ const urlObj = new URL(url);
186
+ const client = http2.connect(`https://${urlObj.host}`);
187
+ const chunks = [];
188
+ let responseHeaders = {};
189
+ client.on('error', (err) => {
190
+ client.close();
191
+ reject(err);
192
+ });
193
+ const req = client.request({
194
+ ':method': 'POST',
195
+ ':path': urlObj.pathname,
196
+ ':authority': urlObj.host,
197
+ ':scheme': 'https',
198
+ ...headers,
199
+ });
200
+ req.on('response', (hdrs) => {
201
+ responseHeaders = hdrs;
202
+ });
203
+ req.on('data', (chunk) => {
204
+ chunks.push(chunk);
205
+ });
206
+ req.on('end', () => {
207
+ client.close();
208
+ resolve({
209
+ status: Number(responseHeaders[':status']) || 500,
210
+ headers: responseHeaders,
211
+ body: Buffer.concat(chunks),
212
+ });
213
+ });
214
+ req.on('error', (err) => {
215
+ client.close();
216
+ reject(err);
217
+ });
218
+ if (signal) {
219
+ const onAbort = () => {
220
+ req.close();
221
+ client.close();
222
+ reject(new Error('Request aborted'));
223
+ };
224
+ signal.addEventListener('abort', onAbort, { once: true });
225
+ const cleanup = () => {
226
+ signal.removeEventListener('abort', onAbort);
227
+ };
228
+ req.on('end', cleanup);
229
+ req.on('error', cleanup);
230
+ }
231
+ req.write(body);
232
+ req.end();
233
+ });
234
+ }
235
+ async execute(params) {
236
+ const { model, body, stream, credentials, signal } = params;
237
+ const url = this.buildUrl();
238
+ const headers = this.buildHeaders(credentials);
239
+ const transformedBody = this.transformRequest(model, body, stream, credentials);
240
+ try {
241
+ // Streaming requests use incremental HTTP/2 → SSE pipeline
242
+ if (stream) {
243
+ const response = await this.executeStreaming(url, headers, transformedBody, model, body, signal);
244
+ return { response, url, headers, transformedBody: body };
245
+ }
246
+ // Non-streaming: buffer entire response then transform to JSON
247
+ const http2 = await getHttp2();
248
+ const response = http2
249
+ ? await this.makeHttp2Request(url, headers, transformedBody, signal)
250
+ : await this.makeFetchRequest(url, headers, transformedBody, signal);
251
+ if (response.status !== 200) {
252
+ const errorText = response.body?.toString() || 'Unknown error';
253
+ const errorResponse = new Response(JSON.stringify({
254
+ error: {
255
+ message: `[${response.status}]: ${errorText}`,
256
+ type: 'invalid_request_error',
257
+ code: '',
258
+ },
259
+ }), {
260
+ status: response.status,
261
+ headers: { 'Content-Type': 'application/json' },
262
+ });
263
+ return { response: errorResponse, url, headers, transformedBody: body };
264
+ }
265
+ const transformedResponse = this.transformProtobufToJSON(response.body, model, body);
266
+ return { response: transformedResponse, url, headers, transformedBody: body };
267
+ }
268
+ catch (error) {
269
+ const errorResponse = new Response(JSON.stringify({
270
+ error: {
271
+ message: error.message,
272
+ type: 'connection_error',
273
+ code: '',
274
+ },
275
+ }), {
276
+ status: 500,
277
+ headers: { 'Content-Type': 'application/json' },
278
+ });
279
+ return { response: errorResponse, url, headers, transformedBody: body };
280
+ }
281
+ }
282
+ /**
283
+ * Execute a streaming request, piping HTTP/2 data events through
284
+ * StreamingFrameParser for incremental SSE output.
285
+ * Falls back to buffered transformProtobufToSSE when http2 is unavailable.
286
+ */
287
+ async executeStreaming(url, headers, body, model, requestBody, signal) {
288
+ const http2 = await getHttp2();
289
+ if (!http2) {
290
+ const response = await this.makeFetchRequest(url, headers, body, signal);
291
+ if (response.status !== 200) {
292
+ const errorText = response.body?.toString() || 'Unknown error';
293
+ return new Response(JSON.stringify({
294
+ error: {
295
+ message: `[${response.status}]: ${errorText}`,
296
+ type: response.status === 429 ? 'rate_limit_error' : 'invalid_request_error',
297
+ code: '',
298
+ },
299
+ }), { status: response.status, headers: { 'Content-Type': 'application/json' } });
300
+ }
301
+ return this.transformProtobufToSSE(response.body, model, requestBody);
302
+ }
303
+ const responseId = `chatcmpl-cursor-${Date.now()}`;
304
+ const created = Math.floor(Date.now() / 1000);
305
+ return new Promise((resolve, reject) => {
306
+ const urlObj = new URL(url);
307
+ const client = http2.connect(`https://${urlObj.host}`);
308
+ client.on('error', (err) => {
309
+ client.close();
310
+ reject(err);
311
+ });
312
+ const req = client.request({
313
+ ':method': 'POST',
314
+ ':path': urlObj.pathname,
315
+ ':authority': urlObj.host,
316
+ ':scheme': 'https',
317
+ ...headers,
318
+ });
319
+ // Register abort before response headers arrive so cancellation works at all stages
320
+ let streamClosed = false;
321
+ let streamController = null;
322
+ if (signal) {
323
+ const onAbort = () => {
324
+ streamClosed = true;
325
+ // Close the ReadableStream controller so consumers don't hang on reader.read()
326
+ if (streamController) {
327
+ try {
328
+ streamController.close();
329
+ }
330
+ catch {
331
+ /* already closed */
332
+ }
333
+ }
334
+ req.close();
335
+ client.close();
336
+ };
337
+ signal.addEventListener('abort', onAbort, { once: true });
338
+ const cleanup = () => signal.removeEventListener('abort', onAbort);
339
+ req.on('end', cleanup);
340
+ req.on('error', cleanup);
341
+ }
342
+ req.on('response', (hdrs) => {
343
+ const status = Number(hdrs[':status']) || 500;
344
+ if (status !== 200) {
345
+ const errorChunks = [];
346
+ req.on('data', (c) => errorChunks.push(c));
347
+ req.on('end', () => {
348
+ client.close();
349
+ const errorText = Buffer.concat(errorChunks).toString();
350
+ resolve(new Response(JSON.stringify({
351
+ error: {
352
+ message: `[${status}]: ${errorText}`,
353
+ type: 'invalid_request_error',
354
+ code: '',
355
+ },
356
+ }), { status, headers: { 'Content-Type': 'application/json' } }));
357
+ });
358
+ return;
359
+ }
360
+ // Status 200: set up incremental streaming pipeline
361
+ const parser = new cursor_stream_parser_js_1.StreamingFrameParser();
362
+ const enc = new TextEncoder();
363
+ const toolCallsMap = new Map();
364
+ let chunkCount = 0;
365
+ let toolCallCount = 0;
366
+ const readable = new ReadableStream({
367
+ start(controller) {
368
+ streamController = controller;
369
+ const emitSSE = (data) => {
370
+ if (streamClosed)
371
+ return;
372
+ controller.enqueue(enc.encode(`data: ${data}\n\n`));
373
+ };
374
+ const closeStream = () => {
375
+ if (streamClosed)
376
+ return;
377
+ streamClosed = true;
378
+ try {
379
+ controller.close();
380
+ }
381
+ catch {
382
+ /* already closed */
383
+ }
384
+ client.close();
385
+ };
386
+ const buildChunk = (delta, finishReason) => JSON.stringify({
387
+ id: responseId,
388
+ object: 'chat.completion.chunk',
389
+ created,
390
+ model,
391
+ choices: [{ index: 0, delta, finish_reason: finishReason }],
392
+ });
393
+ req.on('data', (chunk) => {
394
+ if (streamClosed)
395
+ return;
396
+ for (const frame of parser.push(chunk)) {
397
+ if (frame.type === 'error') {
398
+ emitSSE(JSON.stringify({
399
+ error: { message: frame.message, type: frame.errorType, code: '' },
400
+ }));
401
+ emitSSE('[DONE]');
402
+ closeStream();
403
+ return;
404
+ }
405
+ if (frame.type === 'toolCall') {
406
+ const tc = frame.toolCall;
407
+ // Emit role chunk on first content
408
+ if (chunkCount === 0) {
409
+ emitSSE(buildChunk({ role: 'assistant', content: '' }, null));
410
+ chunkCount++;
411
+ }
412
+ if (toolCallsMap.has(tc.id)) {
413
+ const existing = toolCallsMap.get(tc.id);
414
+ if (!existing)
415
+ continue;
416
+ existing.function.arguments += tc.function.arguments;
417
+ existing.isLast = tc.isLast;
418
+ if (tc.function.arguments) {
419
+ emitSSE(buildChunk({
420
+ tool_calls: [
421
+ {
422
+ index: existing.index,
423
+ id: tc.id,
424
+ type: 'function',
425
+ function: {
426
+ name: tc.function.name,
427
+ arguments: tc.function.arguments,
428
+ },
429
+ },
430
+ ],
431
+ }, null));
432
+ chunkCount++;
433
+ }
434
+ }
435
+ else {
436
+ const idx = toolCallCount++;
437
+ toolCallsMap.set(tc.id, { ...tc, index: idx });
438
+ emitSSE(buildChunk({
439
+ tool_calls: [
440
+ {
441
+ index: idx,
442
+ id: tc.id,
443
+ type: 'function',
444
+ function: {
445
+ name: tc.function.name,
446
+ arguments: tc.function.arguments,
447
+ },
448
+ },
449
+ ],
450
+ }, null));
451
+ chunkCount++;
452
+ }
453
+ }
454
+ if (frame.type === 'text') {
455
+ const delta = chunkCount === 0 && toolCallCount === 0
456
+ ? { role: 'assistant', content: frame.text }
457
+ : { content: frame.text };
458
+ emitSSE(buildChunk(delta, null));
459
+ chunkCount++;
460
+ }
461
+ }
462
+ });
463
+ req.on('end', () => {
464
+ if (streamClosed)
465
+ return;
466
+ if (chunkCount === 0 && toolCallCount === 0) {
467
+ emitSSE(buildChunk({ role: 'assistant', content: '' }, null));
468
+ }
469
+ emitSSE(JSON.stringify({
470
+ id: responseId,
471
+ object: 'chat.completion.chunk',
472
+ created,
473
+ model,
474
+ choices: [
475
+ {
476
+ index: 0,
477
+ delta: {},
478
+ finish_reason: toolCallCount > 0 ? 'tool_calls' : 'stop',
479
+ },
480
+ ],
481
+ usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
482
+ }));
483
+ emitSSE('[DONE]');
484
+ closeStream();
485
+ });
486
+ req.on('error', (err) => {
487
+ if (!streamClosed) {
488
+ try {
489
+ controller.error(err);
490
+ }
491
+ catch {
492
+ /* already closed */
493
+ }
494
+ }
495
+ client.close();
496
+ });
497
+ },
498
+ });
499
+ resolve(new Response(readable, {
500
+ status: 200,
501
+ headers: {
502
+ 'Content-Type': 'text/event-stream',
503
+ 'Cache-Control': 'no-cache',
504
+ Connection: 'keep-alive',
505
+ },
506
+ }));
507
+ });
508
+ req.on('error', (err) => {
509
+ client.close();
510
+ reject(err);
511
+ });
512
+ req.write(body);
513
+ req.end();
514
+ });
515
+ }
516
+ /**
517
+ * Parse protobuf buffer into frames and extract text/toolcalls.
518
+ * Shared logic between JSON and SSE transformers.
519
+ */
520
+ *parseProtobufFrames(buffer) {
521
+ let offset = 0;
522
+ while (offset < buffer.length) {
523
+ if (offset + 5 > buffer.length)
524
+ break;
525
+ const flags = buffer[offset];
526
+ const length = buffer.readUInt32BE(offset + 1);
527
+ if (offset + 5 + length > buffer.length)
528
+ break;
529
+ let payload = buffer.slice(offset + 5, offset + 5 + length);
530
+ offset += 5 + length;
531
+ payload = (0, cursor_stream_parser_js_1.decompressPayload)(payload, flags);
532
+ // Check for JSON error format
533
+ try {
534
+ const text = payload.toString('utf-8');
535
+ if (text.startsWith('{') && text.includes('"error"')) {
536
+ yield { type: 'error', response: createErrorResponse(JSON.parse(text)) };
537
+ return;
538
+ }
539
+ }
540
+ catch (err) {
541
+ if (process.env.CCS_DEBUG) {
542
+ console.error('[cursor] parseProtobufFrames error parsing failed:', err);
543
+ }
544
+ }
545
+ const result = (0, cursor_protobuf_js_1.extractTextFromResponse)(new Uint8Array(payload));
546
+ // Check for protobuf-decoded error
547
+ if (result.error) {
548
+ const errorLower = result.error.toLowerCase();
549
+ const isRateLimit = errorLower.includes('rate limit') ||
550
+ errorLower.includes('resource_exhausted') ||
551
+ errorLower.includes('too many requests');
552
+ yield {
553
+ type: 'error',
554
+ response: new Response(JSON.stringify({
555
+ error: {
556
+ message: result.error,
557
+ type: isRateLimit ? 'rate_limit_error' : 'server_error',
558
+ code: isRateLimit ? 'rate_limited' : 'cursor_error',
559
+ },
560
+ }), {
561
+ status: isRateLimit ? 429 : 400,
562
+ headers: { 'Content-Type': 'application/json' },
563
+ }),
564
+ };
565
+ return;
566
+ }
567
+ if (result.toolCall) {
568
+ yield { type: 'toolCall', toolCall: result.toolCall };
569
+ }
570
+ if (result.text) {
571
+ yield { type: 'text', text: result.text };
572
+ }
573
+ }
574
+ }
575
+ transformProtobufToJSON(buffer, model, _body) {
576
+ const responseId = `chatcmpl-cursor-${Date.now()}`;
577
+ const created = Math.floor(Date.now() / 1000);
578
+ let totalContent = '';
579
+ const toolCalls = [];
580
+ const toolCallsMap = new Map();
581
+ for (const frame of this.parseProtobufFrames(buffer)) {
582
+ if (frame.type === 'error') {
583
+ return frame.response;
584
+ }
585
+ if (frame.type === 'toolCall') {
586
+ const tc = frame.toolCall;
587
+ if (toolCallsMap.has(tc.id)) {
588
+ const existing = toolCallsMap.get(tc.id);
589
+ if (!existing)
590
+ continue;
591
+ existing.function.arguments += tc.function.arguments;
592
+ existing.isLast = tc.isLast;
593
+ }
594
+ else {
595
+ toolCallsMap.set(tc.id, {
596
+ ...tc,
597
+ index: toolCallsMap.size,
598
+ });
599
+ }
600
+ if (tc.isLast) {
601
+ const finalToolCall = toolCallsMap.get(tc.id);
602
+ if (!finalToolCall)
603
+ continue;
604
+ toolCalls.push({
605
+ id: finalToolCall.id,
606
+ type: finalToolCall.type,
607
+ function: {
608
+ name: finalToolCall.function.name,
609
+ arguments: finalToolCall.function.arguments,
610
+ },
611
+ });
612
+ }
613
+ }
614
+ if (frame.type === 'text') {
615
+ totalContent += frame.text;
616
+ }
617
+ }
618
+ // Finalize remaining tool calls
619
+ for (const id of Array.from(toolCallsMap.keys())) {
620
+ const tc = toolCallsMap.get(id);
621
+ if (!tc)
622
+ continue;
623
+ if (!toolCalls.find((t) => t.id === id)) {
624
+ toolCalls.push({
625
+ id: tc.id,
626
+ type: tc.type,
627
+ function: {
628
+ name: tc.function.name,
629
+ arguments: tc.function.arguments,
630
+ },
631
+ });
632
+ }
633
+ }
634
+ const message = {
635
+ role: 'assistant',
636
+ content: totalContent || null,
637
+ };
638
+ if (toolCalls.length > 0) {
639
+ message.tool_calls = toolCalls;
640
+ }
641
+ const completion = {
642
+ id: responseId,
643
+ object: 'chat.completion',
644
+ created,
645
+ model,
646
+ choices: [
647
+ {
648
+ index: 0,
649
+ message,
650
+ finish_reason: toolCalls.length > 0 ? 'tool_calls' : 'stop',
651
+ },
652
+ ],
653
+ usage: {
654
+ prompt_tokens: 0,
655
+ completion_tokens: 0,
656
+ total_tokens: 0,
657
+ },
658
+ };
659
+ return new Response(JSON.stringify(completion), {
660
+ status: 200,
661
+ headers: { 'Content-Type': 'application/json' },
662
+ });
663
+ }
664
+ /** @deprecated Use executeStreaming() for real-time SSE. Retained for fetch fallback. */
665
+ transformProtobufToSSE(buffer, model, _body) {
666
+ const responseId = `chatcmpl-cursor-${Date.now()}`;
667
+ const created = Math.floor(Date.now() / 1000);
668
+ const chunks = [];
669
+ const toolCalls = [];
670
+ const toolCallsMap = new Map();
671
+ for (const frame of this.parseProtobufFrames(buffer)) {
672
+ if (frame.type === 'error') {
673
+ return frame.response;
674
+ }
675
+ if (frame.type === 'toolCall') {
676
+ const tc = frame.toolCall;
677
+ if (chunks.length === 0) {
678
+ chunks.push(`data: ${JSON.stringify({
679
+ id: responseId,
680
+ object: 'chat.completion.chunk',
681
+ created,
682
+ model,
683
+ choices: [
684
+ {
685
+ index: 0,
686
+ delta: { role: 'assistant', content: '' },
687
+ finish_reason: null,
688
+ },
689
+ ],
690
+ })}\n\n`);
691
+ }
692
+ if (toolCallsMap.has(tc.id)) {
693
+ const existing = toolCallsMap.get(tc.id);
694
+ if (!existing)
695
+ continue;
696
+ existing.function.arguments += tc.function.arguments;
697
+ existing.isLast = tc.isLast;
698
+ if (tc.function.arguments) {
699
+ chunks.push(`data: ${JSON.stringify({
700
+ id: responseId,
701
+ object: 'chat.completion.chunk',
702
+ created,
703
+ model,
704
+ choices: [
705
+ {
706
+ index: 0,
707
+ delta: {
708
+ tool_calls: [
709
+ {
710
+ index: existing.index,
711
+ id: tc.id,
712
+ type: 'function',
713
+ function: {
714
+ name: tc.function.name,
715
+ arguments: tc.function.arguments,
716
+ },
717
+ },
718
+ ],
719
+ },
720
+ finish_reason: null,
721
+ },
722
+ ],
723
+ })}\n\n`);
724
+ }
725
+ }
726
+ else {
727
+ const toolCallIndex = toolCalls.length;
728
+ toolCalls.push({ ...tc, index: toolCallIndex });
729
+ toolCallsMap.set(tc.id, { ...tc, index: toolCallIndex });
730
+ chunks.push(`data: ${JSON.stringify({
731
+ id: responseId,
732
+ object: 'chat.completion.chunk',
733
+ created,
734
+ model,
735
+ choices: [
736
+ {
737
+ index: 0,
738
+ delta: {
739
+ tool_calls: [
740
+ {
741
+ index: toolCallIndex,
742
+ id: tc.id,
743
+ type: 'function',
744
+ function: {
745
+ name: tc.function.name,
746
+ arguments: tc.function.arguments,
747
+ },
748
+ },
749
+ ],
750
+ },
751
+ finish_reason: null,
752
+ },
753
+ ],
754
+ })}\n\n`);
755
+ }
756
+ }
757
+ if (frame.type === 'text') {
758
+ chunks.push(`data: ${JSON.stringify({
759
+ id: responseId,
760
+ object: 'chat.completion.chunk',
761
+ created,
762
+ model,
763
+ choices: [
764
+ {
765
+ index: 0,
766
+ delta: chunks.length === 0 && toolCalls.length === 0
767
+ ? { role: 'assistant', content: frame.text }
768
+ : { content: frame.text },
769
+ finish_reason: null,
770
+ },
771
+ ],
772
+ })}\n\n`);
773
+ }
774
+ }
775
+ if (chunks.length === 0 && toolCalls.length === 0) {
776
+ chunks.push(`data: ${JSON.stringify({
777
+ id: responseId,
778
+ object: 'chat.completion.chunk',
779
+ created,
780
+ model,
781
+ choices: [
782
+ {
783
+ index: 0,
784
+ delta: { role: 'assistant', content: '' },
785
+ finish_reason: null,
786
+ },
787
+ ],
788
+ })}\n\n`);
789
+ }
790
+ chunks.push(`data: ${JSON.stringify({
791
+ id: responseId,
792
+ object: 'chat.completion.chunk',
793
+ created,
794
+ model,
795
+ choices: [
796
+ {
797
+ index: 0,
798
+ delta: {},
799
+ finish_reason: toolCalls.length > 0 ? 'tool_calls' : 'stop',
800
+ },
801
+ ],
802
+ usage: {
803
+ prompt_tokens: 0,
804
+ completion_tokens: 0,
805
+ total_tokens: 0,
806
+ },
807
+ })}\n\n`);
808
+ chunks.push('data: [DONE]\n\n');
809
+ return new Response(chunks.join(''), {
810
+ status: 200,
811
+ headers: {
812
+ 'Content-Type': 'text/event-stream',
813
+ 'Cache-Control': 'no-cache',
814
+ Connection: 'keep-alive',
815
+ },
816
+ });
817
+ }
818
+ }
819
+ exports.CursorExecutor = CursorExecutor;
820
+ exports.default = CursorExecutor;
821
+ //# sourceMappingURL=cursor-executor.js.map