opencode-ext-connector 0.3.0

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 (332) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/LICENSE +29 -0
  3. package/README.md +207 -0
  4. package/THIRD_PARTY_NOTICES.md +201 -0
  5. package/dist/catalog/parse-ids.d.ts +2 -0
  6. package/dist/catalog/parse-ids.js +49 -0
  7. package/dist/core/adapter.d.ts +16 -0
  8. package/dist/core/adapter.js +20 -0
  9. package/dist/core/clock.d.ts +7 -0
  10. package/dist/core/clock.js +1 -0
  11. package/dist/core/deadline.d.ts +13 -0
  12. package/dist/core/deadline.js +54 -0
  13. package/dist/core/errors.d.ts +57 -0
  14. package/dist/core/errors.js +81 -0
  15. package/dist/core/health.d.ts +22 -0
  16. package/dist/core/health.js +30 -0
  17. package/dist/core/http.d.ts +28 -0
  18. package/dist/core/http.js +1 -0
  19. package/dist/core/ids.d.ts +7 -0
  20. package/dist/core/ids.js +23 -0
  21. package/dist/core/lifecycle.d.ts +4 -0
  22. package/dist/core/lifecycle.js +16 -0
  23. package/dist/core/logger.d.ts +21 -0
  24. package/dist/core/logger.js +61 -0
  25. package/dist/core/models.d.ts +41 -0
  26. package/dist/core/models.js +59 -0
  27. package/dist/core/options.d.ts +31 -0
  28. package/dist/core/options.js +62 -0
  29. package/dist/core/process.d.ts +20 -0
  30. package/dist/core/process.js +1 -0
  31. package/dist/http/fetch-transport.d.ts +2 -0
  32. package/dist/http/fetch-transport.js +75 -0
  33. package/dist/http/package-version.d.ts +7 -0
  34. package/dist/http/package-version.js +46 -0
  35. package/dist/http/read-body.d.ts +9 -0
  36. package/dist/http/read-body.js +22 -0
  37. package/dist/index.d.ts +6 -0
  38. package/dist/index.js +5 -0
  39. package/dist/logging/logger.d.ts +4 -0
  40. package/dist/logging/logger.js +11 -0
  41. package/dist/opencode/auth-store.d.ts +19 -0
  42. package/dist/opencode/auth-store.js +107 -0
  43. package/dist/opencode/beta-api.d.ts +3 -0
  44. package/dist/opencode/beta-api.js +1 -0
  45. package/dist/opencode/catalog-bridge.d.ts +3 -0
  46. package/dist/opencode/catalog-bridge.js +85 -0
  47. package/dist/opencode/health-refresh.d.ts +16 -0
  48. package/dist/opencode/health-refresh.js +49 -0
  49. package/dist/opencode/host-options.d.ts +2 -0
  50. package/dist/opencode/host-options.js +49 -0
  51. package/dist/opencode/language-factory.d.ts +13 -0
  52. package/dist/opencode/language-factory.js +37 -0
  53. package/dist/opencode/ollama-probe.d.ts +2 -0
  54. package/dist/opencode/ollama-probe.js +15 -0
  55. package/dist/opencode/ollama-production.d.ts +4 -0
  56. package/dist/opencode/ollama-production.js +10 -0
  57. package/dist/opencode/plugin.d.ts +33 -0
  58. package/dist/opencode/plugin.js +40 -0
  59. package/dist/opencode/process-exit.d.ts +4 -0
  60. package/dist/opencode/process-exit.js +22 -0
  61. package/dist/opencode/provider-entry.d.ts +25 -0
  62. package/dist/opencode/provider-entry.js +1 -0
  63. package/dist/opencode/providers.d.ts +16 -0
  64. package/dist/opencode/providers.js +140 -0
  65. package/dist/opencode/register-integrations.d.ts +3 -0
  66. package/dist/opencode/register-integrations.js +8 -0
  67. package/dist/opencode/reload.d.ts +9 -0
  68. package/dist/opencode/reload.js +30 -0
  69. package/dist/opencode/v1-anthropic-auth.d.ts +9 -0
  70. package/dist/opencode/v1-anthropic-auth.js +55 -0
  71. package/dist/opencode/v1-catalog.d.ts +13 -0
  72. package/dist/opencode/v1-catalog.js +80 -0
  73. package/dist/opencode/v1-language.d.ts +3 -0
  74. package/dist/opencode/v1-language.js +64 -0
  75. package/dist/opencode/v1-module.d.ts +23 -0
  76. package/dist/opencode/v1-module.js +74 -0
  77. package/dist/opencode/v1-session-auth.d.ts +5 -0
  78. package/dist/opencode/v1-session-auth.js +98 -0
  79. package/dist/providers/claude/adapter.d.ts +7 -0
  80. package/dist/providers/claude/adapter.js +37 -0
  81. package/dist/providers/claude/atomic-private-file.d.ts +13 -0
  82. package/dist/providers/claude/atomic-private-file.js +94 -0
  83. package/dist/providers/claude/auth-json.d.ts +2 -0
  84. package/dist/providers/claude/auth-json.js +14 -0
  85. package/dist/providers/claude/auth.d.ts +24 -0
  86. package/dist/providers/claude/auth.js +200 -0
  87. package/dist/providers/claude/cli-version.d.ts +10 -0
  88. package/dist/providers/claude/cli-version.js +36 -0
  89. package/dist/providers/claude/compat-betas.d.ts +5 -0
  90. package/dist/providers/claude/compat-betas.js +54 -0
  91. package/dist/providers/claude/compat-config.d.ts +8 -0
  92. package/dist/providers/claude/compat-config.js +31 -0
  93. package/dist/providers/claude/compat-request.d.ts +12 -0
  94. package/dist/providers/claude/compat-request.js +151 -0
  95. package/dist/providers/claude/compat-response.d.ts +1 -0
  96. package/dist/providers/claude/compat-response.js +54 -0
  97. package/dist/providers/claude/compat-signing.d.ts +9 -0
  98. package/dist/providers/claude/compat-signing.js +24 -0
  99. package/dist/providers/claude/compat-transform.d.ts +3 -0
  100. package/dist/providers/claude/compat-transform.js +224 -0
  101. package/dist/providers/claude/credentials.d.ts +7 -0
  102. package/dist/providers/claude/credentials.js +37 -0
  103. package/dist/providers/claude/emit-stream.d.ts +2 -0
  104. package/dist/providers/claude/emit-stream.js +62 -0
  105. package/dist/providers/claude/keychain-account.d.ts +1 -0
  106. package/dist/providers/claude/keychain-account.js +14 -0
  107. package/dist/providers/claude/language-model.d.ts +8 -0
  108. package/dist/providers/claude/language-model.js +109 -0
  109. package/dist/providers/claude/models.d.ts +9 -0
  110. package/dist/providers/claude/models.js +24 -0
  111. package/dist/providers/claude/prompt.d.ts +42 -0
  112. package/dist/providers/claude/prompt.js +134 -0
  113. package/dist/providers/claude/refresh.d.ts +19 -0
  114. package/dist/providers/claude/refresh.js +85 -0
  115. package/dist/providers/claude/sse-convert.d.ts +6 -0
  116. package/dist/providers/claude/sse-convert.js +74 -0
  117. package/dist/providers/claude/sse.d.ts +71 -0
  118. package/dist/providers/claude/sse.js +123 -0
  119. package/dist/providers/claude/transform.d.ts +32 -0
  120. package/dist/providers/claude/transform.js +52 -0
  121. package/dist/providers/claude/writeback.d.ts +18 -0
  122. package/dist/providers/claude/writeback.js +140 -0
  123. package/dist/providers/command-code/adapter.d.ts +7 -0
  124. package/dist/providers/command-code/adapter.js +37 -0
  125. package/dist/providers/command-code/auth.d.ts +5 -0
  126. package/dist/providers/command-code/auth.js +88 -0
  127. package/dist/providers/command-code/cli-version.d.ts +10 -0
  128. package/dist/providers/command-code/cli-version.js +36 -0
  129. package/dist/providers/command-code/emit-stream.d.ts +2 -0
  130. package/dist/providers/command-code/emit-stream.js +226 -0
  131. package/dist/providers/command-code/errors.d.ts +33 -0
  132. package/dist/providers/command-code/errors.js +105 -0
  133. package/dist/providers/command-code/language-model.d.ts +15 -0
  134. package/dist/providers/command-code/language-model.js +203 -0
  135. package/dist/providers/command-code/models.d.ts +3 -0
  136. package/dist/providers/command-code/models.js +18 -0
  137. package/dist/providers/command-code/record-stream.d.ts +1 -0
  138. package/dist/providers/command-code/record-stream.js +61 -0
  139. package/dist/providers/command-code/request-lifecycle.d.ts +8 -0
  140. package/dist/providers/command-code/request-lifecycle.js +33 -0
  141. package/dist/providers/command-code/request.d.ts +14 -0
  142. package/dist/providers/command-code/request.js +167 -0
  143. package/dist/providers/command-code/session.d.ts +5 -0
  144. package/dist/providers/command-code/session.js +9 -0
  145. package/dist/providers/cursor/adapter.d.ts +7 -0
  146. package/dist/providers/cursor/adapter.js +37 -0
  147. package/dist/providers/cursor/auth.d.ts +6 -0
  148. package/dist/providers/cursor/auth.js +56 -0
  149. package/dist/providers/cursor/blob-store.d.ts +26 -0
  150. package/dist/providers/cursor/blob-store.js +119 -0
  151. package/dist/providers/cursor/bridge-client.d.ts +33 -0
  152. package/dist/providers/cursor/bridge-client.js +200 -0
  153. package/dist/providers/cursor/bridge-event-sanitize.d.ts +3 -0
  154. package/dist/providers/cursor/bridge-event-sanitize.js +49 -0
  155. package/dist/providers/cursor/bridge-limits.d.ts +3 -0
  156. package/dist/providers/cursor/bridge-limits.js +10 -0
  157. package/dist/providers/cursor/bridge-process.d.ts +45 -0
  158. package/dist/providers/cursor/bridge-process.js +211 -0
  159. package/dist/providers/cursor/bridge-protocol.d.ts +68 -0
  160. package/dist/providers/cursor/bridge-protocol.js +49 -0
  161. package/dist/providers/cursor/bridge-serialize.d.ts +3 -0
  162. package/dist/providers/cursor/bridge-serialize.js +53 -0
  163. package/dist/providers/cursor/bridge-wire.d.ts +3 -0
  164. package/dist/providers/cursor/bridge-wire.js +163 -0
  165. package/dist/providers/cursor/checkpoint-store.d.ts +28 -0
  166. package/dist/providers/cursor/checkpoint-store.js +94 -0
  167. package/dist/providers/cursor/connect-frame-stream.d.ts +19 -0
  168. package/dist/providers/cursor/connect-frame-stream.js +160 -0
  169. package/dist/providers/cursor/connect-frame.d.ts +31 -0
  170. package/dist/providers/cursor/connect-frame.js +121 -0
  171. package/dist/providers/cursor/credentials.d.ts +5 -0
  172. package/dist/providers/cursor/credentials.js +22 -0
  173. package/dist/providers/cursor/direct-run-types.d.ts +33 -0
  174. package/dist/providers/cursor/direct-run-types.js +1 -0
  175. package/dist/providers/cursor/direct-run.d.ts +5 -0
  176. package/dist/providers/cursor/direct-run.js +213 -0
  177. package/dist/providers/cursor/direct-runtime.d.ts +23 -0
  178. package/dist/providers/cursor/direct-runtime.js +111 -0
  179. package/dist/providers/cursor/direct-stream.d.ts +28 -0
  180. package/dist/providers/cursor/direct-stream.js +190 -0
  181. package/dist/providers/cursor/exec-reply.d.ts +13 -0
  182. package/dist/providers/cursor/exec-reply.js +102 -0
  183. package/dist/providers/cursor/h2-bridge-response.d.ts +9 -0
  184. package/dist/providers/cursor/h2-bridge-response.js +51 -0
  185. package/dist/providers/cursor/h2-bridge-session.d.ts +24 -0
  186. package/dist/providers/cursor/h2-bridge-session.js +197 -0
  187. package/dist/providers/cursor/h2-bridge.d.ts +2 -0
  188. package/dist/providers/cursor/h2-bridge.js +12865 -0
  189. package/dist/providers/cursor/http2.d.ts +18 -0
  190. package/dist/providers/cursor/http2.js +144 -0
  191. package/dist/providers/cursor/interaction-reply.d.ts +6 -0
  192. package/dist/providers/cursor/interaction-reply.js +52 -0
  193. package/dist/providers/cursor/language-generate.d.ts +2 -0
  194. package/dist/providers/cursor/language-generate.js +35 -0
  195. package/dist/providers/cursor/language-model.d.ts +13 -0
  196. package/dist/providers/cursor/language-model.js +77 -0
  197. package/dist/providers/cursor/legacy-stream.d.ts +11 -0
  198. package/dist/providers/cursor/legacy-stream.js +115 -0
  199. package/dist/providers/cursor/mcp-tools.d.ts +7 -0
  200. package/dist/providers/cursor/mcp-tools.js +15 -0
  201. package/dist/providers/cursor/models.d.ts +14 -0
  202. package/dist/providers/cursor/models.js +116 -0
  203. package/dist/providers/cursor/ndjson.d.ts +3 -0
  204. package/dist/providers/cursor/ndjson.js +100 -0
  205. package/dist/providers/cursor/prompt.d.ts +4 -0
  206. package/dist/providers/cursor/prompt.js +142 -0
  207. package/dist/providers/cursor/proto/checkpoint.d.ts +35 -0
  208. package/dist/providers/cursor/proto/checkpoint.js +114 -0
  209. package/dist/providers/cursor/proto/context.d.ts +64 -0
  210. package/dist/providers/cursor/proto/context.js +178 -0
  211. package/dist/providers/cursor/proto/errors.d.ts +16 -0
  212. package/dist/providers/cursor/proto/errors.js +26 -0
  213. package/dist/providers/cursor/proto/exec-control.d.ts +20 -0
  214. package/dist/providers/cursor/proto/exec-control.js +54 -0
  215. package/dist/providers/cursor/proto/exec.d.ts +47 -0
  216. package/dist/providers/cursor/proto/exec.js +154 -0
  217. package/dist/providers/cursor/proto/fields.d.ts +22 -0
  218. package/dist/providers/cursor/proto/fields.js +97 -0
  219. package/dist/providers/cursor/proto/interaction-query.d.ts +15 -0
  220. package/dist/providers/cursor/proto/interaction-query.js +79 -0
  221. package/dist/providers/cursor/proto/interaction-tool-update.d.ts +10 -0
  222. package/dist/providers/cursor/proto/interaction-tool-update.js +58 -0
  223. package/dist/providers/cursor/proto/interaction-turn-ended.d.ts +6 -0
  224. package/dist/providers/cursor/proto/interaction-turn-ended.js +18 -0
  225. package/dist/providers/cursor/proto/interaction.d.ts +65 -0
  226. package/dist/providers/cursor/proto/interaction.js +185 -0
  227. package/dist/providers/cursor/proto/kv.d.ts +25 -0
  228. package/dist/providers/cursor/proto/kv.js +109 -0
  229. package/dist/providers/cursor/proto/mcp-result.d.ts +40 -0
  230. package/dist/providers/cursor/proto/mcp-result.js +150 -0
  231. package/dist/providers/cursor/proto/mcp.d.ts +19 -0
  232. package/dist/providers/cursor/proto/mcp.js +78 -0
  233. package/dist/providers/cursor/proto/model.d.ts +11 -0
  234. package/dist/providers/cursor/proto/model.js +36 -0
  235. package/dist/providers/cursor/proto/output-location.d.ts +7 -0
  236. package/dist/providers/cursor/proto/output-location.js +53 -0
  237. package/dist/providers/cursor/proto/prewarm.d.ts +16 -0
  238. package/dist/providers/cursor/proto/prewarm.js +68 -0
  239. package/dist/providers/cursor/proto/request-context.d.ts +16 -0
  240. package/dist/providers/cursor/proto/request-context.js +58 -0
  241. package/dist/providers/cursor/proto/request.d.ts +44 -0
  242. package/dist/providers/cursor/proto/request.js +111 -0
  243. package/dist/providers/cursor/proto/run-request.d.ts +19 -0
  244. package/dist/providers/cursor/proto/run-request.js +68 -0
  245. package/dist/providers/cursor/proto/server.d.ts +35 -0
  246. package/dist/providers/cursor/proto/server.js +93 -0
  247. package/dist/providers/cursor/proto/unknown-field.d.ts +1 -0
  248. package/dist/providers/cursor/proto/unknown-field.js +22 -0
  249. package/dist/providers/cursor/proto-value.d.ts +3 -0
  250. package/dist/providers/cursor/proto-value.js +170 -0
  251. package/dist/providers/cursor/proto-wire.d.ts +22 -0
  252. package/dist/providers/cursor/proto-wire.js +224 -0
  253. package/dist/providers/cursor/protocol-failure.d.ts +18 -0
  254. package/dist/providers/cursor/protocol-failure.js +45 -0
  255. package/dist/providers/cursor/recovery.d.ts +48 -0
  256. package/dist/providers/cursor/recovery.js +102 -0
  257. package/dist/providers/cursor/request-blob-ownership.d.ts +15 -0
  258. package/dist/providers/cursor/request-blob-ownership.js +39 -0
  259. package/dist/providers/cursor/request-build.d.ts +18 -0
  260. package/dist/providers/cursor/request-build.js +123 -0
  261. package/dist/providers/cursor/request-history.d.ts +32 -0
  262. package/dist/providers/cursor/request-history.js +163 -0
  263. package/dist/providers/cursor/request-input.d.ts +75 -0
  264. package/dist/providers/cursor/request-input.js +123 -0
  265. package/dist/providers/cursor/run-events.d.ts +15 -0
  266. package/dist/providers/cursor/run-events.js +43 -0
  267. package/dist/providers/cursor/run-session-expiry.d.ts +15 -0
  268. package/dist/providers/cursor/run-session-expiry.js +21 -0
  269. package/dist/providers/cursor/run-session.d.ts +50 -0
  270. package/dist/providers/cursor/run-session.js +205 -0
  271. package/dist/providers/cursor/run.d.ts +9 -0
  272. package/dist/providers/cursor/run.js +151 -0
  273. package/dist/providers/cursor/runner.d.ts +1 -0
  274. package/dist/providers/cursor/runner.js +55 -0
  275. package/dist/providers/cursor/server-dispatch-types.d.ts +83 -0
  276. package/dist/providers/cursor/server-dispatch-types.js +1 -0
  277. package/dist/providers/cursor/server-dispatch.d.ts +3 -0
  278. package/dist/providers/cursor/server-dispatch.js +178 -0
  279. package/dist/providers/cursor/session-state.d.ts +21 -0
  280. package/dist/providers/cursor/session-state.js +118 -0
  281. package/dist/providers/cursor/session.d.ts +1 -0
  282. package/dist/providers/cursor/session.js +26 -0
  283. package/dist/providers/cursor/settle-cleanup.d.ts +1 -0
  284. package/dist/providers/cursor/settle-cleanup.js +11 -0
  285. package/dist/providers/cursor/stream-adapter.d.ts +17 -0
  286. package/dist/providers/cursor/stream-adapter.js +99 -0
  287. package/dist/providers/cursor/tool-continuation.d.ts +14 -0
  288. package/dist/providers/cursor/tool-continuation.js +134 -0
  289. package/dist/providers/cursor/tool-stream.d.ts +2 -0
  290. package/dist/providers/cursor/tool-stream.js +220 -0
  291. package/dist/providers/cursor/usage.d.ts +3 -0
  292. package/dist/providers/cursor/usage.js +35 -0
  293. package/dist/providers/ollama/adapter.d.ts +8 -0
  294. package/dist/providers/ollama/adapter.js +65 -0
  295. package/dist/providers/ollama/catalog-state.d.ts +17 -0
  296. package/dist/providers/ollama/catalog-state.js +40 -0
  297. package/dist/providers/ollama/cloud-catalog.d.ts +3 -0
  298. package/dist/providers/ollama/cloud-catalog.js +82 -0
  299. package/dist/providers/ollama/errors.d.ts +14 -0
  300. package/dist/providers/ollama/errors.js +19 -0
  301. package/dist/providers/ollama/generate.d.ts +2 -0
  302. package/dist/providers/ollama/generate.js +49 -0
  303. package/dist/providers/ollama/html-links.d.ts +1 -0
  304. package/dist/providers/ollama/html-links.js +30 -0
  305. package/dist/providers/ollama/http.d.ts +12 -0
  306. package/dist/providers/ollama/http.js +67 -0
  307. package/dist/providers/ollama/index.d.ts +8 -0
  308. package/dist/providers/ollama/index.js +8 -0
  309. package/dist/providers/ollama/language-model.d.ts +11 -0
  310. package/dist/providers/ollama/language-model.js +62 -0
  311. package/dist/providers/ollama/local-catalog.d.ts +3 -0
  312. package/dist/providers/ollama/local-catalog.js +38 -0
  313. package/dist/providers/ollama/ndjson.d.ts +3 -0
  314. package/dist/providers/ollama/ndjson.js +51 -0
  315. package/dist/providers/ollama/prompt.d.ts +8 -0
  316. package/dist/providers/ollama/prompt.js +141 -0
  317. package/dist/providers/ollama/protocol.d.ts +56 -0
  318. package/dist/providers/ollama/protocol.js +26 -0
  319. package/dist/providers/ollama/runtime.d.ts +11 -0
  320. package/dist/providers/ollama/runtime.js +128 -0
  321. package/dist/providers/ollama/stream.d.ts +8 -0
  322. package/dist/providers/ollama/stream.js +110 -0
  323. package/dist/sdk/command-code.d.ts +5 -0
  324. package/dist/sdk/command-code.js +6 -0
  325. package/dist/sdk/cursor.d.ts +5 -0
  326. package/dist/sdk/cursor.js +6 -0
  327. package/dist/sdk/ollama.d.ts +5 -0
  328. package/dist/sdk/ollama.js +6 -0
  329. package/dist/server.d.ts +12 -0
  330. package/dist/server.js +94 -0
  331. package/docs/README.ko.md +189 -0
  332. package/package.json +75 -0
