@nimbus-dev/connectors 0.1.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 (301) hide show
  1. package/LICENSE +662 -0
  2. package/NOTICE +40 -0
  3. package/README.md +111 -0
  4. package/connectors/airflow/nimbus.extension.json +16 -0
  5. package/connectors/airflow/src/search-filter.ts +38 -0
  6. package/connectors/airflow/src/server.ts +91 -0
  7. package/connectors/apple/nimbus.extension.json +40 -0
  8. package/connectors/apple/src/apple-mail-core.ts +43 -0
  9. package/connectors/apple/src/caldav-core.ts +116 -0
  10. package/connectors/apple/src/calendar-tools.ts +278 -0
  11. package/connectors/apple/src/server.ts +541 -0
  12. package/connectors/apple/src/tools.ts +138 -0
  13. package/connectors/argocd/nimbus.extension.json +16 -0
  14. package/connectors/argocd/src/search-filter.ts +24 -0
  15. package/connectors/argocd/src/server.ts +158 -0
  16. package/connectors/athena/nimbus.extension.json +15 -0
  17. package/connectors/athena/src/server.ts +6 -0
  18. package/connectors/athena/src/tools.ts +181 -0
  19. package/connectors/aws/nimbus.extension.json +21 -0
  20. package/connectors/aws/src/server.ts +191 -0
  21. package/connectors/azure/nimbus.extension.json +15 -0
  22. package/connectors/azure/src/server.ts +139 -0
  23. package/connectors/bigeye/nimbus.extension.json +16 -0
  24. package/connectors/bigeye/src/search-filter.ts +22 -0
  25. package/connectors/bigeye/src/server.ts +161 -0
  26. package/connectors/bigquery/nimbus.extension.json +15 -0
  27. package/connectors/bigquery/src/server.ts +6 -0
  28. package/connectors/bigquery/src/tools.ts +175 -0
  29. package/connectors/bitbucket/nimbus.extension.json +15 -0
  30. package/connectors/bitbucket/src/server.ts +252 -0
  31. package/connectors/bitrise/nimbus.extension.json +15 -0
  32. package/connectors/bitrise/src/search-filter.ts +11 -0
  33. package/connectors/bitrise/src/server.ts +83 -0
  34. package/connectors/canva/nimbus.extension.json +15 -0
  35. package/connectors/canva/src/search-filter.ts +23 -0
  36. package/connectors/canva/src/server.ts +77 -0
  37. package/connectors/circleci/nimbus.extension.json +15 -0
  38. package/connectors/circleci/src/server.ts +184 -0
  39. package/connectors/cloud-logging/nimbus.extension.json +15 -0
  40. package/connectors/cloud-logging/src/server.ts +6 -0
  41. package/connectors/cloud-logging/src/tools.ts +115 -0
  42. package/connectors/cloudwatch/nimbus.extension.json +15 -0
  43. package/connectors/cloudwatch/src/server.ts +6 -0
  44. package/connectors/cloudwatch/src/tools.ts +144 -0
  45. package/connectors/codemagic/nimbus.extension.json +15 -0
  46. package/connectors/codemagic/src/search-filter.ts +11 -0
  47. package/connectors/codemagic/src/server.ts +74 -0
  48. package/connectors/confluence/nimbus.extension.json +15 -0
  49. package/connectors/confluence/src/kb-append.ts +25 -0
  50. package/connectors/confluence/src/server.ts +309 -0
  51. package/connectors/dagster/nimbus.extension.json +16 -0
  52. package/connectors/dagster/src/search-filter.ts +53 -0
  53. package/connectors/dagster/src/server.ts +218 -0
  54. package/connectors/databricks/nimbus.extension.json +16 -0
  55. package/connectors/databricks/src/search-filter.ts +28 -0
  56. package/connectors/databricks/src/server.ts +81 -0
  57. package/connectors/datadog/nimbus.extension.json +15 -0
  58. package/connectors/datadog/src/server.ts +56 -0
  59. package/connectors/dataprofile/nimbus.extension.json +16 -0
  60. package/connectors/dataprofile/src/profile.ts +298 -0
  61. package/connectors/dataprofile/src/server.ts +6 -0
  62. package/connectors/dataprofile/src/tools.ts +69 -0
  63. package/connectors/dbt/nimbus.extension.json +16 -0
  64. package/connectors/dbt/src/search-filter.ts +28 -0
  65. package/connectors/dbt/src/server.ts +96 -0
  66. package/connectors/dependencytrack/nimbus.extension.json +16 -0
  67. package/connectors/dependencytrack/src/search-filter.ts +24 -0
  68. package/connectors/dependencytrack/src/server.ts +80 -0
  69. package/connectors/discord/nimbus.extension.json +15 -0
  70. package/connectors/discord/src/server.ts +93 -0
  71. package/connectors/elasticsearch/nimbus.extension.json +16 -0
  72. package/connectors/elasticsearch/src/server.ts +6 -0
  73. package/connectors/elasticsearch/src/tools.ts +110 -0
  74. package/connectors/fastmail/nimbus.extension.json +16 -0
  75. package/connectors/fastmail/src/jmap-core.ts +86 -0
  76. package/connectors/fastmail/src/server.ts +194 -0
  77. package/connectors/fastmail/src/tools.ts +143 -0
  78. package/connectors/figma/nimbus.extension.json +15 -0
  79. package/connectors/figma/src/search-filter.ts +24 -0
  80. package/connectors/figma/src/server.ts +117 -0
  81. package/connectors/firebase/nimbus.extension.json +15 -0
  82. package/connectors/firebase/src/search-filter.ts +30 -0
  83. package/connectors/firebase/src/server.ts +94 -0
  84. package/connectors/firebase/src/token.ts +41 -0
  85. package/connectors/flagsmith/nimbus.extension.json +15 -0
  86. package/connectors/flagsmith/src/search-filter.ts +29 -0
  87. package/connectors/flagsmith/src/server.ts +102 -0
  88. package/connectors/flux/nimbus.extension.json +16 -0
  89. package/connectors/flux/src/search-filter.ts +42 -0
  90. package/connectors/flux/src/server.ts +182 -0
  91. package/connectors/gcp/nimbus.extension.json +15 -0
  92. package/connectors/gcp/src/server.ts +140 -0
  93. package/connectors/github/nimbus.extension.json +15 -0
  94. package/connectors/github/src/server.ts +347 -0
  95. package/connectors/github-actions/nimbus.extension.json +15 -0
  96. package/connectors/github-actions/src/server.ts +213 -0
  97. package/connectors/gitlab/nimbus.extension.json +15 -0
  98. package/connectors/gitlab/src/server.ts +392 -0
  99. package/connectors/gmail/nimbus.extension.json +15 -0
  100. package/connectors/gmail/src/server.ts +277 -0
  101. package/connectors/google-drive/nimbus.extension.json +15 -0
  102. package/connectors/google-drive/src/drive-query.ts +3 -0
  103. package/connectors/google-drive/src/server.ts +531 -0
  104. package/connectors/google-meet/nimbus.extension.json +15 -0
  105. package/connectors/google-meet/src/server.ts +89 -0
  106. package/connectors/google-photos/nimbus.extension.json +15 -0
  107. package/connectors/google-photos/src/server.ts +139 -0
  108. package/connectors/grafana/nimbus.extension.json +15 -0
  109. package/connectors/grafana/src/server.ts +61 -0
  110. package/connectors/great-expectations/nimbus.extension.json +16 -0
  111. package/connectors/great-expectations/src/gx-parse.ts +292 -0
  112. package/connectors/great-expectations/src/server.ts +6 -0
  113. package/connectors/great-expectations/src/tools.ts +91 -0
  114. package/connectors/greenhouse/nimbus.extension.json +15 -0
  115. package/connectors/greenhouse/src/search-filter.ts +65 -0
  116. package/connectors/greenhouse/src/server.ts +67 -0
  117. package/connectors/hubspot/nimbus.extension.json +15 -0
  118. package/connectors/hubspot/src/search-filter.ts +28 -0
  119. package/connectors/hubspot/src/server.ts +81 -0
  120. package/connectors/iac/nimbus.extension.json +14 -0
  121. package/connectors/iac/src/server.ts +145 -0
  122. package/connectors/imap/nimbus.extension.json +16 -0
  123. package/connectors/imap/src/imap-core.ts +110 -0
  124. package/connectors/imap/src/server.ts +232 -0
  125. package/connectors/imap/src/tools.ts +40 -0
  126. package/connectors/intercom/nimbus.extension.json +15 -0
  127. package/connectors/intercom/src/search-filter.ts +46 -0
  128. package/connectors/intercom/src/server.ts +65 -0
  129. package/connectors/jenkins/nimbus.extension.json +15 -0
  130. package/connectors/jenkins/src/jenkins-api.ts +156 -0
  131. package/connectors/jenkins/src/server.ts +223 -0
  132. package/connectors/jira/nimbus.extension.json +15 -0
  133. package/connectors/jira/src/server.ts +309 -0
  134. package/connectors/kubernetes/nimbus.extension.json +15 -0
  135. package/connectors/kubernetes/src/server.ts +163 -0
  136. package/connectors/launchdarkly/nimbus.extension.json +15 -0
  137. package/connectors/launchdarkly/src/search-filter.ts +31 -0
  138. package/connectors/launchdarkly/src/server.ts +79 -0
  139. package/connectors/lever/nimbus.extension.json +15 -0
  140. package/connectors/lever/src/search-filter.ts +35 -0
  141. package/connectors/lever/src/server.ts +64 -0
  142. package/connectors/linear/nimbus.extension.json +15 -0
  143. package/connectors/linear/src/server.ts +467 -0
  144. package/connectors/localdb/nimbus.extension.json +16 -0
  145. package/connectors/localdb/src/server.ts +6 -0
  146. package/connectors/localdb/src/sql-scan.ts +158 -0
  147. package/connectors/localdb/src/tools.ts +67 -0
  148. package/connectors/looker/nimbus.extension.json +16 -0
  149. package/connectors/looker/src/search-filter.ts +22 -0
  150. package/connectors/looker/src/server.ts +254 -0
  151. package/connectors/mendeley/nimbus.extension.json +15 -0
  152. package/connectors/mendeley/src/search-filter.ts +68 -0
  153. package/connectors/mendeley/src/server.ts +59 -0
  154. package/connectors/mercury/nimbus.extension.json +15 -0
  155. package/connectors/mercury/src/search-filter.ts +11 -0
  156. package/connectors/mercury/src/server.ts +61 -0
  157. package/connectors/metabase/nimbus.extension.json +16 -0
  158. package/connectors/metabase/src/search-filter.ts +22 -0
  159. package/connectors/metabase/src/server.ts +83 -0
  160. package/connectors/miro/nimbus.extension.json +15 -0
  161. package/connectors/miro/src/search-filter.ts +24 -0
  162. package/connectors/miro/src/server.ts +76 -0
  163. package/connectors/mlflow/nimbus.extension.json +16 -0
  164. package/connectors/mlflow/src/search-filter.ts +38 -0
  165. package/connectors/mlflow/src/server.ts +167 -0
  166. package/connectors/monte-carlo/nimbus.extension.json +16 -0
  167. package/connectors/monte-carlo/src/search-filter.ts +11 -0
  168. package/connectors/monte-carlo/src/server.ts +241 -0
  169. package/connectors/netlify/nimbus.extension.json +15 -0
  170. package/connectors/netlify/src/search-filter.ts +37 -0
  171. package/connectors/netlify/src/server.ts +64 -0
  172. package/connectors/newrelic/nimbus.extension.json +15 -0
  173. package/connectors/newrelic/src/server.ts +48 -0
  174. package/connectors/notion/nimbus.extension.json +15 -0
  175. package/connectors/notion/src/kb-append.ts +23 -0
  176. package/connectors/notion/src/server.ts +343 -0
  177. package/connectors/obsidian/nimbus.extension.json +19 -0
  178. package/connectors/obsidian/src/server.ts +433 -0
  179. package/connectors/onedrive/nimbus.extension.json +15 -0
  180. package/connectors/onedrive/src/server.ts +256 -0
  181. package/connectors/outlook/nimbus.extension.json +15 -0
  182. package/connectors/outlook/src/server.ts +358 -0
  183. package/connectors/outlook/src/tool-scope-policy.ts +53 -0
  184. package/connectors/pagerduty/nimbus.extension.json +15 -0
  185. package/connectors/pagerduty/src/server.ts +140 -0
  186. package/connectors/pipedrive/nimbus.extension.json +15 -0
  187. package/connectors/pipedrive/src/search-filter.ts +11 -0
  188. package/connectors/pipedrive/src/server.ts +62 -0
  189. package/connectors/powerbi/nimbus.extension.json +16 -0
  190. package/connectors/powerbi/src/search-filter.ts +22 -0
  191. package/connectors/powerbi/src/server.ts +249 -0
  192. package/connectors/prefect/nimbus.extension.json +16 -0
  193. package/connectors/prefect/src/search-filter.ts +40 -0
  194. package/connectors/prefect/src/server.ts +94 -0
  195. package/connectors/protonmail/nimbus.extension.json +16 -0
  196. package/connectors/protonmail/src/mail-core.ts +96 -0
  197. package/connectors/protonmail/src/server.ts +231 -0
  198. package/connectors/protonmail/src/tools.ts +45 -0
  199. package/connectors/raindrop/nimbus.extension.json +15 -0
  200. package/connectors/raindrop/src/search-filter.ts +20 -0
  201. package/connectors/raindrop/src/server.ts +107 -0
  202. package/connectors/ramp/nimbus.extension.json +15 -0
  203. package/connectors/ramp/src/search-filter.ts +37 -0
  204. package/connectors/ramp/src/server.ts +109 -0
  205. package/connectors/readwise/nimbus.extension.json +15 -0
  206. package/connectors/readwise/src/search-filter.ts +64 -0
  207. package/connectors/readwise/src/server.ts +92 -0
  208. package/connectors/sagemaker/nimbus.extension.json +15 -0
  209. package/connectors/sagemaker/src/server.ts +6 -0
  210. package/connectors/sagemaker/src/tools.ts +121 -0
  211. package/connectors/salesforce/nimbus.extension.json +15 -0
  212. package/connectors/salesforce/src/search-filter.ts +23 -0
  213. package/connectors/salesforce/src/server.ts +94 -0
  214. package/connectors/semgrep/nimbus.extension.json +15 -0
  215. package/connectors/semgrep/src/search-filter.ts +31 -0
  216. package/connectors/semgrep/src/server.ts +108 -0
  217. package/connectors/sentry/nimbus.extension.json +15 -0
  218. package/connectors/sentry/src/server.ts +66 -0
  219. package/connectors/slack/nimbus.extension.json +15 -0
  220. package/connectors/slack/src/server.ts +360 -0
  221. package/connectors/snowflake/nimbus.extension.json +16 -0
  222. package/connectors/snowflake/src/search-filter.ts +26 -0
  223. package/connectors/snowflake/src/server.ts +236 -0
  224. package/connectors/snyk/nimbus.extension.json +15 -0
  225. package/connectors/snyk/src/search-filter.ts +28 -0
  226. package/connectors/snyk/src/server.ts +144 -0
  227. package/connectors/sonarqube/nimbus.extension.json +15 -0
  228. package/connectors/sonarqube/src/search-filter.ts +28 -0
  229. package/connectors/sonarqube/src/server.ts +115 -0
  230. package/connectors/stackoverflow/nimbus.extension.json +15 -0
  231. package/connectors/stackoverflow/src/search-filter.ts +52 -0
  232. package/connectors/stackoverflow/src/server.ts +81 -0
  233. package/connectors/storybook/nimbus.extension.json +16 -0
  234. package/connectors/storybook/src/server.ts +6 -0
  235. package/connectors/storybook/src/storybook-parse.ts +67 -0
  236. package/connectors/storybook/src/tools.ts +68 -0
  237. package/connectors/stripe/nimbus.extension.json +15 -0
  238. package/connectors/stripe/src/search-filter.ts +19 -0
  239. package/connectors/stripe/src/server.ts +65 -0
  240. package/connectors/superset/nimbus.extension.json +16 -0
  241. package/connectors/superset/src/search-filter.ts +22 -0
  242. package/connectors/superset/src/server.ts +119 -0
  243. package/connectors/tableau/nimbus.extension.json +16 -0
  244. package/connectors/tableau/src/search-filter.ts +22 -0
  245. package/connectors/tableau/src/server.ts +248 -0
  246. package/connectors/teams/nimbus.extension.json +20 -0
  247. package/connectors/teams/src/bot-send.ts +72 -0
  248. package/connectors/teams/src/server.ts +257 -0
  249. package/connectors/testflight/nimbus.extension.json +15 -0
  250. package/connectors/testflight/src/jwt.ts +27 -0
  251. package/connectors/testflight/src/search-filter.ts +30 -0
  252. package/connectors/testflight/src/server.ts +74 -0
  253. package/connectors/vercel/nimbus.extension.json +15 -0
  254. package/connectors/vercel/src/search-filter.ts +34 -0
  255. package/connectors/vercel/src/server.ts +79 -0
  256. package/connectors/vertex-ai/nimbus.extension.json +15 -0
  257. package/connectors/vertex-ai/src/server.ts +6 -0
  258. package/connectors/vertex-ai/src/tools.ts +145 -0
  259. package/connectors/wiz/nimbus.extension.json +15 -0
  260. package/connectors/wiz/src/search-filter.ts +50 -0
  261. package/connectors/wiz/src/server.ts +192 -0
  262. package/connectors/workday/nimbus.extension.json +19 -0
  263. package/connectors/workday/src/search-filter.ts +5 -0
  264. package/connectors/workday/src/server.ts +78 -0
  265. package/connectors/workday/src/tools.ts +1 -0
  266. package/connectors/zendesk/nimbus.extension.json +16 -0
  267. package/connectors/zendesk/src/search-filter.ts +11 -0
  268. package/connectors/zendesk/src/server.ts +77 -0
  269. package/connectors/zoom/nimbus.extension.json +15 -0
  270. package/connectors/zoom/src/encode-meeting-path.ts +5 -0
  271. package/connectors/zoom/src/recordings-window.ts +34 -0
  272. package/connectors/zoom/src/search-filter.ts +42 -0
  273. package/connectors/zoom/src/server.ts +98 -0
  274. package/connectors/zotero/nimbus.extension.json +15 -0
  275. package/connectors/zotero/src/search-filter.ts +81 -0
  276. package/connectors/zotero/src/server.ts +82 -0
  277. package/package.json +152 -0
  278. package/shared/atlassian-json-fetch.ts +42 -0
  279. package/shared/audit-chain.ts +133 -0
  280. package/shared/connector-mode.ts +60 -0
  281. package/shared/consent-kit.ts +323 -0
  282. package/shared/fetch-bearer-json.ts +18 -0
  283. package/shared/header-safe.ts +28 -0
  284. package/shared/imap-bodystructure.ts +113 -0
  285. package/shared/imap-mail-core.ts +57 -0
  286. package/shared/imap-tool-kit.ts +293 -0
  287. package/shared/join-api-path.ts +18 -0
  288. package/shared/kb-markdown.ts +142 -0
  289. package/shared/mcp-search-tool.ts +37 -0
  290. package/shared/mcp-tool-kit.ts +35 -0
  291. package/shared/nimbus-spawn.ts +128 -0
  292. package/shared/rest-tool-kit.ts +18 -0
  293. package/shared/run-cli-json.ts +55 -0
  294. package/shared/run-read-only-mcp-connector.ts +59 -0
  295. package/shared/safe-cli-arg.ts +55 -0
  296. package/shared/search-filter.ts +140 -0
  297. package/shared/strip-trailing-slashes.ts +7 -0
  298. package/shared/write-scope.ts +58 -0
  299. package/standalone/src/bin.ts +19 -0
  300. package/standalone/src/launcher.ts +186 -0
  301. package/standalone/tsconfig.json +8 -0
