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,46 @@
1
+ const REGISTRY_URL = "https://registry.npmjs.org";
2
+ const SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
3
+ function versionFromPayload(body) {
4
+ let parsed;
5
+ try {
6
+ parsed = JSON.parse(new TextDecoder().decode(body));
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed)) {
12
+ return null;
13
+ }
14
+ const version = Reflect.get(parsed, "version");
15
+ return typeof version === "string" && SEMVER_PATTERN.test(version) ? version : null;
16
+ }
17
+ export function createPackageVersionResolver(options) {
18
+ const url = `${REGISTRY_URL}/${encodeURIComponent(options.packageName)}/latest`;
19
+ let resolved = null;
20
+ let inFlight = null;
21
+ const lookup = async (signal) => {
22
+ try {
23
+ const response = await options.transport.request({ method: "GET", url, headers: { accept: "application/json" }, body: null }, signal);
24
+ if (response.status < 200 || response.status >= 300) {
25
+ return null;
26
+ }
27
+ return versionFromPayload(response.body);
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ };
33
+ return async (signal) => {
34
+ if (resolved !== null) {
35
+ return resolved;
36
+ }
37
+ if (inFlight === null) {
38
+ inFlight = lookup(signal).then((version) => {
39
+ resolved = version;
40
+ inFlight = null;
41
+ return version;
42
+ });
43
+ }
44
+ return inFlight;
45
+ };
46
+ }
@@ -0,0 +1,9 @@
1
+ import type { HttpHeaders, HttpRequest, HttpTransport } from "../core/http";
2
+ export type HttpBodyStream = {
3
+ readonly status: number;
4
+ readonly statusText?: string;
5
+ readonly headers: HttpHeaders;
6
+ readonly chunks: AsyncIterable<Uint8Array>;
7
+ readonly bodyPresent: boolean;
8
+ };
9
+ export declare function openHttpBody(transport: HttpTransport, request: HttpRequest, signal: AbortSignal): Promise<HttpBodyStream>;
@@ -0,0 +1,22 @@
1
+ export async function openHttpBody(transport, request, signal) {
2
+ if (transport.stream !== undefined) {
3
+ const streamed = await transport.stream(request, signal);
4
+ return {
5
+ status: streamed.status,
6
+ ...(streamed.statusText === undefined ? {} : { statusText: streamed.statusText }),
7
+ headers: streamed.headers,
8
+ chunks: streamed.body,
9
+ bodyPresent: streamed.bodyPresent ?? true,
10
+ };
11
+ }
12
+ const response = await transport.request(request, signal);
13
+ return {
14
+ status: response.status,
15
+ ...(response.statusText === undefined ? {} : { statusText: response.statusText }),
16
+ headers: response.headers,
17
+ chunks: (async function* () {
18
+ yield response.body;
19
+ })(),
20
+ bodyPresent: response.bodyPresent ?? true,
21
+ };
22
+ }
@@ -0,0 +1,6 @@
1
+ import type { Plugin as V1Plugin } from "@opencode-ai/plugin";
2
+ export declare const connectorServer: V1Plugin;
3
+ export declare const claudeAuthServer: V1Plugin;
4
+ export declare const cursorAuthServer: V1Plugin;
5
+ export declare const commandCodeAuthServer: V1Plugin;
6
+ export declare const ollamaAuthServer: V1Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export const connectorServer = async (input, options) => (await import("./server.js")).connectorServer(input, options);
2
+ export const claudeAuthServer = async (input, options) => (await import("./server.js")).claudeAuthServer(input, options);
3
+ export const cursorAuthServer = async (input, options) => (await import("./server.js")).cursorAuthServer(input, options);
4
+ export const commandCodeAuthServer = async (input, options) => (await import("./server.js")).commandCodeAuthServer(input, options);
5
+ export const ollamaAuthServer = async (input, options) => (await import("./server.js")).ollamaAuthServer(input, options);
@@ -0,0 +1,4 @@
1
+ import type { Clock } from "../core/clock";
2
+ import { type ConnectorLogger, type LogSink } from "../core/logger";
3
+ export declare function createConsoleLogSink(): LogSink;
4
+ export declare function createConsoleLogger(clock: Clock): ConnectorLogger;
@@ -0,0 +1,11 @@
1
+ import { createConnectorLogger, } from "../core/logger";
2
+ export function createConsoleLogSink() {
3
+ return {
4
+ write(record) {
5
+ console.info(JSON.stringify(record));
6
+ },
7
+ };
8
+ }
9
+ export function createConsoleLogger(clock) {
10
+ return createConnectorLogger(clock, createConsoleLogSink());
11
+ }
@@ -0,0 +1,19 @@
1
+ export type OpenCodeAuthProvider = "anthropic" | "cursor" | "command-code" | "ollama";
2
+ export type OpenCodeAuthMatch = {
3
+ readonly kind: "oauth";
4
+ } | {
5
+ readonly kind: "marker";
6
+ } | {
7
+ readonly kind: "api-key";
8
+ readonly key: string;
9
+ };
10
+ export type OpenCodeAuthStore = {
11
+ readonly matchAuth: (provider: OpenCodeAuthProvider) => Promise<OpenCodeAuthMatch | null>;
12
+ };
13
+ export type OpenCodeAuthStoreOptions = {
14
+ readonly env: Readonly<Record<string, string | undefined>>;
15
+ readonly platform?: string;
16
+ readonly readFile?: (path: string) => Promise<string>;
17
+ };
18
+ export declare function opencodeAuthJsonPaths(env: Readonly<Record<string, string | undefined>>, platform?: string): readonly string[];
19
+ export declare function createOpenCodeAuthStore(options: OpenCodeAuthStoreOptions): OpenCodeAuthStore;
@@ -0,0 +1,107 @@
1
+ import { readFile as readAuthFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
5
+ const authRootSchema = z.record(z.string(), z.unknown());
6
+ const oauthRecordSchema = z
7
+ .object({
8
+ type: z.literal("oauth"),
9
+ access: z.string().min(1),
10
+ refresh: z.string().min(1),
11
+ expires: z.number().finite(),
12
+ accountId: z.string().optional(),
13
+ enterpriseUrl: z.string().optional(),
14
+ })
15
+ .strict();
16
+ const apiRecordSchema = z
17
+ .object({
18
+ type: z.literal("api"),
19
+ key: z.string().min(1),
20
+ metadata: z.record(z.string(), z.string()).optional(),
21
+ })
22
+ .strict();
23
+ export function opencodeAuthJsonPaths(env, platform = process.platform) {
24
+ const home = env["HOME"] ?? homedir();
25
+ const dataHome = env["XDG_DATA_HOME"];
26
+ if (dataHome !== undefined && dataHome.length > 0) {
27
+ return [join(dataHome, "opencode", "auth.json")];
28
+ }
29
+ const paths = [];
30
+ if (platform === "darwin") {
31
+ paths.push(join(home, "Library", "Application Support", "opencode", "auth.json"));
32
+ }
33
+ else if (platform === "win32") {
34
+ paths.push(join(env["LOCALAPPDATA"] ?? join(home, "AppData", "Local"), "opencode", "auth.json"));
35
+ }
36
+ else {
37
+ paths.push(join(home, ".local", "share", "opencode", "auth.json"));
38
+ }
39
+ return [...new Set(paths)];
40
+ }
41
+ function isMissingFile(error) {
42
+ return error instanceof Error && "code" in error && Reflect.get(error, "code") === "ENOENT";
43
+ }
44
+ function parseAuthMatch(provider, value) {
45
+ switch (provider) {
46
+ case "anthropic":
47
+ return oauthRecordSchema.safeParse(value).success ? { kind: "oauth" } : null;
48
+ case "cursor": {
49
+ const parsed = apiRecordSchema.safeParse(value);
50
+ return parsed.success && parsed.data.key === "cli-session:cursor" ? { kind: "marker" } : null;
51
+ }
52
+ case "ollama": {
53
+ const parsed = apiRecordSchema.safeParse(value);
54
+ return parsed.success && parsed.data.key === "cli-session:ollama" ? { kind: "marker" } : null;
55
+ }
56
+ case "command-code": {
57
+ const parsed = apiRecordSchema.safeParse(value);
58
+ if (!parsed.success) {
59
+ return null;
60
+ }
61
+ if (parsed.data.key === "cli-session:command-code") {
62
+ return { kind: "marker" };
63
+ }
64
+ return parsed.data.key.startsWith("cli-session:")
65
+ ? null
66
+ : { kind: "api-key", key: parsed.data.key };
67
+ }
68
+ }
69
+ }
70
+ export function createOpenCodeAuthStore(options) {
71
+ const readFile = options.readFile ?? ((path) => readAuthFile(path, "utf8"));
72
+ const paths = opencodeAuthJsonPaths(options.env, options.platform);
73
+ return {
74
+ matchAuth: async (provider) => {
75
+ for (const path of paths) {
76
+ let raw;
77
+ try {
78
+ raw = await readFile(path);
79
+ }
80
+ catch (error) {
81
+ if (isMissingFile(error)) {
82
+ continue;
83
+ }
84
+ throw error;
85
+ }
86
+ let parsedJson;
87
+ try {
88
+ parsedJson = JSON.parse(raw);
89
+ }
90
+ catch (error) {
91
+ if (error instanceof SyntaxError) {
92
+ continue;
93
+ }
94
+ throw error;
95
+ }
96
+ const root = authRootSchema.safeParse(parsedJson);
97
+ if (root.success) {
98
+ const match = parseAuthMatch(provider, root.data[provider]);
99
+ if (match !== null) {
100
+ return match;
101
+ }
102
+ }
103
+ }
104
+ return null;
105
+ },
106
+ };
107
+ }
@@ -0,0 +1,3 @@
1
+ export type { AISDKHooks, CatalogDraft, CatalogHooks, CatalogProviderRecord, IntegrationDraft, Plugin, PluginContext, Registration, } from "@opencode-ai/plugin/v2/promise";
2
+ export { define } from "@opencode-ai/plugin/v2/promise";
3
+ export type { IntegrationEnvMethod, ModelV2Info, ProviderV2Info } from "@opencode-ai/sdk/v2/types";
@@ -0,0 +1 @@
1
+ export { define } from "@opencode-ai/plugin/v2/promise";
@@ -0,0 +1,3 @@
1
+ import type { CatalogPublisher } from "../core/adapter";
2
+ import type { CatalogDraft } from "./beta-api";
3
+ export declare function createCatalogPublisher(draft: CatalogDraft): CatalogPublisher;
@@ -0,0 +1,85 @@
1
+ import { InvalidArgumentError, OperationCancelledError } from "../core/errors";
2
+ function assertNever(_value) {
3
+ throw new InvalidArgumentError("snapshot.status");
4
+ }
5
+ function defaultProviderApi() {
6
+ return {
7
+ type: "aisdk",
8
+ package: "opencode-ext-connector",
9
+ };
10
+ }
11
+ function defaultModelFields(modelId) {
12
+ return {
13
+ api: {
14
+ id: modelId,
15
+ type: "aisdk",
16
+ package: "opencode-ext-connector",
17
+ },
18
+ capabilities: {
19
+ tools: true,
20
+ input: ["text"],
21
+ output: ["text"],
22
+ },
23
+ request: {
24
+ headers: {},
25
+ body: {},
26
+ },
27
+ variants: [],
28
+ time: {
29
+ released: 0,
30
+ },
31
+ cost: [],
32
+ status: "active",
33
+ enabled: true,
34
+ limit: {
35
+ context: 0,
36
+ output: 0,
37
+ },
38
+ };
39
+ }
40
+ export function createCatalogPublisher(draft) {
41
+ return {
42
+ publish: async (snapshot, signal) => {
43
+ if (signal.aborted) {
44
+ throw new OperationCancelledError("publish-catalog");
45
+ }
46
+ const providerId = snapshot.providerId;
47
+ switch (snapshot.status) {
48
+ case "unavailable":
49
+ draft.provider.remove(providerId);
50
+ return;
51
+ case "ready":
52
+ case "stale": {
53
+ draft.provider.update(providerId, (provider) => {
54
+ provider.id = providerId;
55
+ provider.name = providerId;
56
+ provider.disabled = false;
57
+ provider.api = defaultProviderApi();
58
+ provider.request = { headers: {}, body: {} };
59
+ });
60
+ const nextIds = new Set(snapshot.models.map((model) => model.id));
61
+ const existing = draft.provider.get(providerId);
62
+ if (existing !== undefined) {
63
+ for (const modelId of existing.models.keys()) {
64
+ if (!nextIds.has(modelId)) {
65
+ draft.model.remove(providerId, modelId);
66
+ }
67
+ }
68
+ }
69
+ for (const model of snapshot.models) {
70
+ draft.model.update(providerId, model.id, (info) => {
71
+ info.id = model.id;
72
+ info.providerID = providerId;
73
+ info.name = model.id;
74
+ info.enabled = true;
75
+ Object.assign(info, defaultModelFields(model.id));
76
+ });
77
+ }
78
+ return;
79
+ }
80
+ default:
81
+ assertNever(snapshot);
82
+ }
83
+ },
84
+ };
85
+ }
@@ -0,0 +1,16 @@
1
+ import type { CatalogPublisher, ProviderAdapter } from "../core/adapter";
2
+ import type { Clock } from "../core/clock";
3
+ import { type HealthPolicy, type HealthState } from "../core/health";
4
+ import type { ProviderId } from "../core/ids";
5
+ import type { ConnectorLogger } from "../core/logger";
6
+ export type HealthStore = Map<ProviderId, HealthState>;
7
+ export declare function refreshAdaptersWithHealth(options: {
8
+ readonly adapters: readonly ProviderAdapter[];
9
+ readonly publisher: CatalogPublisher;
10
+ readonly logger: ConnectorLogger;
11
+ readonly clock: Clock;
12
+ readonly health: HealthPolicy;
13
+ readonly store: HealthStore;
14
+ readonly signal: AbortSignal;
15
+ readonly snapshotTimeoutMs?: number;
16
+ }): Promise<void>;
@@ -0,0 +1,49 @@
1
+ import { refreshProviderCatalog } from "../core/adapter";
2
+ import { createDeadline } from "../core/deadline";
3
+ import { ConnectorError } from "../core/errors";
4
+ import { createInitialHealthState, reduceHealth, } from "../core/health";
5
+ function errorMessage(error) {
6
+ if (error instanceof Error) {
7
+ return error.message;
8
+ }
9
+ return "unknown";
10
+ }
11
+ export async function refreshAdaptersWithHealth(options) {
12
+ const nowMs = options.clock.nowMs();
13
+ for (const adapter of options.adapters) {
14
+ const current = options.store.get(adapter.providerId) ?? createInitialHealthState();
15
+ if (current.retryAtMs !== null && nowMs < current.retryAtMs) {
16
+ options.logger.log("debug", "provider.snapshot.deferred", {
17
+ providerId: adapter.providerId,
18
+ retryAtMs: current.retryAtMs,
19
+ });
20
+ continue;
21
+ }
22
+ const deadline = createDeadline({
23
+ clock: options.clock,
24
+ timeoutMs: options.snapshotTimeoutMs ?? 30_000,
25
+ parentSignal: options.signal,
26
+ });
27
+ try {
28
+ const snapshot = await refreshProviderCatalog({
29
+ adapter,
30
+ publisher: options.publisher,
31
+ signal: deadline.signal,
32
+ });
33
+ const status = snapshot.status === "ready" ? "ready" : snapshot.status;
34
+ options.store.set(adapter.providerId, reduceHealth(current, { status, atMs: nowMs }, options.health));
35
+ }
36
+ catch (error) {
37
+ options.store.set(adapter.providerId, reduceHealth(current, { status: "unavailable", atMs: nowMs }, options.health));
38
+ const retryable = error instanceof ConnectorError ? error.retryable : false;
39
+ options.logger.log("warn", "provider.snapshot.failed", {
40
+ providerId: adapter.providerId,
41
+ retryable,
42
+ message: errorMessage(error),
43
+ });
44
+ }
45
+ finally {
46
+ await deadline.dispose();
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,2 @@
1
+ import type { ConnectorOptionsInput } from "../core/options";
2
+ export declare function pickConnectorOptionsInput(input: unknown): ConnectorOptionsInput;
@@ -0,0 +1,49 @@
1
+ function positiveInteger(value) {
2
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
3
+ }
4
+ function nonNegativeInteger(value) {
5
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
6
+ }
7
+ function pickCredentialRefresh(value) {
8
+ if (typeof value !== "object" || value === null) {
9
+ return undefined;
10
+ }
11
+ const rawMode = "mode" in value ? value.mode : undefined;
12
+ const mode = rawMode === "auto" || rawMode === "never" ? rawMode : undefined;
13
+ const leadMs = "leadMs" in value ? nonNegativeInteger(value.leadMs) : undefined;
14
+ if (mode === undefined && leadMs === undefined) {
15
+ return undefined;
16
+ }
17
+ return { mode, leadMs };
18
+ }
19
+ function pickHealth(value) {
20
+ if (typeof value !== "object" || value === null) {
21
+ return undefined;
22
+ }
23
+ const initialBackoffMs = "initialBackoffMs" in value ? positiveInteger(value.initialBackoffMs) : undefined;
24
+ const maximumBackoffMs = "maximumBackoffMs" in value ? positiveInteger(value.maximumBackoffMs) : undefined;
25
+ if (initialBackoffMs === undefined && maximumBackoffMs === undefined) {
26
+ return undefined;
27
+ }
28
+ return { initialBackoffMs, maximumBackoffMs };
29
+ }
30
+ export function pickConnectorOptionsInput(input) {
31
+ if (typeof input !== "object" || input === null) {
32
+ return {};
33
+ }
34
+ return {
35
+ providers: "providers" in input && Array.isArray(input.providers)
36
+ ? input.providers.filter((provider) => provider === "claude" ||
37
+ provider === "cursor" ||
38
+ provider === "command-code" ||
39
+ provider === "ollama")
40
+ : undefined,
41
+ snapshotTimeoutMs: "snapshotTimeoutMs" in input ? positiveInteger(input.snapshotTimeoutMs) : undefined,
42
+ writeBackCredentials: "writeBackCredentials" in input && typeof input.writeBackCredentials === "boolean"
43
+ ? input.writeBackCredentials
44
+ : undefined,
45
+ credentialRefresh: "credentialRefresh" in input ? pickCredentialRefresh(input.credentialRefresh) : undefined,
46
+ catalogReloadMs: "catalogReloadMs" in input ? nonNegativeInteger(input.catalogReloadMs) : undefined,
47
+ health: "health" in input ? pickHealth(input.health) : undefined,
48
+ };
49
+ }
@@ -0,0 +1,13 @@
1
+ import type { LanguageModelV3 } from "@ai-sdk/provider";
2
+ import type { HttpTransport } from "../core/http";
3
+ import type { CursorDirectRuntime } from "../providers/cursor/direct-runtime";
4
+ import type { OllamaRuntime } from "../providers/ollama/runtime";
5
+ export type ConnectorLanguageDeps = {
6
+ readonly env: Readonly<Record<string, string | undefined>>;
7
+ readonly transport: HttpTransport;
8
+ readonly readClaudeToken: (signal: AbortSignal) => Promise<string | null>;
9
+ readonly cursorRuntime: CursorDirectRuntime;
10
+ readonly ollamaRuntime: OllamaRuntime;
11
+ readonly commandCodeApiKey?: string;
12
+ };
13
+ export declare function createConnectorLanguage(deps: ConnectorLanguageDeps): (providerID: string, modelId: string) => LanguageModelV3 | null;
@@ -0,0 +1,37 @@
1
+ import { createClaudeLanguageModel } from "../providers/claude/language-model";
2
+ import { readCommandCodeAccessToken } from "../providers/command-code/auth";
3
+ import { createCommandCodeLanguageModel } from "../providers/command-code/language-model";
4
+ import { createCursorLanguageModel } from "../providers/cursor/language-model";
5
+ import { createOllamaLanguageModel } from "../providers/ollama/language-model";
6
+ export function createConnectorLanguage(deps) {
7
+ return (providerID, modelId) => {
8
+ if (providerID === "claude") {
9
+ return createClaudeLanguageModel({
10
+ modelId,
11
+ transport: deps.transport,
12
+ readAccessToken: deps.readClaudeToken,
13
+ });
14
+ }
15
+ if (providerID === "cursor") {
16
+ return createCursorLanguageModel({
17
+ modelId,
18
+ runPrompt: async () => null,
19
+ directRuntime: deps.cursorRuntime,
20
+ });
21
+ }
22
+ if (providerID === "command-code") {
23
+ return createCommandCodeLanguageModel({
24
+ modelId,
25
+ transport: deps.transport,
26
+ env: deps.env,
27
+ readAccessToken: (signal) => deps.commandCodeApiKey === undefined
28
+ ? readCommandCodeAccessToken(deps.env, signal)
29
+ : Promise.resolve(deps.commandCodeApiKey),
30
+ });
31
+ }
32
+ if (providerID === "ollama") {
33
+ return createOllamaLanguageModel({ modelId, runtime: deps.ollamaRuntime });
34
+ }
35
+ return null;
36
+ };
37
+ }
@@ -0,0 +1,2 @@
1
+ import type { OllamaFetch } from "../providers/ollama/http";
2
+ export declare function probeLocalOllama(fetch: OllamaFetch): Promise<boolean>;
@@ -0,0 +1,15 @@
1
+ import { OperationCancelledError } from "../core/errors";
2
+ import { OllamaCatalogError } from "../providers/ollama/errors";
3
+ import { listLocalOllamaModels } from "../providers/ollama/local-catalog";
4
+ const OLLAMA_PROBE_TIMEOUT_MS = 5_000;
5
+ export async function probeLocalOllama(fetch) {
6
+ try {
7
+ await listLocalOllamaModels(fetch, AbortSignal.timeout(OLLAMA_PROBE_TIMEOUT_MS));
8
+ return true;
9
+ }
10
+ catch (error) {
11
+ if (error instanceof OllamaCatalogError || error instanceof OperationCancelledError)
12
+ return false;
13
+ throw error;
14
+ }
15
+ }
@@ -0,0 +1,4 @@
1
+ import { type OllamaCatalogState } from "../providers/ollama/catalog-state";
2
+ import { type OllamaRuntime } from "../providers/ollama/runtime";
3
+ export declare const productionOllamaCatalog: OllamaCatalogState;
4
+ export declare const productionOllamaRuntime: OllamaRuntime;
@@ -0,0 +1,10 @@
1
+ import { createOllamaCatalogState, } from "../providers/ollama/catalog-state";
2
+ import { productionOllamaFetch } from "../providers/ollama/http";
3
+ import { createOllamaRuntime } from "../providers/ollama/runtime";
4
+ export const productionOllamaCatalog = createOllamaCatalogState({
5
+ fetch: productionOllamaFetch,
6
+ });
7
+ export const productionOllamaRuntime = createOllamaRuntime({
8
+ catalog: productionOllamaCatalog,
9
+ fetch: productionOllamaFetch,
10
+ });
@@ -0,0 +1,33 @@
1
+ import type { LanguageModelV3 } from "@ai-sdk/provider";
2
+ import type { CatalogPublisher, ProviderAdapter } from "../core/adapter";
3
+ import type { Clock } from "../core/clock";
4
+ import type { HealthPolicy } from "../core/health";
5
+ import type { ConnectorLogger } from "../core/logger";
6
+ import type { CatalogDraft, Registration } from "./beta-api";
7
+ import { type HealthStore } from "./health-refresh";
8
+ export declare const PLUGIN_ID: string;
9
+ export type CatalogHost = {
10
+ readonly transform: (callback: (draft: CatalogDraft) => Promise<void> | void) => Promise<Registration>;
11
+ };
12
+ export type LanguageHost = {
13
+ readonly language: (callback: (input: {
14
+ readonly model: {
15
+ readonly providerID: string;
16
+ readonly id: string;
17
+ };
18
+ language?: LanguageModelV3;
19
+ }) => void) => Promise<Registration>;
20
+ };
21
+ export type ConnectorSetupOptions = {
22
+ readonly catalog: CatalogHost;
23
+ readonly adapters: readonly ProviderAdapter[];
24
+ readonly logger: ConnectorLogger;
25
+ readonly createPublisher: (draft: CatalogDraft) => CatalogPublisher;
26
+ readonly clock?: Clock;
27
+ readonly health?: HealthPolicy;
28
+ readonly healthStore?: HealthStore;
29
+ readonly aisdk?: LanguageHost;
30
+ readonly createLanguage?: (providerID: string, modelId: string) => LanguageModelV3 | null;
31
+ readonly snapshotTimeoutMs?: number;
32
+ };
33
+ export declare function setupConnector(options: ConnectorSetupOptions): Promise<Registration>;
@@ -0,0 +1,40 @@
1
+ import { refreshAdaptersWithHealth } from "./health-refresh";
2
+ export const PLUGIN_ID = "opencode-ext-connector";
3
+ export async function setupConnector(options) {
4
+ const catalogRegistration = await options.catalog.transform(async (draft) => {
5
+ const publisher = options.createPublisher(draft);
6
+ await refreshAdaptersWithHealth({
7
+ adapters: options.adapters,
8
+ publisher,
9
+ logger: options.logger,
10
+ clock: options.clock ?? {
11
+ nowMs: () => 0,
12
+ schedule: () => ({
13
+ cancel: () => undefined,
14
+ [Symbol.dispose]: () => undefined,
15
+ }),
16
+ },
17
+ health: options.health ?? { initialBackoffMs: 1_000, maximumBackoffMs: 60_000 },
18
+ store: options.healthStore ?? new Map(),
19
+ signal: new AbortController().signal,
20
+ snapshotTimeoutMs: options.snapshotTimeoutMs ?? 30_000,
21
+ });
22
+ });
23
+ if (options.aisdk !== undefined && options.createLanguage !== undefined) {
24
+ const createLanguage = options.createLanguage;
25
+ await options.aisdk.language((input) => {
26
+ const language = createLanguage(input.model.providerID, input.model.id);
27
+ if (language !== null) {
28
+ input.language = language;
29
+ }
30
+ });
31
+ }
32
+ return {
33
+ dispose: async () => {
34
+ await catalogRegistration.dispose();
35
+ for (const adapter of options.adapters) {
36
+ await adapter.dispose();
37
+ }
38
+ },
39
+ };
40
+ }
@@ -0,0 +1,4 @@
1
+ export declare function bindProcessExit(dispose: () => Promise<void>, emitter?: {
2
+ readonly once: (event: string, listener: () => void) => void;
3
+ readonly off?: (event: string, listener: () => void) => void;
4
+ }): () => void;
@@ -0,0 +1,22 @@
1
+ export function bindProcessExit(dispose, emitter = process) {
2
+ let ran = false;
3
+ const listener = () => {
4
+ if (ran) {
5
+ return;
6
+ }
7
+ ran = true;
8
+ void dispose();
9
+ };
10
+ const events = ["beforeExit", "SIGINT", "SIGTERM"];
11
+ for (const event of events) {
12
+ emitter.once(event, listener);
13
+ }
14
+ return () => {
15
+ ran = true;
16
+ if (emitter.off !== undefined) {
17
+ for (const event of events) {
18
+ emitter.off(event, listener);
19
+ }
20
+ }
21
+ };
22
+ }