@@ -0,0 +1,53 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { sanitizeBridgeEvent } from "./bridge-event-sanitize";
3
+ import { MAX_BRIDGE_LINE_LENGTH } from "./bridge-limits";
4
+ import { CursorBridgeProtocolError, } from "./bridge-protocol";
5
+ function assertNever(value) {
6
+ void value;
7
+ throw new CursorBridgeProtocolError("invalid-message");
8
+ }
9
+ function encodeBase64(payload) {
10
+ return Buffer.from(payload).toString("base64");
11
+ }
12
+ function serializeLine(value) {
13
+ const line = JSON.stringify(value);
14
+ if (line.length > MAX_BRIDGE_LINE_LENGTH) {
15
+ throw new CursorBridgeProtocolError("line-too-long");
16
+ }
17
+ return `${line}\n`;
18
+ }
19
+ export function serializeBridgeCommand(command) {
20
+ switch (command.kind) {
21
+ case "open":
22
+ case "abort":
23
+ case "close":
24
+ return serializeLine(command);
25
+ case "write-frame":
26
+ return serializeLine({
27
+ kind: command.kind,
28
+ id: command.id,
29
+ payload: encodeBase64(command.payload),
30
+ });
31
+ default:
32
+ return assertNever(command);
33
+ }
34
+ }
35
+ export function serializeBridgeEvent(event, context) {
36
+ const sanitized = sanitizeBridgeEvent(event, context.accessToken);
37
+ switch (sanitized.kind) {
38
+ case "opened":
39
+ case "headers":
40
+ case "trailers":
41
+ case "end":
42
+ case "error":
43
+ return serializeLine(sanitized);
44
+ case "data":
45
+ return serializeLine({
46
+ kind: sanitized.kind,
47
+ id: sanitized.id,
48
+ payload: encodeBase64(sanitized.payload),
49
+ });
50
+ default:
51
+ return assertNever(sanitized);
52
+ }
53
+ }
@@ -0,0 +1,3 @@
1
+ import { type BridgeCommand, type BridgeEvent } from "./bridge-protocol";
2
+ export declare function parseBridgeCommandLine(line: string): BridgeCommand;
3
+ export declare function parseBridgeEventLine(line: string): BridgeEvent;
@@ -0,0 +1,163 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { z } from "zod";
3
+ import { isSensitiveBridgeHeader } from "./bridge-event-sanitize";
4
+ import { MAX_BRIDGE_BASE64_CHARACTERS, MAX_BRIDGE_LINE_LENGTH } from "./bridge-limits";
5
+ import { CursorBridgeProtocolError } from "./bridge-protocol";
6
+ const BridgeIdSchema = z
7
+ .string()
8
+ .min(1)
9
+ .max(128)
10
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
11
+ const HeaderNameSchema = z
12
+ .string()
13
+ .min(1)
14
+ .max(128)
15
+ .regex(/^[!#$%&'*+.^_`|~0-9a-z-]+$/);
16
+ const HeaderValueSchema = z
17
+ .string()
18
+ .max(8192)
19
+ .refine((value) => !/[\r\n]/.test(value));
20
+ const HeadersSchema = z.record(HeaderNameSchema, HeaderValueSchema);
21
+ const OpenHeadersSchema = z.record(HeaderNameSchema.refine((value) => !isSensitiveBridgeHeader(value)), HeaderValueSchema);
22
+ const PathSchema = z
23
+ .string()
24
+ .min(1)
25
+ .max(2048)
26
+ .regex(/^\/[^\r\n]*$/);
27
+ const AccessTokenSchema = z
28
+ .string()
29
+ .min(1)
30
+ .max(8192)
31
+ .refine((value) => !/[\r\n]/.test(value));
32
+ function isCanonicalBase64(value) {
33
+ if (value.length === 0) {
34
+ return true;
35
+ }
36
+ if (value.length % 4 !== 0 ||
37
+ !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
38
+ return false;
39
+ }
40
+ return Buffer.from(value, "base64").toString("base64") === value;
41
+ }
42
+ const Base64Schema = z.string().max(MAX_BRIDGE_BASE64_CHARACTERS).refine(isCanonicalBase64, {
43
+ message: "invalid-base64",
44
+ });
45
+ const OpenCommandSchema = z
46
+ .object({
47
+ kind: z.literal("open"),
48
+ id: BridgeIdSchema,
49
+ accessToken: AccessTokenSchema,
50
+ path: PathSchema,
51
+ headers: OpenHeadersSchema,
52
+ })
53
+ .strict();
54
+ const WriteFrameCommandSchema = z
55
+ .object({ kind: z.literal("write-frame"), id: BridgeIdSchema, payload: Base64Schema })
56
+ .strict();
57
+ const AbortCommandSchema = z.object({ kind: z.literal("abort"), id: BridgeIdSchema }).strict();
58
+ const CloseCommandSchema = z.object({ kind: z.literal("close"), id: BridgeIdSchema }).strict();
59
+ const CommandSchema = z.discriminatedUnion("kind", [
60
+ OpenCommandSchema,
61
+ WriteFrameCommandSchema,
62
+ AbortCommandSchema,
63
+ CloseCommandSchema,
64
+ ]);
65
+ const OpenedEventSchema = z.object({ kind: z.literal("opened"), id: BridgeIdSchema }).strict();
66
+ const HeadersEventSchema = z
67
+ .object({
68
+ kind: z.literal("headers"),
69
+ id: BridgeIdSchema,
70
+ status: z.number().int().min(100).max(599),
71
+ headers: HeadersSchema,
72
+ })
73
+ .strict();
74
+ const DataEventSchema = z
75
+ .object({ kind: z.literal("data"), id: BridgeIdSchema, payload: Base64Schema })
76
+ .strict();
77
+ const TrailersEventSchema = z
78
+ .object({ kind: z.literal("trailers"), id: BridgeIdSchema, headers: HeadersSchema })
79
+ .strict();
80
+ const EndEventSchema = z.object({ kind: z.literal("end"), id: BridgeIdSchema }).strict();
81
+ const ErrorEventSchema = z
82
+ .object({
83
+ kind: z.literal("error"),
84
+ id: BridgeIdSchema,
85
+ code: z.string().min(1).max(128),
86
+ message: z.string().min(1).max(4096),
87
+ })
88
+ .strict();
89
+ const EventSchema = z.discriminatedUnion("kind", [
90
+ OpenedEventSchema,
91
+ HeadersEventSchema,
92
+ DataEventSchema,
93
+ TrailersEventSchema,
94
+ EndEventSchema,
95
+ ErrorEventSchema,
96
+ ]);
97
+ function assertNever(value) {
98
+ void value;
99
+ throw new CursorBridgeProtocolError("invalid-message");
100
+ }
101
+ function parseJson(line) {
102
+ if (line.length > MAX_BRIDGE_LINE_LENGTH) {
103
+ throw new CursorBridgeProtocolError("line-too-long");
104
+ }
105
+ try {
106
+ return JSON.parse(line);
107
+ }
108
+ catch {
109
+ throw new CursorBridgeProtocolError("malformed-json");
110
+ }
111
+ }
112
+ function parseWireCommand(line) {
113
+ const result = CommandSchema.safeParse(parseJson(line));
114
+ if (!result.success) {
115
+ const code = result.error.issues.some((issue) => issue.message === "invalid-base64")
116
+ ? "invalid-base64"
117
+ : "invalid-message";
118
+ throw new CursorBridgeProtocolError(code);
119
+ }
120
+ return result.data;
121
+ }
122
+ function parseWireEvent(line) {
123
+ const result = EventSchema.safeParse(parseJson(line));
124
+ if (!result.success) {
125
+ const code = result.error.issues.some((issue) => issue.message === "invalid-base64")
126
+ ? "invalid-base64"
127
+ : "invalid-message";
128
+ throw new CursorBridgeProtocolError(code);
129
+ }
130
+ return result.data;
131
+ }
132
+ function decodeBase64(payload) {
133
+ return new Uint8Array(Buffer.from(payload, "base64"));
134
+ }
135
+ export function parseBridgeCommandLine(line) {
136
+ const command = parseWireCommand(line);
137
+ switch (command.kind) {
138
+ case "open":
139
+ return command;
140
+ case "write-frame":
141
+ return { kind: command.kind, id: command.id, payload: decodeBase64(command.payload) };
142
+ case "abort":
143
+ case "close":
144
+ return command;
145
+ default:
146
+ return assertNever(command);
147
+ }
148
+ }
149
+ export function parseBridgeEventLine(line) {
150
+ const event = parseWireEvent(line);
151
+ switch (event.kind) {
152
+ case "opened":
153
+ case "headers":
154
+ case "trailers":
155
+ case "end":
156
+ case "error":
157
+ return event;
158
+ case "data":
159
+ return { kind: event.kind, id: event.id, payload: decodeBase64(event.payload) };
160
+ default:
161
+ return assertNever(event);
162
+ }
163
+ }
@@ -0,0 +1,28 @@
1
+ import type { Clock } from "../../core/clock";
2
+ import { type CursorBlobId, type CursorBlobStore } from "./blob-store";
3
+ import type { CursorSessionId } from "./session-state";
4
+ export type CursorCheckpoint = {
5
+ readonly blobIds: readonly CursorBlobId[];
6
+ readonly bytes: Uint8Array;
7
+ };
8
+ export type CursorCheckpointUpdate = {
9
+ readonly blobIds?: readonly CursorBlobId[];
10
+ readonly bytes: Uint8Array;
11
+ readonly sessionId: CursorSessionId;
12
+ };
13
+ export type CursorCheckpointStoreOptions = {
14
+ readonly blobStore: CursorBlobStore;
15
+ readonly clock: Clock;
16
+ readonly maxBlobReferences?: number;
17
+ readonly maxBytes: number;
18
+ readonly maxEntries: number;
19
+ /** Logical milliseconds; zero expires checkpoints on the next store operation. */
20
+ readonly ttlMs: number;
21
+ };
22
+ export type CursorCheckpointStore = {
23
+ readonly invalidate: (sessionId: CursorSessionId) => void;
24
+ readonly resume: (sessionId: CursorSessionId) => CursorCheckpoint | null;
25
+ readonly size: () => number;
26
+ readonly update: (checkpoint: CursorCheckpointUpdate) => boolean;
27
+ };
28
+ export declare function createCursorCheckpointStore(options: CursorCheckpointStoreOptions): CursorCheckpointStore;
@@ -0,0 +1,94 @@
1
+ import { requireCursorStoreTtl, requirePositiveCursorStoreBound, } from "./blob-store";
2
+ export function createCursorCheckpointStore(options) {
3
+ const maxBlobReferences = options.maxBlobReferences ?? 64;
4
+ requirePositiveCursorStoreBound(maxBlobReferences, "maxBlobReferences");
5
+ requirePositiveCursorStoreBound(options.maxBytes, "maxBytes");
6
+ requirePositiveCursorStoreBound(options.maxEntries, "maxEntries");
7
+ requireCursorStoreTtl(options.ttlMs);
8
+ const entries = new Map();
9
+ let totalBytes = 0;
10
+ const release = (entry) => {
11
+ for (const blobId of entry.blobIds) {
12
+ options.blobStore.release(blobId);
13
+ }
14
+ };
15
+ const remove = (sessionId) => {
16
+ const entry = entries.get(sessionId);
17
+ if (entry !== undefined) {
18
+ entries.delete(sessionId);
19
+ totalBytes -= entry.bytes.byteLength;
20
+ release(entry);
21
+ }
22
+ };
23
+ const evictExpired = (nowMs) => {
24
+ for (const [sessionId, entry] of entries) {
25
+ if (nowMs - entry.lastAccessMs >= options.ttlMs) {
26
+ remove(sessionId);
27
+ }
28
+ }
29
+ };
30
+ const evictToBounds = () => {
31
+ while (entries.size > options.maxEntries || totalBytes > options.maxBytes) {
32
+ const oldest = entries.keys().next().value;
33
+ if (oldest === undefined) {
34
+ return;
35
+ }
36
+ remove(oldest);
37
+ }
38
+ };
39
+ const insert = (sessionId, entry) => {
40
+ remove(sessionId);
41
+ entries.set(sessionId, entry);
42
+ totalBytes += entry.bytes.byteLength;
43
+ };
44
+ const invalidate = remove;
45
+ return {
46
+ invalidate,
47
+ resume: (sessionId) => {
48
+ const nowMs = options.clock.nowMs();
49
+ evictExpired(nowMs);
50
+ const entry = entries.get(sessionId);
51
+ if (entry === undefined) {
52
+ return null;
53
+ }
54
+ for (const blobId of entry.blobIds) {
55
+ if (!options.blobStore.has(blobId)) {
56
+ invalidate(sessionId);
57
+ return null;
58
+ }
59
+ }
60
+ entries.delete(sessionId);
61
+ entries.set(sessionId, { ...entry, lastAccessMs: nowMs });
62
+ return { blobIds: [...entry.blobIds], bytes: new Uint8Array(entry.bytes) };
63
+ },
64
+ size: () => {
65
+ evictExpired(options.clock.nowMs());
66
+ return entries.size;
67
+ },
68
+ update: (checkpoint) => {
69
+ const nowMs = options.clock.nowMs();
70
+ evictExpired(nowMs);
71
+ const blobIds = checkpoint.blobIds ?? [];
72
+ if (checkpoint.bytes.byteLength > options.maxBytes || blobIds.length > maxBlobReferences) {
73
+ return false;
74
+ }
75
+ const pinned = [];
76
+ for (const blobId of blobIds) {
77
+ if (!options.blobStore.pin(blobId)) {
78
+ for (const pinnedId of pinned) {
79
+ options.blobStore.release(pinnedId);
80
+ }
81
+ return false;
82
+ }
83
+ pinned.push(blobId);
84
+ }
85
+ insert(checkpoint.sessionId, {
86
+ blobIds: [...blobIds],
87
+ bytes: new Uint8Array(checkpoint.bytes),
88
+ lastAccessMs: nowMs,
89
+ });
90
+ evictToBounds();
91
+ return entries.has(checkpoint.sessionId);
92
+ },
93
+ };
94
+ }
@@ -0,0 +1,19 @@
1
+ export type ConnectStreamFrame = {
2
+ readonly kind: "message";
3
+ readonly bytes: Uint8Array;
4
+ } | {
5
+ readonly kind: "end";
6
+ readonly error: {
7
+ readonly code: string;
8
+ readonly message: string;
9
+ } | null;
10
+ };
11
+ export type ConnectFrameStreamDecoder = {
12
+ readonly push: (chunk: Uint8Array) => readonly ConnectStreamFrame[];
13
+ readonly finish: () => void;
14
+ };
15
+ export type ConnectFrameStreamDecoderOptions = {
16
+ readonly maxFrameBytes?: number;
17
+ readonly maxMessageBytes?: number;
18
+ };
19
+ export declare function createConnectFrameStreamDecoder(options?: ConnectFrameStreamDecoderOptions): ConnectFrameStreamDecoder;
@@ -0,0 +1,160 @@
1
+ import { z } from "zod";
2
+ import { CONNECT_COMPRESSED, CONNECT_END_STREAM, MAX_CONNECT_FRAME_BYTES, MAX_CONNECT_MESSAGE_BYTES, } from "./connect-frame";
3
+ import { CursorProtocolError } from "./proto/errors";
4
+ const ConnectCodeSchema = z.enum([
5
+ "canceled",
6
+ "unknown",
7
+ "invalid_argument",
8
+ "deadline_exceeded",
9
+ "not_found",
10
+ "already_exists",
11
+ "permission_denied",
12
+ "resource_exhausted",
13
+ "failed_precondition",
14
+ "aborted",
15
+ "out_of_range",
16
+ "unimplemented",
17
+ "internal",
18
+ "unavailable",
19
+ "data_loss",
20
+ "unauthenticated",
21
+ ]);
22
+ const ConnectEndStreamSchema = z
23
+ .object({
24
+ error: z
25
+ .object({
26
+ code: ConnectCodeSchema,
27
+ message: z.string().min(1),
28
+ details: z.array(z.unknown()).optional(),
29
+ })
30
+ .strict()
31
+ .optional(),
32
+ metadata: z.record(z.string(), z.array(z.string())).optional(),
33
+ })
34
+ .strict();
35
+ function validateBound(value) {
36
+ if (!Number.isSafeInteger(value) || value < 0) {
37
+ throw new CursorProtocolError("malformed", "Connect stream", "invalid size bound");
38
+ }
39
+ }
40
+ function parseEndStream(bytes) {
41
+ let decoded;
42
+ try {
43
+ decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
44
+ }
45
+ catch (error) {
46
+ if (!(error instanceof Error))
47
+ throw error;
48
+ throw new CursorProtocolError("malformed", "Connect end-stream", "invalid UTF-8");
49
+ }
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(decoded);
53
+ }
54
+ catch (error) {
55
+ if (!(error instanceof Error))
56
+ throw error;
57
+ throw new CursorProtocolError("malformed", "Connect end-stream", "invalid JSON");
58
+ }
59
+ const result = ConnectEndStreamSchema.safeParse(parsed);
60
+ if (!result.success) {
61
+ throw new CursorProtocolError("malformed", "Connect end-stream", "invalid envelope");
62
+ }
63
+ const error = result.data.error;
64
+ return {
65
+ kind: "end",
66
+ error: error === undefined ? null : { code: error.code, message: error.message },
67
+ };
68
+ }
69
+ export function createConnectFrameStreamDecoder(options = {}) {
70
+ const maxMessageBytes = options.maxMessageBytes ?? MAX_CONNECT_MESSAGE_BYTES;
71
+ const maxFrameBytes = options.maxFrameBytes ?? MAX_CONNECT_FRAME_BYTES;
72
+ validateBound(maxMessageBytes);
73
+ validateBound(maxFrameBytes);
74
+ const header = new Uint8Array(5);
75
+ let headerBytes = 0;
76
+ let payload = new Uint8Array();
77
+ let payloadBytes = 0;
78
+ let endStream = false;
79
+ let ended = false;
80
+ const reset = () => {
81
+ headerBytes = 0;
82
+ payload = new Uint8Array();
83
+ payloadBytes = 0;
84
+ endStream = false;
85
+ };
86
+ const complete = () => {
87
+ const frame = endStream
88
+ ? parseEndStream(payload)
89
+ : { kind: "message", bytes: payload };
90
+ if (endStream)
91
+ ended = true;
92
+ reset();
93
+ return frame;
94
+ };
95
+ const parseHeader = () => {
96
+ const flags = header.at(0);
97
+ if (flags === undefined) {
98
+ throw new CursorProtocolError("truncated", "Connect stream", "frame flags are absent");
99
+ }
100
+ if ((flags & ~(CONNECT_COMPRESSED | CONNECT_END_STREAM)) !== 0) {
101
+ throw new CursorProtocolError("unsupported-flags", "Connect stream", `unsupported flags 0x${flags.toString(16)}`);
102
+ }
103
+ if ((flags & CONNECT_COMPRESSED) !== 0) {
104
+ throw new CursorProtocolError("unsupported-flags", "Connect stream", "compressed payload requires a negotiated decompressor");
105
+ }
106
+ const length = new DataView(header.buffer, header.byteOffset + 1, 4).getUint32(0);
107
+ if (length > maxMessageBytes) {
108
+ throw new CursorProtocolError("oversized", "Connect stream", `message exceeds ${maxMessageBytes} bytes`);
109
+ }
110
+ if (length + 5 > maxFrameBytes) {
111
+ throw new CursorProtocolError("oversized", "Connect stream", `frame exceeds ${maxFrameBytes} bytes`);
112
+ }
113
+ endStream = (flags & CONNECT_END_STREAM) !== 0;
114
+ payload = new Uint8Array(length);
115
+ return length === 0 ? complete() : null;
116
+ };
117
+ return {
118
+ push(chunk) {
119
+ if (ended && chunk.length > 0) {
120
+ throw new CursorProtocolError("malformed", "Connect stream", "data follows end-stream");
121
+ }
122
+ const frames = [];
123
+ let offset = 0;
124
+ while (offset < chunk.length) {
125
+ if (headerBytes < header.length) {
126
+ const count = Math.min(header.length - headerBytes, chunk.length - offset);
127
+ header.set(chunk.subarray(offset, offset + count), headerBytes);
128
+ headerBytes += count;
129
+ offset += count;
130
+ if (headerBytes < header.length)
131
+ continue;
132
+ const empty = parseHeader();
133
+ if (empty !== null)
134
+ frames.push(empty);
135
+ if (ended && offset < chunk.length) {
136
+ throw new CursorProtocolError("malformed", "Connect stream", "data follows end-stream");
137
+ }
138
+ if (headerBytes === 0)
139
+ continue;
140
+ }
141
+ const count = Math.min(payload.length - payloadBytes, chunk.length - offset);
142
+ payload.set(chunk.subarray(offset, offset + count), payloadBytes);
143
+ payloadBytes += count;
144
+ offset += count;
145
+ if (payloadBytes === payload.length) {
146
+ frames.push(complete());
147
+ if (ended && offset < chunk.length) {
148
+ throw new CursorProtocolError("malformed", "Connect stream", "data follows end-stream");
149
+ }
150
+ }
151
+ }
152
+ return frames;
153
+ },
154
+ finish() {
155
+ if (headerBytes !== 0 || payloadBytes !== 0) {
156
+ throw new CursorProtocolError("truncated", "Connect stream", "trailing partial frame");
157
+ }
158
+ },
159
+ };
160
+ }
@@ -0,0 +1,31 @@
1
+ export declare const CONNECT_COMPRESSED: number;
2
+ export declare const CONNECT_END_STREAM: number;
3
+ export declare const MAX_CONNECT_MESSAGE_BYTES: number;
4
+ export declare const MAX_CONNECT_FRAME_BYTES: number;
5
+ export type ConnectFrame = {
6
+ readonly compressed: boolean;
7
+ readonly endStream: boolean;
8
+ readonly bytes: Uint8Array;
9
+ };
10
+ export type ConnectFrameOptions = {
11
+ readonly compressed?: boolean;
12
+ readonly endStream?: boolean;
13
+ readonly maxMessageBytes?: number;
14
+ };
15
+ export type ConnectDecodeOptions = {
16
+ readonly maxFrameBytes?: number;
17
+ readonly maxMessageBytes?: number;
18
+ };
19
+ export type ConnectDecompressor = (bytes: Uint8Array, maxOutputBytes: number) => Uint8Array;
20
+ export type ConnectPayloadDecodeOptions = {
21
+ readonly maxOutputBytes: number;
22
+ readonly decompressor?: ConnectDecompressor;
23
+ };
24
+ export declare function decodeConnectFramePayload(frame: ConnectFrame, options: ConnectPayloadDecodeOptions): Uint8Array;
25
+ export declare function encodeConnectFrame(message: Uint8Array, options?: ConnectFrameOptions): Uint8Array;
26
+ export declare function decodeConnectFrames(buffer: Uint8Array): readonly ConnectFrame[];
27
+ export declare function decodeConnectFramesWithRest(buffer: Uint8Array): {
28
+ readonly frames: readonly ConnectFrame[];
29
+ readonly rest: Uint8Array;
30
+ };
31
+ export declare function decodeConnectFramesStrict(buffer: Uint8Array, options?: ConnectDecodeOptions): readonly ConnectFrame[];
@@ -0,0 +1,121 @@
1
+ // Derived from Rahularya01/pi-cursor Connect envelope handling. Licensed under MIT.
2
+ // See THIRD_PARTY_NOTICES.md.
3
+ import { CursorProtocolError } from "./proto/errors";
4
+ export const CONNECT_COMPRESSED = 0x01;
5
+ export const CONNECT_END_STREAM = 0x02;
6
+ export const MAX_CONNECT_MESSAGE_BYTES = 64 * 1024 * 1024;
7
+ export const MAX_CONNECT_FRAME_BYTES = MAX_CONNECT_MESSAGE_BYTES + 5;
8
+ export function decodeConnectFramePayload(frame, options) {
9
+ validateBound(options.maxOutputBytes, "Connect payload");
10
+ if (frame.endStream) {
11
+ throw new CursorProtocolError("malformed", "Connect frame", "end-stream has no protobuf payload");
12
+ }
13
+ if (!frame.compressed) {
14
+ if (frame.bytes.length > options.maxOutputBytes) {
15
+ throw new CursorProtocolError("oversized", "Connect payload", `message exceeds ${options.maxOutputBytes} bytes`);
16
+ }
17
+ return frame.bytes;
18
+ }
19
+ if (options.decompressor === undefined) {
20
+ throw new CursorProtocolError("unsupported-flags", "Connect frame", "compressed payload requires a negotiated decompressor");
21
+ }
22
+ const payload = options.decompressor(frame.bytes, options.maxOutputBytes);
23
+ if (payload.length > options.maxOutputBytes) {
24
+ throw new CursorProtocolError("oversized", "Connect payload", `decompressed message exceeds ${options.maxOutputBytes} bytes`);
25
+ }
26
+ return payload;
27
+ }
28
+ function validateBound(value, context) {
29
+ if (!Number.isSafeInteger(value) || value < 0) {
30
+ throw new CursorProtocolError("malformed", context, "invalid size bound");
31
+ }
32
+ }
33
+ export function encodeConnectFrame(message, options = {}) {
34
+ const maxMessageBytes = options.maxMessageBytes ?? MAX_CONNECT_MESSAGE_BYTES;
35
+ validateBound(maxMessageBytes, "Connect frame");
36
+ if (message.length > maxMessageBytes) {
37
+ throw new CursorProtocolError("oversized", "Connect frame", `message exceeds ${maxMessageBytes} bytes`);
38
+ }
39
+ const frame = new Uint8Array(5 + message.length);
40
+ frame[0] =
41
+ (options.compressed === true ? CONNECT_COMPRESSED : 0) |
42
+ (options.endStream === true ? CONNECT_END_STREAM : 0);
43
+ const view = new DataView(frame.buffer, frame.byteOffset, 5);
44
+ view.setUint32(1, message.length);
45
+ frame.set(message, 5);
46
+ return frame;
47
+ }
48
+ export function decodeConnectFrames(buffer) {
49
+ const frames = [];
50
+ let offset = 0;
51
+ while (offset + 5 <= buffer.length) {
52
+ const flags = buffer[offset] ?? 0;
53
+ const view = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 4);
54
+ const length = view.getUint32(0);
55
+ const start = offset + 5;
56
+ const end = start + length;
57
+ if (end > buffer.length) {
58
+ break;
59
+ }
60
+ frames.push({
61
+ compressed: (flags & CONNECT_COMPRESSED) !== 0,
62
+ endStream: (flags & CONNECT_END_STREAM) !== 0,
63
+ bytes: buffer.subarray(start, end),
64
+ });
65
+ offset = end;
66
+ }
67
+ return frames;
68
+ }
69
+ export function decodeConnectFramesWithRest(buffer) {
70
+ const frames = decodeConnectFrames(buffer);
71
+ let offset = 0;
72
+ for (const frame of frames) {
73
+ offset += 5 + frame.bytes.length;
74
+ }
75
+ return { frames, rest: buffer.subarray(offset) };
76
+ }
77
+ export function decodeConnectFramesStrict(buffer, options = {}) {
78
+ const context = "Connect frame";
79
+ const maxMessageBytes = options.maxMessageBytes ?? MAX_CONNECT_MESSAGE_BYTES;
80
+ const maxFrameBytes = options.maxFrameBytes ?? MAX_CONNECT_FRAME_BYTES;
81
+ validateBound(maxMessageBytes, context);
82
+ validateBound(maxFrameBytes, context);
83
+ const frames = [];
84
+ let offset = 0;
85
+ while (offset < buffer.length) {
86
+ if (buffer.length - offset < 5) {
87
+ throw new CursorProtocolError("truncated", context, "frame header is incomplete");
88
+ }
89
+ const flags = buffer[offset];
90
+ if (flags === undefined) {
91
+ throw new CursorProtocolError("truncated", context, "frame flags are absent");
92
+ }
93
+ if ((flags & ~(CONNECT_COMPRESSED | CONNECT_END_STREAM)) !== 0) {
94
+ throw new CursorProtocolError("unsupported-flags", context, `unsupported flags 0x${flags.toString(16)}`);
95
+ }
96
+ if ((flags & (CONNECT_COMPRESSED | CONNECT_END_STREAM)) ===
97
+ (CONNECT_COMPRESSED | CONNECT_END_STREAM)) {
98
+ throw new CursorProtocolError("unsupported-flags", context, "end-stream envelope cannot be compressed");
99
+ }
100
+ const view = new DataView(buffer.buffer, buffer.byteOffset + offset + 1, 4);
101
+ const length = view.getUint32(0);
102
+ if (length > maxMessageBytes) {
103
+ throw new CursorProtocolError("oversized", context, `message exceeds ${maxMessageBytes} bytes`);
104
+ }
105
+ if (length + 5 > maxFrameBytes) {
106
+ throw new CursorProtocolError("oversized", context, `frame exceeds ${maxFrameBytes} bytes`);
107
+ }
108
+ const start = offset + 5;
109
+ const end = start + length;
110
+ if (end > buffer.length) {
111
+ throw new CursorProtocolError("truncated", context, "frame payload is incomplete");
112
+ }
113
+ frames.push({
114
+ compressed: (flags & CONNECT_COMPRESSED) !== 0,
115
+ endStream: (flags & CONNECT_END_STREAM) !== 0,
116
+ bytes: buffer.subarray(start, end),
117
+ });
118
+ offset = end;
119
+ }
120
+ return frames;
121
+ }
@@ -0,0 +1,5 @@
1
+ export type CursorCredentials = {
2
+ readonly accessToken: string;
3
+ readonly refreshToken: string | null;
4
+ };
5
+ export declare function parseCursorCredentials(value: unknown): CursorCredentials | null;