@@ -0,0 +1,293 @@
1
+ import type { WriteToolRegistrar } from "./consent-kit.ts";
2
+ /**
3
+ * Shared tool-layer helpers for the IMAP/JMAP email connectors (imap, protonmail).
4
+ * Extracted to eliminate byte-identical duplication in tools.ts across those two
5
+ * connectors. FastMail (JMAP) uses a different view shape (id vs uid, no mailbox
6
+ * field) and different arg schemas — its view transformer stays local.
7
+ *
8
+ * Scope: Zod arg schemas, the shared message view transformer, and the shared
9
+ * `envInt` + `previewFromParts` server-layer helpers. Class bodies and
10
+ * connector-specific envelope/meta mappers are deliberately NOT extracted here
11
+ * (they implement different local interfaces and are out of scope per the dedup
12
+ * brief).
13
+ */
14
+
15
+ import { z } from "zod";
16
+
17
+ import { headerLine } from "./header-safe.ts";
18
+ import { capPreview, clampLimit } from "./imap-mail-core.ts";
19
+ import { createRegisterSimpleTool, type McpListResult, mcpJsonResult } from "./mcp-tool-kit.ts";
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Shared Zod arg schemas (byte-identical across imap + protonmail tools.ts)
23
+ // ---------------------------------------------------------------------------
24
+
25
+ const listArgs = z.object({
26
+ mailbox: z.string().min(1).optional(),
27
+ limit: z.number().int().min(1).max(200).optional(),
28
+ });
29
+
30
+ const getArgs = z.object({
31
+ uid: z.number().int().min(1),
32
+ mailbox: z.string().min(1).optional(),
33
+ });
34
+
35
+ const searchArgs = z.object({
36
+ query: z.string().min(1).max(500),
37
+ mailbox: z.string().min(1).optional(),
38
+ limit: z.number().int().min(1).max(200).optional(),
39
+ });
40
+
41
+ const sendArgs = z.object({
42
+ to: headerLine({ min: 1 }),
43
+ subject: headerLine({ min: 1, max: 998 }),
44
+ body: z.string().max(1_000_000),
45
+ cc: headerLine().optional(),
46
+ bcc: headerLine().optional(),
47
+ });
48
+
49
+ /** Shared Zod schemas for the IMAP/protonmail tool arg payloads. */
50
+ export const emailToolSchemas = {
51
+ listArgs,
52
+ getArgs,
53
+ searchArgs,
54
+ sendArgs,
55
+ } as const;
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Shared view transformer (imap + protonmail tools.ts — viewMessage)
59
+ // ---------------------------------------------------------------------------
60
+
61
+ /**
62
+ * The minimal shape that `viewEmailMessage` reads from a message meta object.
63
+ * imap's `ImapMessageMeta` and protonmail's `MailMessageMeta` both satisfy this.
64
+ */
65
+ export interface EmailMessageMeta {
66
+ readonly uid: number;
67
+ readonly mailbox: string;
68
+ readonly uidValidity: string | null;
69
+ readonly envelope: {
70
+ readonly date?: Date | string | null;
71
+ readonly subject?: string | null;
72
+ readonly messageId?: string | null;
73
+ readonly from?: readonly { readonly name?: string; readonly address?: string }[];
74
+ readonly to?: readonly { readonly name?: string; readonly address?: string }[];
75
+ readonly cc?: readonly { readonly name?: string; readonly address?: string }[];
76
+ };
77
+ readonly attachments: readonly {
78
+ readonly filename: string | null;
79
+ readonly sizeBytes: number | null;
80
+ readonly mimeType: string | null;
81
+ }[];
82
+ readonly preview: string;
83
+ }
84
+
85
+ /**
86
+ * Reduce an {@link EmailMessageMeta} to a JSON-safe view. Returns HEADERS,
87
+ * attachment METADATA (filename/size/mimetype), and the capped preview — never
88
+ * attachment bytes or a full body. Addresses are formatted by the caller-supplied
89
+ * `formatAddr` so each connector can use its own local `formatAddress` helper.
90
+ */
91
+ export function viewEmailMessage(
92
+ m: EmailMessageMeta,
93
+ formatAddr: (a: { readonly name?: string; readonly address?: string }) => string,
94
+ ): Record<string, unknown> {
95
+ const env = m.envelope;
96
+ return {
97
+ uid: m.uid,
98
+ mailbox: m.mailbox,
99
+ uidValidity: m.uidValidity,
100
+ messageId: env.messageId ?? null,
101
+ subject: env.subject ?? null,
102
+ date: env.date instanceof Date ? env.date.toISOString() : (env.date ?? null),
103
+ from: (env.from ?? []).map(formatAddr),
104
+ to: (env.to ?? []).map(formatAddr),
105
+ cc: (env.cc ?? []).map(formatAddr),
106
+ attachments: m.attachments.map((a) => ({
107
+ filename: a.filename,
108
+ sizeBytes: a.sizeBytes,
109
+ mimeType: a.mimeType,
110
+ })),
111
+ preview: m.preview,
112
+ };
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // Shared server-layer helpers (server.ts — envInt, previewFromParts)
117
+ // ---------------------------------------------------------------------------
118
+
119
+ /**
120
+ * Parse an environment variable as an integer in the range [1, 65535]. Returns
121
+ * `fallback` if the variable is absent, empty, non-numeric, or out of range.
122
+ * Byte-identical to the `envInt` helper in imap/server.ts and protonmail/server.ts.
123
+ */
124
+ export function envInt(name: string, fallback: number): number {
125
+ const raw = process.env[name]?.trim();
126
+ if (raw === undefined || raw === "") {
127
+ return fallback;
128
+ }
129
+ const n = Number(raw);
130
+ return Number.isFinite(n) && n > 0 && n <= 65535 ? Math.trunc(n) : fallback;
131
+ }
132
+
133
+ /**
134
+ * Extract and cap a plain-text body preview from the map of IMAP body parts
135
+ * fetched by imapflow. Looks up part keys `partKey → "1" → "TEXT"` in order.
136
+ * Returns an empty string when nothing is found. Byte-equivalent to the
137
+ * `previewFromParts` helpers in imap/server.ts and protonmail/server.ts.
138
+ */
139
+ export function previewFromParts(parts: Map<string, Buffer> | undefined, partKey: string): string {
140
+ if (parts === undefined) {
141
+ return "";
142
+ }
143
+ const buf = parts.get(partKey) ?? parts.get("1") ?? parts.get("TEXT");
144
+ if (buf === undefined) {
145
+ return "";
146
+ }
147
+ return capPreview(buf.toString("utf8"));
148
+ }
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Shared tool-registration factory (imap + protonmail tools.ts — the 4 blocks)
152
+ // ---------------------------------------------------------------------------
153
+
154
+ /** Read surface the email tool factory depends on (imap's ImapClient / protonmail's MailClient both satisfy it). */
155
+ export interface EmailReadClient {
156
+ list(options: { mailbox?: string; limit?: number }): Promise<EmailMessageMeta[]>;
157
+ get(uid: number, mailbox?: string): Promise<EmailMessageMeta | null>;
158
+ search(options: { query: string; mailbox?: string; limit?: number }): Promise<EmailMessageMeta[]>;
159
+ }
160
+
161
+ /** Send surface the email tool factory depends on (imap's/protonmail's SmtpMailer satisfy it). */
162
+ export interface EmailSendMailer {
163
+ send(input: { to: string; subject: string; body: string; cc?: string; bcc?: string }): Promise<{
164
+ messageId: string | null;
165
+ accepted: readonly string[];
166
+ rejected: readonly string[];
167
+ }>;
168
+ }
169
+
170
+ /** The four tool descriptions, supplied verbatim by each connector. */
171
+ export interface EmailToolDescriptions {
172
+ readonly list: string;
173
+ readonly get: string;
174
+ readonly search: string;
175
+ readonly send: string;
176
+ }
177
+
178
+ /**
179
+ * Register the 4 email tools (`<prefix>_list|_get|_search|_mail_send`) onto an
180
+ * MCP server. The IMAP client + SMTP mailer are injected so tests exercise the
181
+ * tool surface without opening sockets. Byte-equivalent to the hand-written
182
+ * `registerImapTools` / `registerProtonmailTools` bodies it replaces — the tool
183
+ * names (via `toolPrefix`) and descriptions are the only per-connector inputs.
184
+ */
185
+ export function registerEmailConnectorTools(opts: {
186
+ server: { tool: (...args: never) => unknown };
187
+ toolPrefix: string;
188
+ descriptions: EmailToolDescriptions;
189
+ client: EmailReadClient;
190
+ mailer: EmailSendMailer;
191
+ formatAddr: (a: { readonly name?: string; readonly address?: string }) => string;
192
+ /**
193
+ * The connector's write registrar, for `<prefix>_mail_send`.
194
+ *
195
+ * Passed IN rather than built here on purpose: `standaloneEligibility` reads a connector's own
196
+ * `server.ts`/`tools.ts` to decide whether its mutations are gated, and a registrar constructed
197
+ * inside this shared module would be invisible there — the connector would be refused despite
198
+ * being hardened. Requiring it also means a new email connector cannot forget it.
199
+ */
200
+ registerWriteTool: WriteToolRegistrar;
201
+ }): void {
202
+ const { server, toolPrefix, descriptions, client, mailer, formatAddr, registerWriteTool } = opts;
203
+ const registerSimpleTool = createRegisterSimpleTool(server);
204
+ const { listArgs, getArgs, searchArgs, sendArgs } = emailToolSchemas;
205
+
206
+ registerSimpleTool(
207
+ `${toolPrefix}_list`,
208
+ descriptions.list,
209
+ listArgs.shape,
210
+ async (args: unknown): Promise<McpListResult> => {
211
+ const parsed = listArgs.safeParse(args);
212
+ if (!parsed.success) {
213
+ throw new Error(parsed.error.message);
214
+ }
215
+ const o: { mailbox?: string; limit?: number } = {};
216
+ if (parsed.data.mailbox !== undefined) {
217
+ o.mailbox = parsed.data.mailbox;
218
+ }
219
+ o.limit = clampLimit(parsed.data.limit);
220
+ const items = await client.list(o);
221
+ return mcpJsonResult({ items: items.map((m) => viewEmailMessage(m, formatAddr)) });
222
+ },
223
+ );
224
+
225
+ registerSimpleTool(
226
+ `${toolPrefix}_get`,
227
+ descriptions.get,
228
+ getArgs.shape,
229
+ async (args: unknown): Promise<McpListResult> => {
230
+ const parsed = getArgs.safeParse(args);
231
+ if (!parsed.success) {
232
+ throw new Error(parsed.error.message);
233
+ }
234
+ const m = await client.get(parsed.data.uid, parsed.data.mailbox);
235
+ return mcpJsonResult(m === null ? { item: null } : { item: viewEmailMessage(m, formatAddr) });
236
+ },
237
+ );
238
+
239
+ registerSimpleTool(
240
+ `${toolPrefix}_search`,
241
+ descriptions.search,
242
+ searchArgs.shape,
243
+ async (args: unknown): Promise<McpListResult> => {
244
+ const parsed = searchArgs.safeParse(args);
245
+ if (!parsed.success) {
246
+ throw new Error(parsed.error.message);
247
+ }
248
+ const o: { query: string; mailbox?: string; limit?: number } = {
249
+ query: parsed.data.query,
250
+ limit: clampLimit(parsed.data.limit),
251
+ };
252
+ if (parsed.data.mailbox !== undefined) {
253
+ o.mailbox = parsed.data.mailbox;
254
+ }
255
+ const items = await client.search(o);
256
+ return mcpJsonResult({ matches: items.map((m) => viewEmailMessage(m, formatAddr)) });
257
+ },
258
+ );
259
+
260
+ registerWriteTool(
261
+ `${toolPrefix}_mail_send`,
262
+ {
263
+ mutates: `${toolPrefix}.mail.send`,
264
+ // A sent mail cannot be recalled and nothing remains to query, so the recipient and
265
+ // subject ARE the pre-state.
266
+ recoverable: false,
267
+ capturePreState: (p) => Promise.resolve({ to: p.to, subject: p.subject }),
268
+ scopeTargetOf: (p) => ({ kind: "recipient", value: p.to }),
269
+ },
270
+ descriptions.send,
271
+ sendArgs,
272
+ async (parsedData): Promise<McpListResult> => {
273
+ const parsed = { success: true as const, data: parsedData };
274
+ const input: { to: string; subject: string; body: string; cc?: string; bcc?: string } = {
275
+ to: parsed.data.to,
276
+ subject: parsed.data.subject,
277
+ body: parsed.data.body,
278
+ };
279
+ if (parsed.data.cc !== undefined && parsed.data.cc !== "") {
280
+ input.cc = parsed.data.cc;
281
+ }
282
+ if (parsed.data.bcc !== undefined && parsed.data.bcc !== "") {
283
+ input.bcc = parsed.data.bcc;
284
+ }
285
+ const res = await mailer.send(input);
286
+ return mcpJsonResult({
287
+ messageId: res.messageId,
288
+ accepted: res.accepted,
289
+ rejected: res.rejected,
290
+ });
291
+ },
292
+ );
293
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Join `path` onto `baseUrl`, passing an already-absolute URL straight through
3
+ * (pagination cursors commonly arrive absolute).
4
+ *
5
+ * The absolute test is the full `http://` / `https://` scheme, not a bare
6
+ * `http` prefix: a RELATIVE path that merely starts with those four letters —
7
+ * `httpbin/status`, or an `http-headers` endpoint — was returned unjoined,
8
+ * silently dropping the base URL and issuing the request against a relative
9
+ * path. Narrow enough that it would have surfaced in production rather than in
10
+ * review.
11
+ */
12
+ export function joinApiPath(baseUrl: string, path: string): string {
13
+ if (path.startsWith("http://") || path.startsWith("https://")) {
14
+ return path;
15
+ }
16
+ const suffix = path.startsWith("/") ? path : `/${path}`;
17
+ return `${baseUrl}${suffix}`;
18
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Minimal, robust converter from the SIMPLE markdown the tribal answer-synthesizer emits
3
+ * (plain paragraphs + `-` bulleted lists; no headers/tables/code/HTML) into Notion blocks and
4
+ * Confluence storage HTML, plus a Sources section built from citations. The body parser is a
5
+ * line-walker that NEVER throws on unexpected markup: any non-empty, non-bullet line becomes a
6
+ * paragraph (so a stray `#` or ``` line degrades to literal paragraph text, not an error).
7
+ */
8
+
9
+ export interface KbCitation {
10
+ itemId: string;
11
+ channelId: string;
12
+ url: string | null;
13
+ }
14
+
15
+ export type KbLine = { kind: "bullet" | "paragraph"; text: string };
16
+
17
+ /** Classify each non-blank line as a bullet (`- ` prefix) or a paragraph (everything else). */
18
+ export function parseSimpleMarkdown(md: string): KbLine[] {
19
+ const out: KbLine[] = [];
20
+ for (const raw of md.split("\n")) {
21
+ const line = raw.trim();
22
+ if (line === "") continue;
23
+ const bullet = /^[-*]\s+(\S.*)?$/.exec(line);
24
+ if (bullet === null) {
25
+ out.push({ kind: "paragraph", text: line });
26
+ } else {
27
+ out.push({ kind: "bullet", text: bullet[1] ?? "" });
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+
33
+ function escapeHtml(s: string): string {
34
+ return s
35
+ .replaceAll("&", "&amp;")
36
+ .replaceAll("<", "&lt;")
37
+ .replaceAll(">", "&gt;")
38
+ .replaceAll('"', "&quot;")
39
+ .replaceAll("'", "&#39;");
40
+ }
41
+
42
+ function citationLabel(c: KbCitation): string {
43
+ return `${c.channelId} · ${c.itemId}`;
44
+ }
45
+
46
+ function notionRichText(content: string, url?: string | null): Record<string, unknown>[] {
47
+ const text: Record<string, unknown> = { content: content.slice(0, 2000) };
48
+ if (url !== undefined && url !== null && url !== "") text["link"] = { url };
49
+ return [{ type: "text", text }];
50
+ }
51
+
52
+ /** Body → Notion blocks, then a "Sources:" paragraph + one bulleted link per citation. */
53
+ export function markdownToNotionBlocks(
54
+ md: string,
55
+ citations: readonly KbCitation[],
56
+ ): Record<string, unknown>[] {
57
+ const blocks: Record<string, unknown>[] = [];
58
+ for (const line of parseSimpleMarkdown(md)) {
59
+ if (line.kind === "bullet") {
60
+ blocks.push({
61
+ object: "block",
62
+ type: "bulleted_list_item",
63
+ bulleted_list_item: { rich_text: notionRichText(line.text) },
64
+ });
65
+ } else {
66
+ blocks.push({
67
+ object: "block",
68
+ type: "paragraph",
69
+ paragraph: { rich_text: notionRichText(line.text) },
70
+ });
71
+ }
72
+ }
73
+ if (citations.length > 0) {
74
+ blocks.push({
75
+ object: "block",
76
+ type: "paragraph",
77
+ paragraph: { rich_text: notionRichText("Sources:") },
78
+ });
79
+ for (const c of citations) {
80
+ blocks.push({
81
+ object: "block",
82
+ type: "bulleted_list_item",
83
+ bulleted_list_item: { rich_text: notionRichText(citationLabel(c), c.url) },
84
+ });
85
+ }
86
+ }
87
+ return blocks;
88
+ }
89
+
90
+ /** Body → Confluence storage HTML, then a Sources `<p>` + `<ul>` of citation links. */
91
+ export function markdownToConfluenceStorage(md: string, citations: readonly KbCitation[]): string {
92
+ const parts: string[] = [];
93
+ let bulletBuffer: string[] = [];
94
+ const flushBullets = (): void => {
95
+ if (bulletBuffer.length === 0) return;
96
+ const lis = bulletBuffer.map((b) => `<li>${b}</li>`).join("");
97
+ parts.push(`<ul>${lis}</ul>`);
98
+ bulletBuffer = [];
99
+ };
100
+ for (const line of parseSimpleMarkdown(md)) {
101
+ if (line.kind === "bullet") {
102
+ bulletBuffer.push(escapeHtml(line.text));
103
+ } else {
104
+ flushBullets();
105
+ parts.push(`<p>${escapeHtml(line.text)}</p>`);
106
+ }
107
+ }
108
+ flushBullets();
109
+ if (citations.length > 0) {
110
+ parts.push("<p>Sources:</p>");
111
+ const items = citations.map((c) => {
112
+ const label = escapeHtml(citationLabel(c));
113
+ return c.url !== null && c.url !== ""
114
+ ? `<li><a href="${escapeHtml(c.url)}">${label}</a></li>`
115
+ : `<li>${label}</li>`;
116
+ });
117
+ parts.push(`<ul>${items.join("")}</ul>`);
118
+ }
119
+ return parts.join("");
120
+ }
121
+
122
+ /** Parse + validate a citations JSON string into KbCitation[]; tolerant, never throws on shape. */
123
+ export function parseCitationsJson(raw: string): KbCitation[] {
124
+ let parsed: unknown;
125
+ try {
126
+ parsed = JSON.parse(raw) as unknown;
127
+ } catch {
128
+ return [];
129
+ }
130
+ if (!Array.isArray(parsed)) return [];
131
+ const out: KbCitation[] = [];
132
+ for (const v of parsed) {
133
+ if (v === null || typeof v !== "object") continue;
134
+ const rec = v as Record<string, unknown>;
135
+ const itemId = rec["itemId"];
136
+ const channelId = rec["channelId"];
137
+ const url = rec["url"];
138
+ if (typeof itemId !== "string" || typeof channelId !== "string") continue;
139
+ out.push({ itemId, channelId, url: typeof url === "string" ? url : null });
140
+ }
141
+ return out;
142
+ }
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { type McpListResult, mcpJsonResult, type ZodObjectSchema } from "./mcp-tool-kit.ts";
3
+ import type { SearchMatchOptions } from "./search-filter.ts";
4
+
5
+ /** A `makeQueryFilter(...)` result — the shape every connector search filter has. */
6
+ export type SearchFilter = (
7
+ rows: readonly unknown[],
8
+ opts: SearchMatchOptions,
9
+ ) => readonly unknown[];
10
+
11
+ /**
12
+ * Build the shared connector search input schema. `query` is always a non-empty
13
+ * string; `maxLimit` is the per-connector cap (varies: 100/200/500/50/1000/2000)
14
+ * and MUST be passed verbatim from each call site so behavior is unchanged.
15
+ */
16
+ export function searchToolInputSchema(maxLimit = 100): ZodObjectSchema<SearchMatchOptions> {
17
+ return z.object({
18
+ query: z.string().min(1),
19
+ limit: z.number().int().min(1).max(maxLimit).optional(),
20
+ });
21
+ }
22
+
23
+ /**
24
+ * Build the `{ matches }` envelope: filter the rows when they are an array, else
25
+ * return an empty match set. Verbatim equivalent of the per-connector tail
26
+ * `const matches = Array.isArray(X) ? filter(X, { query, limit }) : []; return jsonResult({ matches })`.
27
+ * `rows` stays `unknown` because external payloads are untyped at the boundary
28
+ * (Non-Negotiable #7).
29
+ */
30
+ export function matchesResult(
31
+ rows: unknown,
32
+ filter: SearchFilter,
33
+ opts: SearchMatchOptions,
34
+ ): McpListResult {
35
+ const matches = Array.isArray(rows) ? filter(rows, opts) : [];
36
+ return mcpJsonResult({ matches });
37
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * mcp-tool-kit — re-export of the MCP connector helpers now owned by
3
+ * `@nimbus-dev/sdk/connector-kit`.
4
+ *
5
+ * These helpers originated here and were upstreamed to the SDK in 1.11.0. The
6
+ * SDK is now the single owner; this module stays as the connector-facing import
7
+ * path so the ~99 connectors keep their existing relative imports.
8
+ *
9
+ * Named re-exports, deliberately not `export *`: a star would also re-export the
10
+ * sibling kits' symbols (fetch-bearer-json, rest-tool-kit) through this path,
11
+ * widening the surface each connector sees and making a two-module import
12
+ * ambiguous. Keep this list matched to the symbols this file used to define.
13
+ */
14
+
15
+ export type {
16
+ HttpJsonBodyResponse,
17
+ HttpTextResponse,
18
+ McpListResult,
19
+ RegisterSimpleToolFn,
20
+ ZodObjectSchema,
21
+ } from "@nimbus-dev/sdk/connector-kit";
22
+ export {
23
+ createRegisterSimpleTool,
24
+ createZodToolRegistrar,
25
+ encodeBasicAuthHeader,
26
+ fetchWithTimeout,
27
+ mcpJsonResult,
28
+ mcpJsonResultFromTextIfOk,
29
+ mcpJsonResultIfOk,
30
+ parseJsonTextIfOk,
31
+ putOptionalBoolean,
32
+ putOptionalNonEmptyString,
33
+ registerZodTool,
34
+ requireProcessEnv,
35
+ } from "@nimbus-dev/sdk/connector-kit";
@@ -0,0 +1,128 @@
1
+ import { spawn } from "node:child_process";
2
+
3
+ export type SpawnResult = { code: number; stdout: string; stderr: string };
4
+
5
+ /**
6
+ * Node implementation. Exported so it can be tested directly: the suite runs under Bun, so
7
+ * `nimbusSpawn` would otherwise never exercise this branch and it would ship unverified.
8
+ *
9
+ * Two Node APIs are deliberately NOT used, and the reasons are not obvious:
10
+ *
11
+ * - `spawnSync` blocks the event loop. In a stdio MCP server that must keep answering JSON-RPC —
12
+ * including an in-flight `elicitation/create` round-trip — a synchronous spawn deadlocks the
13
+ * consent gate against itself.
14
+ * - `execFile` caps captured output at a 1 MB `maxBuffer` by default and errors past it. The
15
+ * `Bun.spawn` path reads stdout uncapped, and `aws logs` / `gcloud logging` JSON routinely
16
+ * exceeds 1 MB, so `execFile` would be a silent-truncation regression dressed up as a
17
+ * portability fix.
18
+ *
19
+ * Output is accumulated as raw `Buffer`s and decoded ONCE at the end. Decoding each chunk with
20
+ * `chunk.toString("utf8")` corrupts any multi-byte character that straddles a chunk boundary, and
21
+ * chunk boundaries are a function of pipe timing — so it fails intermittently, on non-ASCII data,
22
+ * in production.
23
+ */
24
+ export function spawnViaNode(
25
+ command: readonly string[],
26
+ env: Record<string, string | undefined>,
27
+ ): Promise<SpawnResult> {
28
+ const [bin, ...args] = command;
29
+ if (bin === undefined) {
30
+ return Promise.resolve({ code: 1, stdout: "", stderr: "empty command" });
31
+ }
32
+ return new Promise((resolveP) => {
33
+ const child = spawn(bin, args, { env: { ...process.env, ...env } });
34
+ const outChunks: Buffer[] = [];
35
+ const errChunks: Buffer[] = [];
36
+ child.stdout.on("data", (c: Buffer) => {
37
+ outChunks.push(c);
38
+ });
39
+ child.stderr.on("data", (c: Buffer) => {
40
+ errChunks.push(c);
41
+ });
42
+ const decode = (): { stdout: string; stderr: string } => ({
43
+ stdout: Buffer.concat(outChunks).toString("utf8"),
44
+ stderr: Buffer.concat(errChunks).toString("utf8"),
45
+ });
46
+ child.on("error", (e: Error) => {
47
+ const d = decode();
48
+ resolveP({ code: 1, stdout: d.stdout, stderr: `${d.stderr}${e.message}` });
49
+ });
50
+ child.on("close", (code: number | null) => {
51
+ resolveP({ code: code ?? 1, ...decode() });
52
+ });
53
+ });
54
+ }
55
+
56
+ /** Bun implementation. Draining the whole stream decodes multi-byte characters correctly. */
57
+ export async function spawnViaBun(
58
+ command: readonly string[],
59
+ env: Record<string, string | undefined>,
60
+ ): Promise<SpawnResult> {
61
+ if (command.length === 0) {
62
+ return { code: 1, stdout: "", stderr: "empty command" };
63
+ }
64
+ // `Bun.spawn` THROWS synchronously when the binary does not exist, where `child_process` emits
65
+ // an "error" event instead. Both branches must honour the same contract — resolve with a
66
+ // non-zero code, never reject — or a caller's behaviour would depend on the runtime.
67
+ try {
68
+ const proc = Bun.spawn([...command], {
69
+ env: { ...process.env, ...env },
70
+ stdout: "pipe",
71
+ stderr: "pipe",
72
+ });
73
+ const code = await proc.exited;
74
+ const stdout = await new Response(proc.stdout).text();
75
+ const stderr = await new Response(proc.stderr).text();
76
+ return { code, stdout, stderr };
77
+ } catch (e) {
78
+ return { code: 1, stdout: "", stderr: e instanceof Error ? e.message : String(e) };
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Whether `Bun.spawn` is usable on the given global.
84
+ *
85
+ * Takes the global as a PARAMETER rather than reading `globalThis` directly, because Bun defines
86
+ * its global as non-writable AND non-configurable — it cannot be stubbed or redefined, so the
87
+ * "absent" side is otherwise unreachable from a suite that runs under Bun. That side is the one
88
+ * the standalone npx artifact always takes, so it must be exercisable.
89
+ */
90
+ export function detectBunSpawn(g: unknown = globalThis): boolean {
91
+ const bun = (g as { Bun?: { spawn?: unknown } }).Bun;
92
+ return typeof bun?.spawn === "function";
93
+ }
94
+
95
+ /** The shape both implementations share, and what `selectSpawnImpl` hands back. */
96
+ export type SpawnImpl = (
97
+ command: readonly string[],
98
+ env: Record<string, string | undefined>,
99
+ ) => Promise<SpawnResult>;
100
+
101
+ /**
102
+ * Which implementation a given global calls for.
103
+ *
104
+ * Returning the FUNCTION rather than taking a `useBun` flag is deliberate: a boolean selector
105
+ * argument makes one entry point mean two things, and the two implementations are already exported
106
+ * as the two things it selected between. Injecting the global — for the same
107
+ * non-writable/non-configurable reason `detectBunSpawn` takes one — is what keeps the Node side
108
+ * reachable from a suite that runs under Bun.
109
+ */
110
+ export function selectSpawnImpl(g: unknown = globalThis): SpawnImpl {
111
+ return detectBunSpawn(g) ? spawnViaBun : spawnViaNode;
112
+ }
113
+
114
+ /**
115
+ * Spawn a CLI and collect its output, on Bun or Node.
116
+ *
117
+ * Detection happens at CALL time, not module load, so a test that swaps `Bun.spawn` is still
118
+ * honoured. That is not incidental: `cloudwatch/test/tools.test.ts` stubs `Bun.spawn` globally, and
119
+ * routing unconditionally through Node made it spawn a real `aws` and hang the suite.
120
+ *
121
+ * Never rejects: a spawn failure resolves with a non-zero code, matching the previous behaviour.
122
+ */
123
+ export function nimbusSpawn(
124
+ command: readonly string[],
125
+ env: Record<string, string | undefined>,
126
+ ): Promise<SpawnResult> {
127
+ return selectSpawnImpl()(command, env);
128
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * rest-tool-kit — re-export of the shared REST fetch wrapper for Bearer-auth
3
+ * connectors, now owned by `@nimbus-dev/sdk/connector-kit`.
4
+ *
5
+ * These helpers originated here and were upstreamed to the SDK in 1.11.0. The
6
+ * SDK is now the single owner; this module stays as the connector-facing import
7
+ * path so the ~99 connectors keep their existing relative imports.
8
+ *
9
+ * Named re-exports, deliberately not `export *` — see the note in
10
+ * `mcp-tool-kit.ts`.
11
+ */
12
+
13
+ export type {
14
+ RestFetcherConfig,
15
+ RestFetchResult,
16
+ RestToolRegistrar,
17
+ } from "@nimbus-dev/sdk/connector-kit";
18
+ export { makeRestFetcher, makeRestToolRegistrar } from "@nimbus-dev/sdk/connector-kit";